Hi!

The attached patch incorporates most of OpenBSD fixes to install(1).
It does not include manpage update.  Most significant changes are:

o All TODOs are acted upon.

o New flags: -b and -B

: -b    Backup any existing files before overwriting them by
:       renaming them to file.old.  See -B for specifying a
:       different backup suffix.
: 
: -B suffix
:       Use suffix as the backup suffix if -b is given.

o New flag: -S (atomic install)

: -S    Safe copy.  Normally, install unlinks an existing target before
:       installing the new file.  With the -S flag a temporary file is
:       used and then renamed to be the target.  The reason this is safer
:       is that if the copy or rename fails, the existing target is left
:       untouched.

o The -c flag is now the default, and only provided for backwards
  compatibility.  We now never remove the original file.

o Flags -v and -D were withdrawn.

o strip(1) failure is not considered fatal.

Please review.


Thanks,
-- 
Ruslan Ermilov          Oracle Developer/DBA,
[EMAIL PROTECTED]           Sunbay Software AG,
[EMAIL PROTECTED]          FreeBSD committer,
+380.652.512.251        Simferopol, Ukraine

http://www.FreeBSD.org  The Power To Serve
http://www.oracle.com   Enabling The Information Age
Index: xinstall.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/xinstall/xinstall.c,v
retrieving revision 1.40
diff -u -p -r1.40 xinstall.c
--- xinstall.c  2000/10/08 09:17:56     1.40
+++ xinstall.c  2001/04/20 15:42:20
@@ -45,21 +45,6 @@ static const char rcsid[] =
   "$FreeBSD: src/usr.bin/xinstall/xinstall.c,v 1.40 2000/10/08 09:17:56 bde Exp $";
 #endif /* not lint */
 
-/*-
- * Todo:
- * o for -C, compare original files except in -s case.
- * o for -C, don't change anything if nothing needs be changed.  In
- *   particular, don't toggle the immutable flags just to allow null
- *   attribute changes and don't clear the dump flag.  (I think inode
- *   ctimes are not updated for null attribute changes, but this is a
- *   bug.)
- * o independent of -C, if a copy must be made, then copy to a tmpfile,
- *   set all attributes except the immutable flags, then rename, then
- *   set the immutable flags.  It's annoying that the immutable flags
- *   defeat the atomicicity of rename - it seems that there must be
- *   a window where the target is not immutable.
- */
-
 #include <sys/param.h>
 #include <sys/wait.h>
 #include <sys/mman.h>
@@ -82,45 +67,29 @@ static const char rcsid[] =
 
 #include "pathnames.h"
 
-/* Bootstrap aid - this doesn't exist in most older releases */
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *)-1)        /* from <sys/mman.h> */
-#endif
-
-int debug, docompare, docopy, dodir, dopreserve, dostrip, nommap, verbose;
-int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
-char *group, *owner, pathbuf[MAXPATHLEN];
-char pathbuf2[MAXPATHLEN];
-
 #define        DIRECTORY       0x01            /* Tell install it's a directory. */
 #define        SETFLAGS        0x02            /* Tell install to set flags. */
 #define        NOCHANGEBITS    (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
+#define        BACKUP_SUFFIX   ".old"
 
+struct passwd *pp;
+struct group *gp;
+int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy;
+int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+char pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
+char *suffix = BACKUP_SUFFIX;
+uid_t uid;
+gid_t gid;
+
 void   copy __P((int, char *, int, char *, off_t));
-int    compare __P((int, const char *, int, const char *, 
-                    const struct stat *, const struct stat *));
+int    compare __P((int, const char *, size_t, int, const char *, size_t));
+int    create_newfile __P((char *, struct stat *));
+int    create_tempfile __P((char *, char *, size_t));
 void   install __P((char *, char *, u_long, u_int));
 void   install_dir __P((char *));
 void   strip __P((char *));
-void   usage __P((void));
 int    trymmap __P((int));
-
-#define ALLOW_NUMERIC_IDS 1
-#ifdef ALLOW_NUMERIC_IDS
-
-uid_t   uid = -1;
-gid_t   gid = -1;
-
-uid_t  resolve_uid __P((char *));
-gid_t  resolve_gid __P((char *));
-u_long numeric_id __P((char *, char *));
-
-#else
-
-struct passwd *pp;
-struct group *gp;
-
-#endif /* ALLOW_NUMERIC_IDS */
+void   usage __P((void));
 
 int
 main(argc, argv)
