1 /* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1989, 1993, 1994\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)chmod.c 8.4 (Berkeley) 03/31/94"; 16 #endif /* not lint */ 17 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 21 #include <err.h> 22 #include <errno.h> 23 #include <fts.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 void usage __P((void)); 30 31 int 32 main(argc, argv) 33 int argc; 34 char *argv[]; 35 { 36 register FTS *ftsp; 37 register FTSENT *p; 38 register int oct, omode; 39 mode_t *set; 40 int Hflag, Lflag, Pflag, Rflag, ch, fflag, fts_options, hflag, rval; 41 char *ep, *mode; 42 43 Hflag = Lflag = Pflag = Rflag = fflag = hflag = 0; 44 while ((ch = getopt(argc, argv, "HLPRXfgorstuwx")) != EOF) 45 switch (ch) { 46 case 'H': 47 Hflag = 1; 48 Lflag = Pflag = 0; 49 break; 50 case 'L': 51 Lflag = 1; 52 Hflag = Pflag = 0; 53 break; 54 case 'P': 55 Pflag = 1; 56 Hflag = Lflag = 0; 57 break; 58 case 'R': 59 Rflag = 1; 60 break; 61 case 'f': /* XXX: undocumented. */ 62 fflag = 1; 63 break; 64 case 'h': 65 /* 66 * In System V (and probably POSIX.2) the -h option 67 * causes chmod to change the mode of the symbolic 68 * link. 4.4BSD's symbolic links don't have modes, 69 * so it's an undocumented noop. Do syntax checking, 70 * though. 71 */ 72 hflag = 1; 73 break; 74 /* 75 * XXX 76 * "-[rwx]" are valid mode commands. If they are the entire 77 * argument, getopt has moved past them, so decrement optind. 78 * Regardless, we're done argument processing. 79 */ 80 case 'g': case 'o': case 'r': case 's': 81 case 't': case 'u': case 'w': case 'X': case 'x': 82 if (argv[optind - 1][0] == '-' && 83 argv[optind - 1][1] == ch && 84 argv[optind - 1][2] == '\0') 85 --optind; 86 goto done; 87 case '?': 88 default: 89 usage(); 90 } 91 done: argv += optind; 92 argc -= optind; 93 94 if (argc < 2) 95 usage(); 96 97 fts_options = FTS_PHYSICAL; 98 if (Rflag) { 99 if (hflag) 100 errx(1, 101 "the -R and -h options may not be specified together."); 102 if (Hflag) 103 fts_options |= FTS_COMFOLLOW; 104 if (Lflag) { 105 fts_options &= ~FTS_PHYSICAL; 106 fts_options |= FTS_LOGICAL; 107 } 108 } 109 110 mode = *argv; 111 if (*mode >= '0' && *mode <= '7') { 112 omode = (int)strtol(mode, &ep, 8); 113 if (omode < 0 || *ep) 114 errx(1, "invalid file mode: %s", mode); 115 oct = 1; 116 } else { 117 if ((set = setmode(mode)) == NULL) 118 errx(1, "invalid file mode: %s", mode); 119 oct = 0; 120 } 121 122 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 123 err(1, NULL); 124 for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 125 switch (p->fts_info) { 126 case FTS_D: 127 if (Rflag) /* Change it at FTS_DP. */ 128 continue; 129 fts_set(ftsp, p, FTS_SKIP); 130 break; 131 case FTS_DC: /* Ignore. */ 132 continue; 133 case FTS_DNR: /* Warn, chmod, continue. */ 134 errno = p->fts_errno; 135 warn("%s", p->fts_path); 136 rval = 1; 137 break; 138 case FTS_ERR: /* Warn, continue. */ 139 case FTS_NS: 140 errno = p->fts_errno; 141 warn("%s", p->fts_path); 142 rval = 1; 143 continue; 144 case FTS_SL: /* Ignore. */ 145 case FTS_SLNONE: 146 /* 147 * The only symlinks that end up here are ones that 148 * don't point to anything and ones that we found 149 * doing a physical walk. 150 */ 151 continue; 152 default: 153 break; 154 } 155 if (chmod(p->fts_accpath, oct ? omode : 156 getmode(set, p->fts_statp->st_mode)) && !fflag) { 157 warn(p->fts_path); 158 rval = 1; 159 } 160 } 161 exit(rval); 162 } 163 164 void 165 usage() 166 { 167 (void)fprintf(stderr, 168 "usage: chmod [-R [-H | -L | -P]] mode file ...\n"); 169 exit(1); 170 } 171