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