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)
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. */
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>. */
33 #include <sys/types.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 ();
55 # define endpwent() ((void) 0)
58 char *parse_user_spec ();
59 void strip_trailing_slashes ();
66 CH_NO_CHANGE_REQUESTED
71 /* Print a message for each file that is processed. */
74 /* Print a message for each file whose attributes we change. */
77 /* Do not be verbose. This is the default. */
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. */
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. */
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. */
114 REFERENCE_FILE_OPTION
= CHAR_MAX
+ 1,
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
},
133 /* Tell the user how/if the user and group of FILE have been changed.
134 CHANGED describes what (if anything) has happened. */
137 describe_change (const char *file
, enum Change_status changed
)
141 if (changed
== CH_NOT_APPLIED
)
143 printf (_("neither symbolic link %s nor referent has been changed\n"),
151 fmt
= _("owner of %s changed to ");
154 fmt
= _("failed to change owner of %s to ");
156 case CH_NO_CHANGE_REQUESTED
:
157 fmt
= _("owner of %s retained as ");
164 printf ("%s.%s\n", username
, groupname
);
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. */
174 change_file_owner (int cmdline_arg
, const char *file
, uid_t user
, gid_t group
)
176 struct stat file_stats
;
181 if (lstat (file
, &file_stats
))
183 if (force_silent
== 0)
184 error (0, errno
, "%s", file
);
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
)
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
)
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
);
221 if (force_silent
== 0)
222 error (0, errno
, "%s", file
);
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
);
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. */
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'. */
253 name_space
= savedir (dir
, statp
->st_size
);
254 if (name_space
== NULL
)
258 if (force_silent
== 0)
259 error (0, errno
, "%s", dir
);
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
);
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
);
293 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
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
);
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\
323 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
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. */
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)
350 case REFERENCE_FILE_OPTION
:
351 reference_file
= optarg
;
353 case DEREFERENCE_OPTION
:
360 verbosity
= V_changes_only
;
371 case_GETOPT_HELP_CHAR
;
372 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
378 if (argc
- optind
+ (reference_file
? 1 : 0) <= 1)
380 error (0, 0, _("too few arguments"));
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
;
396 const char *e
= parse_user_spec (argv
[optind
], &user
, &group
,
397 &username
, &groupname
);
399 error (1, 0, "%s: %s", argv
[optind
], e
);
400 if (username
== NULL
)
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
)