add confSORT_QUEUE_BY_HOST and confBROKEN_SMTP_PEERS
[unix-history] / usr / src / usr.sbin / chown / chown.c
index 1ecf8c3..259acba 100644 (file)
@@ -1,56 +1,89 @@
 /*
 /*
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to the University of California at Berkeley. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission. This software
- * is provided ``as is'' without express or implied warranty.
+ * %sccs.include.redist.c%
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1988 Regents of the University of California.\n\
- All rights reserved.\n";
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
      The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)chown.c    5.9 (Berkeley) %G%";
+static char sccsid[] = "@(#)chown.c    8.8 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
-#include <sys/dir.h>
-#include <pwd.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
 #include <grp.h>
 #include <grp.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <stdio.h>
-#include <ctype.h>
-
-static int ischown, uid, gid, fflag, rflag, retval;
-static char *gname, *myname;
-
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void   a_gid __P((char *));
+void   a_uid __P((char *));
+void   chownerr __P((char *));
+u_long id __P((char *, char *));
+void   usage __P((void));
+
+uid_t uid;
+gid_t gid;
+int Rflag, ischown, fflag;
+char *gname, *myname;
+
+int
 main(argc, argv)
        int argc;
 main(argc, argv)
        int argc;
-       char **argv;
+       char *argv[];
 {
 {
-       extern char *optarg;
-       extern int optind;
-       register char *cp;
-       int ch;
-       char *index(), *rindex();
-
+       FTS *ftsp;
+       FTSENT *p;
+       int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
+       char *cp;
+       
        myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
        ischown = myname[2] == 'o';
        myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
        ischown = myname[2] == 'o';
-
-       while ((ch = getopt(argc, argv, "Rf")) != EOF)
-               switch((char)ch) {
+       
+       Hflag = Lflag = Pflag = hflag = 0;
+       while ((ch = getopt(argc, argv, "HLPRfh")) != EOF)
+               switch (ch) {
+               case 'H':
+                       Hflag = 1;
+                       Lflag = Pflag = 0;
+                       break;
+               case 'L':
+                       Lflag = 1;
+                       Hflag = Pflag = 0;
+                       break;
+               case 'P':
+                       Pflag = 1;
+                       Hflag = Lflag = 0;
+                       break;
                case 'R':
                case 'R':
-                       rflag++;
+                       Rflag = 1;
                        break;
                case 'f':
                        break;
                case 'f':
-                       fflag++;
+                       fflag = 1;
+                       break;
+               case 'h':
+                       /*
+                        * In System V (and probably POSIX.2) the -h option
+                        * causes chown/chgrp to change the owner/group of
+                        * the symbolic link.  4.4BSD's symbolic links don't
+                        * have owners/groups, so it's an undocumented noop.
+                        * Do syntax checking, though.
+                        */
+                       hflag = 1;
                        break;
                case '?':
                default:
                        break;
                case '?':
                default:
@@ -62,150 +95,153 @@ main(argc, argv)
        if (argc < 2)
                usage();
 
        if (argc < 2)
                usage();
 
+       fts_options = FTS_PHYSICAL;
+       if (Rflag) {
+               if (hflag)
+                       errx(1,
+               "the -R and -h options may not be specified together.");
+               if (Hflag)
+                       fts_options |= FTS_COMFOLLOW;
+               if (Lflag) {
+                       fts_options &= ~FTS_PHYSICAL;
+                       fts_options |= FTS_LOGICAL;
+               }
+       }
+
+       uid = gid = -1;
        if (ischown) {
        if (ischown) {
-               if (cp = index(*argv, '.')) {
+#ifdef SUPPORT_DOT
+               if ((cp = strchr(*argv, '.')) != NULL) {
                        *cp++ = '\0';
                        *cp++ = '\0';
-                       setgid(cp);
+                       a_gid(cp);
+               } else
+#endif
+               if ((cp = strchr(*argv, ':')) != NULL) {
+                       *cp++ = '\0';
+                       a_gid(cp);
+               } 
+               a_uid(*argv);
+       } else 
+               a_gid(*argv);
+
+       if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+               err(1, NULL);
+
+       for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+               switch (p->fts_info) {
+               case FTS_D:
+                       if (Rflag)              /* Change it at FTS_DP. */
+                               continue;
+                       fts_set(ftsp, p, FTS_SKIP);
+                       break;
+               case FTS_DNR:                   /* Warn, chown, continue. */
+                       warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+                       rval = 1;
+                       break;
+               case FTS_ERR:                   /* Warn, continue. */
+               case FTS_NS:
+                       warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+                       rval = 1;
+                       continue;
+               case FTS_SL:                    /* Ignore. */
+               case FTS_SLNONE:
+                       /*
+                        * The only symlinks that end up here are ones that
+                        * don't point to anything and ones that we found
+                        * doing a physical walk.
+                        */
+                       continue;
+               default:
+                       break;
+               }
+               if (chown(p->fts_accpath, uid, gid) && !fflag) {
+                       chownerr(p->fts_path);
+                       rval = 1;
                }
                }
-               else
-                       gid = -1;
-               setuid(*argv);
-       }
-       else {
-               uid = -1;
-               setgid(*argv);
        }
        }
-
-       while (*++argv)
-               change(*argv);
-       exit(retval);
+       if (errno)
+               err(1, "fts_read");
+       exit(rval);
 }
 
 }
 