@@ -132,20 +101,23 @@ main(argc, argv)
        u_long fset;
        u_int iflags;
        int ch, no_target;
-       char *flags, *to_name;
+       char *flags, *to_name, *group = NULL, *owner = NULL;
 
        iflags = 0;
-       while ((ch = getopt(argc, argv, "CcdDf:g:m:Mo:psv")) != -1)
+       while ((ch = getopt(argc, argv, "BbCcdf:g:Mm:o:pSs")) != -1)
                switch((char)ch) {
+               case 'B':
+                       suffix = optarg;
+                       /* FALLTHROUGH */
+               case 'b':
+                       dobackup = 1;
+                       break;
                case 'C':
-                       docompare = docopy = 1;
+                       docompare = 1;
                        break;
                case 'c':
-                       docopy = 1;
+                       /* For backwards compatibility. */
                        break;
-               case 'D':
-                       debug++;
-                       break;
                case 'd':
                        dodir = 1;
                        break;
@@ -158,6 +130,9 @@ main(argc, argv)
                case 'g':
                        group = optarg;
                        break;
+               case 'M':
+                       nommap = 1;
+                       break;
                case 'm':
                        if (!(set = setmode(optarg)))
                                errx(EX_USAGE, "invalid file mode: %s",
@@ -165,21 +140,18 @@ main(argc, argv)
                        mode = getmode(set, 0);
                        free(set);
                        break;
-               case 'M':
-                       nommap = 1;
-                       break;
                case 'o':
                        owner = optarg;
                        break;
                case 'p':
-                       docompare = docopy = dopreserve = 1;
+                       docompare = dopreserve = 1;
+                       break;
+               case 'S':
+                       safecopy = 1;
                        break;
                case 's':
                        dostrip = 1;
                        break;
-               case 'v':
-                       verbose = 1;
-                       break;
                case '?':
                default:
                        usage();
@@ -188,29 +160,24 @@ main(argc, argv)
        argv += optind;
 
        /* some options make no sense when creating directories */
-       if (dostrip && dodir)
+       if ((safecopy || docompare || dostrip) && dodir)
                usage();
 
        /* must have at least two arguments, except when creating directories */
        if (argc < 2 && !dodir)
                usage();
-
-#ifdef ALLOW_NUMERIC_IDS
 
-       if (owner)
-               uid = resolve_uid(owner);
-       if (group)
-               gid = resolve_gid(group);
+       /* need to make a temp copy so we can compare stripped version */
+       if (docompare && dostrip)
+               safecopy = 1;
 
-#else
-
        /* get group and owner id's */
-       if (owner && !(pp = getpwnam(owner)))
-               errx(EX_NOUSER, "unknown user %s", owner);
-       if (group && !(gp = getgrnam(group)))
+       if (group && !(gp = getgrnam(group)) && !isdigit(*group))
                errx(EX_NOUSER, "unknown group %s", group);
-
-#endif /* ALLOW_NUMERIC_IDS */
+       gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : 
+(gid_t)-1;
+       if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner))
+               errx(EX_NOUSER, "unknown user %s", owner);
+       uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : 
+(uid_t)-1;
 
        if (dodir) {
                for (; *argv != NULL; ++argv)
@@ -220,7 +187,7 @@ main(argc, argv)
        }
 
        no_target = stat(to_name = argv[argc - 1], &to_sb);
-       if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
+       if (!no_target && S_ISDIR(to_sb.st_mode)) {
                for (; *argv != to_name; ++argv)
                        install(*argv, to_name, fset, iflags | DIRECTORY);
                exit(EX_OK);
@@ -242,72 +209,12 @@ main(argc, argv)
                    to_sb.st_ino == from_sb.st_ino)
                        errx(EX_USAGE, 
                            "%s and %s are the same file", *argv, to_name);
