Line data Source code
1 : /* chown -- change user and group ownership of files
2 : Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation, Inc.
3 :
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 3 of the License, or
7 : (at your option) any later version.
8 :
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.
13 :
14 : You should have received a copy of the GNU General Public License
15 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
16 :
17 : /*
18 : | user
19 : | unchanged explicit
20 : -------------|-------------------------+-------------------------|
21 : g unchanged | --- | chown u |
22 : r |-------------------------+-------------------------|
23 : o explicit | chgrp g or chown .g | chown u.g |
24 : u |-------------------------+-------------------------|
25 : p from passwd| --- | chown u. |
26 : |-------------------------+-------------------------|
27 :
28 : Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
29 :
30 : #include <config.h>
31 : #include <stdio.h>
32 : #include <sys/types.h>
33 : #include <getopt.h>
34 :
35 : #include "system.h"
36 : #include "chown-core.h"
37 : #include "error.h"
38 : #include "fts_.h"
39 : #include "quote.h"
40 : #include "root-dev-ino.h"
41 : #include "userspec.h"
42 :
43 : /* The official name of this program (e.g., no `g' prefix). */
44 : #define PROGRAM_NAME "chown"
45 :
46 : #define AUTHORS "David MacKenzie", "Jim Meyering"
47 :
48 : /* The name the program was run with. */
49 : char *program_name;
50 :
51 : /* The argument to the --reference option. Use the owner and group IDs
52 : of this file. This file must exist. */
53 : static char *reference_file;
54 :
55 : /* For long options that have no equivalent short option, use a
56 : non-character as a pseudo short option, starting with CHAR_MAX + 1. */
57 : enum
58 : {
59 : DEREFERENCE_OPTION = CHAR_MAX + 1,
60 : FROM_OPTION,
61 : NO_PRESERVE_ROOT,
62 : PRESERVE_ROOT,
63 : REFERENCE_FILE_OPTION
64 : };
65 :
66 : static struct option const long_options[] =
67 : {
68 : {"recursive", no_argument, NULL, 'R'},
69 : {"changes", no_argument, NULL, 'c'},
70 : {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
71 : {"from", required_argument, NULL, FROM_OPTION},
72 : {"no-dereference", no_argument, NULL, 'h'},
73 : {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
74 : {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
75 : {"quiet", no_argument, NULL, 'f'},
76 : {"silent", no_argument, NULL, 'f'},
77 : {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
78 : {"verbose", no_argument, NULL, 'v'},
79 : {GETOPT_HELP_OPTION_DECL},
80 : {GETOPT_VERSION_OPTION_DECL},
81 : {NULL, 0, NULL, 0}
82 : };
83 :
84 : void
85 42 : usage (int status)
86 : {
87 42 : if (status != EXIT_SUCCESS)
88 41 : fprintf (stderr, _("Try `%s --help' for more information.\n"),
89 : program_name);
90 : else
91 : {
92 1 : printf (_("\
93 : Usage: %s [OPTION]... [OWNER][:[GROUP]] FILE...\n\
94 : or: %s [OPTION]... --reference=RFILE FILE...\n\
95 : "),
96 : program_name, program_name);
97 1 : fputs (_("\
98 : Change the owner and/or group of each FILE to OWNER and/or GROUP.\n\
99 : With --reference, change the owner and group of each FILE to those of RFILE.\n\
100 : \n\
101 : -c, --changes like verbose but report only when a change is made\n\
102 : --dereference affect the referent of each symbolic link (this is\n\
103 : the default), rather than the symbolic link itself\n\
104 : "), stdout);
105 1 : fputs (_("\
106 : -h, --no-dereference affect each symbolic link instead of any referenced\n\
107 : file (useful only on systems that can change the\n\
108 : ownership of a symlink)\n\
109 : "), stdout);
110 1 : fputs (_("\
111 : --from=CURRENT_OWNER:CURRENT_GROUP\n\
112 : change the owner and/or group of each file only if\n\
113 : its current owner and/or group match those specified\n\
114 : here. Either may be omitted, in which case a match\n\
115 : is not required for the omitted attribute.\n\
116 : "), stdout);
117 1 : fputs (_("\
118 : --no-preserve-root do not treat `/' specially (the default)\n\
119 : --preserve-root fail to operate recursively on `/'\n\
120 : "), stdout);
121 1 : fputs (_("\
122 : -f, --silent, --quiet suppress most error messages\n\
123 : --reference=RFILE use RFILE's owner and group rather than\n\
124 : specifying OWNER:GROUP values\n\
125 : -R, --recursive operate on files and directories recursively\n\
126 : -v, --verbose output a diagnostic for every file processed\n\
127 : \n\
128 : "), stdout);
129 1 : fputs (_("\
130 : The following options modify how a hierarchy is traversed when the -R\n\
131 : option is also specified. If more than one is specified, only the final\n\
132 : one takes effect.\n\
133 : \n\
134 : -H if a command line argument is a symbolic link\n\
135 : to a directory, traverse it\n\
136 : -L traverse every symbolic link to a directory\n\
137 : encountered\n\
138 : -P do not traverse any symbolic links (default)\n\
139 : \n\
140 : "), stdout);
141 1 : fputs (HELP_OPTION_DESCRIPTION, stdout);
142 1 : fputs (VERSION_OPTION_DESCRIPTION, stdout);
143 1 : fputs (_("\
144 : \n\
145 : Owner is unchanged if missing. Group is unchanged if missing, but changed\n\
146 : to login group if implied by a `:' following a symbolic OWNER.\n\
147 : OWNER and GROUP may be numeric as well as symbolic.\n\
148 : "), stdout);
149 1 : printf (_("\
150 : \n\
151 : Examples:\n\
152 : %s root /u Change the owner of /u to \"root\".\n\
153 : %s root:staff /u Likewise, but also change its group to \"staff\".\n\
154 : %s -hR root /u Change the owner of /u and subfiles to \"root\".\n\
155 : "),
156 : program_name, program_name, program_name);
157 1 : emit_bug_reporting_address ();
158 : }
159 42 : exit (status);
160 : }
161 :
162 : int
163 85 : main (int argc, char **argv)
164 : {
165 85 : bool preserve_root = false;
166 :
167 85 : uid_t uid = -1; /* Specified uid; -1 if not to be changed. */
168 85 : gid_t gid = -1; /* Specified gid; -1 if not to be changed. */
169 :
170 : /* Change the owner (group) of a file only if it has this uid (gid).
171 : -1 means there's no restriction. */
172 85 : uid_t required_uid = -1;
173 85 : gid_t required_gid = -1;
174 :
175 : /* Bit flags that control how fts works. */
176 85 : int bit_flags = FTS_PHYSICAL;
177 :
178 : /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
179 : specified. */
180 85 : int dereference = -1;
181 :
182 : struct Chown_option chopt;
183 : bool ok;
184 : int optc;
185 :
186 : initialize_main (&argc, &argv);
187 85 : program_name = argv[0];
188 85 : setlocale (LC_ALL, "");
189 : bindtextdomain (PACKAGE, LOCALEDIR);
190 : textdomain (PACKAGE);
191 :
192 85 : atexit (close_stdout);
193 :
194 85 : chopt_init (&chopt);
195 :
196 199 : while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, NULL))
197 : != -1)
198 : {
199 39 : switch (optc)
200 : {
201 6 : case 'H': /* Traverse command-line symlinks-to-directories. */
202 6 : bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
203 6 : break;
204 :
205 4 : case 'L': /* Traverse all symlinks-to-directories. */
206 4 : bit_flags = FTS_LOGICAL;
207 4 : break;
208 :
209 1 : case 'P': /* Traverse no symlinks-to-directories. */
210 1 : bit_flags = FTS_PHYSICAL;
211 1 : break;
212 :
213 1 : case 'h': /* --no-dereference: affect symlinks */
214 1 : dereference = 0;
215 1 : break;
216 :
217 1 : case DEREFERENCE_OPTION: /* --dereference: affect the referent
218 : of each symlink */
219 1 : dereference = 1;
220 1 : break;
221 :
222 0 : case NO_PRESERVE_ROOT:
223 0 : preserve_root = false;
224 0 : break;
225 :
226 1 : case PRESERVE_ROOT:
227 1 : preserve_root = true;
228 1 : break;
229 :
230 2 : case REFERENCE_FILE_OPTION:
231 2 : reference_file = optarg;
232 2 : break;
233 :
234 3 : case FROM_OPTION:
235 : {
236 : char *u_dummy, *g_dummy;
237 3 : const char *e = parse_user_spec (optarg,
238 : &required_uid, &required_gid,
239 : &u_dummy, &g_dummy);
240 3 : if (e)
241 1 : error (EXIT_FAILURE, 0, "%s: %s", e, quote (optarg));
242 2 : break;
243 : }
244 :
245 5 : case 'R':
246 5 : chopt.recurse = true;
247 5 : break;
248 :
249 3 : case 'c':
250 3 : chopt.verbosity = V_changes_only;
251 3 : break;
252 :
253 1 : case 'f':
254 1 : chopt.force_silent = true;
255 1 : break;
256 :
257 2 : case 'v':
258 2 : chopt.verbosity = V_high;
259 2 : break;
260 :
261 1 : case_GETOPT_HELP_CHAR;
262 0 : case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
263 8 : default:
264 8 : usage (EXIT_FAILURE);
265 : }
266 : }
267 :
268 75 : if (chopt.recurse)
269 : {
270 5 : if (bit_flags == FTS_PHYSICAL)
271 : {
272 2 : if (dereference == 1)
273 0 : error (EXIT_FAILURE, 0,
274 : _("-R --dereference requires either -H or -L"));
275 2 : dereference = 0;
276 : }
277 : }
278 : else
279 : {
280 70 : bit_flags = FTS_PHYSICAL;
281 : }
282 75 : chopt.affect_symlink_referent = (dereference != 0);
283 :
284 75 : if (argc - optind < (reference_file ? 1 : 2))
285 : {
286 33 : if (argc <= optind)
287 16 : error (0, 0, _("missing operand"));
288 : else
289 17 : error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
290 33 : usage (EXIT_FAILURE);
291 : }
292 :
293 42 : if (reference_file)
294 : {
295 : struct stat ref_stats;
296 1 : if (stat (reference_file, &ref_stats))
297 1 : error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
298 : quote (reference_file));
299 :
300 0 : uid = ref_stats.st_uid;
301 0 : gid = ref_stats.st_gid;
302 0 : chopt.user_name = uid_to_name (ref_stats.st_uid);
303 0 : chopt.group_name = gid_to_name (ref_stats.st_gid);
304 : }
305 : else
306 : {
307 41 : const char *e = parse_user_spec (argv[optind], &uid, &gid,
308 : &chopt.user_name, &chopt.group_name);
309 41 : if (e)
310 14 : error (EXIT_FAILURE, 0, "%s: %s", e, quote (argv[optind]));
311 :
312 : /* If a group is specified but no user, set the user name to the
313 : empty string so that diagnostics say "ownership :GROUP"
314 : rather than "group GROUP". */
315 27 : if (!chopt.user_name && chopt.group_name)
316 3 : chopt.user_name = "";
317 :
318 27 : optind++;
319 : }
320 :
321 27 : if (chopt.recurse & preserve_root)
322 : {
323 : static struct dev_ino dev_ino_buf;
324 0 : chopt.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
325 0 : if (chopt.root_dev_ino == NULL)
326 0 : error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
327 : quote ("/"));
328 : }
329 :
330 27 : ok = chown_files (argv + optind, bit_flags,
331 : uid, gid,
332 : required_uid, required_gid, &chopt);
333 :
334 21 : chopt_free (&chopt);
335 :
336 21 : exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
337 : }
|