BSD 4_4_Lite1 release
[unix-history] / usr / src / usr.sbin / chown / chown.c
index 298211b..9121777 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1988, 1993
+ * Copyright (c) 1988, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  *     The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 #ifndef lint
 static char copyright[] =
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1988, 1993\n\
+"@(#) Copyright (c) 1988, 1993, 1994\n\
        The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
        The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)chown.c    8.1 (Berkeley) 6/6/93";
+static char sccsid[] = "@(#)chown.c    8.8 (Berkeley) 4/4/94";
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
-#include <sys/errno.h>
+
+#include <ctype.h>
 #include <dirent.h>
 #include <dirent.h>
+#include <err.h>
+#include <errno.h>
 #include <fts.h>
 #include <fts.h>
-#include <pwd.h>
 #include <grp.h>
 #include <grp.h>
-#include <unistd.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <stdio.h>
-#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 #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));
 
 
-int ischown, uid, gid, fflag, rflag, retval;
+uid_t uid;
+gid_t gid;
+int Rflag, ischown, fflag;
 char *gname, *myname;
 
 char *gname, *myname;
 
+int
 main(argc, argv)
        int argc;
 main(argc, argv)
        int argc;
-       char **argv;
+       char *argv[];
 {
 {
-       extern int optind;
-       register FTS *ftsp;
-       register FTSENT *p;
-       register char *cp;
-       int ch;
-       int fts_options;
-       int hflag, Hflag;
+       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';
        
-       hflag = Hflag = 0;
-       fts_options = FTS_NOSTAT | FTS_PHYSICAL;
-       while ((ch = getopt(argc, argv, "HRfh")) != 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 = 1;
+                       Rflag = 1;
                        break;
                case 'f':
                        fflag = 1;
                        break;
                case 'h':
                        break;
                case 'f':
                        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;
                        hflag = 1;
-                       fts_options &= ~FTS_PHYSICAL;
-                       fts_options |= FTS_LOGICAL;
-                       break;
-               case 'H':
-                       Hflag = 1;
-                       fts_options |= FTS_COMFOLLOW;
                        break;
                case '?':
                default:
                        break;
                case '?':
                default:
@@ -101,137 +121,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) {
 #ifdef SUPPORT_DOT
        uid = gid = -1;
        if (ischown) {
 #ifdef SUPPORT_DOT
-               if (cp = index(*argv, '.')) {
+               if ((cp = strchr(*argv, '.')) != NULL) {
                        *cp++ = '\0';
                        a_gid(cp);
                } else
 #endif
                        *cp++ = '\0';
                        a_gid(cp);
                } else
 #endif
-               if (cp = index(*argv, ':')) {
+               if ((cp = strchr(*argv, ':')) != NULL) {
                        *cp++ = '\0';
                        a_gid(cp);
                } 
                a_uid(*argv);
                        *cp++ = '\0';
                        a_gid(cp);
                } 
                a_uid(*argv);
-       }
-       else 
+       } else 
                a_gid(*argv);
 
                a_gid(*argv);
 
-       if (!(ftsp = fts_open(++argv, fts_options, 0))) {
-               (void)fprintf(stderr,
-                   "%s: %s.\n", myname, strerror(errno));
-               exit(1);
-       }
-       while (p = fts_read(ftsp)) {
-               if (p->fts_info == FTS_D) {
-                       if (rflag)
+       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;
                                continue;
-                       else
-                               fts_set(ftsp, p, FTS_SKIP);
-               }
-               if (p->fts_info == FTS_SL &&
-                   !(hflag || (Hflag && p->fts_level == FTS_ROOTLEVEL)))
+                       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;
                        continue;
-               if (p->fts_info == FTS_ERR) {
-                       error(p->fts_path);
+               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;
                        continue;
+               default:
+                       break;
                }
                }
-               if (chown(p->fts_accpath, uid, gid) && !fflag)
+               if (chown(p->fts_accpath, uid, gid) && !fflag) {
                        chownerr(p->fts_path);
                        chownerr(p->fts_path);
+                       rval = 1;
+               }
        }
        }
-       exit(retval);
+       if (errno)
+               err(1, "fts_read");
+       exit(rval);
 }
 
 }
 
+void
 a_gid(s)
 a_gid(s)
-       register char *s;
+       char *s;
 {
        struct group *gr;
 
 {
        struct group *gr;
 
-       if (!*s) {
-               gid = -1;                       /* argument was "uid." */
+       if (*s == '\0')                 /* Argument was "uid[:.]". */
                return;
                return;
-       }
        gname = s;
        gname = s;
-       if (gr = getgrnam(s))
-               gid = gr->gr_gid;
-       else {
-               for (; *s && isdigit(*s); ++s);
-               if (!*s)
-                       gid = atoi(gname);
-               else {
-                       (void)fprintf(stderr, "%s: unknown group id: %s\n",
-                           myname, gname);
-                       exit(1);
-               }
-       }
+       gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
 }
 
 }
 
+void
 a_uid(s)
 a_uid(s)
-       register char *s;
+       char *s;
 {
        struct passwd *pw;
 {
        struct passwd *pw;
-       char *uname;
 
 
-       if (!*s) {
-               uid = -1;                       /* argument was ".gid" */
+       if (*s == '\0')                 /* Argument was "[:.]gid". */
                return;
                return;
-       }
-       if (pw = getpwnam(s))
-               uid = pw->pw_uid;
-       else {
-               for (uname = s; *s && isdigit(*s); ++s);
-               if (!*s)
-                       uid = atoi(uname);
-               else {
-                       (void)fprintf(stderr,
-                           "chown: unknown user id: %s\n", uname);
-                       exit(1);
-               }
-       }
+       uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
+}
+
+u_long
+id(name, type)
+       char *name, *type;
+{
+       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);
 }
 
 }
 
+void
 chownerr(file)
        char *file;
 {
        static int euid = -1, ngroups = -1;
 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())) {
+       /* Check for chown without being root. */
+       if (errno != EPERM ||
+           uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
                if (fflag)
                        exit(0);
                if (fflag)
                        exit(0);
-               error(file);
-               exit(1);
+               err(1, "%s", file);
        }
        }
-       /* check group membership; kernel just returns EPERM */
-       if (gid != -1 && ngroups == -1) {
-               int groups[NGROUPS];
 
 
+       /* 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);
                ngroups = getgroups(NGROUPS, groups);
                while (--ngroups >= 0 && gid != groups[ngroups]);
                if (ngroups < 0) {
                        if (fflag)
                                exit(0);
-                       (void)fprintf(stderr,
-                           "%s: you are not a member of group %s.\n",
-                           myname, gname);
-                       exit(1);
+                       errx(1, "you are not a member of group %s", gname);
                }
        }
                }
        }
-       if (!fflag)
-               error(file);
-}
 
 
-error(name)
-       char *name;
-{
-       (void)fprintf(stderr, "%s: %s: %s\n", myname, name, strerror(errno));
-       retval = 1;
+       if (!fflag) 
+               warn("%s", file);
 }
 
 }
 
+void
 usage()
 {
 usage()
 {
-       (void)fprintf(stderr, "usage: %s [-HRfh] %s file ...\n", myname,
-           ischown ? "[owner][:group]" : "group");
+       (void)fprintf(stderr,
+           "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
+           myname, ischown ? "[owner][:group]" : "group");
        exit(1);
 }
        exit(1);
 }