From 7c478bd95313f5f23a4c958a745db2134aa03244 Mon Sep 17 00:00:00 2001 From: "stevel@tonic-gate" Date: Tue, 14 Jun 2005 00:00:00 -0700 Subject: OpenSolaris Launch --- usr/src/cmd/chgrp/Makefile | 68 ++++++ usr/src/cmd/chgrp/chgrp.c | 543 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 611 insertions(+) create mode 100644 usr/src/cmd/chgrp/Makefile create mode 100644 usr/src/cmd/chgrp/chgrp.c (limited to 'usr/src/cmd/chgrp') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.2.3