BSD 4_3_Tahoe release
[unix-history] / usr / src / etc / chown.c
index 757b55a..e9233ce 100644 (file)
 /*
 /*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, 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'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
 #ifndef lint
 char copyright[] =
  */
 
 #ifndef lint
 char copyright[] =
-"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+"@(#) Copyright (c) 1988 Regents of the University of California.\n\
  All rights reserved.\n";
  All rights reserved.\n";
-#endif
+#endif /* not lint */
 
 #ifndef lint
 
 #ifndef lint
-static char sccsid[] = "@(#)chown.c    5.6 (Berkeley) 5/29/86";
-#endif
+static char sccsid[] = "@(#)chown.c    5.11 (Berkeley) 6/18/88";
+#endif /* not lint */
 
 
-/*
- * chown [-fR] uid[.gid] file ...
- */
-
-#include <stdio.h>
-#include <ctype.h>
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <pwd.h>
 #include <sys/dir.h>
 #include <sys/dir.h>
+#include <pwd.h>
 #include <grp.h>
 #include <grp.h>
-#include <strings.h>
+#include <stdio.h>
+#include <ctype.h>
 
 
-struct passwd *pwd;
-struct passwd *getpwnam();
-struct stat stbuf;
-int    uid;
-int    status;
-int    fflag;
-int    rflag;
+static int ischown, uid, gid, fflag, rflag, retval;
+static char *gname, *myname;
 
 main(argc, argv)
 
 main(argc, argv)
-       char *argv[];
+       int argc;
+       char **argv;
 {
 {
-       register int c, gid;
-       register char *cp, *group;
-       struct group *grp;
+       extern char *optarg;
+       extern int optind;
+       register char *cp;
+       int ch;
+       char *index(), *rindex();
 
 
-       argc--, argv++;
-       while (argc > 0 && argv[0][0] == '-') {
-               for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
+       myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
+       ischown = myname[2] == 'o';
 
 
+       while ((ch = getopt(argc, argv, "Rf")) != EOF)
+               switch((char)ch) {
+               case 'R':
+                       rflag++;
+                       break;
                case 'f':
                        fflag++;
                        break;
                case 'f':
                        fflag++;
                        break;
+               case '?':
+               default:
+                       usage();
+               }
+       argv += optind;
+       argc -= optind;
 
 
-               case 'R':
-                       rflag++;
-                       break;
+       if (argc < 2)
+               usage();
 
 
-               default:
-                       fatal(255, "unknown option: %c", *cp);
+       if (ischown) {
+               if (cp = index(*argv, '.')) {
+                       *cp++ = '\0';
+                       setgid(cp);
                }
                }
-               argv++, argc--;
+               else
+                       gid = -1;
+               setuid(*argv);
        }
        }
-       if (argc < 2) {
-               fprintf(stderr, "usage: chown [-fR] owner[.group] file ...\n");
-               exit(-1);
+       else {
+               uid = -1;
+               setgid(*argv);
        }
        }
-       gid = -1;
-       group = index(argv[0], '.');
-       if (group != NULL) {
-               *group++ = '\0';
-               if (!isnumber(group)) {
-                       if ((grp = getgrnam(group)) == NULL)
-                               fatal(255, "unknown group: %s",group);
-                       gid = grp -> gr_gid;
-                       (void) endgrent();
-               } else if (*group != '\0')
-                       gid = atoi(group);
+
+       while (*++argv)
+               change(*argv);
+       exit(retval);
+}
+
+static
+setgid(s)
+       register char *s;
+{
+       struct group *gr, *getgrnam();
+
+       if (!*s) {
+               gid = -1;                       /* argument was "uid." */
+               return;
        }
        }
-       if (!isnumber(argv[0])) {
-               if ((pwd = getpwnam(argv[0])) == NULL)
-                       fatal(255, "unknown user id: %s",argv[0]);
-               uid = pwd->pw_uid;
-       } else
-               uid = atoi(argv[0]);
-       for (c = 1; c < argc; c++) {
-               /* do stat for directory arguments */
-               if (lstat(argv[c], &stbuf) < 0) {
-                       status += Perror(argv[c]);
-                       continue;
-               }
-               if (rflag && ((stbuf.st_mode&S_IFMT) == S_IFDIR)) {
-                       status += chownr(argv[c], uid, gid);
-                       continue;
-               }
-               if (chown(argv[c], uid, gid)) {
-                       status += Perror(argv[c]);
-                       continue;
+       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;
        }
        }
-       exit(status);
 }
 
 }
 
