1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*b30d1939SAndy Fiddaman * Copyright (c) 1992-2012 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6*b30d1939SAndy Fiddaman * Eclipse Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10*b30d1939SAndy Fiddaman * http://www.eclipse.org/org/documents/epl-v10.html *
11*b30d1939SAndy Fiddaman * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
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*b30d1939SAndy Fiddaman "[-?\n@(#)$Id: chmod (AT&T Research) 2012-04-20 $\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, "
957c2fbfb3SApril 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.]"
110*b30d1939SAndy Fiddaman "[h|l:symlink?Change the mode of symbolic links on systems that "
111*b30d1939SAndy Fiddaman "support \blchmod\b(2). Implies \b--physical\b.]"
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.]"
1147c2fbfb3SApril 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 "}"
125*b30d1939SAndy Fiddaman "[+SEE ALSO?\bchgrp\b(1), \bchown\b(1), \blchmod\b(1), \btw\b(1), \bgetconf\b(1), "
126*b30d1939SAndy Fiddaman "\bls\b(1), \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>
1383e14f97fSRoger A. Faulkner #include <fts_fix.h>
139da2e3ebdSchin
140*b30d1939SAndy Fiddaman #ifndef ENOSYS
141*b30d1939SAndy Fiddaman #define ENOSYS EINVAL
142*b30d1939SAndy Fiddaman #endif
143*b30d1939SAndy Fiddaman
144da2e3ebdSchin #include "FEATURE/symlink"
145da2e3ebdSchin
146da2e3ebdSchin #if defined(__STDPP__directive) && defined(__STDPP__hide)
147da2e3ebdSchin __STDPP__directive pragma pp:nohide lchmod
148da2e3ebdSchin #else
149da2e3ebdSchin #undef lchmod
150da2e3ebdSchin #endif
151da2e3ebdSchin
152da2e3ebdSchin extern int lchmod(const char*, mode_t);
153da2e3ebdSchin
154*b30d1939SAndy Fiddaman /*
155*b30d1939SAndy Fiddaman * NOTE: we only use the native lchmod() on symlinks just in case
156*b30d1939SAndy Fiddaman * the implementation is a feckless stub
157*b30d1939SAndy Fiddaman */
158*b30d1939SAndy Fiddaman
159da2e3ebdSchin int
b_chmod(int argc,char ** argv,Shbltin_t * context)160*b30d1939SAndy Fiddaman b_chmod(int argc, char** argv, Shbltin_t* context)
161da2e3ebdSchin {
162da2e3ebdSchin register int mode;
163da2e3ebdSchin register int force = 0;
164da2e3ebdSchin register int flags;
165da2e3ebdSchin register char* amode = 0;
166da2e3ebdSchin register FTS* fts;
167da2e3ebdSchin register FTSENT*ent;
168da2e3ebdSchin char* last;
169da2e3ebdSchin int (*chmodf)(const char*, mode_t);
17034f9b3eeSRoland Mainz int logical = 1;
171da2e3ebdSchin int notify = 0;
172da2e3ebdSchin int ignore = 0;
1737c2fbfb3SApril Chin int show = 0;
174da2e3ebdSchin int chlink = 0;
175da2e3ebdSchin struct stat st;
176da2e3ebdSchin
177da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
178*b30d1939SAndy Fiddaman flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
179da2e3ebdSchin
180da2e3ebdSchin /*
181da2e3ebdSchin * NOTE: we diverge from the normal optget boilerplate
182da2e3ebdSchin * to allow `chmod -x etc' to fall through
183da2e3ebdSchin */
184da2e3ebdSchin
185da2e3ebdSchin for (;;)
186da2e3ebdSchin {
187da2e3ebdSchin switch (optget(argv, usage))
188da2e3ebdSchin {
189da2e3ebdSchin case 'c':
190da2e3ebdSchin notify = 1;
191da2e3ebdSchin continue;
192da2e3ebdSchin case 'f':
193da2e3ebdSchin force = 1;
194da2e3ebdSchin continue;
195da2e3ebdSchin case 'h':
196da2e3ebdSchin chlink = 1;
197da2e3ebdSchin continue;
198da2e3ebdSchin case 'i':
199da2e3ebdSchin ignore = 1;
200da2e3ebdSchin continue;
2017c2fbfb3SApril Chin case 'n':
2027c2fbfb3SApril Chin show = 1;
2037c2fbfb3SApril Chin continue;
204da2e3ebdSchin case 'v':
205da2e3ebdSchin notify = 2;
206da2e3ebdSchin continue;
207da2e3ebdSchin case 'F':
208da2e3ebdSchin if (stat(opt_info.arg, &st))
209da2e3ebdSchin error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
210da2e3ebdSchin mode = st.st_mode;
211da2e3ebdSchin amode = "";
212da2e3ebdSchin continue;
213da2e3ebdSchin case 'H':
214da2e3ebdSchin flags |= FTS_META|FTS_PHYSICAL;
21534f9b3eeSRoland Mainz logical = 0;
216da2e3ebdSchin continue;
217da2e3ebdSchin case 'L':
218da2e3ebdSchin flags &= ~(FTS_META|FTS_PHYSICAL);
21934f9b3eeSRoland Mainz logical = 0;
220da2e3ebdSchin continue;
221da2e3ebdSchin case 'P':
222da2e3ebdSchin flags &= ~FTS_META;
223da2e3ebdSchin flags |= FTS_PHYSICAL;
22434f9b3eeSRoland Mainz logical = 0;
225da2e3ebdSchin continue;
226da2e3ebdSchin case 'R':
227da2e3ebdSchin flags &= ~FTS_TOP;
22834f9b3eeSRoland Mainz logical = 0;
229da2e3ebdSchin continue;
230da2e3ebdSchin case '?':
231da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg);
232da2e3ebdSchin break;
233da2e3ebdSchin }
234da2e3ebdSchin break;
235da2e3ebdSchin }
236da2e3ebdSchin argv += opt_info.index;
237da2e3ebdSchin if (error_info.errors || !*argv || !amode && !*(argv + 1))
238da2e3ebdSchin error(ERROR_usage(2), "%s", optusage(NiL));
239*b30d1939SAndy Fiddaman if (chlink)
240*b30d1939SAndy Fiddaman {
241*b30d1939SAndy Fiddaman flags &= ~FTS_META;
242*b30d1939SAndy Fiddaman flags |= FTS_PHYSICAL;
243*b30d1939SAndy Fiddaman logical = 0;
244*b30d1939SAndy Fiddaman }
24534f9b3eeSRoland Mainz if (logical)
24634f9b3eeSRoland Mainz flags &= ~(FTS_META|FTS_PHYSICAL);
247da2e3ebdSchin if (ignore)
248da2e3ebdSchin ignore = umask(0);
249da2e3ebdSchin if (amode)
250da2e3ebdSchin amode = 0;
251da2e3ebdSchin else
252da2e3ebdSchin {
253da2e3ebdSchin amode = *argv++;
254da2e3ebdSchin mode = strperm(amode, &last, 0);
255da2e3ebdSchin if (*last)
256da2e3ebdSchin {
257da2e3ebdSchin if (ignore)
258da2e3ebdSchin umask(ignore);
259da2e3ebdSchin error(ERROR_exit(1), "%s: invalid mode", amode);
260da2e3ebdSchin }
261da2e3ebdSchin }
262da2e3ebdSchin if (!(fts = fts_open(argv, flags, NiL)))
263da2e3ebdSchin {
264da2e3ebdSchin if (ignore)
265da2e3ebdSchin umask(ignore);
266da2e3ebdSchin error(ERROR_system(1), "%s: not found", *argv);
267da2e3ebdSchin }
2687c2fbfb3SApril Chin while (!sh_checksig(context) && (ent = fts_read(fts)))
269da2e3ebdSchin switch (ent->fts_info)
270da2e3ebdSchin {
271da2e3ebdSchin case FTS_SL:
272*b30d1939SAndy Fiddaman case FTS_SLNONE:
273*b30d1939SAndy Fiddaman if (chlink)
274da2e3ebdSchin {
275*b30d1939SAndy Fiddaman #if _lib_lchmod
276*b30d1939SAndy Fiddaman chmodf = lchmod;
277*b30d1939SAndy Fiddaman goto commit;
278*b30d1939SAndy Fiddaman #else
279*b30d1939SAndy Fiddaman if (!force)
280*b30d1939SAndy Fiddaman {
281*b30d1939SAndy Fiddaman errno = ENOSYS;
282*b30d1939SAndy Fiddaman error(ERROR_system(0), "%s: cannot change symlink mode", ent->fts_path);
283*b30d1939SAndy Fiddaman }
284*b30d1939SAndy Fiddaman #endif
285da2e3ebdSchin }
286*b30d1939SAndy Fiddaman break;
287da2e3ebdSchin case FTS_F:
288da2e3ebdSchin case FTS_D:
289da2e3ebdSchin anyway:
290*b30d1939SAndy Fiddaman chmodf = chmod;
291*b30d1939SAndy Fiddaman #if _lib_lchmod
292*b30d1939SAndy Fiddaman commit:
293*b30d1939SAndy Fiddaman #endif
294da2e3ebdSchin if (amode)
295da2e3ebdSchin mode = strperm(amode, &last, ent->fts_statp->st_mode);
2967c2fbfb3SApril Chin if (show || (*chmodf)(ent->fts_accpath, mode) >= 0)
297da2e3ebdSchin {
298da2e3ebdSchin if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM))
299da2e3ebdSchin sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1);
300da2e3ebdSchin }
301da2e3ebdSchin else if (!force)
302*b30d1939SAndy Fiddaman error(ERROR_system(0), "%s: cannot change mode", ent->fts_path);
303da2e3ebdSchin break;
304da2e3ebdSchin case FTS_DC:
305da2e3ebdSchin if (!force)
306*b30d1939SAndy Fiddaman error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
307da2e3ebdSchin break;
308da2e3ebdSchin case FTS_DNR:
309da2e3ebdSchin if (!force)
310*b30d1939SAndy Fiddaman error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
311da2e3ebdSchin goto anyway;
312da2e3ebdSchin case FTS_DNX:
313da2e3ebdSchin if (!force)
314*b30d1939SAndy Fiddaman error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
315da2e3ebdSchin goto anyway;
316da2e3ebdSchin case FTS_NS:
317da2e3ebdSchin if (!force)
318*b30d1939SAndy Fiddaman error(ERROR_system(0), "%s: not found", ent->fts_path);
319da2e3ebdSchin break;
320da2e3ebdSchin }
321da2e3ebdSchin fts_close(fts);
322da2e3ebdSchin if (ignore)
323da2e3ebdSchin umask(ignore);
324da2e3ebdSchin return error_info.errors != 0;
325da2e3ebdSchin }
326