do the chown first; under VFS chown loses setid bits
[unix-history] / usr / src / usr.bin / xinstall / xinstall.c
index 43cda1e..127005c 100644 (file)
@@ -1,18 +1,29 @@
 /*
  * Copyright (c) 1987 Regents of the University of California.
 /*
  * Copyright (c) 1987 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * 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[] =
 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
  All rights reserved.\n";
  */
 
 #ifndef lint
 char copyright[] =
 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
  All rights reserved.\n";
-#endif not lint
+#endif /* not lint */
 
 #ifndef lint
 
 #ifndef lint
-static char sccsid[] = "@(#)xinstall.c 5.2 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)xinstall.c 5.15 (Berkeley) %G%";
+#endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 
 #include <sys/param.h>
 #include <sys/stat.h>
@@ -22,34 +33,32 @@ static char sccsid[] = "@(#)xinstall.c      5.2 (Berkeley) %G%";
 #include <pwd.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <paths.h>
 
 
-#define        YES             1                       /* yes/true */
-#define        DEF_GROUP       "staff"                 /* default group */
-#define        DEF_OWNER       "root"                  /* default owner */
+#define        PERROR(head, msg) { \
+       fputs(head, stderr); \
+       perror(msg); \
+}
 
 
-static int     docopy, dostrip,
-               mode = 0755;
-static char    *group = DEF_GROUP,
-               *owner = DEF_OWNER,
-               *path;
+static struct passwd *pp;
+static struct group *gp;
+static int docopy, dostrip, mode = 0755;
+static char *group, *owner, pathbuf[MAXPATHLEN];
 
 main(argc, argv)
 
 main(argc, argv)
-       int     argc;
-       char    **argv;
+       int argc;
+       char **argv;
 {
 {
-       extern char     *optarg, *sys_errlist[];
-       extern int      optind, errno;
-       register int    to_fd;
-       struct stat     from_sb, to_sb;
-       struct passwd   *pp;
-       struct group    *gp;
-       int     ch;
-       char    pbuf[MAXPATHLEN];
+       extern char *optarg;
+       extern int optind;
+       struct stat from_sb, to_sb;
+       int ch, no_target;
+       char *to_name;
 
        while ((ch = getopt(argc, argv, "cg:m:o:s")) != EOF)
                switch((char)ch) {
                case 'c':
 
        while ((ch = getopt(argc, argv, "cg:m:o:s")) != EOF)
                switch((char)ch) {
                case 'c':
-                       docopy = YES;
+                       docopy = 1;
                        break;
                case 'g':
                        group = optarg;
                        break;
                case 'g':
                        group = optarg;
@@ -61,7 +70,7 @@ main(argc, argv)
                        owner = optarg;
                        break;
                case 's':
                        owner = optarg;
                        break;
                case 's':
-                       dostrip = YES;
+                       dostrip = 1;
                        break;
                case '?':
                default:
                        break;
                case '?':
                default:
@@ -69,124 +78,132 @@ main(argc, argv)
                }
        argc -= optind;
        argv += optind;
                }
        argc -= optind;
        argv += optind;
-       if (argc != 2)
+       if (argc < 2)
                usage();
 
        /* get group and owner id's */
                usage();
 
        /* get group and owner id's */
-       if (!(gp = getgrnam(group))) {
+       if (group && !(gp = getgrnam(group))) {
                fprintf(stderr, "install: unknown group %s.\n", group);
                exit(1);
        }
                fprintf(stderr, "install: unknown group %s.\n", group);
                exit(1);
        }
-       if (!(pp = getpwnam(owner))) {
+       if (owner && !(pp = getpwnam(owner))) {
                fprintf(stderr, "install: unknown user %s.\n", owner);
                exit(1);
        }
 
                fprintf(stderr, "install: unknown user %s.\n", owner);
                exit(1);
        }
 
-       /* check source */
-       if (stat(argv[0], &from_sb)) {
-               fprintf(stderr, "install: fstat: %s: %s\n", argv[0], sys_errlist[errno]);
-               exit(1);
-       }
-       if (!(from_sb.st_mode & S_IFREG)) {
-               fprintf(stderr, "install: %s isn't a regular file.\n", argv[0]);
-               exit(1);
+       no_target = stat(to_name = argv[argc - 1], &to_sb);
+       if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
+               for (; *argv != to_name; ++argv)
+                       install(*argv, to_name, 1);
+               exit(0);
        }
 
        }
 
-       /* build target path, find out if target is same as source */
-       if (!stat(path = argv[1], &to_sb)) {
-               if (to_sb.st_mode & S_IFDIR) {
-                       (void)sprintf(path = pbuf, "%s/%s", argv[1], argv[0]);
-                       if (stat(path, &to_sb))
-                               goto nocompare;
-                       if (to_sb.st_mode & S_IFDIR) {
-                               fprintf(stderr, "install: %s is a directory.\n", path);
-                               exit(1);
-                       }
+       /* can't do file1 file2 directory/file */
+       if (argc != 2)
+               usage();
+
+       if (!no_target) {
+               if (stat(*argv, &from_sb)) {
+                       fprintf(stderr, "install: can't find %s.\n", *argv);
+                       exit(1);
+               }
+               if ((to_sb.st_mode & S_IFMT) != S_IFREG) {
+                       fprintf(stderr, "install: %s isn't a regular file.\n", to_name);
+                       exit(1);
                }
                if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino) {
                }
                if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino) {
-                       fprintf(stderr, "install: %s and %s are the same file.\n", argv[0], path);
+                       fprintf(stderr, "install: %s and %s are the same file.\n", *argv, to_name);
                        exit(1);
                }
                /* unlink now... avoid ETXTBSY errors later */
                        exit(1);
                }
                /* unlink now... avoid ETXTBSY errors later */
-               (void)unlink(path);
-       }
-
-nocompare:
-       /* open target, set mode, owner, group */
-       if ((to_fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0)) < 0) {
-               fprintf(stderr, "install: %s: %s\n", path, sys_errlist[errno]);
-               exit(1);
-       }
-       if (fchmod(to_fd, mode)) {
-               fprintf(stderr, "install: fchmod: %s: %s\n", path, sys_errlist[errno]);
-               bad();
+               (void)unlink(to_name);
        }
        }