-isnumber(s)
-       char *s;
+static
+setuid(s)
+       register char *s;
 {
 {
-       register c;
+       struct passwd *pwd, *getpwnam();
+       char *beg;
 
 
-       while(c = *s++)
-               if (!isdigit(c))
-                       return (0);
-       return (1);
+       if (!*s) {
+               uid = -1;                       /* argument was ".gid" */
+               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;
+       }
 }
 
 }
 
-chownr(dir, uid, gid)
-       char *dir;
+static
+change(file)
+       char *file;
 {
        register DIR *dirp;
        register struct direct *dp;
 {
        register DIR *dirp;
        register struct direct *dp;
-       struct stat st;
-       char savedir[1024];
-       int ecode;
-       extern char *getwd();
-
-       if (getwd(savedir) == (char *)0)
-               fatal(255, "%s", savedir);
-       /*
-        * Change what we are given before doing it's contents.
-        */
-       if (chown(dir, uid, gid) < 0 && Perror(dir))
-               return (1);
-       if (chdir(dir) < 0) {
-               Perror(dir);
-               return (1);
+       struct stat buf;
+
+       if (chown(file, uid, gid)) {
+               chownerr(file);
+               return;
        }
        }
-       if ((dirp = opendir(".")) == NULL) {
-               Perror(dir);
-               return (1);
+       if (!rflag)
+               return;
+       if (lstat(file, &buf)) {
+               err(file);
+               return;
        }
        }
-       dp = readdir(dirp);
-       dp = readdir(dirp); /* read "." and ".." */
-       ecode = 0;
-       for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
-               if (lstat(dp->d_name, &st) < 0) {
-                       ecode = Perror(dp->d_name);
-                       if (ecode)
-                               break;
-                       continue;
+       if ((buf.st_mode & S_IFMT) == S_IFDIR) {
+               if (chdir(file) < 0 || !(dirp = opendir("."))) {
+                       err(file);
+                       return;
                }
                }
-               if ((st.st_mode&S_IFMT) == S_IFDIR) {
-                       ecode = chownr(dp->d_name, uid, gid);
-                       if (ecode)
-                               break;
-                       continue;
+               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);
                }
                }
-               if (chown(dp->d_name, uid, gid) < 0 &&
-                   (ecode = Perror(dp->d_name)))
-                       break;
        }
        }
-       closedir(dirp);
-       if (chdir(savedir) < 0)
-               fatal(255, "can't change back to %s", savedir);
-       return (ecode);
 }
 
 }
 
-error(fmt, a)
-       char *fmt, *a;
+static
+chownerr(file)
+       char *file;
 {
 {
+       static int euid = -1, ngroups = -1;
 
 
-       if (!fflag) {
-               fprintf(stderr, "chown: ");
-               fprintf(stderr, fmt, a);
-               putc('\n', stderr);
+       /* check for chown without being root */
+       if (uid != -1 && euid == -1 && (euid = geteuid())) {
+               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);
+               }
        }
        }
-       return (!fflag);
+       err(file);
 }
 
 }
 
-fatal(status, fmt, a)
-       int status;
-       char *fmt, *a;
+static
+err(s)
+       char *s;
 {
 {
-
-       fflag = 0;
-       (void) error(fmt, a);
-       exit(status);
+       if (fflag)
+               return;
+       fprintf(stderr, "%s: ", myname);
+       perror(s);
+       retval = -1;
 }
 
 }
 
-Perror(s)
-       char *s;
+static
+usage()
 {
 {
-
-       if (!fflag) {
-               fprintf(stderr, "chown: ");
-               perror(s);
-       }
-       return (!fflag);
+       fprintf(stderr, "usage: %s [-Rf] %s file ...\n", myname,
+           ischown ? "owner[.group]" : "group");
+       exit(-1);
 }
 }