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