1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2008 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) 2007-09-10 $\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.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 notify = 0; 162 int ignore = 0; 163 int show = 0; 164 #if _lib_lchmod 165 int chlink = 0; 166 #endif 167 struct stat st; 168 169 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 170 flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR; 171 172 /* 173 * NOTE: we diverge from the normal optget boilerplate 174 * to allow `chmod -x etc' to fall through 175 */ 176 177 for (;;) 178 { 179 switch (optget(argv, usage)) 180 { 181 case 'c': 182 notify = 1; 183 continue; 184 case 'f': 185 force = 1; 186 continue; 187 case 'h': 188 #if _lib_lchmod 189 chlink = 1; 190 #endif 191 continue; 192 case 'i': 193 ignore = 1; 194 continue; 195 case 'n': 196 show = 1; 197 continue; 198 case 'v': 199 notify = 2; 200 continue; 201 case 'F': 202 if (stat(opt_info.arg, &st)) 203 error(ERROR_exit(1), "%s: cannot stat", opt_info.arg); 204 mode = st.st_mode; 205 amode = ""; 206 continue; 207 case 'H': 208 flags |= FTS_META|FTS_PHYSICAL; 209 continue; 210 case 'L': 211 flags &= ~(FTS_META|FTS_PHYSICAL); 212 continue; 213 case 'P': 214 flags &= ~FTS_META; 215 flags |= FTS_PHYSICAL; 216 continue; 217 case 'R': 218 flags &= ~FTS_TOP; 219 continue; 220 case '?': 221 error(ERROR_usage(2), "%s", opt_info.arg); 222 break; 223 } 224 break; 225 } 226 argv += opt_info.index; 227 if (error_info.errors || !*argv || !amode && !*(argv + 1)) 228 error(ERROR_usage(2), "%s", optusage(NiL)); 229 if (ignore) 230 ignore = umask(0); 231 if (amode) 232 amode = 0; 233 else 234 { 235 amode = *argv++; 236 mode = strperm(amode, &last, 0); 237 if (*last) 238 { 239 if (ignore) 240 umask(ignore); 241 error(ERROR_exit(1), "%s: invalid mode", amode); 242 } 243 } 244 chmodf = 245 #if _lib_lchmod 246 chlink ? lchmod : 247 #endif 248 chmod; 249 if (!(fts = fts_open(argv, flags, NiL))) 250 { 251 if (ignore) 252 umask(ignore); 253 error(ERROR_system(1), "%s: not found", *argv); 254 } 255 while (!sh_checksig(context) && (ent = fts_read(fts))) 256 switch (ent->fts_info) 257 { 258 case FTS_SL: 259 if (chmodf == chmod) 260 { 261 if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1) 262 fts_set(NiL, ent, FTS_FOLLOW); 263 break; 264 } 265 /*FALLTHROUGH*/ 266 case FTS_F: 267 case FTS_D: 268 case FTS_SLNONE: 269 anyway: 270 if (amode) 271 mode = strperm(amode, &last, ent->fts_statp->st_mode); 272 if (show || (*chmodf)(ent->fts_accpath, mode) >= 0) 273 { 274 if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM)) 275 sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1); 276 } 277 else if (!force) 278 error(ERROR_system(0), "%s: cannot change mode", ent->fts_accpath); 279 break; 280 case FTS_DC: 281 if (!force) 282 error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath); 283 break; 284 case FTS_DNR: 285 if (!force) 286 error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath); 287 goto anyway; 288 case FTS_DNX: 289 if (!force) 290 error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath); 291 goto anyway; 292 case FTS_NS: 293 if (!force) 294 error(ERROR_system(0), "%s: not found", ent->fts_accpath); 295 break; 296 } 297 fts_close(fts); 298 if (ignore) 299 umask(ignore); 300 return error_info.errors != 0; 301 } 302