1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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) 2012-04-20 $\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|l:symlink?Change the mode of symbolic links on systems that " 111 "support \blchmod\b(2). Implies \b--physical\b.]" 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), \blchmod\b(1), \btw\b(1), \bgetconf\b(1), " 126 "\bls\b(1), \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 #ifndef ENOSYS 141 #define ENOSYS EINVAL 142 #endif 143 144 #include "FEATURE/symlink" 145 146 #if defined(__STDPP__directive) && defined(__STDPP__hide) 147 __STDPP__directive pragma pp:nohide lchmod 148 #else 149 #undef lchmod 150 #endif 151 152 extern int lchmod(const char*, mode_t); 153 154 /* 155 * NOTE: we only use the native lchmod() on symlinks just in case 156 * the implementation is a feckless stub 157 */ 158 159 int 160 b_chmod(int argc, char** argv, Shbltin_t* context) 161 { 162 register int mode; 163 register int force = 0; 164 register int flags; 165 register char* amode = 0; 166 register FTS* fts; 167 register FTSENT*ent; 168 char* last; 169 int (*chmodf)(const char*, mode_t); 170 int logical = 1; 171 int notify = 0; 172 int ignore = 0; 173 int show = 0; 174 int chlink = 0; 175 struct stat st; 176 177 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 178 flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR; 179 180 /* 181 * NOTE: we diverge from the normal optget boilerplate 182 * to allow `chmod -x etc' to fall through 183 */ 184 185 for (;;) 186 { 187 switch (optget(argv, usage)) 188 { 189 case 'c': 190 notify = 1; 191 continue; 192 case 'f': 193 force = 1; 194 continue; 195 case 'h': 196 chlink = 1; 197 continue; 198 case 'i': 199 ignore = 1; 200 continue; 201 case 'n': 202 show = 1; 203 continue; 204 case 'v': 205 notify = 2; 206 continue; 207 case 'F': 208 if (stat(opt_info.arg, &st)) 209 error(ERROR_exit(1), "%s: cannot stat", opt_info.arg); 210 mode = st.st_mode; 211 amode = ""; 212 continue; 213 case 'H': 214 flags |= FTS_META|FTS_PHYSICAL; 215 logical = 0; 216 continue; 217 case 'L': 218 flags &= ~(FTS_META|FTS_PHYSICAL); 219 logical = 0; 220 continue; 221 case 'P': 222 flags &= ~FTS_META; 223 flags |= FTS_PHYSICAL; 224 logical = 0; 225 continue; 226 case 'R': 227 flags &= ~FTS_TOP; 228 logical = 0; 229 continue; 230 case '?': 231 error(ERROR_usage(2), "%s", opt_info.arg); 232 break; 233 } 234 break; 235 } 236 argv += opt_info.index; 237 if (error_info.errors || !*argv || !amode && !*(argv + 1)) 238 error(ERROR_usage(2), "%s", optusage(NiL)); 239 if (chlink) 240 { 241 flags &= ~FTS_META; 242 flags |= FTS_PHYSICAL; 243 logical = 0; 244 } 245 if (logical) 246 flags &= ~(FTS_META|FTS_PHYSICAL); 247 if (ignore) 248 ignore = umask(0); 249 if (amode) 250 amode = 0; 251 else 252 { 253 amode = *argv++; 254 mode = strperm(amode, &last, 0); 255 if (*last) 256 { 257 if (ignore) 258 umask(ignore); 259 error(ERROR_exit(1), "%s: invalid mode", amode); 260 } 261 } 262 if (!(fts = fts_open(argv, flags, NiL))) 263 { 264 if (ignore) 265 umask(ignore); 266 error(ERROR_system(1), "%s: not found", *argv); 267 } 268 while (!sh_checksig(context) && (ent = fts_read(fts))) 269 switch (ent->fts_info) 270 { 271 case FTS_SL: 272 case FTS_SLNONE: 273 if (chlink) 274 { 275 #if _lib_lchmod 276 chmodf = lchmod; 277 goto commit; 278 #else 279 if (!force) 280 { 281 errno = ENOSYS; 282 error(ERROR_system(0), "%s: cannot change symlink mode", ent->fts_path); 283 } 284 #endif 285 } 286 break; 287 case FTS_F: 288 case FTS_D: 289 anyway: 290 chmodf = chmod; 291 #if _lib_lchmod 292 commit: 293 #endif 294 if (amode) 295 mode = strperm(amode, &last, ent->fts_statp->st_mode); 296 if (show || (*chmodf)(ent->fts_accpath, mode) >= 0) 297 { 298 if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM)) 299 sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1); 300 } 301 else if (!force) 302 error(ERROR_system(0), "%s: cannot change mode", ent->fts_path); 303 break; 304 case FTS_DC: 305 if (!force) 306 error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path); 307 break; 308 case FTS_DNR: 309 if (!force) 310 error(ERROR_system(0), "%s: cannot read directory", ent->fts_path); 311 goto anyway; 312 case FTS_DNX: 313 if (!force) 314 error(ERROR_system(0), "%s: cannot search directory", ent->fts_path); 315 goto anyway; 316 case FTS_NS: 317 if (!force) 318 error(ERROR_system(0), "%s: not found", ent->fts_path); 319 break; 320 } 321 fts_close(fts); 322 if (ignore) 323 umask(ignore); 324 return error_info.errors != 0; 325 } 326