14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1992-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * * 204887Schin ***********************************************************************/ 214887Schin #pragma prototyped 224887Schin /* 234887Schin * David Korn 244887Schin * Glenn Fowler 254887Schin * AT&T Research 264887Schin * 274887Schin * chmod 284887Schin */ 294887Schin 304887Schin static const char usage[] = 31*8462SApril.Chin@Sun.COM "[-?\n@(#)$Id: chmod (AT&T Research) 2007-09-10 $\n]" 324887Schin USAGE_LICENSE 334887Schin "[+NAME?chmod - change the access permissions of files]" 344887Schin "[+DESCRIPTION?\bchmod\b changes the permission of each file " 354887Schin "according to mode, which can be either a symbolic representation " 364887Schin "of changes to make, or an octal number representing the bit " 374887Schin "pattern for the new permissions.]" 384887Schin "[+?Symbolic mode strings consist of one or more comma separated list " 394887Schin "of operations that can be perfomed on the mode. Each operation is of " 404887Schin "the form \auser\a \aop\a \aperm\a where \auser\a is zero or more of " 414887Schin "the following letters:]{" 424887Schin "[+u?User permission bits.]" 434887Schin "[+g?Group permission bits.]" 444887Schin "[+o?Other permission bits.]" 454887Schin "[+a?All permission bits. This is the default if none are specified.]" 464887Schin "}" 474887Schin "[+?The \aperm\a portion consists of zero or more of the following letters:]{" 484887Schin "[+r?Read permission.]" 494887Schin "[+s?Setuid when \bu\b is selected for \awho\a and setgid when \bg\b " 504887Schin "is selected for \awho\a.]" 514887Schin "[+w?Write permission.]" 524887Schin "[+x?Execute permission for files, search permission for directories.]" 534887Schin "[+X?Same as \bx\b except that it is ignored for files that do not " 544887Schin "already have at least one \bx\b bit set.]" 554887Schin "[+l?Exclusive lock bit on systems that support it. Group execute " 564887Schin "must be off.]" 574887Schin "[+t?Sticky bit on systems that support it.]" 584887Schin "}" 594887Schin "[+?The \aop\a portion consists of one or more of the following characters:]{" 604887Schin "[++?Cause the permission selected to be added to the existing " 614887Schin "permissions. | is equivalent to +.]" 624887Schin "[+-?Cause the permission selected to be removed to the existing " 634887Schin "permissions.]" 644887Schin "[+=?Cause the permission to be set to the given permissions.]" 654887Schin "[+&?Cause the permission selected to be \aand\aed with the existing " 664887Schin "permissions.]" 674887Schin "[+^?Cause the permission selected to be propagated to more " 684887Schin "restrictive groups.]" 694887Schin "}" 704887Schin "[+?Symbolic modes with the \auser\a portion omitted are subject to " 714887Schin "\bumask\b(2) settings unless the \b=\b \aop\a or the " 724887Schin "\b--ignore-umask\b option is specified.]" 734887Schin "[+?A numeric mode is from one to four octal digits (0-7), " 744887Schin "derived by adding up the bits with values 4, 2, and 1. " 754887Schin "Any omitted digits are assumed to be leading zeros. The " 764887Schin "first digit selects the set user ID (4) and set group ID " 774887Schin "(2) and save text image (1) attributes. The second digit " 784887Schin "selects permissions for the user who owns the file: read " 794887Schin "(4), write (2), and execute (1); the third selects permissions" 804887Schin "for other users in the file's group, with the same values; " 814887Schin "and the fourth for other users not in the file's group, with " 824887Schin "the same values.]" 834887Schin 844887Schin "[+?For symbolic links, by default, \bchmod\b changes the mode on the file " 854887Schin "referenced by the symbolic link, not on the symbolic link itself. " 864887Schin "The \b-h\b options can be specified to change the mode of the link. " 874887Schin "When traversing directories with \b-R\b, \bchmod\b either follows " 884887Schin "symbolic links or does not follow symbolic links, based on the " 894887Schin "options \b-H\b, \b-L\b, and \b-P\b. The configuration parameter " 904887Schin "\bPATH_RESOLVE\b determines the default behavior if none of these " 914887Schin "options is specified.]" 924887Schin 934887Schin "[+?When the \b-c\b or \b-v\b options are specified, change notifications " 944887Schin "are written to standard output using the format, " 95*8462SApril.Chin@Sun.COM "\b%s: mode changed to %0.4o (%s)\b, with arguments of the " 964887Schin "pathname, the numeric mode, and the resulting permission bits as " 974887Schin "would be displayed by the \bls\b command.]" 984887Schin 994887Schin "[+?For backwards compatibility, if an invalid option is given that is a valid " 1004887Schin "symbolic mode specification, \bchmod\b treats this as a mode " 1014887Schin "specification rather than as an option specification.]" 1024887Schin 1034887Schin "[H:metaphysical?Follow symbolic links for command arguments; otherwise don't " 1044887Schin "follow symbolic links when traversing directories.]" 1054887Schin "[L:logical|follow?Follow symbolic links when traversing directories.]" 1064887Schin "[P:physical|nofollow?Don't follow symbolic links when traversing directories.]" 1074887Schin "[R:recursive?Change the mode for files in subdirectories recursively.]" 1084887Schin "[c:changes?Describe only files whose permission actually change.]" 1094887Schin "[f:quiet|silent?Do not report files whose permissioins fail to change.]" 1104887Schin "[h:symlink?Change the mode of the symbolic links on systems that " 1114887Schin "support this.]" 1124887Schin "[i:ignore-umask?Ignore the \bumask\b(2) value in symbolic mode " 1134887Schin "expressions. This is probably how you expect \bchmod\b to work.]" 114*8462SApril.Chin@Sun.COM "[n:show?Show actions but do not change any file modes.]" 1154887Schin "[F:reference?Omit the \amode\a operand and use the mode of \afile\a " 1164887Schin "instead.]:[file]" 1174887Schin "[v:verbose?Describe changed permissions of all files.]" 1184887Schin "\n" 1194887Schin "\nmode file ...\n" 1204887Schin "\n" 1214887Schin "[+EXIT STATUS?]{" 1224887Schin "[+0?All files changed successfully.]" 1234887Schin "[+>0?Unable to change mode of one or more files.]" 1244887Schin "}" 1254887Schin "[+SEE ALSO?\bchgrp\b(1), \bchown\b(1), \btw\b(1), \bgetconf\b(1), \bls\b(1), " 1264887Schin "\bumask\b(2)]" 1274887Schin ; 1284887Schin 1294887Schin 1304887Schin #if defined(__STDPP__directive) && defined(__STDPP__hide) 1314887Schin __STDPP__directive pragma pp:hide lchmod 1324887Schin #else 1334887Schin #define lchmod ______lchmod 1344887Schin #endif 1354887Schin 1364887Schin #include <cmd.h> 1374887Schin #include <ls.h> 1384887Schin #include <fts.h> 1394887Schin 1404887Schin #include "FEATURE/symlink" 1414887Schin 1424887Schin #if defined(__STDPP__directive) && defined(__STDPP__hide) 1434887Schin __STDPP__directive pragma pp:nohide lchmod 1444887Schin #else 1454887Schin #undef lchmod 1464887Schin #endif 1474887Schin 1484887Schin extern int lchmod(const char*, mode_t); 1494887Schin 1504887Schin int 1514887Schin b_chmod(int argc, char** argv, void* context) 1524887Schin { 1534887Schin register int mode; 1544887Schin register int force = 0; 1554887Schin register int flags; 1564887Schin register char* amode = 0; 1574887Schin register FTS* fts; 1584887Schin register FTSENT*ent; 1594887Schin char* last; 1604887Schin int (*chmodf)(const char*, mode_t); 1614887Schin int notify = 0; 1624887Schin int ignore = 0; 163*8462SApril.Chin@Sun.COM int show = 0; 1644887Schin #if _lib_lchmod 1654887Schin int chlink = 0; 1664887Schin #endif 1674887Schin struct stat st; 1684887Schin 1694887Schin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 1704887Schin flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR; 1714887Schin 1724887Schin /* 1734887Schin * NOTE: we diverge from the normal optget boilerplate 1744887Schin * to allow `chmod -x etc' to fall through 1754887Schin */ 1764887Schin 1774887Schin for (;;) 1784887Schin { 1794887Schin switch (optget(argv, usage)) 1804887Schin { 1814887Schin case 'c': 1824887Schin notify = 1; 1834887Schin continue; 1844887Schin case 'f': 1854887Schin force = 1; 1864887Schin continue; 1874887Schin case 'h': 1884887Schin #if _lib_lchmod 1894887Schin chlink = 1; 1904887Schin #endif 1914887Schin continue; 1924887Schin case 'i': 1934887Schin ignore = 1; 1944887Schin continue; 195*8462SApril.Chin@Sun.COM case 'n': 196*8462SApril.Chin@Sun.COM show = 1; 197*8462SApril.Chin@Sun.COM continue; 1984887Schin case 'v': 1994887Schin notify = 2; 2004887Schin continue; 2014887Schin case 'F': 2024887Schin if (stat(opt_info.arg, &st)) 2034887Schin error(ERROR_exit(1), "%s: cannot stat", opt_info.arg); 2044887Schin mode = st.st_mode; 2054887Schin amode = ""; 2064887Schin continue; 2074887Schin case 'H': 2084887Schin flags |= FTS_META|FTS_PHYSICAL; 2094887Schin continue; 2104887Schin case 'L': 2114887Schin flags &= ~(FTS_META|FTS_PHYSICAL); 2124887Schin continue; 2134887Schin case 'P': 2144887Schin flags &= ~FTS_META; 2154887Schin flags |= FTS_PHYSICAL; 2164887Schin continue; 2174887Schin case 'R': 2184887Schin flags &= ~FTS_TOP; 2194887Schin continue; 2204887Schin case '?': 2214887Schin error(ERROR_usage(2), "%s", opt_info.arg); 2224887Schin break; 2234887Schin } 2244887Schin break; 2254887Schin } 2264887Schin argv += opt_info.index; 2274887Schin if (error_info.errors || !*argv || !amode && !*(argv + 1)) 2284887Schin error(ERROR_usage(2), "%s", optusage(NiL)); 2294887Schin if (ignore) 2304887Schin ignore = umask(0); 2314887Schin if (amode) 2324887Schin amode = 0; 2334887Schin else 2344887Schin { 2354887Schin amode = *argv++; 2364887Schin mode = strperm(amode, &last, 0); 2374887Schin if (*last) 2384887Schin { 2394887Schin if (ignore) 2404887Schin umask(ignore); 2414887Schin error(ERROR_exit(1), "%s: invalid mode", amode); 2424887Schin } 2434887Schin } 2444887Schin chmodf = 2454887Schin #if _lib_lchmod 2464887Schin chlink ? lchmod : 2474887Schin #endif 2484887Schin chmod; 2494887Schin if (!(fts = fts_open(argv, flags, NiL))) 2504887Schin { 2514887Schin if (ignore) 2524887Schin umask(ignore); 2534887Schin error(ERROR_system(1), "%s: not found", *argv); 2544887Schin } 255*8462SApril.Chin@Sun.COM while (!sh_checksig(context) && (ent = fts_read(fts))) 2564887Schin switch (ent->fts_info) 2574887Schin { 2584887Schin case FTS_SL: 2594887Schin if (chmodf == chmod) 2604887Schin { 2614887Schin if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1) 2624887Schin fts_set(NiL, ent, FTS_FOLLOW); 2634887Schin break; 2644887Schin } 2654887Schin /*FALLTHROUGH*/ 2664887Schin case FTS_F: 2674887Schin case FTS_D: 2684887Schin case FTS_SLNONE: 2694887Schin anyway: 2704887Schin if (amode) 2714887Schin mode = strperm(amode, &last, ent->fts_statp->st_mode); 272*8462SApril.Chin@Sun.COM if (show || (*chmodf)(ent->fts_accpath, mode) >= 0) 2734887Schin { 2744887Schin if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM)) 2754887Schin sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1); 2764887Schin } 2774887Schin else if (!force) 2784887Schin error(ERROR_system(0), "%s: cannot change mode", ent->fts_accpath); 2794887Schin break; 2804887Schin case FTS_DC: 2814887Schin if (!force) 2824887Schin error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath); 2834887Schin break; 2844887Schin case FTS_DNR: 2854887Schin if (!force) 2864887Schin error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath); 2874887Schin goto anyway; 2884887Schin case FTS_DNX: 2894887Schin if (!force) 2904887Schin error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath); 2914887Schin goto anyway; 2924887Schin case FTS_NS: 2934887Schin if (!force) 2944887Schin error(ERROR_system(0), "%s: not found", ent->fts_accpath); 2954887Schin break; 2964887Schin } 2974887Schin fts_close(fts); 2984887Schin if (ignore) 2994887Schin umask(ignore); 3004887Schin return error_info.errors != 0; 3014887Schin } 302