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
,
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 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
},
125 /* Tell the user how/if the user and group of FILE have been changed.
126 CHANGED describes what (if anything) has happened. */
129 describe_change (const char *file
, enum Change_status changed
)
133 if (changed
== CH_NOT_APPLIED
)
135 printf (_("neither symbolic link %s nor referent has been changed\n"),
143 fmt
= _("owner of %s changed to ");
146 fmt
= _("failed to change owner of %s to ");
148 case CH_NO_CHANGE_REQUESTED
:
149 fmt
= _("owner of %s retained as ");
156 printf ("%s.%s\n", username
, groupname
);
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. */
166 change_file_owner (int cmdline_arg
, const char *file
, uid_t user
, gid_t group
)
168 struct stat file_stats
;
173 if (lstat (file
, &file_stats
))
175 if (force_silent
== 0)
176 error (0, errno
, "%s", file
);
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
)
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
)
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
);
213 if (force_silent
== 0)
214 error (0, errno
, "%s", file
);
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
);
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. */
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'. */
244 name_space
= savedir (dir
, (unsigned int) statp
->st_size
);
245 if (name_space
== NULL
)
249 if (force_silent
== 0)
250 error (0, errno
, "%s", dir
);
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
);
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
);
284 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
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
);
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\
314 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
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. */
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)
342 reference_file
= optarg
;
351 verbosity
= V_changes_only
;
362 case_GETOPT_HELP_CHAR
;
363 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
369 if (argc
- optind
+ (reference_file
? 1 : 0) <= 1)
371 error (0, 0, _("too few arguments"));
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
;
387 const char *e
= parse_user_spec (argv
[optind
], &user
, &group
,
388 &username
, &groupname
);
390 error (1, 0, "%s: %s", argv
[optind
], e
);
391 if (username
== NULL
)
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
)