-/*
- * XXX - It's not at all clear why this code was here, since it completely
- * duplicates code install().  The version in install() handles the -C flag
- * correctly, so we'll just disable this for now.
- */
-#if 0
-               /*
-                * Unlink now... avoid ETXTBSY errors later.  Try and turn
-                * off the append/immutable bits -- if we fail, go ahead,
-                * it might work.
-                */
-               if (to_sb.st_flags & NOCHANGEBITS)
-                       (void)chflags(to_name,
-                           to_sb.st_flags & ~(NOCHANGEBITS));
-               (void)unlink(to_name);
-#endif
        }
        install(*argv, to_name, fset, iflags);
        exit(EX_OK);
        /* NOTREACHED */
 }
 
-#ifdef ALLOW_NUMERIC_IDS
-
-uid_t
-resolve_uid(s)
-       char *s;
-{
-       struct passwd *pw;
-
-       return ((pw = getpwnam(s)) == NULL) ?
-               (uid_t) numeric_id(s, "user") : pw->pw_uid;
-}
-
-gid_t
-resolve_gid(s)
-       char *s;
-{
-       struct group *gr;
-
-       return ((gr = getgrnam(s)) == NULL) ?
-               (gid_t) numeric_id(s, "group") : gr->gr_gid;
-}
-
-u_long
-numeric_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(EX_NOUSER, "%s", name);
-       if (*ep != '\0')
-               errx(EX_NOUSER, "unknown %s %s", type, name);
-       return (val);
-}
-
-#endif /* ALLOW_NUMERIC_IDS */
-
 /*
  * install --
  *     build a path name and install the file
@@ -319,12 +226,15 @@ install(from_name, to_name, fset, flags)
        u_int flags;
 {
        struct stat from_sb, to_sb;
-       int devnull, from_fd, to_fd, serrno;
-       char *p, *old_to_name = 0;
+       struct utimbuf utb;
+       int devnull, from_fd, to_fd, serrno, files_match = 0;
+       char *p;
 
-       if (debug >= 2 && !docompare)
-               fprintf(stderr, "install: invoked without -C for %s to %s\n",
-                       from_name, to_name);
+#ifdef __GNUC__                                /* XXX: to shut up gcc warnings */
+        (void)&from_fd;
+#endif
+
+       (void)memset((void *)&from_sb, 0, sizeof(from_sb));
 
        /* If try to install NULL file to a directory, fails. */
        if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
@@ -343,143 +253,155 @@ install(from_name, to_name, fset, flags)
                }
                devnull = 0;
        } else {
-               from_sb.st_flags = 0;   /* XXX */
                devnull = 1;
        }
 
-       if (docompare) {
-               old_to_name = to_name;
-               /*
-                * Make a new temporary file in the same file system
-                * (actually, in in the same directory) as the target so
-                * that the temporary file can be renamed to the target.
-                */
-               snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name);
-               p = strrchr(pathbuf2, '/');
-               p = (p == NULL ? pathbuf2 : p + 1);
-               snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX");
-               to_fd = mkstemp(pathbuf2);
+       if (stat(to_name, &to_sb) == 0) {
+               /* Only compare against regular files. */
+               if (docompare && !S_ISREG(to_sb.st_mode)) {
+                       docompare = 0;
+                       errno = EFTYPE;
+                       warn("%s", to_name);
+               }
+       } else if (docompare) {
+               /* File does not exist so silently ignore compare flag. */
+               docompare = 0;
+       }
+
+       if (safecopy) {
+               to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));
                if (to_fd < 0)
