*** empty log message ***
[coreutils.git] / src / chown.c
bloba91e886c8d0ac80204352a2d4851d288454f8cea
1 /* chown -- change user and group ownership of files
2 Copyright (C) 89, 90, 91, 1995-1999 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 | user
20 | unchanged explicit
21 -------------|-------------------------+-------------------------|
22 g unchanged | --- | chown u |
23 r |-------------------------+-------------------------|
24 o explicit | chgrp g or chown .g | chown u.g |
25 u |-------------------------+-------------------------|
26 p from passwd| --- | chown u. |
27 |-------------------------+-------------------------|
29 Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
31 #include <config.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <getopt.h>
38 #include "system.h"
39 #include "error.h"
40 #include "lchown.h"
41 #include "savedir.h"
43 /* The official name of this program (e.g., no `g' prefix). */
44 #define PROGRAM_NAME "chown"
46 #define AUTHORS "David MacKenzie"
48 #ifndef _POSIX_VERSION
49 struct passwd *getpwnam ();
50 struct group *getgrnam ();
51 struct group *getgrgid ();
52 #endif
54 #if ! HAVE_ENDPWENT
55 # define endpwent() ((void) 0)
56 #endif
58 char *parse_user_spec ();
59 void strip_trailing_slashes ();
61 enum Change_status
63 CH_NOT_APPLIED,
64 CH_SUCCEEDED,
65 CH_FAILED,
66 CH_NO_CHANGE_REQUESTED
69 enum Verbosity
71 /* Print a message for each file that is processed. */
72 V_high,
74 /* Print a message for each file whose attributes we change. */
75 V_changes_only,
77 /* Do not be verbose. This is the default. */
78 V_off
81 static int change_dir_owner PARAMS ((const char *dir, uid_t user, gid_t group,
82 struct stat *statp));
84 /* The name the program was run with. */
85 char *program_name;
87 /* If nonzero, and the systems has support for it, change the ownership
88 of symbolic links rather than any files they point to. */
89 static int change_symlinks = 1;
91 /* If nonzero, change the ownership of directories recursively. */
92 static int recurse;
94 /* If nonzero, force silence (no error messages). */
95 static int force_silent;
97 /* Level of verbosity. */
98 static enum Verbosity verbosity = V_off;
100 /* The name of the user to which ownership of the files is being given. */
101 static char *username;
103 /* The name of the group to which ownership of the files is being given. */
104 static char *groupname;
106 /* The argument to the --reference option. Use the owner and group IDs
107 of this file. This file must exist. */
108 static char *reference_file;
110 static struct option const long_options[] =
112 {"recursive", no_argument, 0, 'R'},
113 {"changes", no_argument, 0, 'c'},
114 {"dereference", no_argument, 0, CHAR_MAX + 2},
115 {"no-dereference", no_argument, 0, 'h'},
116 {"quiet", no_argument, 0, 'f'},
117 {"silent", no_argument, 0, 'f'},
118 {"reference", required_argument, 0, CHAR_MAX + 1},
119 {"verbose", no_argument, 0, 'v'},
120 {GETOPT_HELP_OPTION_DECL},
121 {GETOPT_VERSION_OPTION_DECL},
122 {0, 0, 0, 0}
125 /* Tell the user how/if the user and group of FILE have been changed.
126 CHANGED describes what (if anything) has happened. */
128 static void
129 describe_change (const char *file, enum Change_status changed)
131 const char *fmt;
133 if (changed == CH_NOT_APPLIED)
135 printf (_("neither symbolic link %s nor referent has been changed\n"),
136 file);
137 return;
140 switch (changed)
142 case CH_SUCCEEDED:
143 fmt = _("owner of %s changed to ");
144 break;
145 case CH_FAILED:
146 fmt = _("failed to change owner of %s to ");
147 break;
148 case CH_NO_CHANGE_REQUESTED:
149 fmt = _("owner of %s retained as ");
150 break;
151 default:
152 abort ();
154 printf (fmt, file);
155 if (groupname)
156 printf ("%s.%s\n", username, groupname);
157 else
158 printf ("%s\n", username);
161 /* Change the ownership of FILE to UID USER and GID GROUP.
162 If it is a directory and -R is given, recurse.
163 Return 0 if successful, 1 if errors occurred. */
165 static int
166 change_file_owner (int cmdline_arg, const char *file, uid_t user, gid_t group)
168 struct stat file_stats;
169 uid_t newuser;
170 gid_t newgroup;
171 int errors = 0;
173 if (lstat (file, &file_stats))
175 if (force_silent == 0)
176 error (0, errno, "%s", file);
177 return 1;
180 newuser = user == (uid_t) -1 ? file_stats.st_uid : user;
181 newgroup = group == (gid_t) -1 ? file_stats.st_gid : group;
182 if (newuser != file_stats.st_uid || newgroup != file_stats.st_gid)
184 int fail;
185 int symlink_changed = 1;
187 if (S_ISLNK (file_stats.st_mode) && change_symlinks)
189 fail = lchown (file, newuser, newgroup);
191 /* Ignore the failure if it's due to lack of support (ENOSYS)
192 and this is not a command line argument. */
193 if (!cmdline_arg && fail && errno == ENOSYS)
195 fail = 0;
196 symlink_changed = 0;
199 else
201 fail = chown (file, newuser, newgroup);
204 if (verbosity == V_high || (verbosity == V_changes_only && !fail))
206 enum Change_status ch_status = (! symlink_changed ? CH_NOT_APPLIED
207 : (fail ? CH_FAILED : CH_SUCCEEDED));
208 describe_change (file, ch_status);
211 if (fail)
213 if (force_silent == 0)
214 error (0, errno, "%s", file);
215 errors = 1;
218 else if (verbosity == V_high)
220 describe_change (file, CH_NO_CHANGE_REQUESTED);
223 if (recurse && S_ISDIR (file_stats.st_mode))
224 errors |= change_dir_owner (file, user, group, &file_stats);
225 return errors;
228 /* Recursively change the ownership of the files in directory DIR
229 to UID USER and GID GROUP.
230 STATP points to the results of lstat on DIR.
231 Return 0 if successful, 1 if errors occurred. */
233 static int
234 change_dir_owner (const char *dir, uid_t user, gid_t group, struct stat *statp)
236 char *name_space, *namep;
237 char *path; /* Full path of each entry to process. */
238 unsigned dirlength; /* Length of `dir' and '\0'. */
239 unsigned filelength; /* Length of each pathname to process. */
240 unsigned pathlength; /* Bytes allocated for `path'. */
241 int errors = 0;
243 errno = 0;
244 name_space = savedir (dir, (unsigned int) statp->st_size);
245 if (name_space == NULL)
247 if (errno)
249 if (force_silent == 0)
250 error (0, errno, "%s", dir);
251 return 1;
253 else
254 error (1, 0, _("virtual memory exhausted"));
257 dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
258 pathlength = dirlength + 1;
259 /* Give `path' a dummy value; it will be reallocated before first use. */
260 path = xmalloc (pathlength);
261 strcpy (path, dir);
262 path[dirlength - 1] = '/';
264 for (namep = name_space; *namep; namep += filelength - dirlength)
266 filelength = dirlength + strlen (namep) + 1;
267 if (filelength > pathlength)
269 pathlength = filelength * 2;
270 path = xrealloc (path, pathlength);
272 strcpy (path + dirlength, namep);
273 errors |= change_file_owner (0, path, user, group);
275 free (path);
276 free (name_space);
277 return errors;
280 void
281 usage (int status)
283 if (status != 0)
284 fprintf (stderr, _("Try `%s --help' for more information.\n"),
285 program_name);
286 else
288 printf (_("\
289 Usage: %s [OPTION]... OWNER[.[GROUP]] FILE...\n\
290 or: %s [OPTION]... .GROUP FILE...\n\
291 or: %s [OPTION]... --reference=RFILE FILE...\n\
293 program_name, program_name, program_name);
294 printf (_("\
295 Change the owner and/or group of each FILE to OWNER and/or GROUP.\n\
297 -c, --changes be verbose whenever change occurs\n\
298 --dereference affect the referent of each symbolic link, rather\n\
299 than the symbolic link itself\n\
300 -h, --no-dereference affect symbolic links instead of any referenced file\n\
301 (available only on systems that can change the\n\
302 ownership of a symlink)\n\
303 -f, --silent, --quiet suppress most error messages\n\
304 --reference=RFILE use the owner and group of RFILE instead of using\n\
305 explicit OWNER.GROUP values\n\
306 -R, --recursive operate on files and directories recursively\n\
307 -v, --verbose explain what is being done\n\
308 --help display this help and exit\n\
309 --version output version information and exit\n\
311 Owner is unchanged if missing. Group is unchanged if missing, but changed\n\
312 to login group if implied by a period. A colon may replace the period.\n\
313 "));
314 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
315 close_stdout ();
317 exit (status);
321 main (int argc, char **argv)
323 uid_t user = (uid_t) -1; /* New uid; -1 if not to be changed. */
324 gid_t group = (uid_t) -1; /* New gid; -1 if not to be changed. */
325 int errors = 0;
326 int optc;
328 program_name = argv[0];
329 setlocale (LC_ALL, "");
330 bindtextdomain (PACKAGE, LOCALEDIR);
331 textdomain (PACKAGE);
333 recurse = force_silent = 0;
335 while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1)
337 switch (optc)
339 case 0:
340 break;
341 case CHAR_MAX + 1:
342 reference_file = optarg;
343 break;
344 case CHAR_MAX + 2:
345 change_symlinks = 0;
346 break;
347 case 'R':
348 recurse = 1;
349 break;
350 case 'c':
351 verbosity = V_changes_only;
352 break;
353 case 'f':
354 force_silent = 1;
355 break;
356 case 'h':
357 change_symlinks = 1;
358 break;
359 case 'v':
360 verbosity = V_high;
361 break;
362 case_GETOPT_HELP_CHAR;
363 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
364 default:
365 usage (1);
369 if (argc - optind + (reference_file ? 1 : 0) <= 1)
371 error (0, 0, _("too few arguments"));
372 usage (1);
375 if (reference_file)
377 struct stat ref_stats;
379 if (stat (reference_file, &ref_stats))
380 error (1, errno, "%s", reference_file);
382 user = ref_stats.st_uid;
383 group = ref_stats.st_gid;
385 else
387 const char *e = parse_user_spec (argv[optind], &user, &group,
388 &username, &groupname);
389 if (e)
390 error (1, 0, "%s: %s", argv[optind], e);
391 if (username == NULL)
392 username = "";
394 optind++;
397 for (; optind < argc; ++optind)
399 strip_trailing_slashes (argv[optind]);
400 errors |= change_file_owner (1, argv[optind], user, group);
403 if (verbosity != V_off)
404 close_stdout ();
405 exit (errors);