-       if (fchown(to_fd, pp->pw_uid, gp->gr_gid)) {
-               fprintf(stderr, "install: fchown: %s: %s\n", path, sys_errlist[errno]);
-               bad();
-       }
-
-       if (dostrip) {
-               strip(to_fd, argv[0], path);
-               if (docopy)
-                       exit(0);
-       }
-       else if (docopy) {
-               copy(argv[0], to_fd, path);
-               exit(0);
-       }
-       else if (rename(argv[0], path))
-               copy(argv[0], to_fd, path);
-       (void)unlink(argv[0]);
+       install(*argv, to_name, 0);
        exit(0);
 }
 
 /*
        exit(0);
 }
 
 /*
- * copy --
- *     copy from one file to another
+ * install --
+ *     build a path name and install the file
  */
  */
-static
-copy(from_name, to_fd, to_name)
-       register int    to_fd;
-       char    *from_name, *to_name;
+install(from_name, to_name, isdir)
+       char *from_name, *to_name;
+       int isdir;
 {
 {
-       register int    n, from_fd;
-       char    buf[MAXBSIZE];
+       struct stat from_sb;
+       int devnull, from_fd, to_fd;
+       char *C, *rindex();
 
 
-       if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
-               fprintf(stderr, "install: open: %s: %s\n", from_name, sys_errlist[errno]);
-               bad();
+       /* if try to install NULL file to a directory, fails */
+       if (isdir || strcmp(from_name, _PATH_DEVNULL)) {
+               if (stat(from_name, &from_sb)) {
+                       fprintf(stderr, "install: can't find %s.\n", from_name);
+                       exit(1);
+               }
+               if ((from_sb.st_mode & S_IFMT) != S_IFREG) {
+                       fprintf(stderr, "install: %s isn't a regular file.\n", from_name);
+                       exit(1);
+               }
+               /* build the target path */
+               if (isdir) {
+                       (void)sprintf(pathbuf, "%s/%s", to_name, (C = rindex(from_name, '/')) ? ++C : from_name);
+                       to_name = pathbuf;
+               }
+               devnull = 0;
        }
        }
-       while ((n = read(from_fd, buf, sizeof(buf))) > 0)
-               if (write(to_fd, buf, n) != n) {
-                       fprintf(stderr, "install: write: %s: %s\n", to_name, sys_errlist[errno]);
-                       bad();
+       else
+               devnull = 1;
+
+       /* unlink now... avoid ETXTBSY errors later */
+       (void)unlink(to_name);
+
+       /* create target */
+       if ((to_fd = open(to_name, O_CREAT|O_WRONLY|O_TRUNC, 0)) < 0) {
+               PERROR("install: ", to_name);
+               exit(1);
+       }
+       if (!devnull) {
+               if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
+                       (void)unlink(to_name);
+                       PERROR("install: open: ", from_name);
+                       exit(1);
                }
                }
-       if (n == -1) {
-               fprintf(stderr, "install: read: %s: %s\n", from_name, sys_errlist[errno]);
+               if (dostrip)
+                       strip(from_fd, from_name, to_fd, to_name);
+               else
+                       copy(from_fd, from_name, to_fd, to_name);
+               (void)close(from_fd);
+               if (!docopy)
+                       (void)unlink(from_name);
+       }
+       if ((group || owner) && fchown(to_fd, owner ? pp->pw_uid : -1,
+           group ? gp->gr_gid : -1)) {
+               PERROR("install: fchown: ", to_name);
                bad();
        }
                bad();
        }
+       /* set owner, group, mode for target */
+       if (fchmod(to_fd, mode)) {
+               PERROR("install: fchmod: ", to_name);
+               bad();
+       }
+       (void)close(to_fd);
 }
 
 /*
  * strip --
  *     copy file, strip(1)'ing it at the same time
  */
 }
 
 /*
  * strip --
  *     copy file, strip(1)'ing it at the same time
  */
