From owner-svn-src-stable@FreeBSD.ORG Fri Mar 15 15:19:34 2013 Return-Path: Delivered-To: svn-src-stable@freebsd.org Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id BF606EB7; Fri, 15 Mar 2013 15:19:34 +0000 (UTC) (envelope-from brooks@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id A90E8860; Fri, 15 Mar 2013 15:19:34 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.6/8.14.6) with ESMTP id r2FFJYMA060497; Fri, 15 Mar 2013 15:19:34 GMT (envelope-from brooks@svn.freebsd.org) Received: (from brooks@localhost) by svn.freebsd.org (8.14.6/8.14.5/Submit) id r2FFJYjS060493; Fri, 15 Mar 2013 15:19:34 GMT (envelope-from brooks@svn.freebsd.org) Message-Id: <201303151519.r2FFJYjS060493@svn.freebsd.org> From: Brooks Davis Date: Fri, 15 Mar 2013 15:19:34 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r248331 - in stable/9: . usr.bin/xinstall X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Mar 2013 15:19:34 -0000 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 Log metadata in mtree format. * -D Log paths relative to . * -h Log digest of type . * -T Specify which mtree tags to log. * -l 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 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 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 #include #include +#include +#include #include #include +#include +#include +#include +#include #include #include #include #include #include #include +#include + +#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 ***