-                       /* XXX should fall back to not comparing. */
-                       err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name);
-               to_name = pathbuf2;
+                       err(EX_OSERR, "%s", tempfile);
+       } else if (docompare && !dostrip) {
+               if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
+                       err(EX_OSERR, "%s", to_name);
        } else {
-               /*
-                * Unlink now... avoid errors later.  Try to turn off the
-                * append/immutable bits -- if we fail, go ahead, it might
-                * work.
-                */
-               if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS)
-                       (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
-               unlink(to_name);
-
-               /* Create target. */
-               to_fd = open(to_name,
-                            O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
-               if (to_fd < 0)
+               if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
                        err(EX_OSERR, "%s", to_name);
        }
 
        if (!devnull) {
                if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
                        serrno = errno;
-                       (void)unlink(to_name);
+                       (void)unlink(safecopy ? tempfile : to_name);
                        errno = serrno;
                        err(EX_OSERR, "%s", from_name);
                }
-               copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
-               (void)close(from_fd);
+
+               if (docompare && !safecopy) {
+                       files_match = !(compare(from_fd, from_name,
+                                       (size_t)from_sb.st_size, to_fd,
+                                       to_name, (size_t)to_sb.st_size));
+
+                       /* Truncate "to" file for copy unless we match */
+                       if (!files_match) {
+                               (void)close(to_fd);
+                               if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
+                                       err(EX_OSERR, "%s", to_name);
+                       }
+               }
+               if (!files_match)
+                       copy(from_fd, from_name, to_fd,
+                            safecopy ? tempfile : to_name, from_sb.st_size);
        }
 
        if (dostrip) {
-               (void)close(to_fd);
+               strip(safecopy ? tempfile : to_name);
 
-               strip(to_name);
-
-               /* Reopen target. */
-               to_fd = open(to_name, O_RDWR, 0);
+               /*
+                * Re-open our fd on the target, in case we used a strip
+                * that does not work in-place -- like GNU binutils strip.
+                */
+               close(to_fd);
+               to_fd = open(safecopy ? tempfile : to_name, O_RDONLY, 0);
                if (to_fd < 0)
-                       err(EX_OSERR, "%s", to_name);
+                       err(EX_OSERR, "stripping %s", to_name);
        }
 
        /*
-        * Unfortunately, because we strip the installed file and not the
-        * original one, it is impossible to do the comparison without
-        * first laboriously copying things over and then comparing.
-        * It may be possible to better optimize the !dostrip case, however.
-        * For further study.
+        * Compare the (possibly stripped) temp file to the target.
         */
