1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * * 20 ***********************************************************************/ 21 #pragma prototyped 22 /* 23 * David Korn 24 * Glenn Fowler 25 * AT&T Research 26 * 27 * chmod 28 */ 29 30 static const char usage[] = 31 "[-?\n@(#)$Id: chmod (AT&T Research) 2007-07-26 $\n]" 32 USAGE_LICENSE 33 "[+NAME?chmod - change the access permissions of files]" 34 "[+DESCRIPTION?\bchmod\b changes the permission of each file " 35 "according to mode, which can be either a symbolic representation " 36 "of changes to make, or an octal number representing the bit " 37 "pattern for the new permissions.]" 38 "[+?Symbolic mode strings consist of one or more comma separated list " 39 "of operations that can be perfomed on the mode. Each operation is of " 40 "the form \auser\a \aop\a \aperm\a where \auser\a is zero or more of " 41 "the following letters:]{" 42 "[+u?User permission bits.]" 43 "[+g?Group permission bits.]" 44 "[+o?Other permission bits.]" 45 "[+a?All permission bits. This is the default if none are specified.]" 46 "}" 47 "[+?The \aperm\a portion consists of zero or more of the following letters:]{" 48 "[+r?Read permission.]" 49 "[+s?Setuid when \bu\b is selected for \awho\a and setgid when \bg\b " 50 "is selected for \awho\a.]" 51 "[+w?Write permission.]" 52 "[+x?Execute permission for files, search permission for directories.]" 53 "[+X?Same as \bx\b except that it is ignored for files that do not " 54 "already have at least one \bx\b bit set.]" 55 "[+l?Exclusive lock bit on systems that support it. Group execute " 56 "must be off.]" 57 "[+t?Sticky bit on systems that support it.]" 58 "}" 59 "[+?The \aop\a portion consists of one or more of the following characters:]{" 60 "[++?Cause the permission selected to be added to the existing " 61 "permissions. | is equivalent to +.]" 62 "[+-?Cause the permission selected to be removed to the existing " 63 "permissions.]" 64 "[+=?Cause the permission to be set to the given permissions.]" 65 "[+&?Cause the permission selected to be \aand\aed with the existing " 66 "permissions.]" 67 "[+^?Cause the permission selected to be propagated to more " 68 "restrictive groups.]" 69 "}" 70 "[+?Symbolic modes with the \auser\a portion omitted are subject to " 71 "\bumask\b(2) settings unless the \b=\b \aop\a or the " 72 "\b--ignore-umask\b option is specified.]" 73 "[+?A numeric mode is from one to four octal digits (0-7), " 74 "derived by adding up the bits with values 4, 2, and 1. " 75 "Any omitted digits are assumed to be leading zeros. The " 76 "first digit selects the set user ID (4) and set group ID " 77 "(2) and save text image (1) attributes. The second digit " 78 "selects permissions for the user who owns the file: read " 79 "(4), write (2), and execute (1); the third selects permissions" 80 "for other users in the file's group, with the same values; " 81 "and the fourth for other users not in the file's group, with " 82 "the same values.]" 83 84 "[+?For symbolic links, by default, \bchmod\b changes the mode on the file " 85 "referenced by the symbolic link, not on the symbolic link itself. " 86 "The \b-h\b options can be specified to change the mode of the link. " 87 "When traversing directories with \b-R\b, \bchmod\b either follows " 88 "symbolic links or does not follow symbolic links, based on the " 89 "options \b-H\b, \b-L\b, and \b-P\b. The configuration parameter " 90 "\bPATH_RESOLVE\b determines the default behavior if none of these " 91 "options is specified.]" 92 93 "[+?When the \b-c\b or \b-v\b options are specified, change notifications " 94 "are written to standard output using the format, " 95 "\bmode of %s changed to %0.4o (%s)\b, with arguments of the " 96 "pathname, the numeric mode, and the resulting permission bits as " 97 "would be displayed by the \bls\b command.]" 98 99 "[+?For backwards compatibility, if an invalid option is given that is a valid " 100 "symbolic mode specification, \bchmod\b treats this as a mode " 101 "specification rather than as an option specification.]" 102 103 "[H:metaphysical?Follow symbolic links for command arguments; otherwise don't " 104 "follow symbolic links when traversing directories.]" 105 "[L:logical|follow?Follow symbolic links when traversing directories.]" 106 "[P:physical|nofollow?Don't follow symbolic links when traversing directories.]" 107 "[R:recursive?Change the mode for files in subdirectories recursively.]" 108 "[c:changes?Describe only files whose permission actually change.]" 109 "[f:quiet|silent?Do not report files whose permissioins fail to change.]" 110 "[h:symlink?Change the mode of the symbolic links on systems that " 111 "support this.]" 112 "[i:ignore-umask?Ignore the \bumask\b(2) value in symbolic mode " 113 "expressions. This is probably how you expect \bchmod\b to work.]" 114 "[F:reference?Omit the \amode\a operand and use the mode of \afile\a " 115 "instead.]:[file]" 116 "[v:verbose?Describe changed permissions of all files.]" 117 "\n" 118 "\nmode file ...\n" 119 "\n" 120 "[+EXIT STATUS?]{" 121 "[+0?All files changed successfully.]" 122 "[+>0?Unable to change mode of one or more files.]" 123 "}" 124 "[+SEE ALSO?\bchgrp\b(1), \bchown\b(1), \btw\b(1), \bgetconf\b(1), \bls\b(1), " 125 "\bumask\b(2)]" 126 ; 127 128 129 #if defined(__STDPP__directive) && defined(__STDPP__hide) 130 __STDPP__directive pragma pp:hide lchmod 131 #else 132 #define lchmod ______lchmod 133 #endif 134 135 #include <cmd.h> 136 #include <ls.h> 137 #include <fts.h> 138 139 #include "FEATURE/symlink" 140 141 #if defined(__STDPP__directive) && defined(__STDPP__hide) 142 __STDPP__directive pragma pp:nohide lchmod 143 #else 144 #undef lchmod 145 #endif 146 147 extern int lchmod(const char*, mode_t); 148 149 int 150 b_chmod(int argc, char** argv, void* context) 151 { 152 register int mode; 153 register int force = 0; 154 register int flags; 155 register char* amode = 0; 156 register FTS* fts; 157 register FTSENT*ent; 158 char* last; 159 int (*chmodf)(const char*, mode_t); 160 int notify = 0; 161 int ignore = 0; 162 #if _lib_lchmod 163 int chlink = 0; 164 #endif 165 struct stat st; 166 167 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 168 flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR; 169 170 /* 171 * NOTE: we diverge from the normal optget boilerplate 172 * to allow `chmod -x etc' to fall through 173 */ 174 175 for (;;) 176 { 177 switch (optget(argv, usage)) 178 { 179 case 'c': 180 notify = 1; 181 continue; 182 case 'f': 183 force = 1; 184 continue; 185 case 'h': 186 #if _lib_lchmod 187 chlink = 1; 188 #endif 189 continue; 190 case 'i': 191 ignore = 1; 192 continue; 193 case 'v': 194 notify = 2; 195 continue; 196 case 'F': 197 if (stat(opt_info.arg, &st)) 198 error(ERROR_exit(1), "%s: cannot stat", opt_info.arg); 199 mode = st.st_mode; 200 amode = ""; 201 continue; 202 case 'H': 203 flags |= FTS_META|FTS_PHYSICAL; 204 continue; 205 case 'L': 206 flags &= ~(FTS_META|FTS_PHYSICAL); 207 continue; 208 case 'P': 209 flags &= ~FTS_META; 210 flags |= FTS_PHYSICAL; 211 continue; 212 case 'R': 213 flags &= ~FTS_TOP; 214 continue; 215 case '?': 216 error(ERROR_usage(2), "%s", opt_info.arg); 217 break; 218 } 219 break; 220 } 221 argv += opt_info.index; 222 if (error_info.errors || !*argv || !amode && !*(argv + 1)) 223 error(ERROR_usage(2), "%s", optusage(NiL)); 224 if (ignore) 225 ignore = umask(0); 226 if (amode) 227 amode = 0; 228 else 229 { 230 amode = *argv++; 231 mode = strperm(amode, &last, 0); 232 if (*last) 233 { 234 if (ignore) 235 umask(ignore); 236 error(ERROR_exit(1), "%s: invalid mode", amode); 237 } 238 } 239 chmodf = 240 #if _lib_lchmod 241 chlink ? lchmod : 242 #endif 243 chmod; 244 if (!(fts = fts_open(argv, flags, NiL))) 245 { 246 if (ignore) 247 umask(ignore); 248 error(ERROR_system(1), "%s: not found", *argv); 249 } 250 while (!cmdquit() && (ent = fts_read(fts))) 251 switch (ent->fts_info) 252 { 253 case FTS_SL: 254 if (chmodf == chmod) 255 { 256 if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1) 257 fts_set(NiL, ent, FTS_FOLLOW); 258 break; 259 } 260 /*FALLTHROUGH*/ 261 case FTS_F: 262 case FTS_D: 263 case FTS_SLNONE: 264 anyway: 265 if (amode) 266 mode = strperm(amode, &last, ent->fts_statp->st_mode); 267 if ((*chmodf)(ent->fts_accpath, mode) >= 0) 268 { 269 if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM)) 270 sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1); 271 } 272 else if (!force) 273 error(ERROR_system(0), "%s: cannot change mode", ent->fts_accpath); 274 break; 275 case FTS_DC: 276 if (!force) 277 error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath); 278 break; 279 case FTS_DNR: 280 if (!force) 281 error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath); 282 goto anyway; 283 case FTS_DNX: 284 if (!force) 285 error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath); 286 goto anyway; 287 case FTS_NS: 288 if (!force) 289 error(ERROR_system(0), "%s: not found", ent->fts_accpath); 290 break; 291 } 292 fts_close(fts); 293 if (ignore) 294 umask(ignore); 295 return error_info.errors != 0; 296 } 297