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.8 (Berkeley) 04/01/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 FTS *ftsp; 37 FTSENT *p; 38 mode_t *set; 39 long val; 40 int oct, omode; 41 int Hflag, Lflag, Pflag, Rflag, ch, fflag, fts_options, hflag, rval; 42 char *ep, *mode; 43 44 Hflag = Lflag = Pflag = Rflag = fflag = hflag = 0; 45 while ((ch = getopt(argc, argv, "HLPRXfgorstuwx")) != EOF) 46 switch (ch) { 47 case 'H': 48 Hflag = 1; 49 Lflag = Pflag = 0; 50 break; 51 case 'L': 52 Lflag = 1; 53 Hflag = Pflag = 0; 54 break; 55 case 'P': 56 Pflag = 1; 57 Hflag = Lflag = 0; 58 break; 59 case 'R': 60 Rflag = 1; 61 break; 62 case 'f': /* XXX: undocumented. */ 63 fflag = 1; 64 break; 65 case 'h': 66 /* 67 * In System V (and probably POSIX.2) the -h option 68 * causes chmod to change the mode of the symbolic 69 * link. 4.4BSD's symbolic links don't have modes, 70 * so it's an undocumented noop. Do syntax checking, 71 * though. 72 */ 73 hflag = 1; 74 break; 75 /* 76 * XXX 77 * "-[rwx]" are valid mode commands. If they are the entire 78 * argument, getopt has moved past them, so decrement optind. 79 * Regardless, we're done argument processing. 80 */ 81 case 'g': case 'o': case 'r': case 's': 82 case 't': case 'u': case 'w': case 'X': case 'x': 83 if (argv[optind - 1][0] == '-' && 84 argv[optind - 1][1] == ch && 85 argv[optind - 1][2] == '\0') 86 --optind; 87 goto done; 88 case '?': 89 default: 90 usage(); 91 } 92 done: argv += optind; 93 argc -= optind; 94 95 if (argc < 2) 96 usage(); 97 98 fts_options = FTS_PHYSICAL; 99 if (Rflag) { 100 if (hflag) 101 errx(1, 102 "the -R and -h options may not be specified together."); 103 if (Hflag) 104 fts_options |= FTS_COMFOLLOW; 105 if (Lflag) { 106 fts_options &= ~FTS_PHYSICAL; 107 fts_options |= FTS_LOGICAL; 108 } 109 } 110 111 mode = *argv; 112 if (*mode >= '0' && *mode <= '7') { 113 errno = 0; 114 val = strtol(mode, &ep, 8); 115 if (val > INT_MAX || val < 0) 116 errno = ERANGE; 117 if (errno) 118 err(1, "invalid file mode: %s", mode); 119 if (*ep) 120 errx(1, "invalid file mode: %s", mode); 121 omode = val; 122 oct = 1; 123 } else { 124 if ((set = setmode(mode)) == NULL) 125 errx(1, "invalid file mode: %s", mode); 126 oct = 0; 127 } 128 129 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 130 err(1, NULL); 131 for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 132 switch (p->fts_info) { 133 case FTS_D: 134 if (Rflag) /* Change it at FTS_DP. */ 135 continue; 136 fts_set(ftsp, p, FTS_SKIP); 137 break; 138 case FTS_DNR: /* Warn, chmod, continue. */ 139 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 140 rval = 1; 141 break; 142 case FTS_ERR: /* Warn, continue. */ 143 case FTS_NS: 144 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 145 rval = 1; 146 continue; 147 case FTS_SL: /* Ignore. */ 148 case FTS_SLNONE: 149 /* 150 * The only symlinks that end up here are ones that 151 * don't point to anything and ones that we found 152 * doing a physical walk. 153 */ 154 continue; 155 default: 156 break; 157 } 158 if (chmod(p->fts_accpath, oct ? omode : 159 getmode(set, p->fts_statp->st_mode)) && !fflag) { 160 warn(p->fts_path); 161 rval = 1; 162 } 163 } 164 if (errno) 165 err(1, "fts_read"); 166 exit(rval); 167 } 168 169 void 170 usage() 171 { 172 (void)fprintf(stderr, 173 "usage: chmod [-R [-H | -L | -P]] mode file ...\n"); 174 exit(1); 175 } 176