-       if (docompare) {
-               struct stat old_sb, new_sb, timestamp_sb;
-               int old_fd;
-               struct utimbuf utb;
-
-               old_fd = open(old_to_name, O_RDONLY, 0);
-               if (old_fd < 0 && errno == ENOENT)
-                       goto different;
-               if (old_fd < 0)
-                       err(EX_OSERR, "%s", old_to_name);
-               fstat(old_fd, &old_sb);
-               if (old_sb.st_flags & NOCHANGEBITS)
-                       (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS);
-               fstat(to_fd, &new_sb);
-               if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb,
-                           &new_sb)) {
-different:
-                       if (debug != 0)
-                               fprintf(stderr,
-                                       "install: renaming for %s: %s to %s\n",
-                                       from_name, to_name, old_to_name);
-                       if (verbose != 0)
-                               printf("install: %s -> %s\n",
-                                       from_name, old_to_name);
-                       if (dopreserve && stat(from_name, &timestamp_sb) == 0) {
-                               utb.actime = timestamp_sb.st_atime;
-                               utb.modtime = timestamp_sb.st_mtime;
-                               (void)utime(to_name, &utb);
+       if (safecopy && docompare) {
+               int temp_fd = to_fd;
+               struct stat temp_sb;
+
+               /* Re-open to_fd using the real target name. */
+               if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
+                       err(EX_OSERR, "%s", to_name);
+
+               if (fstat(temp_fd, &temp_sb)) {
+                       serrno = errno;
+                       (void)unlink(tempfile);
+                       errno = serrno;
+                       err(EX_OSERR, "%s", tempfile);
+               }
+
+               if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
+                           to_name, (size_t)to_sb.st_size) == 0) {
+                       /*
+                        * If target has more than one link we need to
+                        * replace it in order to snap the extra links.
+                        * Need to preserve target file times, though.
+                        */
+                       if (to_sb.st_nlink != 1) {
+                               utb.actime = to_sb.st_atime;
+                               utb.modtime = to_sb.st_mtime;
+                               (void)utime(tempfile, &utb);
+                       } else {
+                               files_match = 1;
+                               (void)unlink(tempfile);
                        }
-moveit:
-                       if (rename(to_name, old_to_name) < 0) {
+                       (void) close(temp_fd);
+               }
+       }
+
+       /*
+        * Move the new file into place if doing a safe copy
+        * and the files are different (or just not compared).
+        */
+       if (safecopy && !files_match) {
+               /* Try to turn off the immutable bits. */
+               if (to_sb.st_flags & (NOCHANGEBITS))
+                       (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
+               if (dobackup) {
+                       char backup[MAXPATHLEN];
+                       (void)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
+                           suffix);
+                       if (rename(to_name, backup) < 0) {
                                serrno = errno;
-                               unlink(to_name);
-                               unlink(old_to_name);
-                               errno = serrno;
-                               err(EX_OSERR, "rename: %s to %s", to_name,
-                                   old_to_name);
-                       }
-                       close(old_fd);
-               } else {
-                       if (old_sb.st_nlink != 1) {
-                               /*
-                                * Replace the target, although it hasn't
-                                * changed, to snap the extra links.  But
-                                * preserve the target file times.
-                                */
-                               if (fstat(old_fd, &timestamp_sb) == 0) {
-                                       utb.actime = timestamp_sb.st_atime;
-                                       utb.modtime = timestamp_sb.st_mtime;
-                                       (void)utime(to_name, &utb);
-                               }
-                               goto moveit;
+                               unlink(tempfile);
+                               errx(EX_OSERR, "rename: %s to %s: %s", to_name,
+                                    backup, strerror(serrno));
                        }
-                       if (unlink(to_name) < 0)
-                               err(EX_OSERR, "unlink: %s", to_name);
-                       close(to_fd);
-                       to_fd = old_fd;
+               }
+               if (rename(tempfile, to_name) < 0) {
+                       serrno = errno;
+                       unlink(tempfile);
+                       errno = serrno;
+                       err(EX_OSERR, "rename: %s to %s",
+                           tempfile, to_name);
                }
-               to_name = old_to_name;
+
+               /* Re-open to_fd so we aren't hosed by the rename(2). */
+               (void) close(to_fd);
+               if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
+                       err(EX_OSERR, "%s", to_name);
        }
 
        /*
+        * Preserve the timestamp of the source file if necesary.
+        */
+       if (dopreserve && !files_match) {
+               utb.actime = from_sb.st_atime;
+               utb.modtime = from_sb.st_mtime;
+               (void)utime(to_name, &utb);
+       }
+
+       /*
         * Set owner, group, mode for target; do the chown first,
         * chown may lose the setuid bits.
         */
-       if ((group || owner) &&
-#ifdef ALLOW_NUMERIC_IDS
-           fchown(to_fd, owner ? uid : -1, group ? gid : -1)) {
-#else
-           fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
-#endif
+       if ((gid != (gid_t)-1 || uid != (uid_t)-1) && fchown(to_fd, uid, gid)) {
                serrno = errno;
                (void)unlink(to_name);
                errno = serrno;
@@ -514,8 +436,8 @@ moveit:
        }
 
        (void)close(to_fd);
-       if (!docopy && !devnull && unlink(from_name))
-               err(EX_OSERR, "%s", from_name);
+       if (!devnull)
+               (void)close(from_fd);
 }
 
 /*
@@ -523,35 +445,32 @@ moveit:
  *     compare two files; non-zero means files differ
  */
 int
-compare(int from_fd, const char *from_name, int to_fd, const char *to_name,
-       const struct stat *from_sb, const struct stat *to_sb)
+compare(int from_fd, const char *from_name, size_t from_len,
+       int to_fd, const char *to_name, size_t to_len)
 {
        char *p, *q;
        int rv;
-       size_t tsize;
        int done_compare;
 
        rv = 0;
-       if (from_sb->st_size != to_sb->st_size)
+       if (from_len != to_len)
                return 1;
-
-       tsize = (size_t)from_sb->st_size;
 
-       if (tsize <= 8 * 1024 * 1024) {
+       if (from_len <= 8 * 1024 * 1024) {
                done_compare = 0;
                if (trymmap(from_fd) && trymmap(to_fd)) {
-                       p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, 
(off_t)0);
+                       p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, 
+(off_t)0);
                        if (p == (char *)MAP_FAILED)
                                goto out;
-                       q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+                       q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, 
+(off_t)0);
                        if (q == (char *)MAP_FAILED) {
-                               munmap(p, tsize);
+                               munmap(p, from_len);
                                goto out;
                        }
 
-                       rv = memcmp(p, q, tsize);
-                       munmap(p, tsize);
-                       munmap(q, tsize);
+                       rv = memcmp(p, q, from_len);
+                       munmap(p, from_len);
+                       munmap(q, from_len);
                        done_compare = 1;
                }
        out:
@@ -586,6 +505,59 @@ compare(int from_fd, const char *from_na
 }
 
 /*
+ * create_tempfile --
+ *     create a temporary file based on path and open it
+ */
+int
+create_tempfile(path, temp, tsize)
+       char *path;
+       char *temp;
+       size_t tsize;
+{
+       char *p;
+
+       (void)strncpy(temp, path, tsize);
+       temp[tsize - 1] = '\0';
+       if ((p = strrchr(temp, '/')) != NULL)
+               p++;
+       else
+               p = temp;
+       (void)strncpy(p, "INS@XXXXXX", &temp[tsize - 1] - p);
+       temp[tsize - 1] = '\0';
+
+       return (mkstemp(temp));
+}
+
+/*
+ * create_newfile --
+ *     create a new file, overwriting an existing one if necessary
+ */
+int
+create_newfile(path, sbp)
+        char *path;
+       struct stat *sbp;
+{
+       char backup[MAXPATHLEN];
+
+       /*
+        * Unlink now... avoid ETXTBSY errors later.  Try to turn
+        * off the append/immutable bits -- if we fail, go ahead,
+        * it might work.
+        */
+       if (sbp->st_flags & NOCHANGEBITS)
+               (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
+
+       if (dobackup) {
+               (void)snprintf(backup, MAXPATHLEN, "%s%s", path, suffix);
+               if (rename(path, backup) < 0)
+                       err(EX_OSERR, "rename: %s to %s", path, backup);
+       } else
+               (void)unlink(path);
+
+       return(open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+}
+
+/*
  * copy --
  *     copy from one file to another
  */
@@ -600,16 +572,21 @@ copy(from_fd, from_name, to_fd, to_name,
        char *p, buf[MAXBSIZE];
        int done_copy;
 
+       /* Rewind file descriptors. */
+       if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
+               err(EX_OSERR, "lseek: %s", from_name);
+       if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
+               err(EX_OSERR, "lseek: %s", to_name);
+
        /*
         * Mmap and write if less than 8M (the limit is so we don't totally
         * trash memory on big files.  This is really a minor hack, but it
         * wins some CPU back.
         */
        done_copy = 0;
-       if (size <= 8 * 1048576 && trymmap(from_fd)) {
-               if ((p = mmap(NULL, (size_t)size, PROT_READ,
-                               MAP_SHARED, from_fd, (off_t)0)) == (char *)MAP_FAILED)
-                       goto out;
+       if (size <= 8 * 1048576 && trymmap(from_fd) &&
+           (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
+                   from_fd, (off_t)0)) != (char *)MAP_FAILED) {
                if ((nw = write(to_fd, p, size)) != size) {
                        serrno = errno;
                        (void)unlink(to_name);
@@ -617,7 +594,6 @@ copy(from_fd, from_name, to_fd, to_name,
                        err(EX_OSERR, "%s", to_name);
                }
                done_copy = 1;
-       out:
        }
        if (!done_copy) {
                while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
@@ -656,7 +632,7 @@ strip(to_name)
                execlp("strip", "strip", to_name, NULL);
                err(EX_OSERR, "exec(strip)");
        default:
-               if (wait(&status) == -1 || status) {
+               if (wait(&status) == -1 || !WIFEXITED(status)) {
                        (void)unlink(to_name);
                        exit(EX_SOFTWARE);
                        /* NOTREACHED */
@@ -704,11 +680,12 @@ install_dir(path)
 void
 usage()
 {
-       (void)fprintf(stderr,"\
-usage: install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\
-       install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 ...\n\
-             fileN directory\n\
-       install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n");
+       (void)fprintf(stderr, "\
+usage: install [-bCcpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n\
+               file1 file2\n\
+       install [-bCcpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n\
+               file1 ... fileN directory\n\
+       install -d [-g group] [-m mode] [-o owner] directory ...\n");
        exit(EX_USAGE);
        /* NOTREACHED */
 }

Reply via email to