summaryrefslogtreecommitdiff
path: root/usr/src/lib/libcmd/common/chmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libcmd/common/chmod.c')
-rw-r--r--usr/src/lib/libcmd/common/chmod.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/usr/src/lib/libcmd/common/chmod.c b/usr/src/lib/libcmd/common/chmod.c
new file mode 100644
index 0000000000..b1745585a0
--- /dev/null
+++ b/usr/src/lib/libcmd/common/chmod.c
@@ -0,0 +1,296 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * chmod
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: chmod (AT&T Research) 2007-07-26 $\n]"
+USAGE_LICENSE
+"[+NAME?chmod - change the access permissions of files]"
+"[+DESCRIPTION?\bchmod\b changes the permission of each file "
+ "according to mode, which can be either a symbolic representation "
+ "of changes to make, or an octal number representing the bit "
+ "pattern for the new permissions.]"
+"[+?Symbolic mode strings consist of one or more comma separated list "
+ "of operations that can be perfomed on the mode. Each operation is of "
+ "the form \auser\a \aop\a \aperm\a where \auser\a is zero or more of "
+ "the following letters:]{"
+ "[+u?User permission bits.]"
+ "[+g?Group permission bits.]"
+ "[+o?Other permission bits.]"
+ "[+a?All permission bits. This is the default if none are specified.]"
+ "}"
+"[+?The \aperm\a portion consists of zero or more of the following letters:]{"
+ "[+r?Read permission.]"
+ "[+s?Setuid when \bu\b is selected for \awho\a and setgid when \bg\b "
+ "is selected for \awho\a.]"
+ "[+w?Write permission.]"
+ "[+x?Execute permission for files, search permission for directories.]"
+ "[+X?Same as \bx\b except that it is ignored for files that do not "
+ "already have at least one \bx\b bit set.]"
+ "[+l?Exclusive lock bit on systems that support it. Group execute "
+ "must be off.]"
+ "[+t?Sticky bit on systems that support it.]"
+ "}"
+"[+?The \aop\a portion consists of one or more of the following characters:]{"
+ "[++?Cause the permission selected to be added to the existing "
+ "permissions. | is equivalent to +.]"
+ "[+-?Cause the permission selected to be removed to the existing "
+ "permissions.]"
+ "[+=?Cause the permission to be set to the given permissions.]"
+ "[+&?Cause the permission selected to be \aand\aed with the existing "
+ "permissions.]"
+ "[+^?Cause the permission selected to be propagated to more "
+ "restrictive groups.]"
+ "}"
+"[+?Symbolic modes with the \auser\a portion omitted are subject to "
+ "\bumask\b(2) settings unless the \b=\b \aop\a or the "
+ "\b--ignore-umask\b option is specified.]"
+"[+?A numeric mode is from one to four octal digits (0-7), "
+ "derived by adding up the bits with values 4, 2, and 1. "
+ "Any omitted digits are assumed to be leading zeros. The "
+ "first digit selects the set user ID (4) and set group ID "
+ "(2) and save text image (1) attributes. The second digit "
+ "selects permissions for the user who owns the file: read "
+ "(4), write (2), and execute (1); the third selects permissions"
+ "for other users in the file's group, with the same values; "
+ "and the fourth for other users not in the file's group, with "
+ "the same values.]"
+
+"[+?For symbolic links, by default, \bchmod\b changes the mode on the file "
+ "referenced by the symbolic link, not on the symbolic link itself. "
+ "The \b-h\b options can be specified to change the mode of the link. "
+ "When traversing directories with \b-R\b, \bchmod\b either follows "
+ "symbolic links or does not follow symbolic links, based on the "
+ "options \b-H\b, \b-L\b, and \b-P\b. The configuration parameter "
+ "\bPATH_RESOLVE\b determines the default behavior if none of these "
+ "options is specified.]"
+
+"[+?When the \b-c\b or \b-v\b options are specified, change notifications "
+ "are written to standard output using the format, "
+ "\bmode of %s changed to %0.4o (%s)\b, with arguments of the "
+ "pathname, the numeric mode, and the resulting permission bits as "
+ "would be displayed by the \bls\b command.]"
+
+"[+?For backwards compatibility, if an invalid option is given that is a valid "
+ "symbolic mode specification, \bchmod\b treats this as a mode "
+ "specification rather than as an option specification.]"
+
+"[H:metaphysical?Follow symbolic links for command arguments; otherwise don't "
+ "follow symbolic links when traversing directories.]"
+"[L:logical|follow?Follow symbolic links when traversing directories.]"
+"[P:physical|nofollow?Don't follow symbolic links when traversing directories.]"
+"[R:recursive?Change the mode for files in subdirectories recursively.]"
+"[c:changes?Describe only files whose permission actually change.]"
+"[f:quiet|silent?Do not report files whose permissioins fail to change.]"
+"[h:symlink?Change the mode of the symbolic links on systems that "
+ "support this.]"
+"[i:ignore-umask?Ignore the \bumask\b(2) value in symbolic mode "
+ "expressions. This is probably how you expect \bchmod\b to work.]"
+"[F:reference?Omit the \amode\a operand and use the mode of \afile\a "
+ "instead.]:[file]"
+"[v:verbose?Describe changed permissions of all files.]"
+"\n"
+"\nmode file ...\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files changed successfully.]"
+ "[+>0?Unable to change mode of one or more files.]"
+"}"
+"[+SEE ALSO?\bchgrp\b(1), \bchown\b(1), \btw\b(1), \bgetconf\b(1), \bls\b(1), "
+ "\bumask\b(2)]"
+;
+
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:hide lchmod
+#else
+#define lchmod ______lchmod
+#endif
+
+#include <cmd.h>
+#include <ls.h>
+#include <fts.h>
+
+#include "FEATURE/symlink"
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:nohide lchmod
+#else
+#undef lchmod
+#endif
+
+extern int lchmod(const char*, mode_t);
+
+int
+b_chmod(int argc, char** argv, void* context)
+{
+ register int mode;
+ register int force = 0;
+ register int flags;
+ register char* amode = 0;
+ register FTS* fts;
+ register FTSENT*ent;
+ char* last;
+ int (*chmodf)(const char*, mode_t);
+ int notify = 0;
+ int ignore = 0;
+#if _lib_lchmod
+ int chlink = 0;
+#endif
+ struct stat st;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
+
+ /*
+ * NOTE: we diverge from the normal optget boilerplate
+ * to allow `chmod -x etc' to fall through
+ */
+
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'c':
+ notify = 1;
+ continue;
+ case 'f':
+ force = 1;
+ continue;
+ case 'h':
+#if _lib_lchmod
+ chlink = 1;
+#endif
+ continue;
+ case 'i':
+ ignore = 1;
+ continue;
+ case 'v':
+ notify = 2;
+ continue;
+ case 'F':
+ if (stat(opt_info.arg, &st))
+ error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
+ mode = st.st_mode;
+ amode = "";
+ continue;
+ case 'H':
+ flags |= FTS_META|FTS_PHYSICAL;
+ continue;
+ case 'L':
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ continue;
+ case 'P':
+ flags &= ~FTS_META;
+ flags |= FTS_PHYSICAL;
+ continue;
+ case 'R':
+ flags &= ~FTS_TOP;
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || !*argv || !amode && !*(argv + 1))
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (ignore)
+ ignore = umask(0);
+ if (amode)
+ amode = 0;
+ else
+ {
+ amode = *argv++;
+ mode = strperm(amode, &last, 0);
+ if (*last)
+ {
+ if (ignore)
+ umask(ignore);
+ error(ERROR_exit(1), "%s: invalid mode", amode);
+ }
+ }
+ chmodf =
+#if _lib_lchmod
+ chlink ? lchmod :
+#endif
+ chmod;
+ if (!(fts = fts_open(argv, flags, NiL)))
+ {
+ if (ignore)
+ umask(ignore);
+ error(ERROR_system(1), "%s: not found", *argv);
+ }
+ while (!cmdquit() && (ent = fts_read(fts)))
+ switch (ent->fts_info)
+ {
+ case FTS_SL:
+ if (chmodf == chmod)
+ {
+ if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1)
+ fts_set(NiL, ent, FTS_FOLLOW);
+ break;
+ }
+ /*FALLTHROUGH*/
+ case FTS_F:
+ case FTS_D:
+ case FTS_SLNONE:
+ anyway:
+ if (amode)
+ mode = strperm(amode, &last, ent->fts_statp->st_mode);
+ if ((*chmodf)(ent->fts_accpath, mode) >= 0)
+ {
+ if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM))
+ sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1);
+ }
+ else if (!force)
+ error(ERROR_system(0), "%s: cannot change mode", ent->fts_accpath);
+ break;
+ case FTS_DC:
+ if (!force)
+ error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath);
+ break;
+ case FTS_DNR:
+ if (!force)
+ error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath);
+ goto anyway;
+ case FTS_DNX:
+ if (!force)
+ error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath);
+ goto anyway;
+ case FTS_NS:
+ if (!force)
+ error(ERROR_system(0), "%s: not found", ent->fts_accpath);
+ break;
+ }
+ fts_close(fts);
+ if (ignore)
+ umask(ignore);
+ return error_info.errors != 0;
+}