-static
-strip(to_fd, from_name, to_name)
-       register int    to_fd;
-       char    *from_name, *to_name;
+strip(from_fd, from_name, to_fd, to_name)
+       register int from_fd, to_fd;
+       char *from_name, *to_name;
 {
 {
-       typedef struct exec     EXEC;
-       register long   size;
-       register int    n, from_fd;
-       EXEC    head;
-       char    buf[MAXBSIZE];
+       typedef struct exec EXEC;
+       register long size;
+       register int n;
+       EXEC head;
+       char buf[MAXBSIZE];
+       off_t lseek();
 
 
-       if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
-               fprintf(stderr, "install: open: %s: %s\n", from_name, sys_errlist[errno]);
-               bad();
-       }
        if (read(from_fd, (char *)&head, sizeof(head)) < 0 || N_BADMAG(head)) {
                fprintf(stderr, "install: %s not in a.out format.\n", from_name);
                bad();
        if (read(from_fd, (char *)&head, sizeof(head)) < 0 || N_BADMAG(head)) {
                fprintf(stderr, "install: %s not in a.out format.\n", from_name);
                bad();
@@ -197,14 +214,15 @@ strip(to_fd, from_name, to_name)
                if (head.a_magic == ZMAGIC)
                        size += getpagesize() - sizeof(EXEC);
                if (write(to_fd, (char *)&head, sizeof(EXEC)) != sizeof(EXEC)) {
                if (head.a_magic == ZMAGIC)
                        size += getpagesize() - sizeof(EXEC);
                if (write(to_fd, (char *)&head, sizeof(EXEC)) != sizeof(EXEC)) {
-                       fprintf(stderr, "install: write: %s: %s\n", to_name, sys_errlist[errno]);
+                       PERROR("install: write: ", to_name);
                        bad();
                }
                for (; size; size -= n)
                        bad();
                }
                for (; size; size -= n)
+                       /* sizeof(buf) guaranteed to fit in an int */
                        if ((n = read(from_fd, buf, (int)MIN(size, sizeof(buf)))) <= 0)
                                break;
                        else if (write(to_fd, buf, n) != n) {
                        if ((n = read(from_fd, buf, (int)MIN(size, sizeof(buf)))) <= 0)
                                break;
                        else if (write(to_fd, buf, n) != n) {
-                               fprintf(stderr, "install: write: %s: %s\n", to_name, sys_errlist[errno]);
+                               PERROR("install: write: ", to_name);
                                bad();
                        }
                if (size) {
                                bad();
                        }
                if (size) {
@@ -212,9 +230,35 @@ strip(to_fd, from_name, to_name)
                        bad();
                }
                if (n == -1) {
                        bad();
                }
                if (n == -1) {
-                       fprintf(stderr, "install: read: %s: %s\n", from_name, sys_errlist[errno]);
+                       PERROR("install: read: ", from_name);
+                       bad();
+               }
+       }
+       else {
+               (void)lseek(from_fd, 0L, L_SET);
+               copy(from_fd, from_name, to_fd, to_name);
+       }
+}
+
+/*
+ * copy --
+ *     copy from one file to another
+ */
+copy(from_fd, from_name, to_fd, to_name)
+       register int from_fd, to_fd;
+       char *from_name, *to_name;
+{
+       register int n;
+       char buf[MAXBSIZE];
+
+       while ((n = read(from_fd, buf, sizeof(buf))) > 0)
+               if (write(to_fd, buf, n) != n) {
+                       PERROR("install: write: ", to_name);
                        bad();
                }
                        bad();
                }
+       if (n == -1) {
+               PERROR("install: read: ", from_name);
+               bad();
        }
 }
 
        }
 }
 
@@ -222,11 +266,10 @@ strip(to_fd, from_name, to_name)
  * atoo --
  *     octal string to int
  */
  * atoo --
  *     octal string to int
  */
-static
 atoo(str)
 atoo(str)
-       register char   *str;
+       register char *str;
 {
 {
-       register int    val;
+       register int val;
 
        for (val = 0; isdigit(*str); ++str)
                val = val * 8 + *str - '0';
 
        for (val = 0; isdigit(*str); ++str)
                val = val * 8 + *str - '0';
@@ -234,23 +277,21 @@ atoo(str)
 }
 
 /*
 }
 
 /*
- * usage --
- *     print a usage message and die
+ * bad --
+ *     remove created target and die
  */
  */
-static
-usage()
+bad()
 {
 {
-       fputs("usage: install [-cs] [-g group] [-m mode] [-o owner] source destination\n", stderr);
+       (void)unlink(pathbuf);
        exit(1);
 }
 
 /*
        exit(1);
 }
 
 /*
- * bad --
- *     remove created target and die
+ * usage --
+ *     print a usage message and die
  */
  */
-static
-bad()
+usage()
 {
 {
-       (void)unlink(path);
+       fputs("usage: install [-cs] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n", stderr);
        exit(1);
 }
        exit(1);
 }