summaryrefslogtreecommitdiff
path: root/usr/src/cmd/chgrp
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/chgrp
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/chgrp')
-rw-r--r--usr/src/cmd/chgrp/Makefile68
-rw-r--r--usr/src/cmd/chgrp/chgrp.c543
2 files changed, 611 insertions, 0 deletions
diff --git a/usr/src/cmd/chgrp/Makefile b/usr/src/cmd/chgrp/Makefile
new file mode 100644
index 0000000000..64534c28f2
--- /dev/null
+++ b/usr/src/cmd/chgrp/Makefile
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= chgrp
+XPG4PROG= chgrp
+XD= exobjs.xpg4
+EXOBJS= chgrp.o
+XPG4EXOBJS= exobjs.xpg4/chgrp.o
+
+include ../Makefile.cmd
+
+$(XPG4) := CFLAGS += -DXPG4
+CPPFLAGS += -D_FILE_OFFSET_BITS=64
+LDLIBS += -lcmdutils
+
+.KEEP_STATE:
+
+all: $(PROG) $(XPG4)
+
+$(PROG): $(EXOBJS)
+ $(LINK.c) -o $@ $(EXOBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(XPG4): $(XD) $(XPG4EXOBJS)
+ $(LINK.c) -o $@ $(XPG4EXOBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG) $(ROOTXPG4PROG)
+
+clean:
+ -@rm -rf $(EXOBJS) $(XD)
+
+lint: lint_PROG
+
+$(XPG4EXOBJS): $(XD)
+
+$(XD)/%.o: %.c
+ $(COMPILE.c) -o $@ $<
+
+$(XD):
+ -@mkdir -p $@
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/chgrp/chgrp.c b/usr/src/cmd/chgrp/chgrp.c
new file mode 100644
index 0000000000..a4212b19a8
--- /dev/null
+++ b/usr/src/cmd/chgrp/chgrp.c
@@ -0,0 +1,543 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * chgrp [-fhR] gid file ...
+ * chgrp -R [-f] [-H|-L|-P] gid file ...
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/avl.h>
+#include <grp.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <libcmdutils.h>
+#include <errno.h>
+#include <strings.h>
+
+static struct group *gr;
+static struct stat stbuf;
+static struct stat stbuf2;
+static gid_t gid;
+static int hflag = 0,
+ fflag = 0,
+ rflag = 0,
+ Hflag = 0,
+ Lflag = 0,
+ Pflag = 0;
+static int status = 0; /* total number of errors received */
+
+static avl_tree_t *tree; /* search tree to store inode data */
+
+static void usage(void);
+static int isnumber(char *);
+static int Perror(char *);
+static void chgrpr(char *, gid_t);
+
+#ifdef XPG4
+/*
+ * Check to see if we are to follow symlinks specified on the command line.
+ * This assumes we've already checked to make sure neither -h or -P was
+ * specified, so we are just looking to see if -R -L, or -R -H was specified,
+ * or, since -R has the same behavior as -R -L, if -R was specified by itself.
+ * Therefore, all we really need to check for is if -R was specified.
+ */
+#define FOLLOW_CL_LINKS (rflag)
+#else
+/*
+ * Check to see if we are to follow symlinks specified on the command line.
+ * This assumes we've already checked to make sure neither -h or -P was
+ * specified, so we are just looking to see if -R -L, or -R -H was specified.
+ * Note: -R by itself will change the group of a directory referenced by a
+ * symlink however it will not follow the symlink to any other part of the
+ * file hierarchy.
+ */
+#define FOLLOW_CL_LINKS (rflag && (Hflag || Lflag))
+#endif
+
+#ifdef XPG4
+/*
+ * Follow symlinks when traversing directories. Since -R behaves the
+ * same as -R -L, we always want to follow symlinks to other parts
+ * of the file hierarchy unless -H was specified.
+ */
+#define FOLLOW_D_LINKS (!Hflag)
+#else
+/*
+ * Follow symlinks when traversing directories. Only follow symlinks
+ * to other parts of the file hierarchy if -L was specified.
+ */
+#define FOLLOW_D_LINKS (Lflag)
+#endif
+
+#define CHOWN(f, u, g) if (chown(f, u, g) < 0) { \
+ status += Perror(f); \
+ }
+
+#define LCHOWN(f, u, g) if (lchown(f, u, g) < 0) { \
+ status += Perror(f); \
+ }
+/*
+ * We're ignoring errors here because preserving the SET[UG]ID bits is just
+ * a courtesy. This is only used on directories.
+ */
+#define SETUGID_PRESERVE(dir, mode) \
+ if (((mode) & (S_ISGID|S_ISUID)) != 0) \
+ (void) chmod((dir), (mode) & ~S_IFMT)
+
+extern int optind;
+
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+
+ /* set the locale for only the messages system (all else is clean) */
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "RhfHLP")) != EOF)
+ switch (c) {
+ case 'R':
+ rflag++;
+ break;
+ case 'h':
+ hflag++;
+ break;
+ case 'f':
+ fflag++;
+ break;
+ case 'H':
+ /*
+ * If more than one of -H, -L, and -P
+ * are specified, only the last option
+ * specified determines the behavior of
+ * chgrp. In addition, make [-H|-L]
+ * mutually exclusive of -h.
+ */
+ Lflag = Pflag = 0;
+ Hflag++;
+ break;
+ case 'L':
+ Hflag = Pflag = 0;
+ Lflag++;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ Pflag++;
+ break;
+ default:
+ usage();
+ }
+
+ /*
+ * Check for sufficient arguments
+ * or a usage error.
+ */
+ argc -= optind;
+ argv = &argv[optind];
+
+ if ((argc < 2) ||
+ ((Hflag || Lflag || Pflag) && !rflag) ||
+ ((Hflag || Lflag || Pflag) && hflag)) {
+ usage();
+ }
+
+ if (isnumber(argv[0])) {
+ errno = 0;
+ gid = (gid_t)strtol(argv[0], NULL, 10); /* gid is an int */
+ if (errno != 0) {
+ if (errno == ERANGE) {
+ (void) fprintf(stderr, gettext(
+ "chgrp: group id is too large\n"));
+ exit(2);
+ } else {
+ (void) fprintf(stderr, gettext(
+ "chgrp: invalid group id\n"));
+ exit(2);
+ }
+ }
+ } else {
+ if ((gr = getgrnam(argv[0])) == NULL) {
+ (void) fprintf(stderr, "chgrp: ");
+ (void) fprintf(stderr, gettext("unknown group: %s\n"),
+ argv[0]);
+ exit(2);
+ }
+ gid = gr->gr_gid;
+ }
+
+ for (c = 1; c < argc; c++) {
+ tree = NULL;
+ if (lstat(argv[c], &stbuf) < 0) {
+ status += Perror(argv[c]);
+ continue;
+ }
+ if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFLNK)) {
+ if (hflag || Pflag) {
+ /*
+ * Change the group id of the symbolic link
+ * specified on the command line.
+ * Don't follow the symbolic link to
+ * any other part of the file hierarchy.
+ */
+ LCHOWN(argv[c], -1, gid);
+ } else {
+ if (stat(argv[c], &stbuf2) < 0) {
+ status += Perror(argv[c]);
+ continue;
+ }
+ /*
+ * We know that we are to change the
+ * group of the file referenced by the
+ * symlink specified on the command line.
+ * Now check to see if we are to follow
+ * the symlink to any other part of the
+ * file hierarchy.
+ */
+ if (FOLLOW_CL_LINKS) {
+ if ((stbuf2.st_mode & S_IFMT)
+ == S_IFDIR) {
+ /*
+ * We are following symlinks so
+ * traverse into the directory.
+ * Add this node to the search
+ * tree so we don't get into an
+ * endless loop.
+ */
+ if (add_tnode(&tree,
+ stbuf2.st_dev,
+ stbuf2.st_ino) == 1) {
+ chgrpr(argv[c], gid);
+ /*
+ * Try to restore the
+ * SET[UG]ID bits.
+ */
+ SETUGID_PRESERVE(
+ argv[c],
+ stbuf2.st_mode &
+ ~S_IFMT);
+ } else {
+ /*
+ * Error occurred.
+ * rc can't be 0
+ * as this is the first
+ * node to be added to
+ * the search tree.
+ */
+ status += Perror(
+ argv[c]);
+ }
+ } else {
+ /*
+ * Change the group id of the
+ * file referenced by the
+ * symbolic link.
+ */
+ CHOWN(argv[c], -1, gid);
+ }
+ } else {
+ /*
+ * Change the group id of the file
+ * referenced by the symbolic link.
+ */
+ CHOWN(argv[c], -1, gid);
+
+ if ((stbuf2.st_mode & S_IFMT)
+ == S_IFDIR) {
+ /* Reset the SET[UG]ID bits. */
+ SETUGID_PRESERVE(argv[c],
+ stbuf2.st_mode & ~S_IFMT);
+ }
+ }
+ }
+ } else if (rflag && ((stbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /*
+ * Add this node to the search tree so we don't
+ * get into a endless loop.
+ */
+ if (add_tnode(&tree, stbuf.st_dev,
+ stbuf.st_ino) == 1) {
+ chgrpr(argv[c], gid);
+
+ /* Restore the SET[UG]ID bits. */
+ SETUGID_PRESERVE(argv[c],
+ stbuf.st_mode & ~S_IFMT);
+ } else {
+ /*
+ * An error occurred while trying
+ * to add the node to the tree.
+ * Continue on with next file
+ * specified. Note: rc shouldn't
+ * be 0 as this was the first node
+ * being added to the search tree.
+ */
+ status += Perror(argv[c]);
+ }
+ } else {
+ if (hflag || Pflag) {
+ LCHOWN(argv[c], -1, gid);
+ } else {
+ CHOWN(argv[c], -1, gid);
+ }
+ /* If a directory, reset the SET[UG]ID bits. */
+ if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
+ SETUGID_PRESERVE(argv[c],
+ stbuf.st_mode & ~S_IFMT);
+ }
+ }
+ }
+ return (status);
+}
+
+/*
+ * chgrpr() - recursive chown()
+ *
+ * Recursively chowns the input directory then its contents. rflag must
+ * have been set if chgrpr() is called. The input directory should not
+ * be a sym link (this is handled in the calling routine). In
+ * addition, the calling routine should have already added the input
+ * directory to the search tree so we do not get into endless loops.
+ * Note: chgrpr() doesn't need a return value as errors are reported
+ * through the global "status" variable.
+ */
+static void
+chgrpr(char *dir, gid_t gid)
+{
+ struct dirent *dp;
+ DIR *dirp;
+ struct stat st, st2;
+ char savedir[1024];
+
+ if (getcwd(savedir, 1024) == 0) {
+ (void) fprintf(stderr, "chgrp: ");
+ (void) fprintf(stderr, gettext("%s\n"), savedir);
+ exit(255);
+ }
+
+ /*
+ * Attempt to chown the directory, however don't return if we
+ * can't as we still may be able to chown the contents of the
+ * directory. Note: the calling routine resets the SUID bits
+ * on this directory so we don't have to perform an extra 'stat'.
+ */
+ CHOWN(dir, -1, gid);
+
+ if (chdir(dir) < 0) {
+ status += Perror(dir);
+ return;
+ }
+ if ((dirp = opendir(".")) == NULL) {
+ status += Perror(dir);
+ return;
+ }
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if ((strcmp(dp->d_name, ".") == 0) ||
+ (strcmp(dp->d_name, "..") == 0)) {
+ continue; /* skip "." and ".." */
+ }
+ if (lstat(dp->d_name, &st) < 0) {
+ status += Perror(dp->d_name);
+ continue;
+ }
+ if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ if (hflag || Pflag) {
+ /*
+ * Change the group id of the symbolic link
+ * encountered while traversing the
+ * directory. Don't follow the symbolic
+ * link to any other part of the file
+ * hierarchy.
+ */
+ LCHOWN(dp->d_name, -1, gid);
+ } else {
+ if (stat(dp->d_name, &st2) < 0) {
+ status += Perror(dp->d_name);
+ continue;
+ }
+ /*
+ * We know that we are to change the
+ * group of the file referenced by the
+ * symlink encountered while traversing
+ * the directory. Now check to see if we
+ * are to follow the symlink to any other
+ * part of the file hierarchy.
+ */
+ if (FOLLOW_D_LINKS) {
+ if ((st2.st_mode & S_IFMT) == S_IFDIR) {
+ /*
+ * We are following symlinks so
+ * traverse into the directory.
+ * Add this node to the search
+ * tree so we don't get into an
+ * endless loop.
+ */
+ int rc;
+ if ((rc = add_tnode(&tree,
+ st2.st_dev,
+ st2.st_ino)) == 1) {
+ chgrpr(dp->d_name, gid);
+
+ /*
+ * Restore SET[UG]ID
+ * bits.
+ */
+ SETUGID_PRESERVE(
+ dp->d_name,
+ st2.st_mode &
+ ~S_IFMT);
+ } else if (rc == 0) {
+ /* already visited */
+ continue;
+ } else {
+ /*
+ * An error occurred
+ * while trying to add
+ * the node to the tree.
+ */
+ status += Perror(
+ dp->d_name);
+ continue;
+ }
+ } else {
+ /*
+ * Change the group id of the
+ * file referenced by the
+ * symbolic link.
+ */
+ CHOWN(dp->d_name, -1, gid);
+
+ }
+ } else {
+ /*
+ * Change the group id of the file
+ * referenced by the symbolic link.
+ */
+ CHOWN(dp->d_name, -1, gid);
+
+ if ((st2.st_mode & S_IFMT) == S_IFDIR) {
+ /* Restore SET[UG]ID bits. */
+ SETUGID_PRESERVE(dp->d_name,
+ st2.st_mode & ~S_IFMT);
+ }
+ }
+ }
+ } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
+ /*
+ * Add this node to the search tree so we don't
+ * get into a endless loop.
+ */
+ int rc;
+ if ((rc = add_tnode(&tree, st.st_dev,
+ st.st_ino)) == 1) {
+ chgrpr(dp->d_name, gid);
+
+ /* Restore the SET[UG]ID bits. */
+ SETUGID_PRESERVE(dp->d_name,
+ st.st_mode & ~S_IFMT);
+ } else if (rc == 0) {
+ /* already visited */
+ continue;
+ } else {
+ /*
+ * An error occurred while trying
+ * to add the node to the search tree.
+ */
+ status += Perror(dp->d_name);
+ continue;
+ }
+ } else {
+ CHOWN(dp->d_name, -1, gid);
+ }
+ }
+ (void) closedir(dirp);
+ if (chdir(savedir) < 0) {
+ (void) fprintf(stderr, "chgrp: ");
+ (void) fprintf(stderr, gettext("can't change back to %s\n"),
+ savedir);
+ exit(255);
+ }
+}
+
+static int
+isnumber(char *s)
+{
+ int c;
+
+ while ((c = *s++) != '\0')
+ if (!isdigit(c))
+ return (0);
+ return (1);
+}
+
+
+static int
+Perror(char *s)
+{
+ if (!fflag) {
+ (void) fprintf(stderr, "chgrp: ");
+ perror(s);
+ }
+ return (!fflag);
+}
+
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "usage:\n"
+ "\tchgrp [-fhR] group file ...\n"
+ "\tchgrp -R [-f] [-H|-L|-P] group file ...\n"));
+ exit(2);
+}