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