1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2010 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 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) 2009-07-02 $\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 "\b%s: mode 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 "[n:show?Show actions but do not change any file modes.]" 115 "[F:reference?Omit the \amode\a operand and use the mode of \afile\a " 116 "instead.]:[file]" 117 "[v:verbose?Describe changed permissions of all files.]" 118 "\n" 119 "\nmode file ...\n" 120 "\n" 121 "[+EXIT STATUS?]{" 122 "[+0?All files changed successfully.]" 123 "[+>0?Unable to change mode of one or more files.]" 124 "}" 125 "[+SEE ALSO?\bchgrp\b(1), \bchown\b(1), \btw\b(1), \bgetconf\b(1), \bls\b(1), " 126 "\bumask\b(2)]" 127 ; 128 129 130 #if defined(__STDPP__directive) && defined(__STDPP__hide) 131 __STDPP__directive pragma pp:hide lchmod 132 #else 133 #define lchmod ______lchmod 134 #endif 135 136 #include <cmd.h> 137 #include <ls.h> 138 #include <fts_fix.h> 139 140 #include "FEATURE/symlink" 141 142 #if defined(__STDPP__directive) && defined(__STDPP__hide) 143 __STDPP__directive pragma pp:nohide lchmod 144 #else 145 #undef lchmod 146 #endif 147 148 extern int lchmod(const char*, mode_t); 149 150 int 151 b_chmod(int argc, char** argv, void* context) 152 { 153 register int mode; 154 register int force = 0; 155 register int flags; 156 register char* amode = 0; 157 register FTS* fts; 158 register FTSENT*ent; 159 char* last; 160 int (*chmodf)(const char*, mode_t); 161 int logical = 1; 162 int notify = 0; 163 int ignore = 0; 164 int show = 0; 165 #if _lib_lchmod 166 int chlink = 0; 167 #endif 168 struct stat st; 169 170 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 171 flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR; 172 173 /* 174 * NOTE: we diverge from the normal optget boilerplate 175 * to allow `chmod -x etc' to fall through 176 */ 177 178 for (;;) 179 { 180 switch (optget(argv, usage)) 181 { 182 case 'c': 183 notify = 1; 184 continue; 185 case 'f': 186 force = 1; 187 continue; 188 case 'h': 189 #if _lib_lchmod 190 chlink = 1; 191 #endif 192 continue; 193 case 'i': 194 ignore = 1; 195 continue; 196 case 'n': 197 show = 1; 198 continue; 199 case 'v': 200 notify = 2; 201 continue; 202 case 'F': 203 if (stat(opt_info.arg, &st)) 204 error(ERROR_exit(1), "%s: cannot stat", opt_info.arg); 205 mode = st.st_mode; 206 amode = ""; 207 continue; 208 case 'H': 209 flags |= FTS_META|FTS_PHYSICAL; 210 logical = 0; 211 continue; 212 case 'L': 213 flags &= ~(FTS_META|FTS_PHYSICAL); 214 logical = 0; 215 continue; 216 case 'P': 217 flags &= ~FTS_META; 218 flags |= FTS_PHYSICAL; 219 logical = 0; 220 continue; 221 case 'R': 222 flags &= ~FTS_TOP; 223 logical = 0; 224 continue; 225 case '?': 226 error(ERROR_usage(2), "%s", opt_info.arg); 227 break; 228 } 229 break; 230 } 231 argv += opt_info.index; 232 if (error_info.errors || !*argv || !amode && !*(argv + 1)) 233 error(ERROR_usage(2), "%s", optusage(NiL)); 234 if (logical) 235 flags &= ~(FTS_META|FTS_PHYSICAL); 236 if (ignore) 237 ignore = umask(0); 238 if (amode) 239 amode = 0; 240 else 241 { 242 amode = *argv++; 243 mode = strperm(amode, &last, 0); 244 if (*last) 245 { 246 if (ignore) 247 umask(ignore); 248 error(ERROR_exit(1), "%s: invalid mode", amode); 249 } 250 } 251 chmodf = 252 #if _lib_lchmod 253 chlink ? lchmod : 254 #endif 255 chmod; 256 if (!(fts = fts_open(argv, flags, NiL))) 257 { 258 if (ignore) 259 umask(ignore); 260 error(ERROR_system(1), "%s: not found", *argv); 261 } 262 while (!sh_checksig(context) && (ent = fts_read(fts))) 263 switch (ent->fts_info) 264 { 265 case FTS_SL: 266 if (chmodf == chmod) 267 { 268 if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1) 269 fts_set(NiL, ent, FTS_FOLLOW); 270 break; 271 } 272 /*FALLTHROUGH*/ 273 case FTS_F: 274 case FTS_D: 275 case FTS_SLNONE: 276 anyway: 277 if (amode) 278 mode = strperm(amode, &last, ent->fts_statp->st_mode); 279 if (show || (*chmodf)(ent->fts_accpath, mode) >= 0) 280 { 281 if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM)) 282 sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1); 283 } 284 else if (!force) 285 error(ERROR_system(0), "%s: cannot change mode", ent->fts_accpath); 286 break; 287 case FTS_DC: 288 if (!force) 289 error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath); 290 break; 291 case FTS_DNR: 292 if (!force) 293 error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath); 294 goto anyway; 295 case FTS_DNX: 296 if (!force) 297 error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath); 298 goto anyway; 299 case FTS_NS: 300 if (!force) 301 error(ERROR_system(0), "%s: not found", ent->fts_accpath); 302 break; 303 } 304 fts_close(fts); 305 if (ignore) 306 umask(ignore); 307 return error_info.errors != 0; 308 } 309