(noinst_HEADERS): Add nanosleep.h.
[coreutils.git] / src / chown.c
blob9fc30552aa786e36f0cb661c74d251d032a8dce4
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 const 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 const 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 /* For long options that have no equivalent short option, use a
111 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
112 enum
114 REFERENCE_FILE_OPTION = CHAR_MAX + 1,
115 DEREFERENCE_OPTION
118 static struct option const long_options[] =
120 {"recursive", no_argument, 0, 'R'},
121 {"changes", no_argument, 0, 'c'},
122 {"dereference", no_argument, 0, DEREFERENCE_OPTION},
123 {"no-dereference", no_argument, 0, 'h'},
124 {"quiet", no_argument, 0, 'f'},
125 {"silent", no_argument, 0, 'f'},
126 {"reference", required_argument, 0, REFERENCE_FILE_OPTION},
127 {"verbose", no_argument, 0, 'v'},
128 {GETOPT_HELP_OPTION_DECL},
129 {GETOPT_VERSION_OPTION_DECL},
130 {0, 0, 0, 0}
133 /* Tell the user how/if the user and group of FILE have been changed.
134 CHANGED describes what (if anything) has happened. */
136 static void
137 describe_change (const char *file, enum Change_status changed)
139 const char *fmt;
141 if (changed == CH_NOT_APPLIED)
143 printf (_("neither symbolic link %s nor referent has been changed\n"),
144 file);
145 return;
148 switch (changed)
150 case CH_SUCCEEDED:
151 fmt = _("owner of %s changed to ");
152 break;
153 case CH_FAILED:
154 fmt = _("failed to change owner of %s to ");
155 break;
156 case CH_NO_CHANGE_REQUESTED:
157 fmt = _("owner of %s retained as ");
158 break;
159 default:
160 abort ();
162 printf (fmt, file);
163 if (groupname)
164 printf ("%s.%s\n", username, groupname);
165 else
166 printf ("%s\n", username);
169 /* Change the ownership of FILE to UID USER and GID GROUP.
170 If it is a directory and -R is given, recurse.
171 Return 0 if successful, 1 if errors occurred. */
173 static int
174 change_file_owner (int cmdline_arg, const char *file, uid_t user, gid_t group)
176 struct stat file_stats;
177 uid_t newuser;
178 gid_t newgroup;
179 int errors = 0;
181 if (lstat (file, &file_stats))
183 if (force_silent == 0)
184 error (0, errno, "%s", file);
185 return 1;
188 newuser = user == (uid_t) -1 ? file_stats.st_uid : user;
189 newgroup = group == (gid_t) -1 ? file_stats.st_gid : group;
190 if (newuser != file_stats.st_uid || newgroup != file_stats.st_gid)
192 int fail;
193 int symlink_changed = 1;
195 if (S_ISLNK (file_stats.st_mode) && change_symlinks)
197 fail = lchown (file, newuser, newgroup);
199 /* Ignore the failure if it's due to lack of support (ENOSYS)
200 and this is not a command line argument. */
201 if (!cmdline_arg && fail && errno == ENOSYS)
203 fail = 0;
204 symlink_changed = 0;
207 else
209 fail = chown (file, newuser, newgroup);
212 if (verbosity == V_high || (verbosity == V_changes_only && !fail))
214 enum Change_status ch_status = (! symlink_changed ? CH_NOT_APPLIED
215 : (fail ? CH_FAILED : CH_SUCCEEDED));
216 describe_change (file, ch_status);
219 if (fail)
221 if (force_silent == 0)
222 error (0, errno, "%s", file);
223 errors = 1;
226 else if (verbosity == V_high)
228 describe_change (file, CH_NO_CHANGE_REQUESTED);
231 if (recurse && S_ISDIR (file_stats.st_mode))
232 errors |= change_dir_owner (file, user, group, &file_stats);
233 return errors;
236 /* Recursively change the ownership of the files in directory DIR
237 to UID USER and GID GROUP.
238 STATP points to the results of lstat on DIR.
239 Return 0 if successful, 1 if errors occurred. */
241 static int
242 change_dir_owner (const char *dir, uid_t user, gid_t group,
243 const struct stat *statp)
245 char *name_space, *namep;
246 char *path; /* Full path of each entry to process. */
247 unsigned dirlength; /* Length of `dir' and '\0'. */
248 unsigned filelength; /* Length of each pathname to process. */
249 unsigned pathlength; /* Bytes allocated for `path'. */
250 int errors = 0;
252 errno = 0;
253 name_space = savedir (dir, statp->st_size);
254 if (name_space == NULL)
256 if (errno)
258 if (force_silent == 0)
259 error (0, errno, "%s", dir);
260 return 1;
262 else
263 error (1, 0, _("virtual memory exhausted"));
266 dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
267 pathlength = dirlength + 1;
268 /* Give `path' a dummy value; it will be reallocated before first use. */
269 path = xmalloc (pathlength);
270 strcpy (path, dir);
271 path[dirlength - 1] = '/';
273 for (namep = name_space; *namep; namep += filelength - dirlength)
275 filelength = dirlength + strlen (namep) + 1;
276 if (filelength > pathlength)
278 pathlength = filelength * 2;
279 path = xrealloc (path, pathlength);
281 strcpy (path + dirlength, namep);
282 errors |= change_file_owner (0, path, user, group);
284 free (path);
285 free (name_space);
286 return errors;
289 void
290 usage (int status)
292 if (status != 0)
293 fprintf (stderr, _("Try `%s --help' for more information.\n"),
294 program_name);
295 else
297 printf (_("\
298 Usage: %s [OPTION]... OWNER[.[GROUP]] FILE...\n\
299 or: %s [OPTION]... .GROUP FILE...\n\
300 or: %s [OPTION]... --reference=RFILE FILE...\n\
302 program_name, program_name, program_name);
303 printf (_("\
304 Change the owner and/or group of each FILE to OWNER and/or GROUP.\n\
306 -c, --changes like verbose but report only when a change is made\n\
307 --dereference affect the referent of each symbolic link, rather\n\
308 than the symbolic link itself\n\
309 -h, --no-dereference affect symbolic links instead of any referenced file\n\
310 (available only on systems that can change the\n\
311 ownership of a symlink)\n\
312 -f, --silent, --quiet suppress most error messages\n\
313 --reference=RFILE use RFILE's owner and group rather than\n\
314 the specified OWNER.GROUP values\n\
315 -R, --recursive operate on files and directories recursively\n\
316 -v, --verbose output a diagnostic for every file processed\n\
317 --help display this help and exit\n\
318 --version output version information and exit\n\
320 Owner is unchanged if missing. Group is unchanged if missing, but changed\n\
321 to login group if implied by a period. A colon may replace the period.\n\
322 "));
323 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
324 close_stdout ();
326 exit (status);
330 main (int argc, char **argv)
332 uid_t user = (uid_t) -1; /* New uid; -1 if not to be changed. */
333 gid_t group = (uid_t) -1; /* New gid; -1 if not to be changed. */
334 int errors = 0;
335 int optc;
337 program_name = argv[0];
338 setlocale (LC_ALL, "");
339 bindtextdomain (PACKAGE, LOCALEDIR);
340 textdomain (PACKAGE);
342 recurse = force_silent = 0;
344 while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1)
346 switch (optc)
348 case 0:
349 break;
350 case REFERENCE_FILE_OPTION:
351 reference_file = optarg;
352 break;
353 case DEREFERENCE_OPTION:
354 change_symlinks = 0;
355 break;
356 case 'R':
357 recurse = 1;
358 break;
359 case 'c':
360 verbosity = V_changes_only;
361 break;
362 case 'f':
363 force_silent = 1;
364 break;
365 case 'h':
366 change_symlinks = 1;
367 break;
368 case 'v':
369 verbosity = V_high;
370 break;
371 case_GETOPT_HELP_CHAR;
372 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
373 default:
374 usage (1);
378 if (argc - optind + (reference_file ? 1 : 0) <= 1)
380 error (0, 0, _("too few arguments"));
381 usage (1);
384 if (reference_file)
386 struct stat ref_stats;
388 if (stat (reference_file, &ref_stats))
389 error (1, errno, "%s", reference_file);
391 user = ref_stats.st_uid;
392 group = ref_stats.st_gid;
394 else
396 const char *e = parse_user_spec (argv[optind], &user, &group,
397 &username, &groupname);
398 if (e)
399 error (1, 0, "%s: %s", argv[optind], e);
400 if (username == NULL)
401 username = "";
403 optind++;
406 for (; optind < argc; ++optind)
408 strip_trailing_slashes (argv[optind]);
409 errors |= change_file_owner (1, argv[optind], user, group);
412 if (verbosity != V_off)
413 close_stdout ();
414 exit (errors);