file_cmds-188.tar.gz
[apple/file_cmds.git] / chmod / chmod.c
1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char const copyright[] =
36 "@(#) Copyright (c) 1989, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94";
43 #endif
44 #endif /* not lint */
45 #include <sys/cdefs.h>
46 __RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $");
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <fts.h>
54 #include <limits.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #ifdef __APPLE__
61 #include "chmod_acl.h"
62
63 #endif /*__APPLE__*/
64
65 int fflag = 0;
66
67 int main(int, char *[]);
68 void usage(void);
69
70 int
71 main(int argc, char *argv[])
72 {
73 FTS *ftsp = NULL;
74 FTSENT *p = NULL;
75 mode_t *set = NULL;
76 long val = 0;
77 int oct = 0;
78 int Hflag, Lflag, Rflag, ch, fts_options, hflag, rval;
79 int vflag;
80 char *ep, *mode;
81 mode_t newmode, omode;
82 #ifdef __APPLE__
83 unsigned int acloptflags = 0;
84 int aclpos = -1, inheritance_level = 0;
85 int index = 0, acloptlen = 0, ace_arg_not_required = 0;
86 acl_t acl_input = NULL;
87 #endif /* __APPLE__*/
88 int (*change_mode)(const char *, mode_t);
89
90 set = NULL;
91 omode = 0;
92 Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
93 #ifndef __APPLE__
94 while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
95 #else
96 while ((ch = getopt(argc, argv, "ACEHILNPRVXafghinorstuvwx")) != -1)
97 #endif
98 switch (ch) {
99 case 'H':
100 Hflag = 1;
101 Lflag = 0;
102 break;
103 case 'L':
104 Lflag = 1;
105 Hflag = 0;
106 break;
107 case 'P':
108 Hflag = Lflag = 0;
109 break;
110 case 'R':
111 Rflag = 1;
112 break;
113 case 'f':
114 fflag = 1;
115 break;
116 case 'h':
117 /*
118 * In System V (and probably POSIX.2) the -h option
119 * causes chmod to change the mode of the symbolic
120 * link. 4.4BSD's symbolic links didn't have modes,
121 * so it was an undocumented noop. In FreeBSD 3.0,
122 * lchmod(2) is introduced and this option does real
123 * work.
124 */
125 hflag = 1;
126 break;
127 #ifdef __APPLE__
128 case 'a':
129 if (argv[optind - 1][0] == '-' &&
130 argv[optind - 1][1] == ch)
131 --optind;
132 goto done;
133 case 'A':
134 acloptflags |= ACL_FLAG | ACL_TO_STDOUT;
135 ace_arg_not_required = 1;
136 errx(1, "-A not implemented");
137 goto done;
138 case 'E':
139 acloptflags |= ACL_FLAG | ACL_FROM_STDIN;
140 goto done;
141 case 'C':
142 acloptflags |= ACL_FLAG | ACL_CHECK_CANONICITY;
143 ace_arg_not_required = 1;
144 goto done;
145 case 'i':
146 acloptflags |= ACL_FLAG | ACL_REMOVE_INHERIT_FLAG;
147 ace_arg_not_required = 1;
148 goto done;
149 case 'I':
150 acloptflags |= ACL_FLAG | ACL_REMOVE_INHERITED_ENTRIES;
151 ace_arg_not_required = 1;
152 goto done;
153 case 'n':
154 acloptflags |= ACL_FLAG | ACL_NO_TRANSLATE;
155 break;
156 case 'N':
157 acloptflags |= ACL_FLAG | ACL_CLEAR_FLAG;
158 ace_arg_not_required = 1;
159 goto done;
160 case 'V':
161 acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR;
162 ace_arg_not_required = 1;
163 errx(1, "-V not implemented");
164 goto done;
165 #endif /* __APPLE__ */
166 /*
167 * XXX
168 * "-[rwx]" are valid mode commands. If they are the entire
169 * argument, getopt has moved past them, so decrement optind.
170 * Regardless, we're done argument processing.
171 */
172 case 'g': case 'o': case 'r': case 's':
173 case 't': case 'u': case 'w': case 'X': case 'x':
174 if (argv[optind - 1][0] == '-' &&
175 argv[optind - 1][1] == ch &&
176 argv[optind - 1][2] == '\0')
177 --optind;
178 goto done;
179 case 'v':
180 vflag++;
181 break;
182 case '?':
183 default:
184 usage();
185 }
186 done: argv += optind;
187 argc -= optind;
188
189 #ifdef __APPLE__
190 if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2))
191 usage();
192 #else
193 if (argc < 2)
194 usage();
195 #endif
196
197 #ifdef __APPLE__
198 if (!(acloptflags & ACL_FLAG) && ((acloptlen = strlen(argv[0])) > 1) && (argv[0][1] == 'a')) {
199 acloptflags |= ACL_FLAG;
200 switch (argv[0][0]) {
201 case '+':
202 acloptflags |= ACL_SET_FLAG;
203 break;
204 case '-':
205 acloptflags |= ACL_DELETE_FLAG;
206 break;
207 case '=':
208 acloptflags |= ACL_REWRITE_FLAG;
209 break;
210 default:
211 acloptflags &= ~ACL_FLAG;
212 goto apnoacl;
213 }
214
215 if (argc < 3)
216 usage();
217
218 if (acloptlen > 2) {
219 for (index = 2; index < acloptlen; index++) {
220 switch (argv[0][index]) {
221 case '#':
222 acloptflags |= ACL_ORDER_FLAG;
223
224 if (argc < ((acloptflags & ACL_DELETE_FLAG)
225 ? 3 : 4))
226 usage();
227 argv++;
228 argc--;
229 errno = 0;
230 aclpos = strtol(argv[0], &ep, 0);
231
232 if (aclpos > ACL_MAX_ENTRIES
233 || aclpos < 0)
234 errno = ERANGE;
235 if (errno || *ep)
236 err(1, "Invalid ACL entry number: %s", aclpos);
237 if (acloptflags & ACL_DELETE_FLAG)
238 ace_arg_not_required = 1;
239
240 goto apdone;
241 case 'i':
242 acloptflags |= ACL_INHERIT_FLAG;
243 /* The +aii.. syntax to specify
244 * inheritance level is rather unwieldy,
245 * find an alternative.
246 */
247 inheritance_level++;
248 if (inheritance_level > 1)
249 warn("Inheritance across more than one generation is not currently supported");
250 if (inheritance_level >= MAX_INHERITANCE_LEVEL)
251 goto apdone;
252 break;
253 default:
254 errno = EINVAL;
255 usage();
256 }
257 }
258 }
259 apdone:
260 argv++;
261 argc--;
262 }
263 apnoacl:
264 #endif /*__APPLE__*/
265
266 if (Rflag) {
267 fts_options = FTS_PHYSICAL;
268 if (hflag)
269 errx(1,
270 "the -R and -h options may not be specified together.");
271 if (Hflag)
272 fts_options |= FTS_COMFOLLOW;
273 if (Lflag) {
274 fts_options &= ~FTS_PHYSICAL;
275 fts_options |= FTS_LOGICAL;
276 }
277 } else
278 fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
279
280 if (hflag)
281 change_mode = lchmod;
282 else
283 change_mode = chmod;
284 #ifdef __APPLE__
285 if (acloptflags & ACL_FROM_STDIN) {
286 int readval = 0, readtotal = 0;
287
288 mode = (char *) malloc(MAX_ACL_TEXT_SIZE);
289
290 if (mode == NULL)
291 err(1, "Unable to allocate mode string");
292 /* Read the ACEs from STDIN */
293 do {
294 readtotal += readval;
295 readval = read(STDIN_FILENO, mode + readtotal,
296 MAX_ACL_TEXT_SIZE);
297 } while ((readval > 0) && (readtotal <= MAX_ACL_TEXT_SIZE));
298
299 if (0 == readtotal)
300 err(1, "-E specified, but read from STDIN failed");
301 else
302 mode[readtotal - 1] = '\0';
303 --argv;
304 }
305 else
306 #endif /* __APPLE */
307 mode = *argv;
308
309 #ifdef __APPLE__
310 if ((acloptflags & ACL_FLAG)) {
311
312 /* Are we deleting by entry number, verifying
313 * canonicity or performing some other operation that
314 * does not require an input entry? If so, there's no
315 * entry to convert.
316 */
317 if (ace_arg_not_required) {
318 --argv;
319 }
320 else {
321 /* Parse the text into an ACL*/
322 acl_input = parse_acl_entries(mode);
323 if (acl_input == NULL) {
324 errno = EINVAL;
325 err(1, "Invalid ACL specification: %s", mode);
326 }
327 }
328 }
329 else {
330 #endif /* __APPLE__*/
331 if (*mode >= '0' && *mode <= '7') {
332 errno = 0;
333 val = strtol(mode, &ep, 8);
334 if (val > USHRT_MAX || val < 0)
335 errno = ERANGE;
336 if (errno)
337 err(1, "Invalid file mode: %s", mode);
338 if (*ep)
339 errx(1, "Invalid file mode: %s", mode);
340 omode = (mode_t)val;
341 oct = 1;
342 } else {
343 if ((set = setmode(mode)) == NULL)
344 errx(1, "Invalid file mode: %s", mode);
345 oct = 0;
346 }
347 #ifdef __APPLE__
348 }
349 #endif /* __APPLE__*/
350 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
351 err(1, "fts_open");
352 for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
353 switch (p->fts_info) {
354 case FTS_D:
355 if (!Rflag)
356 (void)fts_set(ftsp, p, FTS_SKIP);
357 break;
358 case FTS_DNR: /* Warn, chmod, continue. */
359 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
360 rval = 1;
361 break;
362 case FTS_DP: /* Already changed at FTS_D. */
363 continue;
364 case FTS_NS:
365 if (acloptflags & ACL_FLAG) /* don't need stat for -N */
366 break;
367 case FTS_ERR: /* Warn, continue. */
368 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
369 rval = 1;
370 continue;
371 case FTS_SL: /* Ignore. */
372 case FTS_SLNONE:
373 /*
374 * The only symlinks that end up here are ones that
375 * don't point to anything and ones that we found
376 * doing a physical walk.
377 */
378 if (!hflag)
379 continue;
380 /* else */
381 /* FALLTHROUGH */
382 default:
383 break;
384 }
385 #ifdef __APPLE__
386 /* If an ACL manipulation option was specified, manipulate */
387 if (acloptflags & ACL_FLAG) {
388 if (0 != modify_file_acl(acloptflags, p->fts_accpath, acl_input, aclpos, inheritance_level))
389 rval = 1;
390 }
391 else {
392 #endif /* __APPLE__ */
393 newmode = oct ? omode : getmode(set, p->fts_statp->st_mode);
394 if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
395 continue;
396 if ((*change_mode)(p->fts_accpath, newmode) && !fflag) {
397 warn("%s", p->fts_path);
398 rval = 1;
399 } else {
400 if (vflag) {
401 (void)printf("%s", p->fts_accpath);
402
403 if (vflag > 1) {
404 char m1[12], m2[12];
405
406 strmode(p->fts_statp->st_mode, m1);
407 strmode((p->fts_statp->st_mode &
408 S_IFMT) | newmode, m2);
409
410 (void)printf(": 0%o [%s] -> 0%o [%s]",
411 p->fts_statp->st_mode, m1,
412 (p->fts_statp->st_mode & S_IFMT) |
413 newmode, m2);
414 }
415 (void)printf("\n");
416 }
417
418 }
419 #ifdef __APPLE__
420 }
421 #endif /* __APPLE__*/
422 }
423 if (errno)
424 err(1, "fts_read");
425 #ifdef __APPLE__
426 if (acl_input)
427 acl_free(acl_input);
428 if (mode && (acloptflags & ACL_FROM_STDIN))
429 free(mode);
430
431 #endif /* __APPLE__ */
432 if (set)
433 free(set);
434 exit(rval);
435 }
436
437 void
438 usage(void)
439 {
440 #ifdef __APPLE__
441 (void)fprintf(stderr,
442 "usage:\tchmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n"
443 "\tchmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -i | -I] file ...\n"); /* add -A and -V when implemented */
444 #else
445 (void)fprintf(stderr,
446 "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n");
447 #endif /* __APPLE__ */
448 exit(1);
449 }