Please see PR 177055.  This MFC has broken mergmaster for at least a
couple people.  The PR should really be rated high priority:

http://www.freebsd.org/cgi/query-pr.cgi?pr=177055

http://lists.freebsd.org/pipermail/freebsd-stable/2013-March/072848.html

-- 
| Jeremy Chadwick                                   j...@koitsu.org |
| UNIX Systems Administrator                http://jdc.koitsu.org/ |
| Mountain View, CA, US                                            |
| Making life hard for others since 1977.             PGP 4BD6C0CB |

On Fri, Mar 15, 2013 at 03:19:34PM +0000, Brooks Davis wrote:
> Author: brooks
> Date: Fri Mar 15 15:19:33 2013
> New Revision: 248331
> URL: http://svnweb.freebsd.org/changeset/base/248331
> 
> Log:
>   MFC all changes to install(1) through r246784.  Notable functional
>   changes are:
>   
>   r245617:
>   Introduce six new options from NetBSD:
>    * -M <metalog>   Log metadata in mtree format.
>    * -D <destdir>   Log paths relative to <destdir>.
>    * -h <hash>      Log digest of type <hash>.
>    * -T <tags>      Specify which mtree tags to log.
>    * -l <linkflag>  Create hard or symbolic links (allows logging).
>    * -U             Install without root privileges (owner, group, mode,
>                     and flags can be logged via -M
>   
>   NOTE: In the interest of compatibility with NetBSD and because it is the
>   obvious letter, the nearly useless -M option (disable mmap) has been
>   repurposed.
>   
>   Sponsored by:       DARPA, AFRL
>   Obtained from:      NetBSD
>   Reviewed by:        bz
>   
>   r245312:
>   Implement the -N <dbdir> option which allows an alternate passwd and
>   group file to be used.  This is useful for installing on systems where
>   a user or group does not currently exist.
>   
>   Sponsored by:       DARPA, AFRL
>   Obtained from:      NetBSD
> 
> Modified:
>   stable/9/UPDATING   (contents, props changed)
>   stable/9/usr.bin/xinstall/Makefile
>   stable/9/usr.bin/xinstall/install.1
>   stable/9/usr.bin/xinstall/xinstall.c
> Directory Properties:
>   stable/9/usr.bin/xinstall/   (props changed)
> 
> Modified: stable/9/UPDATING
> ==============================================================================
> --- stable/9/UPDATING Fri Mar 15 14:53:29 2013        (r248330)
> +++ stable/9/UPDATING Fri Mar 15 15:19:33 2013        (r248331)
> @@ -11,6 +11,15 @@ handbook:
>  Items affecting the ports and packages system can be found in
>  /usr/ports/UPDATING.  Please read that file before running portupgrade.
>  
> +20130315:
> +     The install(1) option -M has changed meaning and now takes an
> +     argument that is a file or path to append logs to.  In the
> +     unlikely event that -M was the last option on the command line
> +     and the command line contained at least two files and a target
> +     directory the first file will have logs appended to it.  The -M
> +     option served little practical purpose in the last decade so it's
> +     used expected to be extremely rare.
> +
>  20130225:
>       A new compression method (lz4) has been merged to.  Please refer to
>       zpool-features(7) for more information.
> 
> Modified: stable/9/usr.bin/xinstall/Makefile
> ==============================================================================
> --- stable/9/usr.bin/xinstall/Makefile        Fri Mar 15 14:53:29 2013        
> (r248330)
> +++ stable/9/usr.bin/xinstall/Makefile        Fri Mar 15 15:19:33 2013        
> (r248331)
> @@ -3,6 +3,16 @@
>  
>  PROG=                xinstall
>  PROGNAME=    install
> +SRCS=                xinstall.c getid.c
>  MAN=         install.1
>  
> +.PATH:               ${.CURDIR}/../../contrib/mtree
> +.PATH:               ${.CURDIR}/../../lib/libmd
> +CFLAGS+=     -I${.CURDIR}/../../contrib/mtree
> +CFLAGS+=     -I${.CURDIR}/../../lib/libnetbsd
> +CFLAGS+=     -I${.CURDIR}/../../lib/libmd
> +
> +DPADD+=              ${LIBMD}
> +LDADD+=              -lmd
> +
>  .include <bsd.prog.mk>
> 
> Modified: stable/9/usr.bin/xinstall/install.1
> ==============================================================================
> --- stable/9/usr.bin/xinstall/install.1       Fri Mar 15 14:53:29 2013        
> (r248330)
> +++ stable/9/usr.bin/xinstall/install.1       Fri Mar 15 15:19:33 2013        
> (r248331)
> @@ -28,7 +28,7 @@
>  .\"  From: @(#)install.1     8.1 (Berkeley) 6/6/93
>  .\" $FreeBSD$
>  .\"
> -.Dd March 6, 2006
> +.Dd January 18, 2013
>  .Dt INSTALL 1
>  .Os
>  .Sh NAME
> @@ -36,31 +36,50 @@
>  .Nd install binaries
>  .Sh SYNOPSIS
>  .Nm
> -.Op Fl bCcMpSsv
> +.Op Fl bCcpSsUv
>  .Op Fl B Ar suffix
> +.Op Fl D Ar destdir
>  .Op Fl f Ar flags
>  .Op Fl g Ar group
> +.Op Fl h Ar hash
> +.Op Fl l Ar linkflags
> +.Op Fl M Ar metalog
>  .Op Fl m Ar mode
> +.Op Fl N Ar dbdir
>  .Op Fl o Ar owner
> +.Op Fl T Ar tags
>  .Ar file1 file2
>  .Nm
> -.Op Fl bCcMpSsv
> +.Op Fl bCcpSsUv
>  .Op Fl B Ar suffix
> +.Op Fl D Ar destdir
>  .Op Fl f Ar flags
>  .Op Fl g Ar group
> +.Op Fl h Ar hash
> +.Op Fl l Ar linkflags
> +.Op Fl M Ar metalog
>  .Op Fl m Ar mode
> +.Op Fl N Ar dbdir
>  .Op Fl o Ar owner
> +.Op Fl T Ar tags
>  .Ar file1 ... fileN directory
>  .Nm
>  .Fl d
> -.Op Fl v
> +.Op Fl Uv
> +.Op Fl D Ar destdir
>  .Op Fl g Ar group
> +.Op Fl h Ar hash
> +.Op Fl M Ar metalog
>  .Op Fl m Ar mode
> +.Op Fl N Ar dbdir
>  .Op Fl o Ar owner
> +.Op Fl T Ar tags
>  .Ar directory ...
>  .Sh DESCRIPTION
>  The file(s) are copied
> -to the target file or directory.
> +(or linked if the
> +.Fl l
> +option is specified) to the target file or directory.
>  If the destination is a directory, then the
>  .Ar file
>  is copied into
> @@ -105,6 +124,17 @@ This is actually the default.
>  The
>  .Fl c
>  option is only included for backwards compatibility.
> +.It Fl D Ar destdir
> +Specify the
> +.Ev DESTDIR
> +(top of the file hierarchy) that the items are installed in to.
> +If
> +.Fl M Ar metalog
> +is in use, a leading string of
> +.Dq Ar destdir
> +will be removed from the file names logged to the
> +.Ar metalog .
> +This option does not affect where the actual files are installed.
>  .It Fl d
>  Create directories.
>  Missing parent directories are created as required.
> @@ -115,15 +145,82 @@ for a list of possible flags and their m
>  .It Fl g
>  Specify a group.
>  A numeric GID is allowed.
> -.It Fl M
> -Disable all use of
> -.Xr mmap 2 .
> +.It Fl h Ar hash
> +When copying, calculate the digest of the files with
> +.Ar hash
> +to store in the
> +.Fl M Ar metalog .
> +When
> +.Fl d
> +is given no hash is emitted.
> +Supported digests:
> +.Bl -tag -width rmd160 -offset indent
> +.It Sy none
> +No hash.
> +This is the default.
> +.It Sy md5
> +The MD5 cryptographic message digest.
> +.It Sy rmd160
> +The RMD-160 cryptographic message digest.
> +.It Sy sha1
> +The SHA-1 cryptographic message digest.
> +.It Sy sha256
> +The 256-bits
> +.Tn SHA-2
> +cryptographic message digest of the file.
> +.It Sy sha512
> +The 512-bits
> +.Tn SHA-2
> +cryptographic message digest of the file.
> +.El
> +.It Fl l Ar linkflags
> +Instead of copying the file make a link to the source.
> +The type of the link is determined by the
> +.Ar linkflags
> +argument.
> +Valid
> +.Ar linkflags
> +are:
> +.Ar a
> +(absolute),
> +.Ar r
> +(relative),
> +.Ar h
> +(hard),
> +.Ar s
> +(symbolic),
> +.Ar m
> +(mixed).
> +Absolute and relative have effect only for symbolic links.
> +Mixed links
> +are hard links for files on the same filesystem, symbolic otherwise.
> +.It Fl M Ar metalog
> +Write the metadata associated with each item installed to
> +.Ar metalog
> +in an
> +.Xr mtree 8
> +.Dq full path
> +specification line.
> +The metadata includes: the file name and file type, and depending upon
> +other options, the owner, group, file flags, modification time, and tags.
>  .It Fl m
>  Specify an alternate mode.
>  The default mode is set to rwxr-xr-x (0755).
>  The specified mode may be either an octal or symbolic value; see
>  .Xr chmod 1
>  for a description of possible mode values.
> +.It Fl N
> +Use the user database text file
> +.Pa master.passwd
> +and group database text file
> +.Pa group
> +from
> +.Ar dbdir ,
> +rather than using the results from the system's
> +.Xr getpwnam 3
> +and
> +.Xr getgrnam 3
> +(and related) library calls.
>  .It Fl o
>  Specify an owner.
>  A numeric UID is allowed.
> @@ -156,6 +253,17 @@ number of systems and binary types.
>  See below for how
>  .Nm
>  can be instructed to use another program to strip binaries.
> +.It Fl T Ar tags
> +Specify the
> +.Xr mtree 8
> +tags to write out for the file when using
> +.Fl M Ar metalog .
> +.It Fl U
> +Indicate that install is running unprivileged, and that it should not
> +try to change the owner, the group, or the file flags of the destination.
> +The information that would have been updated can be stored in a log
> +file with
> +.Fl M Ar metalog .
>  .It Fl v
>  Cause
>  .Nm
> @@ -231,6 +339,8 @@ The default was changed to copy in
>  .Xr mv 1 ,
>  .Xr strip 1 ,
>  .Xr mmap 2 ,
> +.Xr getgrnam 3 ,
> +.Xr getpwnam 3 ,
>  .Xr chown 8
>  .Sh HISTORY
>  The
> @@ -238,6 +348,16 @@ The
>  utility appeared in
>  .Bx 4.2 .
>  .Sh BUGS
> +The meaning of the
> +.Fl M
> +option has changed as of
> +.Fx 10
> +and it now takes an argument.
> +Command lines that used the old
> +.Fl M
> +will get an error or in rare cases will append logs to the first of
> +multiple source files rather than installing it.
> +.Pp
>  Temporary files may be left in the target directory if
>  .Nm
>  exits abnormally.
> 
> Modified: stable/9/usr.bin/xinstall/xinstall.c
> ==============================================================================
> --- stable/9/usr.bin/xinstall/xinstall.c      Fri Mar 15 14:53:29 2013        
> (r248330)
> +++ stable/9/usr.bin/xinstall/xinstall.c      Fri Mar 15 15:19:33 2013        
> (r248331)
> @@ -1,4 +1,5 @@
>  /*
> + * Copyright (c) 2012, 2013 SRI International
>   * Copyright (c) 1987, 1993
>   *   The Regents of the University of California.  All rights reserved.
>   *
> @@ -53,14 +54,23 @@ __FBSDID("$FreeBSD$");
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <grp.h>
> +#include <libgen.h>
> +#include <md5.h>
>  #include <paths.h>
>  #include <pwd.h>
> +#include <ripemd.h>
> +#include <sha.h>
> +#include <sha256.h>
> +#include <sha512.h>
>  #include <stdint.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
>  #include <sysexits.h>
>  #include <unistd.h>
> +#include <vis.h>
> +
> +#include "mtree.h"
>  
>  /* Bootstrap aid - this doesn't exist in most older releases */
>  #ifndef MAP_FAILED
> @@ -69,26 +79,63 @@ __FBSDID("$FreeBSD$");
>  
>  #define MAX_CMP_SIZE (16 * 1024 * 1024)
>  
> +#define      LN_ABSOLUTE     0x01
> +#define      LN_RELATIVE     0x02
> +#define      LN_HARD         0x04
> +#define      LN_SYMBOLIC     0x08
> +#define      LN_MIXED        0x10
> +
>  #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;
> -gid_t gid;
> -uid_t uid;
> -int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, 
> verbose;
> -mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
> -const char *suffix = BACKUP_SUFFIX;
> -
> -static int   compare(int, const char *, size_t, int, const char *, size_t);
> -static void  copy(int, const char *, int, const char *, off_t);
> +typedef union {
> +     MD5_CTX         MD5;
> +     RIPEMD160_CTX   RIPEMD160;
> +     SHA1_CTX        SHA1;
> +     SHA256_CTX      SHA256;
> +     SHA512_CTX      SHA512;
> +}    DIGEST_CTX;
> +
> +static enum {
> +     DIGEST_NONE = 0,
> +     DIGEST_MD5,
> +     DIGEST_RIPEMD160,
> +     DIGEST_SHA1,
> +     DIGEST_SHA256,
> +     DIGEST_SHA512,
> +} digesttype = DIGEST_NONE;
> +
> +static gid_t gid;
> +static uid_t uid;
> +static int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv,
> +    safecopy, verbose;
> +static int haveopt_f, haveopt_g, haveopt_m, haveopt_o;
> +static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
> +static FILE *metafp;
> +static const char *group, *owner;
> +static const char *suffix = BACKUP_SUFFIX;
> +static char *destdir, *digest, *fflags, *metafile, *tags;
> +
> +static int   compare(int, const char *, size_t, int, const char *, size_t,
> +                 char **);
> +static char  *copy(int, const char *, int, const char *, off_t);
>  static int   create_newfile(const char *, int, struct stat *);
>  static int   create_tempfile(const char *, char *, size_t);
> +static char  *quiet_mktemp(char *template);
> +static char  *digest_file(const char *);
> +static void  digest_init(DIGEST_CTX *);
> +static void  digest_update(DIGEST_CTX *, const unsigned char *, size_t);
> +static char  *digest_end(DIGEST_CTX *, char *);
> +static int   do_link(const char *, const char *, const struct stat *);
> +static void  do_symlink(const char *, const char *, const struct stat *);
> +static void  makelink(const char *, const char *, const struct stat *);
>  static void  install(const char *, const char *, u_long, u_int);
>  static void  install_dir(char *);
> -static u_long        numeric_id(const char *, const char *);
> +static void  metadata_log(const char *, const char *, struct timeval *,
> +                 const char *, const char *, off_t);
> +static int   parseid(const char *, id_t *);
>  static void  strip(const char *);
>  static int   trymmap(int);
>  static void  usage(void);
> @@ -101,12 +148,13 @@ main(int argc, char *argv[])
>       u_long fset;
>       int ch, no_target;
>       u_int iflags;
> -     char *flags;
> -     const char *group, *owner, *to_name;
> +     char *p;
> +     const char *to_name;
>  
>       iflags = 0;
>       group = owner = NULL;
> -     while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1)
> +     while ((ch = getopt(argc, argv, "B:bCcD:df:g:h:l:M:m:N:o:pSsT:Uv")) !=
> +          -1)
>               switch((char)ch) {
>               case 'B':
>                       suffix = optarg;
> @@ -120,29 +168,69 @@ main(int argc, char *argv[])
>               case 'c':
>                       /* For backwards compatibility. */
>                       break;
> +             case 'D':
> +                     destdir = optarg;
> +                     break;
>               case 'd':
>                       dodir = 1;
>                       break;
>               case 'f':
> -                     flags = optarg;
> -                     if (strtofflags(&flags, &fset, NULL))
> -                             errx(EX_USAGE, "%s: invalid flag", flags);
> -                     iflags |= SETFLAGS;
> +                     haveopt_f = 1;
> +                     fflags = optarg;
>                       break;
>               case 'g':
> +                     haveopt_g = 1;
>                       group = optarg;
>                       break;
> +             case 'h':
> +                     digest = optarg;
> +                     break;
> +             case 'l':
> +                     for (p = optarg; *p != '\0'; p++)
> +                             switch (*p) {
> +                             case 's':
> +                                     dolink &= ~(LN_HARD|LN_MIXED);
> +                                     dolink |= LN_SYMBOLIC;
> +                                     break;
> +                             case 'h':
> +                                     dolink &= ~(LN_SYMBOLIC|LN_MIXED);
> +                                     dolink |= LN_HARD;
> +                                     break;
> +                             case 'm':
> +                                     dolink &= ~(LN_SYMBOLIC|LN_HARD);
> +                                     dolink |= LN_MIXED;
> +                                     break;
> +                             case 'a':
> +                                     dolink &= ~LN_RELATIVE;
> +                                     dolink |= LN_ABSOLUTE;
> +                                     break;
> +                             case 'r':
> +                                     dolink &= ~LN_ABSOLUTE;
> +                                     dolink |= LN_RELATIVE;
> +                                     break;
> +                             default:
> +                                     errx(1, "%c: invalid link type", *p);
> +                                     /* NOTREACHED */
> +                             }
> +                     break;
>               case 'M':
> -                     nommap = 1;
> +                     metafile = optarg;
>                       break;
>               case 'm':
> +                     haveopt_m = 1;
>                       if (!(set = setmode(optarg)))
>                               errx(EX_USAGE, "invalid file mode: %s",
>                                    optarg);
>                       mode = getmode(set, 0);
>                       free(set);
>                       break;
> +             case 'N':
> +                     if (!setup_getid(optarg))
> +                             err(EX_OSERR, "Unable to use user and group "
> +                                 "databases in `%s'", optarg);
> +                     break;
>               case 'o':
> +                     haveopt_o = 1;
>                       owner = optarg;
>                       break;
>               case 'p':
> @@ -154,6 +242,12 @@ main(int argc, char *argv[])
>               case 's':
>                       dostrip = 1;
>                       break;
> +             case 'T':
> +                     tags = optarg;
> +                     break;
> +             case 'U':
> +                     dounpriv = 1;
> +                     break;
>               case 'v':
>                       verbose = 1;
>                       break;
> @@ -179,27 +273,62 @@ main(int argc, char *argv[])
>       if (argc == 0 || (argc == 1 && !dodir))
>               usage();
>  
> +     if (digest != NULL) {
> +             if (strcmp(digest, "none") == 0) {
> +                     digesttype = DIGEST_NONE;
> +             } else if (strcmp(digest, "md5") == 0) {
> +                    digesttype = DIGEST_MD5;
> +             } else if (strcmp(digest, "rmd160") == 0) {
> +                     digesttype = DIGEST_RIPEMD160;
> +             } else if (strcmp(digest, "sha1") == 0) {
> +                     digesttype = DIGEST_SHA1;
> +             } else if (strcmp(digest, "sha256") == 0) {
> +                     digesttype = DIGEST_SHA256;
> +             } else if (strcmp(digest, "sha512") == 0) {
> +                     digesttype = DIGEST_SHA512;
> +             } else {
> +                     warnx("unknown digest `%s'", digest);
> +                     usage();
> +             }
> +     }
> +
>       /* need to make a temp copy so we can compare stripped version */
>       if (docompare && dostrip)
>               safecopy = 1;
>  
>       /* get group and owner id's */
> -     if (group != NULL) {
> -             if ((gp = getgrnam(group)) != NULL)
> -                     gid = gp->gr_gid;
> -             else
> -                     gid = (gid_t)numeric_id(group, "group");
> +     if (group != NULL && !dounpriv) {
> +             if (gid_from_group(group, &gid) == -1) {
> +                     id_t id;
> +                     if (!parseid(group, &id))
> +                             errx(1, "unknown group %s", group);
> +                     gid = id;
> +             }
>       } else
>               gid = (gid_t)-1;
>  
> -     if (owner != NULL) {
> -             if ((pp = getpwnam(owner)) != NULL)
> -                     uid = pp->pw_uid;
> -             else
> -                     uid = (uid_t)numeric_id(owner, "user");
> +     if (owner != NULL && !dounpriv) {
> +             if (uid_from_user(owner, &uid) == -1) {
> +                     id_t id;
> +                     if (!parseid(owner, &id))
> +                             errx(1, "unknown user %s", owner);
> +                     uid = id;
> +             }
>       } else
>               uid = (uid_t)-1;
>  
> +     if (fflags != NULL && !dounpriv) {
> +             if (strtofflags(&fflags, &fset, NULL))
> +                     errx(EX_USAGE, "%s: invalid flag", fflags);
> +             iflags |= SETFLAGS;
> +     }
> +
> +     if (metafile != NULL) {
> +             if ((metafp = fopen(metafile, "a")) == NULL)
> +                     warn("open %s", metafile);
> +     } else
> +             digesttype = DIGEST_NONE;
> +
>       if (dodir) {
>               for (; *argv != NULL; ++argv)
>                       install_dir(*argv);
> @@ -207,8 +336,21 @@ main(int argc, char *argv[])
>               /* NOTREACHED */
>       }
>  
> -     no_target = stat(to_name = argv[argc - 1], &to_sb);
> +     to_name = argv[argc - 1];
> +     no_target = stat(to_name, &to_sb);
>       if (!no_target && S_ISDIR(to_sb.st_mode)) {
> +             if (dolink & LN_SYMBOLIC) {
> +                     if (lstat(to_name, &to_sb) != 0)
> +                             err(EX_OSERR, "%s vanished", to_name);
> +                     if (S_ISLNK(to_sb.st_mode)) {
> +                             if (argc != 2) {
> +                                     errno = ENOTDIR;
> +                                     err(EX_USAGE, "%s", to_name);
> +                             }
> +                             install(*argv, to_name, fset, iflags);
> +                             exit(EX_OK);
> +                     }
> +             }
>               for (; *argv != to_name; ++argv)
>                       install(*argv, to_name, fset, iflags | DIRECTORY);
>               exit(EX_OK);
> @@ -226,7 +368,7 @@ main(int argc, char *argv[])
>               usage();
>       }
>  
> -     if (!no_target) {
> +     if (!no_target && !dolink) {
>               if (stat(*argv, &from_sb))
>                       err(EX_OSERR, "%s", *argv);
>               if (!S_ISREG(to_sb.st_mode)) {
> @@ -243,23 +385,327 @@ main(int argc, char *argv[])
>       /* NOTREACHED */
>  }
>  
> -static u_long
> -numeric_id(const char *name, const char *type)
> +static char *
> +digest_file(const char *name)
> +{
> +
> +     switch (digesttype) {
> +     case DIGEST_MD5:
> +             return (MD5File(name, NULL));
> +     case DIGEST_RIPEMD160:
> +             return (RIPEMD160_File(name, NULL));
> +     case DIGEST_SHA1:
> +             return (SHA1_File(name, NULL));
> +     case DIGEST_SHA256:
> +             return (SHA256_File(name, NULL));
> +     case DIGEST_SHA512:
> +             return (SHA512_File(name, NULL));
> +     default:
> +             return (NULL);
> +     }
> +}
> +
> +static void
> +digest_init(DIGEST_CTX *c)
> +{
> +
> +     switch (digesttype) {
> +     case DIGEST_NONE:
> +             break;
> +     case DIGEST_MD5:
> +             MD5Init(&(c->MD5));
> +             break;
> +     case DIGEST_RIPEMD160:
> +             RIPEMD160_Init(&(c->RIPEMD160));
> +             break;
> +     case DIGEST_SHA1:
> +             SHA1_Init(&(c->SHA1));
> +             break;
> +     case DIGEST_SHA256:
> +             SHA256_Init(&(c->SHA256));
> +             break;
> +     case DIGEST_SHA512:
> +             SHA512_Init(&(c->SHA512));
> +             break;
> +     }
> +}
> +
> +static void
> +digest_update(DIGEST_CTX *c, const unsigned char *data, size_t len)
> +{
> +
> +     switch (digesttype) {
> +     case DIGEST_NONE:
> +             break;
> +     case DIGEST_MD5:
> +             MD5Update(&(c->MD5), data, len);
> +             break;
> +     case DIGEST_RIPEMD160:
> +             RIPEMD160_Update(&(c->RIPEMD160), data, len);
> +             break;
> +     case DIGEST_SHA1:
> +             SHA1_Update(&(c->SHA1), data, len);
> +             break;
> +     case DIGEST_SHA256:
> +             SHA256_Update(&(c->SHA256), data, len);
> +             break;
> +     case DIGEST_SHA512:
> +             SHA512_Update(&(c->SHA512), data, len);
> +             break;
> +     }
> +}
> +
> +static char *
> +digest_end(DIGEST_CTX *c, char *buf)
> +{
> +
> +     switch (digesttype) {
> +     case DIGEST_MD5:
> +             return (MD5End(&(c->MD5), buf));
> +     case DIGEST_RIPEMD160:
> +             return (RIPEMD160_End(&(c->RIPEMD160), buf));
> +     case DIGEST_SHA1:
> +             return (SHA1_End(&(c->SHA1), buf));
> +     case DIGEST_SHA256:
> +             return (SHA256_End(&(c->SHA256), buf));
> +     case DIGEST_SHA512:
> +             return (SHA512_End(&(c->SHA512), buf));
> +     default:
> +             return (NULL);
> +     }
> +}
> +
> +/*
> + * parseid --
> + *   parse uid or gid from arg into id, returning non-zero if successful
> + */
> +static int
> +parseid(const char *name, id_t *id)
> +{
> +     char    *ep;
> +     errno = 0;
> +     *id = (id_t)strtoul(name, &ep, 10);
> +     if (errno || *ep != '\0')
> +             return (0);
> +     return (1);
> +}
> +
> +/*
> + * quiet_mktemp --
> + *   mktemp implementation used mkstemp to avoid mktemp warnings.  We
> + *   really do need mktemp semantics here as we will be creating a link.
> + */
> +static char *
> +quiet_mktemp(char *template)
> +{
> +     int fd;
> +
> +     if ((fd = mkstemp(template)) == -1)
> +             return (NULL);
> +     close (fd);
> +     if (unlink(template) == -1)
> +             err(EX_OSERR, "unlink %s", template);
> +     return (template);
> +}
> +
> +/*
> + * do_link --
> + *   make a hard link, obeying dorename if set
> + *   return -1 on failure
> + */
> +static int
> +do_link(const char *from_name, const char *to_name,
> +    const struct stat *target_sb)
> +{
> +     char tmpl[MAXPATHLEN];
> +     int ret;
> +
> +     if (safecopy && target_sb != NULL) {
> +             (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
> +             /* This usage is safe. */
> +             if (quiet_mktemp(tmpl) == NULL)
> +                     err(EX_OSERR, "%s: mktemp", tmpl);
> +             ret = link(from_name, tmpl);
> +             if (ret == 0) {
> +                     if (target_sb->st_mode & S_IFDIR && rmdir(to_name) ==
> +                         -1) {
> +                             unlink(tmpl);
> +                             err(EX_OSERR, "%s", to_name);
> +                     }
> +                     if (target_sb->st_flags & NOCHANGEBITS)
> +                             (void)chflags(to_name, target_sb->st_flags &
> +                                  ~NOCHANGEBITS);
> +                     unlink(to_name);
> +                     ret = rename(tmpl, to_name);
> +                     /*
> +                      * If rename has posix semantics, then the temporary
> +                      * file may still exist when from_name and to_name point
> +                      * to the same file, so unlink it unconditionally.
> +                      */
> +                     (void)unlink(tmpl);
> +             }
> +             return (ret);
> +     } else
> +             return (link(from_name, to_name));
> +}
> +
> +/*
> + * do_symlink --
> + *   Make a symbolic link, obeying dorename if set. Exit on failure.
> + */
> +static void
> +do_symlink(const char *from_name, const char *to_name,
> +    const struct stat *target_sb)
> +{
> +     char tmpl[MAXPATHLEN];
> +
> +     if (safecopy && target_sb != NULL) {
> +             (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
> +             /* This usage is safe. */
> +             if (quiet_mktemp(tmpl) == NULL)
> +                     err(EX_OSERR, "%s: mktemp", tmpl);
> +
> +             if (symlink(from_name, tmpl) == -1)
> +                     err(EX_OSERR, "symlink %s -> %s", from_name, tmpl);
> +
> +             if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) {
> +                     (void)unlink(tmpl);
> +                     err(EX_OSERR, "%s", to_name);
> +             }
> +             if (target_sb->st_flags & NOCHANGEBITS)
> +                     (void)chflags(to_name, target_sb->st_flags &
> +                          ~NOCHANGEBITS);
> +             unlink(to_name);
> +
> +             if (rename(tmpl, to_name) == -1) {
> +                     /* Remove temporary link before exiting. */
> +                     (void)unlink(tmpl);
> +                     err(EX_OSERR, "%s: rename", to_name);
> +             }
> +     } else {
> +             if (symlink(from_name, to_name) == -1)
> +                     err(EX_OSERR, "symlink %s -> %s", from_name, to_name);
> +     }
> +}
> +
> +/*
> + * makelink --
> + *   make a link from source to destination
> + */
> +static void
> +makelink(const char *from_name, const char *to_name,
> +    const struct stat *target_sb)
>  {
> -     u_long val;
> -     char *ep;
> +     char    src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
> +     struct stat     to_sb;
> +
> +     /* Try hard links first. */
> +     if (dolink & (LN_HARD|LN_MIXED)) {
> +             if (do_link(from_name, to_name, target_sb) == -1) {
> +                     if ((dolink & LN_HARD) || errno != EXDEV)
> +                             err(EX_OSERR, "link %s -> %s", from_name, 
> to_name);
> +             } else {
> +                     if (stat(to_name, &to_sb))
> +                             err(EX_OSERR, "%s: stat", to_name);
> +                     if (S_ISREG(to_sb.st_mode)) {
> +                             /*
> +                              * XXX: hard links to anything other than
> +                              * plain files are not metalogged
> +                              */
> +                             int omode;
> +                             const char *oowner, *ogroup;
> +                             char *offlags;
> +                             char *dres;
> +
> +                             /*
> +                              * XXX: use underlying perms, unless
> +                              * overridden on command line.
> +                              */
> +                             omode = mode;
> +                             if (!haveopt_m)
> +                                     mode = (to_sb.st_mode & 0777);
> +                             oowner = owner;
> +                             if (!haveopt_o)
> +                                     owner = NULL;
> +                             ogroup = group;
> +                             if (!haveopt_g)
> +                                     group = NULL;
> +                             offlags = fflags;
> +                             if (!haveopt_f)
> +                                     fflags = NULL;
> +                             dres = digest_file(from_name);
> +                             metadata_log(to_name, "file", NULL, NULL,
> +                                 dres, to_sb.st_size);
> +                             free(dres);
> +                             mode = omode;
> +                             owner = oowner;
> +                             group = ogroup;
> +                             fflags = offlags;
> +                     }
> +                     return;
> +             }
> +     }
> +
> +     /* Symbolic links. */
> +     if (dolink & LN_ABSOLUTE) {
> +             /* Convert source path to absolute. */
> +             if (realpath(from_name, src) == NULL)
> +                     err(EX_OSERR, "%s: realpath", from_name);
> +             do_symlink(src, to_name, target_sb);
> +             /* XXX: src may point outside of destdir */
> +             metadata_log(to_name, "link", NULL, src, NULL, 0);
> +             return;
> +     }
> +
> +     if (dolink & LN_RELATIVE) {
> +             char *cp, *d, *s;
> +
> +             /* Resolve pathnames. */
> +             if (realpath(from_name, src) == NULL)
> +                     err(EX_OSERR, "%s: realpath", from_name);
> +
> +             /*
> +              * The last component of to_name may be a symlink,
> +              * so use realpath to resolve only the directory.
> +              */
> +             cp = dirname(to_name);
> +             if (realpath(cp, dst) == NULL)
> +                     err(EX_OSERR, "%s: realpath", cp);
> +             /* .. and add the last component. */
> +             if (strcmp(dst, "/") != 0) {
> +                     if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
> +                             errx(1, "resolved pathname too long");
> +             }
> +             cp = basename(to_name);
> +             if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
> +                     errx(1, "resolved pathname too long");
> +
> +             /* Trim common path components. */
> +             for (s = src, d = dst; *s == *d; s++, d++)
> +                     continue;
> +             while (*s != '/')
> +                     s--, d--;
> +
> +             /* Count the number of directories we need to backtrack. */
> +             for (++d, lnk[0] = '\0'; *d; d++)
> +                     if (*d == '/')
> +                             (void)strlcat(lnk, "../", sizeof(lnk));
> +
> +             (void)strlcat(lnk, ++s, sizeof(lnk));
> +
> +             do_symlink(lnk, to_name, target_sb);
> +             /* XXX: Link may point outside of destdir. */
> +             metadata_log(to_name, "link", NULL, lnk, NULL, 0);
> +             return;
> +     }
>  
>       /*
> -      * XXX
> -      * We know that uid_t's and gid_t's are unsigned longs.
> +      * If absolute or relative was not specified, try the names the
> +      * user provided.
>        */
> -     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);
> +     do_symlink(from_name, to_name, target_sb);
> +     /* XXX: from_name may point outside of destdir. */
> +     metadata_log(to_name, "link", NULL, from_name, NULL, 0);
>  }
>  
>  /*
> @@ -274,6 +720,7 @@ install(const char *from_name, const cha
>       int devnull, files_match, from_fd, serrno, target;
>       int tempcopy, temp_fd, to_fd;
>       char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
> +     char *digestresult;
>  
>       files_match = 0;
>       from_fd = -1;
> @@ -281,11 +728,13 @@ install(const char *from_name, const cha
>  
>       /* If try to install NULL file to a directory, fails. */
>       if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
> -             if (stat(from_name, &from_sb))
> -                     err(EX_OSERR, "%s", from_name);
> -             if (!S_ISREG(from_sb.st_mode)) {
> -                     errno = EFTYPE;
> -                     err(EX_OSERR, "%s", from_name);
> +             if (!dolink) {
> +                     if (stat(from_name, &from_sb))
> +                             err(EX_OSERR, "%s", from_name);
> +                     if (!S_ISREG(from_sb.st_mode)) {
> +                             errno = EFTYPE;
> +                             err(EX_OSERR, "%s", from_name);
> +                     }
>               }
>               /* Build the target path. */
>               if (flags & DIRECTORY) {
> @@ -299,7 +748,23 @@ install(const char *from_name, const cha
>               devnull = 1;
>       }
>  
> -     target = stat(to_name, &to_sb) == 0;
> +     if (!dolink)
> +             target = (stat(to_name, &to_sb) == 0);
> +     else
> +             target = (lstat(to_name, &to_sb) == 0);
> +
> +     if (dolink) {
> +             if (target && !safecopy) {
> +                     if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1)
> +                             err(EX_OSERR, "%s", to_name);
> +                     if (to_sb.st_flags & NOCHANGEBITS)
> +                             (void)chflags(to_name,
> +                                 to_sb.st_flags & ~NOCHANGEBITS);
> +                     unlink(to_name);
> +             }
> +             makelink(from_name, to_name, target ? &to_sb : NULL);
> +             return;
> +     }
>  
>       /* Only install to regular files. */
>       if (target && !S_ISREG(to_sb.st_mode)) {
> @@ -323,7 +788,7 @@ install(const char *from_name, const cha
>               else
>                       files_match = !(compare(from_fd, from_name,
>                           (size_t)from_sb.st_size, to_fd,
> -                         to_name, (size_t)to_sb.st_size));
> +                         to_name, (size_t)to_sb.st_size, &digestresult));
>  
>               /* Close "to" file unless we match. */
>               if (!files_match)
> @@ -345,8 +810,10 @@ install(const char *from_name, const cha
>                                   from_name, to_name);
>               }
>               if (!devnull)
> -                     copy(from_fd, from_name, to_fd,
> +                     digestresult = copy(from_fd, from_name, to_fd,
>                            tempcopy ? tempfile : to_name, from_sb.st_size);
> +             else
> +                     digestresult = NULL;
>       }
>  
>       if (dostrip) {
> @@ -380,7 +847,8 @@ install(const char *from_name, const cha
>               }
>  
>               if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
> -                         to_name, (size_t)to_sb.st_size) == 0) {
> +                         to_name, (size_t)to_sb.st_size, &digestresult)
> +                         == 0) {
>                       /*
>                        * If target has more than one link we need to
>                        * replace it in order to snap the extra links.
> @@ -400,6 +868,9 @@ install(const char *from_name, const cha
>               }
>       }
>  
> +     if (dostrip && (!docompare || !target))
> +             digestresult = digest_file(tempfile);
> +
>       /*
> 
> *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
> _______________________________________________
> svn-src-stable-9@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/svn-src-stable-9
> To unsubscribe, send any mail to "svn-src-stable-9-unsubscr...@freebsd.org"
_______________________________________________
svn-src-stable-9@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-stable-9
To unsubscribe, send any mail to "svn-src-stable-9-unsubscr...@freebsd.org"

Reply via email to