11590Srgrimes/* 2245617Sbrooks * Copyright (c) 2012, 2013 SRI International 31590Srgrimes * Copyright (c) 1987, 1993 41590Srgrimes * The Regents of the University of California. All rights reserved. 51590Srgrimes * 61590Srgrimes * Redistribution and use in source and binary forms, with or without 71590Srgrimes * modification, are permitted provided that the following conditions 81590Srgrimes * are met: 91590Srgrimes * 1. Redistributions of source code must retain the above copyright 101590Srgrimes * notice, this list of conditions and the following disclaimer. 111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer in the 131590Srgrimes * documentation and/or other materials provided with the distribution. 141590Srgrimes * 4. Neither the name of the University nor the names of its contributors 151590Srgrimes * may be used to endorse or promote products derived from this software 161590Srgrimes * without specific prior written permission. 171590Srgrimes * 181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281590Srgrimes * SUCH DAMAGE. 291590Srgrimes */ 301590Srgrimes 311590Srgrimes#ifndef lint 3211356Sbdestatic const char copyright[] = 331590Srgrimes"@(#) Copyright (c) 1987, 1993\n\ 341590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3596439Sbde#endif /* not lint */ 361590Srgrimes 3796439Sbde#if 0 381590Srgrimes#ifndef lint 3996439Sbdestatic char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; 4096439Sbde#endif /* not lint */ 4128827Scharnier#endif 421590Srgrimes 4396439Sbde#include <sys/cdefs.h> 4496439Sbde__FBSDID("$FreeBSD$"); 4596439Sbde 461590Srgrimes#include <sys/param.h> 471590Srgrimes#include <sys/mman.h> 4887724Sru#include <sys/mount.h> 491590Srgrimes#include <sys/stat.h> 50107413Sbde#include <sys/time.h> 5187724Sru#include <sys/wait.h> 521590Srgrimes 5311356Sbde#include <err.h> 541590Srgrimes#include <errno.h> 551590Srgrimes#include <fcntl.h> 561590Srgrimes#include <grp.h> 57245617Sbrooks#include <libgen.h> 58245617Sbrooks#include <md5.h> 591590Srgrimes#include <paths.h> 601590Srgrimes#include <pwd.h> 61245617Sbrooks#include <ripemd.h> 62245617Sbrooks#include <sha.h> 63245617Sbrooks#include <sha256.h> 64245617Sbrooks#include <sha512.h> 65237988Skib#include <stdint.h> 661590Srgrimes#include <stdio.h> 671590Srgrimes#include <stdlib.h> 681590Srgrimes#include <string.h> 6987685Smarkm#include <sysexits.h> 7018525Simp#include <unistd.h> 71245617Sbrooks#include <vis.h> 721590Srgrimes 73245312Sbrooks#include "mtree.h" 74245312Sbrooks 7526083Speter/* Bootstrap aid - this doesn't exist in most older releases */ 7626083Speter#ifndef MAP_FAILED 7732490Salex#define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */ 7826083Speter#endif 7926083Speter 8092611Sdes#define MAX_CMP_SIZE (16 * 1024 * 1024) 8192611Sdes 82245617Sbrooks#define LN_ABSOLUTE 0x01 83245617Sbrooks#define LN_RELATIVE 0x02 84245617Sbrooks#define LN_HARD 0x04 85245617Sbrooks#define LN_SYMBOLIC 0x08 86245617Sbrooks#define LN_MIXED 0x10 87245617Sbrooks 881590Srgrimes#define DIRECTORY 0x01 /* Tell install it's a directory. */ 891590Srgrimes#define SETFLAGS 0x02 /* Tell install to set flags. */ 9011356Sbde#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 9177345Sru#define BACKUP_SUFFIX ".old" 921590Srgrimes 93245617Sbrookstypedef union { 94245617Sbrooks MD5_CTX MD5; 95245617Sbrooks RIPEMD160_CTX RIPEMD160; 96245617Sbrooks SHA1_CTX SHA1; 97245617Sbrooks SHA256_CTX SHA256; 98245617Sbrooks SHA512_CTX SHA512; 99245617Sbrooks} DIGEST_CTX; 100245617Sbrooks 101245617Sbrooksstatic enum { 102245617Sbrooks DIGEST_NONE = 0, 103245617Sbrooks DIGEST_MD5, 104245617Sbrooks DIGEST_RIPEMD160, 105245617Sbrooks DIGEST_SHA1, 106245617Sbrooks DIGEST_SHA256, 107245617Sbrooks DIGEST_SHA512, 108245617Sbrooks} digesttype = DIGEST_NONE; 109245617Sbrooks 110227202Sedstatic gid_t gid; 111227202Sedstatic uid_t uid; 112245617Sbrooksstatic int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv, 113245617Sbrooks safecopy, verbose; 114245617Sbrooksstatic int haveopt_f, haveopt_g, haveopt_m, haveopt_o; 115227202Sedstatic mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 116245617Sbrooksstatic FILE *metafp; 117245617Sbrooksstatic const char *group, *owner; 118227202Sedstatic const char *suffix = BACKUP_SUFFIX; 119245617Sbrooksstatic char *destdir, *digest, *fflags, *metafile, *tags; 12077345Sru 121245617Sbrooksstatic int compare(int, const char *, size_t, int, const char *, size_t, 122245617Sbrooks char **); 123245617Sbrooksstatic char *copy(int, const char *, int, const char *, off_t); 124200465Sdelphijstatic int create_newfile(const char *, int, struct stat *); 125200465Sdelphijstatic int create_tempfile(const char *, char *, size_t); 126245617Sbrooksstatic char *quiet_mktemp(char *template); 127245617Sbrooksstatic char *digest_file(const char *); 128245617Sbrooksstatic void digest_init(DIGEST_CTX *); 129245617Sbrooksstatic void digest_update(DIGEST_CTX *, const unsigned char *, size_t); 130245617Sbrooksstatic char *digest_end(DIGEST_CTX *, char *); 131245617Sbrooksstatic int do_link(const char *, const char *, const struct stat *); 132245617Sbrooksstatic void do_symlink(const char *, const char *, const struct stat *); 133245617Sbrooksstatic void makelink(const char *, const char *, const struct stat *); 134200465Sdelphijstatic void install(const char *, const char *, u_long, u_int); 135200465Sdelphijstatic void install_dir(char *); 136245617Sbrooksstatic void metadata_log(const char *, const char *, struct timeval *, 137245617Sbrooks const char *, const char *, off_t); 138245312Sbrooksstatic int parseid(const char *, id_t *); 139200465Sdelphijstatic void strip(const char *); 140200465Sdelphijstatic int trymmap(int); 141200465Sdelphijstatic void usage(void); 1421590Srgrimes 1431590Srgrimesint 144102944Sdwmalonemain(int argc, char *argv[]) 1451590Srgrimes{ 1461590Srgrimes struct stat from_sb, to_sb; 1471590Srgrimes mode_t *set; 1481590Srgrimes u_long fset; 14977345Sru int ch, no_target; 1501590Srgrimes u_int iflags; 151245617Sbrooks char *p; 152245617Sbrooks const char *to_name; 1531590Srgrimes 1541590Srgrimes iflags = 0; 15577345Sru group = owner = NULL; 156245617Sbrooks while ((ch = getopt(argc, argv, "B:bCcD:df:g:h:l:M:m:N:o:pSsT:Uv")) != 157245617Sbrooks -1) 1581590Srgrimes switch((char)ch) { 15977345Sru case 'B': 16077345Sru suffix = optarg; 16177345Sru /* FALLTHROUGH */ 16277345Sru case 'b': 16377345Sru dobackup = 1; 16477345Sru break; 16511356Sbde case 'C': 16677345Sru docompare = 1; 16711356Sbde break; 1681590Srgrimes case 'c': 16977345Sru /* For backwards compatibility. */ 1701590Srgrimes break; 171245617Sbrooks case 'D': 172245617Sbrooks destdir = optarg; 173245617Sbrooks break; 17418551Simp case 'd': 17518551Simp dodir = 1; 17618551Simp break; 1771590Srgrimes case 'f': 178245617Sbrooks haveopt_f = 1; 179245617Sbrooks fflags = optarg; 1801590Srgrimes break; 1811590Srgrimes case 'g': 182245617Sbrooks haveopt_g = 1; 1831590Srgrimes group = optarg; 1841590Srgrimes break; 185245617Sbrooks case 'h': 186245617Sbrooks digest = optarg; 187245617Sbrooks break; 188245617Sbrooks case 'l': 189245617Sbrooks for (p = optarg; *p != '\0'; p++) 190245617Sbrooks switch (*p) { 191245617Sbrooks case 's': 192245617Sbrooks dolink &= ~(LN_HARD|LN_MIXED); 193245617Sbrooks dolink |= LN_SYMBOLIC; 194245617Sbrooks break; 195245617Sbrooks case 'h': 196245617Sbrooks dolink &= ~(LN_SYMBOLIC|LN_MIXED); 197245617Sbrooks dolink |= LN_HARD; 198245617Sbrooks break; 199245617Sbrooks case 'm': 200245617Sbrooks dolink &= ~(LN_SYMBOLIC|LN_HARD); 201245617Sbrooks dolink |= LN_MIXED; 202245617Sbrooks break; 203245617Sbrooks case 'a': 204245617Sbrooks dolink &= ~LN_RELATIVE; 205245617Sbrooks dolink |= LN_ABSOLUTE; 206245617Sbrooks break; 207245617Sbrooks case 'r': 208245617Sbrooks dolink &= ~LN_ABSOLUTE; 209245617Sbrooks dolink |= LN_RELATIVE; 210245617Sbrooks break; 211245617Sbrooks default: 212245617Sbrooks errx(1, "%c: invalid link type", *p); 213245617Sbrooks /* NOTREACHED */ 214245617Sbrooks } 215245617Sbrooks break; 21677345Sru case 'M': 217245617Sbrooks metafile = optarg; 21877345Sru break; 2191590Srgrimes case 'm': 220245617Sbrooks haveopt_m = 1; 2211590Srgrimes if (!(set = setmode(optarg))) 22211356Sbde errx(EX_USAGE, "invalid file mode: %s", 22311356Sbde optarg); 2241590Srgrimes mode = getmode(set, 0); 22541847Simp free(set); 2261590Srgrimes break; 227245312Sbrooks case 'N': 228245312Sbrooks if (!setup_getid(optarg)) 229245617Sbrooks err(EX_OSERR, "Unable to use user and group " 230245312Sbrooks "databases in `%s'", optarg); 231245312Sbrooks break; 2321590Srgrimes case 'o': 233245617Sbrooks haveopt_o = 1; 2341590Srgrimes owner = optarg; 2351590Srgrimes break; 23611356Sbde case 'p': 23777345Sru docompare = dopreserve = 1; 23811356Sbde break; 23977345Sru case 'S': 24077345Sru safecopy = 1; 24177345Sru break; 2421590Srgrimes case 's': 2431590Srgrimes dostrip = 1; 2441590Srgrimes break; 245245617Sbrooks case 'T': 246245617Sbrooks tags = optarg; 247245617Sbrooks break; 248245617Sbrooks case 'U': 249245617Sbrooks dounpriv = 1; 250245617Sbrooks break; 25117546Speter case 'v': 25217546Speter verbose = 1; 25317546Speter break; 2541590Srgrimes case '?': 2551590Srgrimes default: 256125553Sru usage(); 2571590Srgrimes } 2581590Srgrimes argc -= optind; 2591590Srgrimes argv += optind; 26018551Simp 26118551Simp /* some options make no sense when creating directories */ 262127112Sru if (dostrip && dodir) { 263127112Sru warnx("-d and -s may not be specified together"); 264125553Sru usage(); 265127112Sru } 2661590Srgrimes 267156363Sobrien if (getenv("DONTSTRIP") != NULL) { 268156363Sobrien warnx("DONTSTRIP set - will not strip installed binaries"); 269156363Sobrien dostrip = 0; 270156363Sobrien } 271156363Sobrien 27218551Simp /* must have at least two arguments, except when creating directories */ 273125553Sru if (argc == 0 || (argc == 1 && !dodir)) 274125553Sru usage(); 27518551Simp 276245617Sbrooks if (digest != NULL) { 277245617Sbrooks if (strcmp(digest, "none") == 0) { 278245617Sbrooks digesttype = DIGEST_NONE; 279245617Sbrooks } else if (strcmp(digest, "md5") == 0) { 280245617Sbrooks digesttype = DIGEST_MD5; 281245617Sbrooks } else if (strcmp(digest, "rmd160") == 0) { 282245617Sbrooks digesttype = DIGEST_RIPEMD160; 283245617Sbrooks } else if (strcmp(digest, "sha1") == 0) { 284245617Sbrooks digesttype = DIGEST_SHA1; 285245617Sbrooks } else if (strcmp(digest, "sha256") == 0) { 286245617Sbrooks digesttype = DIGEST_SHA256; 287245617Sbrooks } else if (strcmp(digest, "sha512") == 0) { 288245617Sbrooks digesttype = DIGEST_SHA512; 289245617Sbrooks } else { 290245617Sbrooks warnx("unknown digest `%s'", digest); 291245617Sbrooks usage(); 292245617Sbrooks } 293245617Sbrooks } 294245617Sbrooks 29577345Sru /* need to make a temp copy so we can compare stripped version */ 29677345Sru if (docompare && dostrip) 29777345Sru safecopy = 1; 29815069Sjulian 2991590Srgrimes /* get group and owner id's */ 300245617Sbrooks if (group != NULL && !dounpriv) { 301245312Sbrooks if (gid_from_group(group, &gid) == -1) { 302245617Sbrooks id_t id; 303245312Sbrooks if (!parseid(group, &id)) 304245312Sbrooks errx(1, "unknown group %s", group); 305245312Sbrooks gid = id; 306245312Sbrooks } 30777345Sru } else 30877345Sru gid = (gid_t)-1; 3091590Srgrimes 310245617Sbrooks if (owner != NULL && !dounpriv) { 311245312Sbrooks if (uid_from_user(owner, &uid) == -1) { 312245312Sbrooks id_t id; 313245312Sbrooks if (!parseid(owner, &id)) 314245312Sbrooks errx(1, "unknown user %s", owner); 315245312Sbrooks uid = id; 316245312Sbrooks } 31777345Sru } else 31877345Sru uid = (uid_t)-1; 31915069Sjulian 320245617Sbrooks if (fflags != NULL && !dounpriv) { 321245617Sbrooks if (strtofflags(&fflags, &fset, NULL)) 322245617Sbrooks errx(EX_USAGE, "%s: invalid flag", fflags); 323245617Sbrooks iflags |= SETFLAGS; 324245617Sbrooks } 325245617Sbrooks 326245617Sbrooks if (metafile != NULL) { 327245617Sbrooks if ((metafp = fopen(metafile, "a")) == NULL) 328245617Sbrooks warn("open %s", metafile); 329245617Sbrooks } else 330245617Sbrooks digesttype = DIGEST_NONE; 331245617Sbrooks 33218551Simp if (dodir) { 33318551Simp for (; *argv != NULL; ++argv) 33418551Simp install_dir(*argv); 33518551Simp exit(EX_OK); 33618551Simp /* NOTREACHED */ 33718551Simp } 33818551Simp 339245793Sbrooks to_name = argv[argc - 1]; 340245793Sbrooks no_target = stat(to_name, &to_sb); 34177345Sru if (!no_target && S_ISDIR(to_sb.st_mode)) { 342245793Sbrooks if (dolink & LN_SYMBOLIC) { 343245793Sbrooks if (lstat(to_name, &to_sb) != 0) 344245793Sbrooks err(EX_OSERR, "%s vanished", to_name); 345245793Sbrooks if (S_ISLNK(to_sb.st_mode)) { 346245793Sbrooks if (argc != 2) { 347245793Sbrooks errno = ENOTDIR; 348245793Sbrooks err(EX_USAGE, "%s", to_name); 349245793Sbrooks } 350245793Sbrooks install(*argv, to_name, fset, iflags); 351245793Sbrooks exit(EX_OK); 352245793Sbrooks } 353245793Sbrooks } 3541590Srgrimes for (; *argv != to_name; ++argv) 3551590Srgrimes install(*argv, to_name, fset, iflags | DIRECTORY); 35618525Simp exit(EX_OK); 35718525Simp /* NOTREACHED */ 3581590Srgrimes } 3591590Srgrimes 3601590Srgrimes /* can't do file1 file2 directory/file */ 361127112Sru if (argc != 2) { 362174587Sedwin if (no_target) 363174587Sedwin warnx("target directory `%s' does not exist", 364174587Sedwin argv[argc - 1]); 365174587Sedwin else 366174587Sedwin warnx("target `%s' is not a directory", 367174587Sedwin argv[argc - 1]); 368125553Sru usage(); 369127112Sru } 3701590Srgrimes 371245617Sbrooks if (!no_target && !dolink) { 3721590Srgrimes if (stat(*argv, &from_sb)) 37311356Sbde err(EX_OSERR, "%s", *argv); 37411356Sbde if (!S_ISREG(to_sb.st_mode)) { 37511356Sbde errno = EFTYPE; 37611356Sbde err(EX_OSERR, "%s", to_name); 37711356Sbde } 3781590Srgrimes if (to_sb.st_dev == from_sb.st_dev && 3791590Srgrimes to_sb.st_ino == from_sb.st_ino) 38011356Sbde errx(EX_USAGE, 38111356Sbde "%s and %s are the same file", *argv, to_name); 3821590Srgrimes } 3831590Srgrimes install(*argv, to_name, fset, iflags); 38418525Simp exit(EX_OK); 38518525Simp /* NOTREACHED */ 3861590Srgrimes} 3871590Srgrimes 388245617Sbrooksstatic char * 389245617Sbrooksdigest_file(const char *name) 390245617Sbrooks{ 391245617Sbrooks 392245617Sbrooks switch (digesttype) { 393245617Sbrooks case DIGEST_MD5: 394245617Sbrooks return (MD5File(name, NULL)); 395245617Sbrooks case DIGEST_RIPEMD160: 396245617Sbrooks return (RIPEMD160_File(name, NULL)); 397245617Sbrooks case DIGEST_SHA1: 398245617Sbrooks return (SHA1_File(name, NULL)); 399245617Sbrooks case DIGEST_SHA256: 400245617Sbrooks return (SHA256_File(name, NULL)); 401245617Sbrooks case DIGEST_SHA512: 402245617Sbrooks return (SHA512_File(name, NULL)); 403245617Sbrooks default: 404245617Sbrooks return (NULL); 405245617Sbrooks } 406245617Sbrooks} 407245617Sbrooks 408245617Sbrooksstatic void 409245617Sbrooksdigest_init(DIGEST_CTX *c) 410245617Sbrooks{ 411245617Sbrooks 412245617Sbrooks switch (digesttype) { 413245617Sbrooks case DIGEST_NONE: 414245617Sbrooks break; 415245617Sbrooks case DIGEST_MD5: 416245617Sbrooks MD5Init(&(c->MD5)); 417245617Sbrooks break; 418245617Sbrooks case DIGEST_RIPEMD160: 419245617Sbrooks RIPEMD160_Init(&(c->RIPEMD160)); 420245617Sbrooks break; 421245617Sbrooks case DIGEST_SHA1: 422245617Sbrooks SHA1_Init(&(c->SHA1)); 423245617Sbrooks break; 424245617Sbrooks case DIGEST_SHA256: 425245617Sbrooks SHA256_Init(&(c->SHA256)); 426245617Sbrooks break; 427245617Sbrooks case DIGEST_SHA512: 428245617Sbrooks SHA512_Init(&(c->SHA512)); 429245617Sbrooks break; 430245617Sbrooks } 431245617Sbrooks} 432245617Sbrooks 433245617Sbrooksstatic void 434245617Sbrooksdigest_update(DIGEST_CTX *c, const unsigned char *data, size_t len) 435245617Sbrooks{ 436245617Sbrooks 437245617Sbrooks switch (digesttype) { 438245617Sbrooks case DIGEST_NONE: 439245617Sbrooks break; 440245617Sbrooks case DIGEST_MD5: 441245617Sbrooks MD5Update(&(c->MD5), data, len); 442245617Sbrooks break; 443245617Sbrooks case DIGEST_RIPEMD160: 444245617Sbrooks RIPEMD160_Update(&(c->RIPEMD160), data, len); 445245617Sbrooks break; 446245617Sbrooks case DIGEST_SHA1: 447245617Sbrooks SHA1_Update(&(c->SHA1), data, len); 448245617Sbrooks break; 449245617Sbrooks case DIGEST_SHA256: 450245617Sbrooks SHA256_Update(&(c->SHA256), data, len); 451245617Sbrooks break; 452245617Sbrooks case DIGEST_SHA512: 453245617Sbrooks SHA512_Update(&(c->SHA512), data, len); 454245617Sbrooks break; 455245617Sbrooks } 456245617Sbrooks} 457245617Sbrooks 458245617Sbrooksstatic char * 459245617Sbrooksdigest_end(DIGEST_CTX *c, char *buf) 460245617Sbrooks{ 461245617Sbrooks 462245617Sbrooks switch (digesttype) { 463245617Sbrooks case DIGEST_MD5: 464245617Sbrooks return (MD5End(&(c->MD5), buf)); 465245617Sbrooks case DIGEST_RIPEMD160: 466245617Sbrooks return (RIPEMD160_End(&(c->RIPEMD160), buf)); 467245617Sbrooks case DIGEST_SHA1: 468245617Sbrooks return (SHA1_End(&(c->SHA1), buf)); 469245617Sbrooks case DIGEST_SHA256: 470245617Sbrooks return (SHA256_End(&(c->SHA256), buf)); 471245617Sbrooks case DIGEST_SHA512: 472245617Sbrooks return (SHA512_End(&(c->SHA512), buf)); 473245617Sbrooks default: 474245617Sbrooks return (NULL); 475245617Sbrooks } 476245617Sbrooks} 477245617Sbrooks 478245312Sbrooks/* 479245312Sbrooks * parseid -- 480245312Sbrooks * parse uid or gid from arg into id, returning non-zero if successful 481245312Sbrooks */ 482245312Sbrooksstatic int 483245312Sbrooksparseid(const char *name, id_t *id) 48415069Sjulian{ 485245312Sbrooks char *ep; 48615069Sjulian errno = 0; 487245312Sbrooks *id = (id_t)strtoul(name, &ep, 10); 488245312Sbrooks if (errno || *ep != '\0') 489245312Sbrooks return (0); 490245312Sbrooks return (1); 49115069Sjulian} 49215069Sjulian 4931590Srgrimes/* 494245617Sbrooks * quiet_mktemp -- 495245617Sbrooks * mktemp implementation used mkstemp to avoid mktemp warnings. We 496245617Sbrooks * really do need mktemp semantics here as we will be creating a link. 497245617Sbrooks */ 498245617Sbrooksstatic char * 499245617Sbrooksquiet_mktemp(char *template) 500245617Sbrooks{ 501245617Sbrooks int fd; 502245617Sbrooks 503245617Sbrooks if ((fd = mkstemp(template)) == -1) 504245617Sbrooks return (NULL); 505245617Sbrooks close (fd); 506245617Sbrooks if (unlink(template) == -1) 507245617Sbrooks err(EX_OSERR, "unlink %s", template); 508245617Sbrooks return (template); 509245617Sbrooks} 510245617Sbrooks 511245617Sbrooks/* 512245617Sbrooks * do_link -- 513245617Sbrooks * make a hard link, obeying dorename if set 514245617Sbrooks * return -1 on failure 515245617Sbrooks */ 516245617Sbrooksstatic int 517245617Sbrooksdo_link(const char *from_name, const char *to_name, 518245617Sbrooks const struct stat *target_sb) 519245617Sbrooks{ 520245617Sbrooks char tmpl[MAXPATHLEN]; 521245617Sbrooks int ret; 522245617Sbrooks 523245617Sbrooks if (safecopy && target_sb != NULL) { 524245617Sbrooks (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); 525245617Sbrooks /* This usage is safe. */ 526245617Sbrooks if (quiet_mktemp(tmpl) == NULL) 527245617Sbrooks err(EX_OSERR, "%s: mktemp", tmpl); 528245617Sbrooks ret = link(from_name, tmpl); 529245617Sbrooks if (ret == 0) { 530245617Sbrooks if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == 531245617Sbrooks -1) { 532245617Sbrooks unlink(tmpl); 533245617Sbrooks err(EX_OSERR, "%s", to_name); 534245617Sbrooks } 535245617Sbrooks if (target_sb->st_flags & NOCHANGEBITS) 536245617Sbrooks (void)chflags(to_name, target_sb->st_flags & 537245617Sbrooks ~NOCHANGEBITS); 538245617Sbrooks unlink(to_name); 539245617Sbrooks ret = rename(tmpl, to_name); 540245617Sbrooks /* 541245617Sbrooks * If rename has posix semantics, then the temporary 542245617Sbrooks * file may still exist when from_name and to_name point 543245617Sbrooks * to the same file, so unlink it unconditionally. 544245617Sbrooks */ 545245617Sbrooks (void)unlink(tmpl); 546245617Sbrooks } 547245617Sbrooks return (ret); 548245617Sbrooks } else 549245617Sbrooks return (link(from_name, to_name)); 550245617Sbrooks} 551245617Sbrooks 552245617Sbrooks/* 553245617Sbrooks * do_symlink -- 554245617Sbrooks * Make a symbolic link, obeying dorename if set. Exit on failure. 555245617Sbrooks */ 556245617Sbrooksstatic void 557245617Sbrooksdo_symlink(const char *from_name, const char *to_name, 558245617Sbrooks const struct stat *target_sb) 559245617Sbrooks{ 560245617Sbrooks char tmpl[MAXPATHLEN]; 561245617Sbrooks 562245617Sbrooks if (safecopy && target_sb != NULL) { 563245617Sbrooks (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); 564245617Sbrooks /* This usage is safe. */ 565245617Sbrooks if (quiet_mktemp(tmpl) == NULL) 566245617Sbrooks err(EX_OSERR, "%s: mktemp", tmpl); 567245617Sbrooks 568245617Sbrooks if (symlink(from_name, tmpl) == -1) 569245617Sbrooks err(EX_OSERR, "symlink %s -> %s", from_name, tmpl); 570245617Sbrooks 571245617Sbrooks if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) { 572245617Sbrooks (void)unlink(tmpl); 573245617Sbrooks err(EX_OSERR, "%s", to_name); 574245617Sbrooks } 575245617Sbrooks if (target_sb->st_flags & NOCHANGEBITS) 576245617Sbrooks (void)chflags(to_name, target_sb->st_flags & 577245617Sbrooks ~NOCHANGEBITS); 578245617Sbrooks unlink(to_name); 579245617Sbrooks 580245617Sbrooks if (rename(tmpl, to_name) == -1) { 581245617Sbrooks /* Remove temporary link before exiting. */ 582245617Sbrooks (void)unlink(tmpl); 583245617Sbrooks err(EX_OSERR, "%s: rename", to_name); 584245617Sbrooks } 585245617Sbrooks } else { 586245617Sbrooks if (symlink(from_name, to_name) == -1) 587245617Sbrooks err(EX_OSERR, "symlink %s -> %s", from_name, to_name); 588245617Sbrooks } 589245617Sbrooks} 590245617Sbrooks 591245617Sbrooks/* 592245617Sbrooks * makelink -- 593245617Sbrooks * make a link from source to destination 594245617Sbrooks */ 595245617Sbrooksstatic void 596245617Sbrooksmakelink(const char *from_name, const char *to_name, 597245617Sbrooks const struct stat *target_sb) 598245617Sbrooks{ 599245617Sbrooks char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; 600245617Sbrooks struct stat to_sb; 601245617Sbrooks 602245617Sbrooks /* Try hard links first. */ 603245617Sbrooks if (dolink & (LN_HARD|LN_MIXED)) { 604245617Sbrooks if (do_link(from_name, to_name, target_sb) == -1) { 605245617Sbrooks if ((dolink & LN_HARD) || errno != EXDEV) 606245617Sbrooks err(EX_OSERR, "link %s -> %s", from_name, to_name); 607245617Sbrooks } else { 608245617Sbrooks if (stat(to_name, &to_sb)) 609245617Sbrooks err(EX_OSERR, "%s: stat", to_name); 610245617Sbrooks if (S_ISREG(to_sb.st_mode)) { 611245617Sbrooks /* 612245617Sbrooks * XXX: hard links to anything other than 613245617Sbrooks * plain files are not metalogged 614245617Sbrooks */ 615245617Sbrooks int omode; 616245617Sbrooks const char *oowner, *ogroup; 617245617Sbrooks char *offlags; 618245617Sbrooks char *dres; 619245617Sbrooks 620245617Sbrooks /* 621245617Sbrooks * XXX: use underlying perms, unless 622245617Sbrooks * overridden on command line. 623245617Sbrooks */ 624245617Sbrooks omode = mode; 625245617Sbrooks if (!haveopt_m) 626245617Sbrooks mode = (to_sb.st_mode & 0777); 627245617Sbrooks oowner = owner; 628245617Sbrooks if (!haveopt_o) 629245617Sbrooks owner = NULL; 630245617Sbrooks ogroup = group; 631245617Sbrooks if (!haveopt_g) 632245617Sbrooks group = NULL; 633245617Sbrooks offlags = fflags; 634245617Sbrooks if (!haveopt_f) 635245617Sbrooks fflags = NULL; 636245617Sbrooks dres = digest_file(from_name); 637245617Sbrooks metadata_log(to_name, "file", NULL, NULL, 638245617Sbrooks dres, to_sb.st_size); 639245617Sbrooks free(dres); 640245617Sbrooks mode = omode; 641245617Sbrooks owner = oowner; 642245617Sbrooks group = ogroup; 643245617Sbrooks fflags = offlags; 644245617Sbrooks } 645245617Sbrooks return; 646245617Sbrooks } 647245617Sbrooks } 648245617Sbrooks 649245617Sbrooks /* Symbolic links. */ 650245617Sbrooks if (dolink & LN_ABSOLUTE) { 651245617Sbrooks /* Convert source path to absolute. */ 652245617Sbrooks if (realpath(from_name, src) == NULL) 653245617Sbrooks err(EX_OSERR, "%s: realpath", from_name); 654245617Sbrooks do_symlink(src, to_name, target_sb); 655245617Sbrooks /* XXX: src may point outside of destdir */ 656245617Sbrooks metadata_log(to_name, "link", NULL, src, NULL, 0); 657245617Sbrooks return; 658245617Sbrooks } 659245617Sbrooks 660245617Sbrooks if (dolink & LN_RELATIVE) { 661245617Sbrooks char *cp, *d, *s; 662245617Sbrooks 663245617Sbrooks /* Resolve pathnames. */ 664245617Sbrooks if (realpath(from_name, src) == NULL) 665245617Sbrooks err(EX_OSERR, "%s: realpath", from_name); 666245617Sbrooks 667245617Sbrooks /* 668245617Sbrooks * The last component of to_name may be a symlink, 669245617Sbrooks * so use realpath to resolve only the directory. 670245617Sbrooks */ 671245617Sbrooks cp = dirname(to_name); 672245617Sbrooks if (realpath(cp, dst) == NULL) 673245617Sbrooks err(EX_OSERR, "%s: realpath", cp); 674245617Sbrooks /* .. and add the last component. */ 675245617Sbrooks if (strcmp(dst, "/") != 0) { 676245617Sbrooks if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst)) 677245617Sbrooks errx(1, "resolved pathname too long"); 678245617Sbrooks } 679245617Sbrooks cp = basename(to_name); 680245617Sbrooks if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst)) 681245617Sbrooks errx(1, "resolved pathname too long"); 682245617Sbrooks 683245617Sbrooks /* Trim common path components. */ 684245617Sbrooks for (s = src, d = dst; *s == *d; s++, d++) 685245617Sbrooks continue; 686245617Sbrooks while (*s != '/') 687245617Sbrooks s--, d--; 688245617Sbrooks 689245617Sbrooks /* Count the number of directories we need to backtrack. */ 690245617Sbrooks for (++d, lnk[0] = '\0'; *d; d++) 691245617Sbrooks if (*d == '/') 692245617Sbrooks (void)strlcat(lnk, "../", sizeof(lnk)); 693245617Sbrooks 694245617Sbrooks (void)strlcat(lnk, ++s, sizeof(lnk)); 695245617Sbrooks 696245617Sbrooks do_symlink(lnk, to_name, target_sb); 697245617Sbrooks /* XXX: Link may point outside of destdir. */ 698245617Sbrooks metadata_log(to_name, "link", NULL, lnk, NULL, 0); 699245617Sbrooks return; 700245617Sbrooks } 701245617Sbrooks 702245617Sbrooks /* 703245617Sbrooks * If absolute or relative was not specified, try the names the 704245617Sbrooks * user provided. 705245617Sbrooks */ 706245617Sbrooks do_symlink(from_name, to_name, target_sb); 707245617Sbrooks /* XXX: from_name may point outside of destdir. */ 708245617Sbrooks metadata_log(to_name, "link", NULL, from_name, NULL, 0); 709245617Sbrooks} 710245617Sbrooks 711245617Sbrooks/* 7121590Srgrimes * install -- 7131590Srgrimes * build a path name and install the file 7141590Srgrimes */ 715200515Sdelphijstatic void 716102944Sdwmaloneinstall(const char *from_name, const char *to_name, u_long fset, u_int flags) 7171590Srgrimes{ 71877345Sru struct stat from_sb, temp_sb, to_sb; 719106967Speter struct timeval tvb[2]; 72077345Sru int devnull, files_match, from_fd, serrno, target; 72177345Sru int tempcopy, temp_fd, to_fd; 72277345Sru char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; 723245617Sbrooks char *digestresult; 7241590Srgrimes 72577345Sru files_match = 0; 726140817Sssouhlal from_fd = -1; 727140817Sssouhlal to_fd = -1; 72811356Sbde 7291590Srgrimes /* If try to install NULL file to a directory, fails. */ 7301590Srgrimes if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 731245617Sbrooks if (!dolink) { 732245617Sbrooks if (stat(from_name, &from_sb)) 733245617Sbrooks err(EX_OSERR, "%s", from_name); 734245617Sbrooks if (!S_ISREG(from_sb.st_mode)) { 735245617Sbrooks errno = EFTYPE; 736245617Sbrooks err(EX_OSERR, "%s", from_name); 737245617Sbrooks } 73811356Sbde } 7391590Srgrimes /* Build the target path. */ 7401590Srgrimes if (flags & DIRECTORY) { 7411590Srgrimes (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 7421590Srgrimes to_name, 74311356Sbde (p = strrchr(from_name, '/')) ? ++p : from_name); 7441590Srgrimes to_name = pathbuf; 7451590Srgrimes } 7461590Srgrimes devnull = 0; 7471590Srgrimes } else { 7481590Srgrimes devnull = 1; 7491590Srgrimes } 7501590Srgrimes 751245617Sbrooks if (!dolink) 752245617Sbrooks target = (stat(to_name, &to_sb) == 0); 753245617Sbrooks else 754245617Sbrooks target = (lstat(to_name, &to_sb) == 0); 7551590Srgrimes 756245617Sbrooks if (dolink) { 757245617Sbrooks if (target && !safecopy) { 758245617Sbrooks if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1) 759245617Sbrooks err(EX_OSERR, "%s", to_name); 760245617Sbrooks if (to_sb.st_flags & NOCHANGEBITS) 761245617Sbrooks (void)chflags(to_name, 762245617Sbrooks to_sb.st_flags & ~NOCHANGEBITS); 763245617Sbrooks unlink(to_name); 764245617Sbrooks } 765245617Sbrooks makelink(from_name, to_name, target ? &to_sb : NULL); 766245617Sbrooks return; 767245617Sbrooks } 768245617Sbrooks 76977345Sru /* Only install to regular files. */ 77077345Sru if (target && !S_ISREG(to_sb.st_mode)) { 77177345Sru errno = EFTYPE; 77277345Sru warn("%s", to_name); 77377345Sru return; 77477345Sru } 77577345Sru 77677345Sru /* Only copy safe if the target exists. */ 77777345Sru tempcopy = safecopy && target; 77877345Sru 77977345Sru if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) 78077345Sru err(EX_OSERR, "%s", from_name); 78177345Sru 78277345Sru /* If we don't strip, we can compare first. */ 78377345Sru if (docompare && !dostrip && target) { 78477345Sru if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 78511356Sbde err(EX_OSERR, "%s", to_name); 78677345Sru if (devnull) 78777345Sru files_match = to_sb.st_size == 0; 78877345Sru else 78977345Sru files_match = !(compare(from_fd, from_name, 79077345Sru (size_t)from_sb.st_size, to_fd, 791245617Sbrooks to_name, (size_t)to_sb.st_size, &digestresult)); 79277345Sru 79377345Sru /* Close "to" file unless we match. */ 79477345Sru if (!files_match) 79577345Sru (void)close(to_fd); 79611356Sbde } 79711356Sbde 79877345Sru if (!files_match) { 79977345Sru if (tempcopy) { 80077345Sru to_fd = create_tempfile(to_name, tempfile, 80177345Sru sizeof(tempfile)); 80277345Sru if (to_fd < 0) 80377345Sru err(EX_OSERR, "%s", tempfile); 80477345Sru } else { 80577345Sru if ((to_fd = create_newfile(to_name, target, 80677345Sru &to_sb)) < 0) 80777345Sru err(EX_OSERR, "%s", to_name); 80877345Sru if (verbose) 80977345Sru (void)printf("install: %s -> %s\n", 81077345Sru from_name, to_name); 8111590Srgrimes } 81277345Sru if (!devnull) 813245617Sbrooks digestresult = copy(from_fd, from_name, to_fd, 81477345Sru tempcopy ? tempfile : to_name, from_sb.st_size); 815245617Sbrooks else 816245617Sbrooks digestresult = NULL; 8171590Srgrimes } 81811356Sbde 81929379Speter if (dostrip) { 82077345Sru strip(tempcopy ? tempfile : to_name); 82129379Speter 82277345Sru /* 82377345Sru * Re-open our fd on the target, in case we used a strip 82477345Sru * that does not work in-place -- like GNU binutils strip. 82577345Sru */ 82677345Sru close(to_fd); 82777345Sru to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0); 82829379Speter if (to_fd < 0) 82977345Sru err(EX_OSERR, "stripping %s", to_name); 83029379Speter } 83129379Speter 8321590Srgrimes /* 83377345Sru * Compare the stripped temp file with the target. 83411356Sbde */ 83577345Sru if (docompare && dostrip && target) { 83677345Sru temp_fd = to_fd; 83711356Sbde 83877345Sru /* Re-open to_fd using the real target name. */ 83977345Sru if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 84077345Sru err(EX_OSERR, "%s", to_name); 84177345Sru 84277345Sru if (fstat(temp_fd, &temp_sb)) { 84377345Sru serrno = errno; 84477345Sru (void)unlink(tempfile); 84577345Sru errno = serrno; 84677345Sru err(EX_OSERR, "%s", tempfile); 84777345Sru } 84877345Sru 84977345Sru if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd, 850245617Sbrooks to_name, (size_t)to_sb.st_size, &digestresult) 851245617Sbrooks == 0) { 85277345Sru /* 85377345Sru * If target has more than one link we need to 85477345Sru * replace it in order to snap the extra links. 85577345Sru * Need to preserve target file times, though. 85677345Sru */ 85777345Sru if (to_sb.st_nlink != 1) { 858106967Speter tvb[0].tv_sec = to_sb.st_atime; 859106967Speter tvb[0].tv_usec = 0; 860106967Speter tvb[1].tv_sec = to_sb.st_mtime; 861106967Speter tvb[1].tv_usec = 0; 862106967Speter (void)utimes(tempfile, tvb); 86377345Sru } else { 86477345Sru files_match = 1; 86577345Sru (void)unlink(tempfile); 86611356Sbde } 86777345Sru (void) close(temp_fd); 86877345Sru } 86977345Sru } 87077345Sru 871245617Sbrooks if (dostrip && (!docompare || !target)) 872245617Sbrooks digestresult = digest_file(tempfile); 873245617Sbrooks 87477345Sru /* 87577345Sru * Move the new file into place if doing a safe copy 87677345Sru * and the files are different (or just not compared). 87777345Sru */ 87877345Sru if (tempcopy && !files_match) { 87977345Sru /* Try to turn off the immutable bits. */ 88077345Sru if (to_sb.st_flags & NOCHANGEBITS) 88177345Sru (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); 88277345Sru if (dobackup) { 88387724Sru if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name, 88487724Sru suffix) != strlen(to_name) + strlen(suffix)) { 88577345Sru unlink(tempfile); 88677345Sru errx(EX_OSERR, "%s: backup filename too long", 88777345Sru to_name); 88877345Sru } 88977345Sru if (verbose) 89077345Sru (void)printf("install: %s -> %s\n", to_name, backup); 89177345Sru if (rename(to_name, backup) < 0) { 89211356Sbde serrno = errno; 89377345Sru unlink(tempfile); 89411356Sbde errno = serrno; 89511356Sbde err(EX_OSERR, "rename: %s to %s", to_name, 89677345Sru backup); 89711356Sbde } 89811356Sbde } 89977345Sru if (verbose) 90077345Sru (void)printf("install: %s -> %s\n", from_name, to_name); 90177345Sru if (rename(tempfile, to_name) < 0) { 90277345Sru serrno = errno; 90377345Sru unlink(tempfile); 90477345Sru errno = serrno; 90577345Sru err(EX_OSERR, "rename: %s to %s", 90677345Sru tempfile, to_name); 90777345Sru } 90877345Sru 90977345Sru /* Re-open to_fd so we aren't hosed by the rename(2). */ 91077345Sru (void) close(to_fd); 91177345Sru if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 91277345Sru err(EX_OSERR, "%s", to_name); 91311356Sbde } 91411356Sbde 91511356Sbde /* 91677345Sru * Preserve the timestamp of the source file if necessary. 9171590Srgrimes */ 91877345Sru if (dopreserve && !files_match && !devnull) { 919106967Speter tvb[0].tv_sec = from_sb.st_atime; 920106967Speter tvb[0].tv_usec = 0; 921106967Speter tvb[1].tv_sec = from_sb.st_mtime; 922106967Speter tvb[1].tv_usec = 0; 923106967Speter (void)utimes(to_name, tvb); 9241590Srgrimes } 92577345Sru 92677345Sru if (fstat(to_fd, &to_sb) == -1) { 9271590Srgrimes serrno = errno; 9281590Srgrimes (void)unlink(to_name); 92911356Sbde errno = serrno; 93077345Sru err(EX_OSERR, "%s", to_name); 9311590Srgrimes } 9321590Srgrimes 9331590Srgrimes /* 93477345Sru * Set owner, group, mode for target; do the chown first, 93577345Sru * chown may lose the setuid bits. 93677345Sru */ 937245617Sbrooks if (!dounpriv && ((gid != (gid_t)-1 && gid != to_sb.st_gid) || 93877345Sru (uid != (uid_t)-1 && uid != to_sb.st_uid) || 939245617Sbrooks (mode != (to_sb.st_mode & ALLPERMS)))) { 94077345Sru /* Try to turn off the immutable bits. */ 94177345Sru if (to_sb.st_flags & NOCHANGEBITS) 94277345Sru (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS); 94377345Sru } 94477345Sru 945245617Sbrooks if (!dounpriv & 946245617Sbrooks (gid != (gid_t)-1 && gid != to_sb.st_gid) || 94777345Sru (uid != (uid_t)-1 && uid != to_sb.st_uid)) 94877345Sru if (fchown(to_fd, uid, gid) == -1) { 94977345Sru serrno = errno; 95077345Sru (void)unlink(to_name); 95177345Sru errno = serrno; 95277345Sru err(EX_OSERR,"%s: chown/chgrp", to_name); 95377345Sru } 95477345Sru 955245617Sbrooks if (mode != (to_sb.st_mode & ALLPERMS)) { 956246147Sbrooks if (fchmod(to_fd, 957246147Sbrooks dounpriv ? mode & (S_IRWXU|S_IRWXG|S_IRWXO) : mode)) { 95877345Sru serrno = errno; 95977345Sru (void)unlink(to_name); 96077345Sru errno = serrno; 96177345Sru err(EX_OSERR, "%s: chmod", to_name); 96277345Sru } 963245617Sbrooks } 96477345Sru 96577345Sru /* 9661590Srgrimes * If provided a set of flags, set them, otherwise, preserve the 9671590Srgrimes * flags, except for the dump flag. 96836586Speter * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just 96936586Speter * trying to turn off UF_NODUMP. If we're trying to set real flags, 970218909Sbrucec * then warn if the fs doesn't support it, otherwise fail. 9711590Srgrimes */ 972245617Sbrooks if (!dounpriv & !devnull && (flags & SETFLAGS || 973106246Sru (from_sb.st_flags & ~UF_NODUMP) != to_sb.st_flags) && 974106246Sru fchflags(to_fd, 9751590Srgrimes flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 97636586Speter if (flags & SETFLAGS) { 97736586Speter if (errno == EOPNOTSUPP) 97836586Speter warn("%s: chflags", to_name); 97936586Speter else { 98036586Speter serrno = errno; 98136586Speter (void)unlink(to_name); 98236586Speter errno = serrno; 98336586Speter err(EX_OSERR, "%s: chflags", to_name); 98436586Speter } 98536586Speter } 9861590Srgrimes } 9871590Srgrimes 9881590Srgrimes (void)close(to_fd); 98977345Sru if (!devnull) 99077345Sru (void)close(from_fd); 991245617Sbrooks 992245617Sbrooks metadata_log(to_name, "file", tvb, NULL, digestresult, to_sb.st_size); 993245617Sbrooks free(digestresult); 9941590Srgrimes} 9951590Srgrimes 9961590Srgrimes/* 99711356Sbde * compare -- 99811356Sbde * compare two files; non-zero means files differ 99911356Sbde */ 1000200515Sdelphijstatic int 100187685Smarkmcompare(int from_fd, const char *from_name __unused, size_t from_len, 1002245617Sbrooks int to_fd, const char *to_name __unused, size_t to_len, 1003245617Sbrooks char **dresp) 100411356Sbde{ 100511356Sbde char *p, *q; 100611356Sbde int rv; 100718040Speter int done_compare; 1008245617Sbrooks DIGEST_CTX ctx; 100911356Sbde 101051705Sbillf rv = 0; 101177345Sru if (from_len != to_len) 101211356Sbde return 1; 101311356Sbde 101492611Sdes if (from_len <= MAX_CMP_SIZE) { 1015245617Sbrooks if (dresp != NULL) 1016245617Sbrooks digest_init(&ctx); 101718040Speter done_compare = 0; 101818039Speter if (trymmap(from_fd) && trymmap(to_fd)) { 1019245617Sbrooks p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, 1020245617Sbrooks from_fd, (off_t)0); 102121786Salex if (p == (char *)MAP_FAILED) 102218040Speter goto out; 1023245617Sbrooks q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, 1024245617Sbrooks to_fd, (off_t)0); 102521786Salex if (q == (char *)MAP_FAILED) { 102677345Sru munmap(p, from_len); 102718040Speter goto out; 102818040Speter } 102911356Sbde 103077345Sru rv = memcmp(p, q, from_len); 1031245617Sbrooks if (dresp != NULL) 1032245617Sbrooks digest_update(&ctx, p, from_len); 103377345Sru munmap(p, from_len); 103477345Sru munmap(q, from_len); 103518040Speter done_compare = 1; 103618040Speter } 103718040Speter out: 103818040Speter if (!done_compare) { 103918040Speter char buf1[MAXBSIZE]; 104018040Speter char buf2[MAXBSIZE]; 104118039Speter int n1, n2; 104218038Speter 104318039Speter rv = 0; 104418039Speter lseek(from_fd, 0, SEEK_SET); 104518039Speter lseek(to_fd, 0, SEEK_SET); 104618039Speter while (rv == 0) { 104718039Speter n1 = read(from_fd, buf1, sizeof(buf1)); 104818039Speter if (n1 == 0) 104918039Speter break; /* EOF */ 105018039Speter else if (n1 > 0) { 105118039Speter n2 = read(to_fd, buf2, n1); 105218039Speter if (n2 == n1) 105318039Speter rv = memcmp(buf1, buf2, n1); 105418039Speter else 105518039Speter rv = 1; /* out of sync */ 105618039Speter } else 105718039Speter rv = 1; /* read failure */ 1058245617Sbrooks digest_update(&ctx, buf1, n1); 105918039Speter } 106018039Speter lseek(from_fd, 0, SEEK_SET); 106118039Speter lseek(to_fd, 0, SEEK_SET); 106218038Speter } 106318039Speter } else 106418039Speter rv = 1; /* don't bother in this case */ 106518039Speter 1066245617Sbrooks if (dresp != NULL) { 1067245617Sbrooks if (rv == 0) 1068245617Sbrooks *dresp = digest_end(&ctx, NULL); 1069245617Sbrooks else 1070245617Sbrooks (void)digest_end(&ctx, NULL); 1071245617Sbrooks } 1072245617Sbrooks 107311356Sbde return rv; 107411356Sbde} 107511356Sbde 107611356Sbde/* 107777345Sru * create_tempfile -- 107877345Sru * create a temporary file based on path and open it 107977345Sru */ 1080200515Sdelphijstatic int 1081102944Sdwmalonecreate_tempfile(const char *path, char *temp, size_t tsize) 108277345Sru{ 108377345Sru char *p; 108477345Sru 108577345Sru (void)strncpy(temp, path, tsize); 108677345Sru temp[tsize - 1] = '\0'; 108777345Sru if ((p = strrchr(temp, '/')) != NULL) 108877345Sru p++; 108977345Sru else 109077345Sru p = temp; 109177345Sru (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); 109277345Sru temp[tsize - 1] = '\0'; 109377345Sru return (mkstemp(temp)); 109477345Sru} 109577345Sru 109677345Sru/* 109777345Sru * create_newfile -- 109877345Sru * create a new file, overwriting an existing one if necessary 109977345Sru */ 1100200515Sdelphijstatic int 1101102944Sdwmalonecreate_newfile(const char *path, int target, struct stat *sbp) 110277345Sru{ 110377345Sru char backup[MAXPATHLEN]; 1104107428Sfenner int saved_errno = 0; 1105107428Sfenner int newfd; 110677345Sru 110777345Sru if (target) { 110877345Sru /* 110977345Sru * Unlink now... avoid ETXTBSY errors later. Try to turn 111077345Sru * off the append/immutable bits -- if we fail, go ahead, 111177345Sru * it might work. 111277345Sru */ 111377345Sru if (sbp->st_flags & NOCHANGEBITS) 111477345Sru (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); 111577345Sru 111677345Sru if (dobackup) { 111787724Sru if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", 111887724Sru path, suffix) != strlen(path) + strlen(suffix)) 111977345Sru errx(EX_OSERR, "%s: backup filename too long", 112077345Sru path); 112177345Sru (void)snprintf(backup, MAXPATHLEN, "%s%s", 112277345Sru path, suffix); 112377345Sru if (verbose) 112477345Sru (void)printf("install: %s -> %s\n", 112577345Sru path, backup); 112677345Sru if (rename(path, backup) < 0) 112777345Sru err(EX_OSERR, "rename: %s to %s", path, backup); 112877345Sru } else 1129107428Sfenner if (unlink(path) < 0) 1130107428Sfenner saved_errno = errno; 113177345Sru } 113277345Sru 1133107428Sfenner newfd = open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); 1134107428Sfenner if (newfd < 0 && saved_errno != 0) 1135107428Sfenner errno = saved_errno; 1136107428Sfenner return newfd; 113777345Sru} 113877345Sru 113977345Sru/* 11401590Srgrimes * copy -- 11411590Srgrimes * copy from one file to another 11421590Srgrimes */ 1143245617Sbrooksstatic char * 1144102944Sdwmalonecopy(int from_fd, const char *from_name, int to_fd, const char *to_name, 1145102944Sdwmalone off_t size) 11461590Srgrimes{ 1147102944Sdwmalone int nr, nw; 11481590Srgrimes int serrno; 11491590Srgrimes char *p, buf[MAXBSIZE]; 115018040Speter int done_copy; 1151245617Sbrooks DIGEST_CTX ctx; 11521590Srgrimes 115377345Sru /* Rewind file descriptors. */ 115477345Sru if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) 115577345Sru err(EX_OSERR, "lseek: %s", from_name); 115677345Sru if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1) 115777345Sru err(EX_OSERR, "lseek: %s", to_name); 115877345Sru 1159245617Sbrooks digest_init(&ctx); 1160245617Sbrooks 11611590Srgrimes /* 11621590Srgrimes * Mmap and write if less than 8M (the limit is so we don't totally 11631590Srgrimes * trash memory on big files. This is really a minor hack, but it 11641590Srgrimes * wins some CPU back. 11651590Srgrimes */ 116618040Speter done_copy = 0; 116777345Sru if (size <= 8 * 1048576 && trymmap(from_fd) && 116877345Sru (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED, 116977345Sru from_fd, (off_t)0)) != (char *)MAP_FAILED) { 1170237988Skib nw = write(to_fd, p, size); 1171237988Skib if (nw != size) { 117218040Speter serrno = errno; 117318040Speter (void)unlink(to_name); 1174237988Skib if (nw >= 0) { 1175237988Skib errx(EX_OSERR, 1176237988Skib "short write to %s: %jd bytes written, %jd bytes asked to write", 1177237988Skib to_name, (uintmax_t)nw, (uintmax_t)size); 1178237988Skib } else { 1179237988Skib errno = serrno; 1180237988Skib err(EX_OSERR, "%s", to_name); 1181237988Skib } 118218040Speter } 1183245617Sbrooks digest_update(&ctx, p, size); 1184245617Sbrooks (void)munmap(p, size); 118518040Speter done_copy = 1; 118618040Speter } 118718040Speter if (!done_copy) { 1188245617Sbrooks while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { 11891590Srgrimes if ((nw = write(to_fd, buf, nr)) != nr) { 11901590Srgrimes serrno = errno; 11911590Srgrimes (void)unlink(to_name); 1192237988Skib if (nw >= 0) { 1193237988Skib errx(EX_OSERR, 1194237988Skib "short write to %s: %jd bytes written, %jd bytes asked to write", 1195237988Skib to_name, (uintmax_t)nw, 1196237988Skib (uintmax_t)size); 1197237988Skib } else { 1198237988Skib errno = serrno; 1199237988Skib err(EX_OSERR, "%s", to_name); 1200237988Skib } 12011590Srgrimes } 1202245617Sbrooks digest_update(&ctx, buf, nr); 1203245617Sbrooks } 12041590Srgrimes if (nr != 0) { 12051590Srgrimes serrno = errno; 12061590Srgrimes (void)unlink(to_name); 120711356Sbde errno = serrno; 120811356Sbde err(EX_OSERR, "%s", from_name); 12091590Srgrimes } 12101590Srgrimes } 1211245617Sbrooks return (digest_end(&ctx, NULL)); 12121590Srgrimes} 12131590Srgrimes 12141590Srgrimes/* 12151590Srgrimes * strip -- 12161590Srgrimes * use strip(1) to strip the target file 12171590Srgrimes */ 1218200515Sdelphijstatic void 1219102944Sdwmalonestrip(const char *to_name) 12201590Srgrimes{ 122196416Smarcel const char *stripbin; 12221590Srgrimes int serrno, status; 12231590Srgrimes 122440301Sdes switch (fork()) { 12251590Srgrimes case -1: 12261590Srgrimes serrno = errno; 12271590Srgrimes (void)unlink(to_name); 122811356Sbde errno = serrno; 122911356Sbde err(EX_TEMPFAIL, "fork"); 12301590Srgrimes case 0: 123196416Smarcel stripbin = getenv("STRIPBIN"); 123296416Smarcel if (stripbin == NULL) 123396416Smarcel stripbin = "strip"; 123496416Smarcel execlp(stripbin, stripbin, to_name, (char *)NULL); 123596437Sbde err(EX_OSERR, "exec(%s)", stripbin); 12361590Srgrimes default: 123716611Sbde if (wait(&status) == -1 || status) { 123896674Sfanf serrno = errno; 12391590Srgrimes (void)unlink(to_name); 124096674Sfanf errc(EX_SOFTWARE, serrno, "wait"); 124118525Simp /* NOTREACHED */ 124216611Sbde } 12431590Srgrimes } 12441590Srgrimes} 12451590Srgrimes 12461590Srgrimes/* 124718551Simp * install_dir -- 1248204111Suqs * build directory hierarchy 124918551Simp */ 1250200515Sdelphijstatic void 1251102944Sdwmaloneinstall_dir(char *path) 125218551Simp{ 1253102944Sdwmalone char *p; 125418551Simp struct stat sb; 125518551Simp int ch; 125618551Simp 125718551Simp for (p = path;; ++p) 125818551Simp if (!*p || (p != path && *p == '/')) { 125918551Simp ch = *p; 126018551Simp *p = '\0'; 126118551Simp if (stat(path, &sb)) { 126230809Sache if (errno != ENOENT || mkdir(path, 0755) < 0) { 126330795Sache err(EX_OSERR, "mkdir %s", path); 126418551Simp /* NOTREACHED */ 126577345Sru } else if (verbose) 126677345Sru (void)printf("install: mkdir %s\n", 126777345Sru path); 126830809Sache } else if (!S_ISDIR(sb.st_mode)) 126930809Sache errx(EX_OSERR, "%s exists but is not a directory", path); 127018551Simp if (!(*p = ch)) 127118551Simp break; 127218551Simp } 127318551Simp 1274245617Sbrooks if (!dounpriv) { 1275245617Sbrooks if ((gid != (gid_t)-1 || uid != (uid_t)-1) && 1276245617Sbrooks chown(path, uid, gid)) 1277245617Sbrooks warn("chown %u:%u %s", uid, gid, path); 1278245617Sbrooks /* XXXBED: should we do the chmod in the dounpriv case? */ 1279245617Sbrooks if (chmod(path, mode)) 1280245617Sbrooks warn("chmod %o %s", mode, path); 1281245617Sbrooks } 1282245617Sbrooks metadata_log(path, "dir", NULL, NULL, NULL, 0); 128318551Simp} 128418551Simp 128518551Simp/* 1286245617Sbrooks * metadata_log -- 1287245617Sbrooks * if metafp is not NULL, output mtree(8) full path name and settings to 1288245617Sbrooks * metafp, to allow permissions to be set correctly by other tools, 1289245617Sbrooks * or to allow integrity checks to be performed. 1290245617Sbrooks */ 1291245617Sbrooksstatic void 1292245617Sbrooksmetadata_log(const char *path, const char *type, struct timeval *tv, 1293245617Sbrooks const char *slink, const char *digestresult, off_t size) 1294245617Sbrooks{ 1295245617Sbrooks static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; 1296245617Sbrooks const char *p; 1297245617Sbrooks char *buf; 1298245617Sbrooks size_t destlen; 1299245617Sbrooks struct flock metalog_lock; 1300245617Sbrooks 1301245617Sbrooks if (!metafp) 1302245617Sbrooks return; 1303245617Sbrooks /* Buffer for strsvis(3). */ 1304245617Sbrooks buf = (char *)malloc(4 * strlen(path) + 1); 1305245617Sbrooks if (buf == NULL) { 1306245617Sbrooks warnx("%s", strerror(ENOMEM)); 1307245617Sbrooks return; 1308245617Sbrooks } 1309245617Sbrooks 1310245617Sbrooks /* Lock log file. */ 1311245617Sbrooks metalog_lock.l_start = 0; 1312245617Sbrooks metalog_lock.l_len = 0; 1313245617Sbrooks metalog_lock.l_whence = SEEK_SET; 1314245617Sbrooks metalog_lock.l_type = F_WRLCK; 1315245617Sbrooks if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { 1316245617Sbrooks warn("can't lock %s", metafile); 1317245617Sbrooks free(buf); 1318245617Sbrooks return; 1319245617Sbrooks } 1320245617Sbrooks 1321245617Sbrooks /* Remove destdir. */ 1322245617Sbrooks p = path; 1323245617Sbrooks if (destdir) { 1324245617Sbrooks destlen = strlen(destdir); 1325245617Sbrooks if (strncmp(p, destdir, destlen) == 0 && 1326245617Sbrooks (p[destlen] == '/' || p[destlen] == '\0')) 1327245617Sbrooks p += destlen; 1328245617Sbrooks } 1329245617Sbrooks while (*p && *p == '/') 1330245617Sbrooks p++; 1331245617Sbrooks strsvis(buf, p, VIS_OCTAL, extra); 1332245617Sbrooks p = buf; 1333245617Sbrooks /* Print details. */ 1334245617Sbrooks fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type); 1335245617Sbrooks if (owner) 1336245617Sbrooks fprintf(metafp, " uname=%s", owner); 1337245617Sbrooks if (group) 1338245617Sbrooks fprintf(metafp, " gname=%s", group); 1339245617Sbrooks fprintf(metafp, " mode=%#o", mode); 1340245617Sbrooks if (slink) { 1341245617Sbrooks strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */ 1342245617Sbrooks fprintf(metafp, " link=%s", buf); 1343245617Sbrooks } 1344245617Sbrooks if (*type == 'f') /* type=file */ 1345245617Sbrooks fprintf(metafp, " size=%lld", (long long)size); 1346245617Sbrooks if (tv != NULL && dopreserve) 1347245617Sbrooks fprintf(metafp, " time=%lld.%ld", 1348245617Sbrooks (long long)tv[1].tv_sec, (long)tv[1].tv_usec); 1349245617Sbrooks if (digestresult && digest) 1350245617Sbrooks fprintf(metafp, " %s=%s", digest, digestresult); 1351245617Sbrooks if (fflags) 1352245617Sbrooks fprintf(metafp, " flags=%s", fflags); 1353245617Sbrooks if (tags) 1354245617Sbrooks fprintf(metafp, " tags=%s", tags); 1355245617Sbrooks fputc('\n', metafp); 1356245617Sbrooks /* Flush line. */ 1357245617Sbrooks fflush(metafp); 1358245617Sbrooks 1359245617Sbrooks /* Unlock log file. */ 1360245617Sbrooks metalog_lock.l_type = F_UNLCK; 1361245617Sbrooks if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) 1362245617Sbrooks warn("can't unlock %s", metafile); 1363245617Sbrooks free(buf); 1364245617Sbrooks} 1365245617Sbrooks 1366245617Sbrooks/* 13671590Srgrimes * usage -- 13681590Srgrimes * print a usage message and die 13691590Srgrimes */ 1370200515Sdelphijstatic void 1371200465Sdelphijusage(void) 13721590Srgrimes{ 1373125553Sru (void)fprintf(stderr, 1374245617Sbrooks"usage: install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n" 1375245617Sbrooks" [-M log] [-D dest] [-h hash] [-T tags]\n" 1376245617Sbrooks" [-B suffix] [-l linkflags] [-N dbdir]\n" 1377245617Sbrooks" file1 file2\n" 1378245617Sbrooks" install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n" 1379245617Sbrooks" [-M log] [-D dest] [-h hash] [-T tags]\n" 1380245617Sbrooks" [-B suffix] [-l linkflags] [-N dbdir]\n" 1381245617Sbrooks" file1 ... fileN directory\n" 1382245617Sbrooks" install -dU [-vU] [-g group] [-m mode] [-N dbdir] [-o owner]\n" 1383245617Sbrooks" [-M log] [-D dest] [-h hash] [-T tags]\n" 1384245312Sbrooks" directory ...\n"); 138518525Simp exit(EX_USAGE); 138618525Simp /* NOTREACHED */ 13871590Srgrimes} 138818038Speter 138918038Speter/* 139018038Speter * trymmap -- 139118038Speter * return true (1) if mmap should be tried, false (0) if not. 139218038Speter */ 1393200515Sdelphijstatic int 1394102944Sdwmalonetrymmap(int fd) 139518038Speter{ 139632652Sbde/* 139732652Sbde * The ifdef is for bootstrapping - f_fstypename doesn't exist in 139832652Sbde * pre-Lite2-merge systems. 139932652Sbde */ 140032652Sbde#ifdef MFSNAMELEN 140118038Speter struct statfs stfs; 140218038Speter 1403245617Sbrooks if (fstatfs(fd, &stfs) != 0) 140432652Sbde return (0); 140532652Sbde if (strcmp(stfs.f_fstypename, "ufs") == 0 || 140632652Sbde strcmp(stfs.f_fstypename, "cd9660") == 0) 140732652Sbde return (1); 140832360Sjb#endif 140932652Sbde return (0); 141018038Speter} 1411