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