]> git.saurik.com Git - apple/file_cmds.git/blame - chmod/chmod.c
file_cmds-321.100.10.0.1.tar.gz
[apple/file_cmds.git] / chmod / chmod.c
CommitLineData
44a7a5ab
A
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
00337e45 34#include <sys/cdefs.h>
44a7a5ab 35#ifndef lint
00337e45 36__used static char const copyright[] =
44a7a5ab 37"@(#) Copyright (c) 1989, 1993, 1994\n\
6c780a1f 38 The Regents of the University of California. All rights reserved.\n";
44a7a5ab
A
39#endif /* not lint */
40
41#ifndef lint
42#if 0
43static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94";
44a7a5ab
A
44#endif
45#endif /* not lint */
6c780a1f
A
46#include <sys/cdefs.h>
47__RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $");
44a7a5ab
A
48
49#include <sys/types.h>
50#include <sys/stat.h>
51
52#include <err.h>
53#include <errno.h>
54#include <fts.h>
6c780a1f 55#include <limits.h>
44a7a5ab
A
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <unistd.h>
44a7a5ab 60
c59d3020
A
61#ifdef __APPLE__
62#include "chmod_acl.h"
63
64#endif /*__APPLE__*/
65
66int fflag = 0;
67
6c780a1f
A
68int main(int, char *[]);
69void usage(void);
44a7a5ab
A
70
71int
6c780a1f 72main(int argc, char *argv[])
44a7a5ab 73{
c59d3020
A
74 FTS *ftsp = NULL;
75 FTSENT *p = NULL;
76 mode_t *set = NULL;
77 long val = 0;
78 int oct = 0;
40bf83fe 79 int Hflag, Lflag, Pflag, Rflag, ch, fts_options, hflag, rval;
6c780a1f 80 int vflag;
44a7a5ab 81 char *ep, *mode;
6c780a1f 82 mode_t newmode, omode;
c59d3020
A
83#ifdef __APPLE__
84 unsigned int acloptflags = 0;
4d0bb651
A
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;
c59d3020
A
90 acl_t acl_input = NULL;
91#endif /* __APPLE__*/
6c780a1f 92 int (*change_mode)(const char *, mode_t);
44a7a5ab 93
6c780a1f
A
94 set = NULL;
95 omode = 0;
40bf83fe 96 Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0;
f383e97b 97#ifndef __APPLE__
6c780a1f 98 while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
f383e97b 99#else
864a4b6e 100 while ((ch = getopt(argc, argv, "ACEHILNPRVXafghinorstuvwx")) != -1)
f383e97b 101#endif
44a7a5ab
A
102 switch (ch) {
103 case 'H':
104 Hflag = 1;
105 Lflag = 0;
40bf83fe 106 Pflag = 0;
44a7a5ab
A
107 break;
108 case 'L':
109 Lflag = 1;
110 Hflag = 0;
40bf83fe 111 Pflag = 0;
44a7a5ab
A
112 break;
113 case 'P':
114 Hflag = Lflag = 0;
40bf83fe 115 Pflag = 1;
44a7a5ab
A
116 break;
117 case 'R':
118 Rflag = 1;
119 break;
6c780a1f 120 case 'f':
44a7a5ab
A
121 fflag = 1;
122 break;
44a7a5ab
A
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,
6c780a1f 128 * so it was an undocumented noop. In FreeBSD 3.0,
44a7a5ab
A
129 * lchmod(2) is introduced and this option does real
130 * work.
131 */
132 hflag = 1;
133 break;
864a4b6e 134#ifdef __APPLE__
c59d3020
A
135 case 'a':
136 if (argv[optind - 1][0] == '-' &&
137 argv[optind - 1][1] == ch)
138 --optind;
139 goto done;
864a4b6e 140 case 'A':
4d0bb651
A
141// acloptflags |= ACL_FLAG | ACL_TO_STDOUT;
142// ace_arg_not_required = 1;
864a4b6e
A
143 errx(1, "-A not implemented");
144 goto done;
c59d3020
A
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;
40558d9b
A
163 case 'N':
164 acloptflags |= ACL_FLAG | ACL_CLEAR_FLAG;
165 ace_arg_not_required = 1;
166 goto done;
c59d3020 167 case 'V':
4d0bb651
A
168// acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR;
169// ace_arg_not_required = 1;
864a4b6e 170 errx(1, "-V not implemented");
c59d3020 171 goto done;
6c780a1f 172#endif /* __APPLE__ */
44a7a5ab
A
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;
6c780a1f
A
186 case 'v':
187 vflag++;
188 break;
44a7a5ab
A
189 case '?':
190 default:
191 usage();
192 }
193done: argv += optind;
194 argc -= optind;
195
c59d3020
A
196#ifdef __APPLE__
197 if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2))
198 usage();
40bf83fe
A
199 if (!Rflag && (Hflag || Lflag || Pflag))
200 warnx("options -H, -L, -P only useful with -R");
201#else /* !__APPLE__ */
44a7a5ab
A
202 if (argc < 2)
203 usage();
40bf83fe 204#endif /* __APPLE__ */
c59d3020
A
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)
4d0bb651 245 errx(1, "Invalid ACL entry number: %ld", aclpos);
c59d3020
A
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)
40bf83fe 258 warnx("Inheritance across more than one generation is not currently supported");
c59d3020
A
259 if (inheritance_level >= MAX_INHERITANCE_LEVEL)
260 goto apdone;
261 break;
262 default:
263 errno = EINVAL;
264 usage();
265 }
266 }
267 }
268apdone:
269 argv++;
270 argc--;
271 }
272apnoacl:
273#endif /*__APPLE__*/
44a7a5ab 274
44a7a5ab 275 if (Rflag) {
6c780a1f 276 fts_options = FTS_PHYSICAL;
44a7a5ab
A
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 }
6c780a1f
A
286 } else
287 fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
288
44a7a5ab
A
289 if (hflag)
290 change_mode = lchmod;
291 else
6c780a1f 292 change_mode = chmod;
c59d3020
A
293#ifdef __APPLE__
294 if (acloptflags & ACL_FROM_STDIN) {
4d0bb651
A
295 ssize_t readval = 0;
296 size_t readtotal = 0;
c59d3020
A
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)
40bf83fe 310 errx(1, "-E specified, but read from STDIN failed");
c59d3020
A
311 else
312 mode[readtotal - 1] = '\0';
313 --argv;
44a7a5ab 314 }
c59d3020
A
315 else
316#endif /* __APPLE */
317 mode = *argv;
318
319#ifdef __APPLE__
320 if ((acloptflags & ACL_FLAG)) {
44a7a5ab 321
c59d3020
A
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) {
40bf83fe 334 errx(1, "Invalid ACL specification: %s", mode);
c59d3020
A
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__*/
44a7a5ab 359 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
6c780a1f 360 err(1, "fts_open");
44a7a5ab
A
361 for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
362 switch (p->fts_info) {
864a4b6e 363 case FTS_D:
44a7a5ab 364 if (!Rflag)
864a4b6e
A
365 (void)fts_set(ftsp, p, FTS_SKIP);
366 break;
44a7a5ab
A
367 case FTS_DNR: /* Warn, chmod, continue. */
368 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
369 rval = 1;
370 break;
864a4b6e
A
371 case FTS_DP: /* Already changed at FTS_D. */
372 continue;
44a7a5ab 373 case FTS_NS:
864a4b6e
A
374 if (acloptflags & ACL_FLAG) /* don't need stat for -N */
375 break;
376 case FTS_ERR: /* Warn, continue. */
44a7a5ab
A
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 */
44a7a5ab
A
387 if (!hflag)
388 continue;
389 /* else */
390 /* FALLTHROUGH */
44a7a5ab
A
391 default:
392 break;
393 }
c59d3020
A
394#ifdef __APPLE__
395/* If an ACL manipulation option was specified, manipulate */
396 if (acloptflags & ACL_FLAG) {
4d0bb651 397 if (0 != modify_file_acl(acloptflags, p->fts_accpath, acl_input, (int)aclpos, inheritance_level, !hflag))
c59d3020
A
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) {
40bf83fe 406 warn("Unable to change file mode on %s", p->fts_path);
c59d3020
A
407 rval = 1;
408 } else {
409 if (vflag) {
410 (void)printf("%s", p->fts_accpath);
6c780a1f 411
c59d3020
A
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,
6c780a1f 421 (p->fts_statp->st_mode & S_IFMT) |
c59d3020
A
422 newmode, m2);
423 }
424 (void)printf("\n");
6c780a1f 425 }
c59d3020 426
6c780a1f 427 }
c59d3020 428#ifdef __APPLE__
44a7a5ab 429 }
c59d3020 430#endif /* __APPLE__*/
44a7a5ab
A
431 }
432 if (errno)
433 err(1, "fts_read");
c59d3020
A
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);
44a7a5ab 443 exit(rval);
44a7a5ab
A
444}
445
446void
6c780a1f 447usage(void)
44a7a5ab 448{
6c780a1f 449#ifdef __APPLE__
44a7a5ab 450 (void)fprintf(stderr,
864a4b6e 451 "usage:\tchmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n"
40bf83fe 452 "\tchmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -N | -i | -I] file ...\n"); /* add -A and -V when implemented */
44a7a5ab
A
453#else
454 (void)fprintf(stderr,
6c780a1f
A
455 "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n");
456#endif /* __APPLE__ */
44a7a5ab 457 exit(1);
44a7a5ab 458}