xref: /onnv-gate/usr/src/lib/libcmd/common/chmod.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1992-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.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[] =
3110898Sroland.mainz@nrubsig.org "[-?\n@(#)$Id: chmod (AT&T Research) 2009-07-02 $\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, "
958462SApril.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.]"
1148462SApril.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>
138*12068SRoger.Faulkner@Oracle.COM #include <fts_fix.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
b_chmod(int argc,char ** argv,void * context)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);
16110898Sroland.mainz@nrubsig.org 	int		logical = 1;
1624887Schin 	int		notify = 0;
1634887Schin 	int		ignore = 0;
1648462SApril.Chin@Sun.COM 	int		show = 0;
1654887Schin #if _lib_lchmod
1664887Schin 	int		chlink = 0;
1674887Schin #endif
1684887Schin 	struct stat	st;
1694887Schin 
1704887Schin 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
1714887Schin 	flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
1724887Schin 
1734887Schin 	/*
1744887Schin 	 * NOTE: we diverge from the normal optget boilerplate
1754887Schin 	 *	 to allow `chmod -x etc' to fall through
1764887Schin 	 */
1774887Schin 
1784887Schin 	for (;;)
1794887Schin 	{
1804887Schin 		switch (optget(argv, usage))
1814887Schin 		{
1824887Schin 		case 'c':
1834887Schin 			notify = 1;
1844887Schin 			continue;
1854887Schin 		case 'f':
1864887Schin 			force = 1;
1874887Schin 			continue;
1884887Schin 		case 'h':
1894887Schin #if _lib_lchmod
1904887Schin 			chlink = 1;
1914887Schin #endif
1924887Schin 			continue;
1934887Schin 		case 'i':
1944887Schin 			ignore = 1;
1954887Schin 			continue;
1968462SApril.Chin@Sun.COM 		case 'n':
1978462SApril.Chin@Sun.COM 			show = 1;
1988462SApril.Chin@Sun.COM 			continue;
1994887Schin 		case 'v':
2004887Schin 			notify = 2;
2014887Schin 			continue;
2024887Schin 		case 'F':
2034887Schin 			if (stat(opt_info.arg, &st))
2044887Schin 				error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
2054887Schin 			mode = st.st_mode;
2064887Schin 			amode = "";
2074887Schin 			continue;
2084887Schin 		case 'H':
2094887Schin 			flags |= FTS_META|FTS_PHYSICAL;
21010898Sroland.mainz@nrubsig.org 			logical = 0;
2114887Schin 			continue;
2124887Schin 		case 'L':
2134887Schin 			flags &= ~(FTS_META|FTS_PHYSICAL);
21410898Sroland.mainz@nrubsig.org 			logical = 0;
2154887Schin 			continue;
2164887Schin 		case 'P':
2174887Schin 			flags &= ~FTS_META;
2184887Schin 			flags |= FTS_PHYSICAL;
21910898Sroland.mainz@nrubsig.org 			logical = 0;
2204887Schin 			continue;
2214887Schin 		case 'R':
2224887Schin 			flags &= ~FTS_TOP;
22310898Sroland.mainz@nrubsig.org 			logical = 0;
2244887Schin 			continue;
2254887Schin 		case '?':
2264887Schin 			error(ERROR_usage(2), "%s", opt_info.arg);
2274887Schin 			break;
2284887Schin 		}
2294887Schin 		break;
2304887Schin 	}
2314887Schin 	argv += opt_info.index;
2324887Schin 	if (error_info.errors || !*argv || !amode && !*(argv + 1))
2334887Schin 		error(ERROR_usage(2), "%s", optusage(NiL));
23410898Sroland.mainz@nrubsig.org 	if (logical)
23510898Sroland.mainz@nrubsig.org 		flags &= ~(FTS_META|FTS_PHYSICAL);
2364887Schin 	if (ignore)
2374887Schin 		ignore = umask(0);
2384887Schin 	if (amode)
2394887Schin 		amode = 0;
2404887Schin 	else
2414887Schin 	{
2424887Schin 		amode = *argv++;
2434887Schin 		mode = strperm(amode, &last, 0);
2444887Schin 		if (*last)
2454887Schin 		{
2464887Schin 			if (ignore)
2474887Schin 				umask(ignore);
2484887Schin 			error(ERROR_exit(1), "%s: invalid mode", amode);
2494887Schin 		}
2504887Schin 	}
2514887Schin 	chmodf =
2524887Schin #if _lib_lchmod
2534887Schin 		chlink ? lchmod :
2544887Schin #endif
2554887Schin 		chmod;
2564887Schin 	if (!(fts = fts_open(argv, flags, NiL)))
2574887Schin 	{
2584887Schin 		if (ignore)
2594887Schin 			umask(ignore);
2604887Schin 		error(ERROR_system(1), "%s: not found", *argv);
2614887Schin 	}
2628462SApril.Chin@Sun.COM 	while (!sh_checksig(context) && (ent = fts_read(fts)))
2634887Schin 		switch (ent->fts_info)
2644887Schin 		{
2654887Schin 		case FTS_SL:
2664887Schin 			if (chmodf == chmod)
2674887Schin 			{
2684887Schin 				if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1)
2694887Schin 					fts_set(NiL, ent, FTS_FOLLOW);
2704887Schin 				break;
2714887Schin 			}
2724887Schin 			/*FALLTHROUGH*/
2734887Schin 		case FTS_F:
2744887Schin 		case FTS_D:
2754887Schin 		case FTS_SLNONE:
2764887Schin 		anyway:
2774887Schin 			if (amode)
2784887Schin 				mode = strperm(amode, &last, ent->fts_statp->st_mode);
2798462SApril.Chin@Sun.COM 			if (show || (*chmodf)(ent->fts_accpath, mode) >= 0)
2804887Schin 			{
2814887Schin 				if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM))
2824887Schin 					sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1);
2834887Schin 			}
2844887Schin 			else if (!force)
2854887Schin 				error(ERROR_system(0), "%s: cannot change mode", ent->fts_accpath);
2864887Schin 			break;
2874887Schin 		case FTS_DC:
2884887Schin 			if (!force)
2894887Schin 				error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath);
2904887Schin 			break;
2914887Schin 		case FTS_DNR:
2924887Schin 			if (!force)
2934887Schin 				error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath);
2944887Schin 			goto anyway;
2954887Schin 		case FTS_DNX:
2964887Schin 			if (!force)
2974887Schin 				error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath);
2984887Schin 			goto anyway;
2994887Schin 		case FTS_NS:
3004887Schin 			if (!force)
3014887Schin 				error(ERROR_system(0), "%s: not found", ent->fts_accpath);
3024887Schin 			break;
3034887Schin 		}
3044887Schin 	fts_close(fts);
3054887Schin 	if (ignore)
3064887Schin 		umask(ignore);
3074887Schin 	return error_info.errors != 0;
3084887Schin }
309