-static
-setgid(s)
-       register char *s;
+void
+a_gid(s)
+       char *s;
 {
 {
-       struct group *gr, *getgrnam();
+       struct group *gr;
 
 
-       if (!*s) {
-               gid = -1;                       /* argument was "uid." */
+       if (*s == '\0')                 /* Argument was "uid[:.]". */
                return;
                return;
-       }
-       for (gname = s; *s && isdigit(*s); ++s);
-       if (!*s)
-               gid = atoi(gname);
-       else {
-               if (!(gr = getgrnam(gname))) {
-                       if (fflag)
-                               exit(0);
-                       fprintf(stderr, "%s: unknown group id: %s\n",
-                           myname, gname);
-                       exit(-1);
-               }
-               gid = gr->gr_gid;
-       }
+       gname = s;
+       gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
 }
 
 }
 
-static
-setuid(s)
-       register char *s;
+void
+a_uid(s)
+       char *s;
 {
 {
-       struct passwd *pwd, *getpwnam();
-       char *beg;
+       struct passwd *pw;
 
 
-       if (!*s) {
-               uid = -1;                       /* argument was ".gid" */
+       if (*s == '\0')                 /* Argument was "[:.]gid". */
                return;
                return;
-       }
-       for (beg = s; *s && isdigit(*s); ++s);
-       if (!*s)
-               uid = atoi(beg);
-       else {
-               if (!(pwd = getpwnam(beg))) {
-                       if (fflag)
-                               exit(0);
-                       fprintf(stderr, "chown: unknown user id: %s\n", beg);
-                       exit(-1);
-               }
-               uid = pwd->pw_uid;
-       }
+       uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
 }
 
 }
 
-static
-change(file)
-       char *file;
+u_long
+id(name, type)
+       char *name, *type;
 {
 {
-       register DIR *dirp;
-       register struct direct *dp;
-       struct stat buf;
+       u_long val;
+       char *ep;
+
+       /*
+        * XXX
+        * We know that uid_t's and gid_t's are unsigned longs.
+        */
+       errno = 0;
+       val = strtoul(name, &ep, 10);
+       if (errno)
+               err(1, "%s", name);
+       if (*ep != '\0')
+               errx(1, "%s: illegal %s name", name, type);
+       return (val);
+}
 
 
-       if (chown(file, uid, gid)) {
-               static int euid = -1, ngroups = -1;
+void
+chownerr(file)
+       char *file;
+{
+       static int euid = -1, ngroups = -1;
+       int groups[NGROUPS];
+
+       /* Check for chown without being root. */
+       if (errno != EPERM ||
+           uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
+               if (fflag)
+                       exit(0);
+               err(1, "%s", file);
+       }
 
 
-               if (uid != -1 && euid == -1 && (euid = geteuid())) {
+       /* Check group membership; kernel just returns EPERM. */
+       if (gid != -1 && ngroups == -1) {
+               ngroups = getgroups(NGROUPS, groups);
+               while (--ngroups >= 0 && gid != groups[ngroups]);
+               if (ngroups < 0) {
                        if (fflag)
                                exit(0);
                        if (fflag)
                                exit(0);
-                       err(file);
-                       exit(-1);
-               }
-               /* check group membership; kernel just returns EPERM */
-               if (gid != -1 && ngroups == -1) {
-                       int groups[NGROUPS];
-
-                       ngroups = getgroups(NGROUPS, groups);
-                       while (--ngroups >= 0 && gid != groups[ngroups]);
-                       if (ngroups < 0) {
-                               if (fflag)
-                                       exit(0);
-                               fprintf(stderr,
-                                   "%s: you are not a member of group %s.\n",
-                                   myname, gname);
-                               exit(-1);
-                       }
-               }
-               err(file);
-               return;
-       }
-       if (!rflag)
-               return;
-       if (lstat(file, &buf)) {
-               err(file);
-               return;
-       }
-       if ((buf.st_mode & S_IFMT) == S_IFDIR) {
-               if (chdir(file) < 0 || !(dirp = opendir("."))) {
-                       err(file);
-                       return;
-               }
-               for (dp = readdir(dirp); dp; dp = readdir(dirp)) {
-                       if (dp->d_name[0] == '.' && (!dp->d_name[1] ||
-                           dp->d_name[1] == '.' && !dp->d_name[2]))
-                               continue;
-                       change(dp->d_name);
-               }
-               closedir(dirp);
-               if (chdir("..")) {
-                       err("..");
-                       exit(fflag ? 0 : -1);
+                       errx(1, "you are not a member of group %s", gname);
                }
        }
                }
        }
-}
 
 
-static
-err(s)
-       char *s;
-{
-       if (fflag)
-               return;
-       fprintf(stderr, "%s: ", myname);
-       perror(s);
-       retval = -1;
+       if (!fflag) 
+               warn("%s", file);
 }
 
 }
 
-static
+void
 usage()
 {
 usage()
 {
-       fprintf(stderr, "usage: %s [-Rf] %s file ...\n", myname,
-           ischown ? "owner[.group]" : "group");
-       exit(-1);
+       (void)fprintf(stderr,
+           "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
+           myname, ischown ? "[owner][:group]" : "group");
+       exit(1);
 }
 }