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: stable/10/usr.bin/xinstall/xinstall.c 318096 2017-05-09 19:14:26Z bdrewery $"); 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 154304471Sbdrewery fset = 0; 1551590Srgrimes iflags = 0; 15677345Sru group = owner = NULL; 157245617Sbrooks while ((ch = getopt(argc, argv, "B:bCcD:df:g:h:l:M:m:N:o:pSsT:Uv")) != 158245617Sbrooks -1) 1591590Srgrimes switch((char)ch) { 16077345Sru case 'B': 16177345Sru suffix = optarg; 16277345Sru /* FALLTHROUGH */ 16377345Sru case 'b': 16477345Sru dobackup = 1; 16577345Sru break; 16611356Sbde case 'C': 16777345Sru docompare = 1; 16811356Sbde break; 1691590Srgrimes case 'c': 17077345Sru /* For backwards compatibility. */ 1711590Srgrimes break; 172245617Sbrooks case 'D': 173245617Sbrooks destdir = optarg; 174245617Sbrooks break; 17518551Simp case 'd': 17618551Simp dodir = 1; 17718551Simp break; 1781590Srgrimes case 'f': 179245617Sbrooks haveopt_f = 1; 180245617Sbrooks fflags = optarg; 1811590Srgrimes break; 1821590Srgrimes case 'g': 183245617Sbrooks haveopt_g = 1; 1841590Srgrimes group = optarg; 1851590Srgrimes break; 186245617Sbrooks case 'h': 187245617Sbrooks digest = optarg; 188245617Sbrooks break; 189245617Sbrooks case 'l': 190245617Sbrooks for (p = optarg; *p != '\0'; p++) 191245617Sbrooks switch (*p) { 192245617Sbrooks case 's': 193245617Sbrooks dolink &= ~(LN_HARD|LN_MIXED); 194245617Sbrooks dolink |= LN_SYMBOLIC; 195245617Sbrooks break; 196245617Sbrooks case 'h': 197245617Sbrooks dolink &= ~(LN_SYMBOLIC|LN_MIXED); 198245617Sbrooks dolink |= LN_HARD; 199245617Sbrooks break; 200245617Sbrooks case 'm': 201245617Sbrooks dolink &= ~(LN_SYMBOLIC|LN_HARD); 202245617Sbrooks dolink |= LN_MIXED; 203245617Sbrooks break; 204245617Sbrooks case 'a': 205245617Sbrooks dolink &= ~LN_RELATIVE; 206245617Sbrooks dolink |= LN_ABSOLUTE; 207245617Sbrooks break; 208245617Sbrooks case 'r': 209245617Sbrooks dolink &= ~LN_ABSOLUTE; 210245617Sbrooks dolink |= LN_RELATIVE; 211245617Sbrooks break; 212245617Sbrooks default: 213245617Sbrooks errx(1, "%c: invalid link type", *p); 214245617Sbrooks /* NOTREACHED */ 215245617Sbrooks } 216245617Sbrooks break; 21777345Sru case 'M': 218245617Sbrooks metafile = optarg; 21977345Sru break; 2201590Srgrimes case 'm': 221245617Sbrooks haveopt_m = 1; 2221590Srgrimes if (!(set = setmode(optarg))) 22311356Sbde errx(EX_USAGE, "invalid file mode: %s", 22411356Sbde optarg); 2251590Srgrimes mode = getmode(set, 0); 22641847Simp free(set); 2271590Srgrimes break; 228245312Sbrooks case 'N': 229245312Sbrooks if (!setup_getid(optarg)) 230245617Sbrooks err(EX_OSERR, "Unable to use user and group " 231245312Sbrooks "databases in `%s'", optarg); 232245312Sbrooks break; 2331590Srgrimes case 'o': 234245617Sbrooks haveopt_o = 1; 2351590Srgrimes owner = optarg; 2361590Srgrimes break; 23711356Sbde case 'p': 23877345Sru docompare = dopreserve = 1; 23911356Sbde break; 24077345Sru case 'S': 24177345Sru safecopy = 1; 24277345Sru break; 2431590Srgrimes case 's': 2441590Srgrimes dostrip = 1; 2451590Srgrimes break; 246245617Sbrooks case 'T': 247245617Sbrooks tags = optarg; 248245617Sbrooks break; 249245617Sbrooks case 'U': 250245617Sbrooks dounpriv = 1; 251245617Sbrooks break; 25217546Speter case 'v': 25317546Speter verbose = 1; 25417546Speter break; 2551590Srgrimes case '?': 2561590Srgrimes default: 257125553Sru usage(); 2581590Srgrimes } 2591590Srgrimes argc -= optind; 2601590Srgrimes argv += optind; 26118551Simp 26218551Simp /* some options make no sense when creating directories */ 263127112Sru if (dostrip && dodir) { 264127112Sru warnx("-d and -s may not be specified together"); 265125553Sru usage(); 266127112Sru } 2671590Srgrimes 268156363Sobrien if (getenv("DONTSTRIP") != NULL) { 269156363Sobrien warnx("DONTSTRIP set - will not strip installed binaries"); 270156363Sobrien dostrip = 0; 271156363Sobrien } 272156363Sobrien 27318551Simp /* must have at least two arguments, except when creating directories */ 274125553Sru if (argc == 0 || (argc == 1 && !dodir)) 275125553Sru usage(); 27618551Simp 277245617Sbrooks if (digest != NULL) { 278245617Sbrooks if (strcmp(digest, "none") == 0) { 279245617Sbrooks digesttype = DIGEST_NONE; 280245617Sbrooks } else if (strcmp(digest, "md5") == 0) { 281245617Sbrooks digesttype = DIGEST_MD5; 282245617Sbrooks } else if (strcmp(digest, "rmd160") == 0) { 283245617Sbrooks digesttype = DIGEST_RIPEMD160; 284245617Sbrooks } else if (strcmp(digest, "sha1") == 0) { 285245617Sbrooks digesttype = DIGEST_SHA1; 286245617Sbrooks } else if (strcmp(digest, "sha256") == 0) { 287245617Sbrooks digesttype = DIGEST_SHA256; 288245617Sbrooks } else if (strcmp(digest, "sha512") == 0) { 289245617Sbrooks digesttype = DIGEST_SHA512; 290245617Sbrooks } else { 291245617Sbrooks warnx("unknown digest `%s'", digest); 292245617Sbrooks usage(); 293245617Sbrooks } 294245617Sbrooks } 295245617Sbrooks 29677345Sru /* need to make a temp copy so we can compare stripped version */ 29777345Sru if (docompare && dostrip) 29877345Sru safecopy = 1; 29915069Sjulian 3001590Srgrimes /* get group and owner id's */ 301245617Sbrooks if (group != NULL && !dounpriv) { 302245312Sbrooks if (gid_from_group(group, &gid) == -1) { 303245617Sbrooks id_t id; 304245312Sbrooks if (!parseid(group, &id)) 305245312Sbrooks errx(1, "unknown group %s", group); 306245312Sbrooks gid = id; 307245312Sbrooks } 30877345Sru } else 30977345Sru gid = (gid_t)-1; 3101590Srgrimes 311245617Sbrooks if (owner != NULL && !dounpriv) { 312245312Sbrooks if (uid_from_user(owner, &uid) == -1) { 313245312Sbrooks id_t id; 314245312Sbrooks if (!parseid(owner, &id)) 315245312Sbrooks errx(1, "unknown user %s", owner); 316245312Sbrooks uid = id; 317245312Sbrooks } 31877345Sru } else 31977345Sru uid = (uid_t)-1; 32015069Sjulian 321245617Sbrooks if (fflags != NULL && !dounpriv) { 322245617Sbrooks if (strtofflags(&fflags, &fset, NULL)) 323245617Sbrooks errx(EX_USAGE, "%s: invalid flag", fflags); 324245617Sbrooks iflags |= SETFLAGS; 325245617Sbrooks } 326245617Sbrooks 327245617Sbrooks if (metafile != NULL) { 328245617Sbrooks if ((metafp = fopen(metafile, "a")) == NULL) 329245617Sbrooks warn("open %s", metafile); 330245617Sbrooks } else 331245617Sbrooks digesttype = DIGEST_NONE; 332245617Sbrooks 33318551Simp if (dodir) { 33418551Simp for (; *argv != NULL; ++argv) 33518551Simp install_dir(*argv); 33618551Simp exit(EX_OK); 33718551Simp /* NOTREACHED */ 33818551Simp } 33918551Simp 340245793Sbrooks to_name = argv[argc - 1]; 341245793Sbrooks no_target = stat(to_name, &to_sb); 34277345Sru if (!no_target && S_ISDIR(to_sb.st_mode)) { 343245793Sbrooks if (dolink & LN_SYMBOLIC) { 344245793Sbrooks if (lstat(to_name, &to_sb) != 0) 345245793Sbrooks err(EX_OSERR, "%s vanished", to_name); 346245793Sbrooks if (S_ISLNK(to_sb.st_mode)) { 347245793Sbrooks if (argc != 2) { 348245793Sbrooks errno = ENOTDIR; 349245793Sbrooks err(EX_USAGE, "%s", to_name); 350245793Sbrooks } 351245793Sbrooks install(*argv, to_name, fset, iflags); 352245793Sbrooks exit(EX_OK); 353245793Sbrooks } 354245793Sbrooks } 3551590Srgrimes for (; *argv != to_name; ++argv) 3561590Srgrimes install(*argv, to_name, fset, iflags | DIRECTORY); 35718525Simp exit(EX_OK); 35818525Simp /* NOTREACHED */ 3591590Srgrimes } 3601590Srgrimes 3611590Srgrimes /* can't do file1 file2 directory/file */ 362127112Sru if (argc != 2) { 363174587Sedwin if (no_target) 364174587Sedwin warnx("target directory `%s' does not exist", 365174587Sedwin argv[argc - 1]); 366174587Sedwin else 367174587Sedwin warnx("target `%s' is not a directory", 368174587Sedwin argv[argc - 1]); 369125553Sru usage(); 370127112Sru } 3711590Srgrimes 372245617Sbrooks if (!no_target && !dolink) { 3731590Srgrimes if (stat(*argv, &from_sb)) 37411356Sbde err(EX_OSERR, "%s", *argv); 37511356Sbde if (!S_ISREG(to_sb.st_mode)) { 37611356Sbde errno = EFTYPE; 37711356Sbde err(EX_OSERR, "%s", to_name); 37811356Sbde } 3791590Srgrimes if (to_sb.st_dev == from_sb.st_dev && 3801590Srgrimes to_sb.st_ino == from_sb.st_ino) 38111356Sbde errx(EX_USAGE, 38211356Sbde "%s and %s are the same file", *argv, to_name); 3831590Srgrimes } 3841590Srgrimes install(*argv, to_name, fset, iflags); 38518525Simp exit(EX_OK); 38618525Simp /* NOTREACHED */ 3871590Srgrimes} 3881590Srgrimes 389245617Sbrooksstatic char * 390245617Sbrooksdigest_file(const char *name) 391245617Sbrooks{ 392245617Sbrooks 393245617Sbrooks switch (digesttype) { 394245617Sbrooks case DIGEST_MD5: 395245617Sbrooks return (MD5File(name, NULL)); 396245617Sbrooks case DIGEST_RIPEMD160: 397245617Sbrooks return (RIPEMD160_File(name, NULL)); 398245617Sbrooks case DIGEST_SHA1: 399245617Sbrooks return (SHA1_File(name, NULL)); 400245617Sbrooks case DIGEST_SHA256: 401245617Sbrooks return (SHA256_File(name, NULL)); 402245617Sbrooks case DIGEST_SHA512: 403245617Sbrooks return (SHA512_File(name, NULL)); 404245617Sbrooks default: 405245617Sbrooks return (NULL); 406245617Sbrooks } 407245617Sbrooks} 408245617Sbrooks 409245617Sbrooksstatic void 410245617Sbrooksdigest_init(DIGEST_CTX *c) 411245617Sbrooks{ 412245617Sbrooks 413245617Sbrooks switch (digesttype) { 414245617Sbrooks case DIGEST_NONE: 415245617Sbrooks break; 416245617Sbrooks case DIGEST_MD5: 417245617Sbrooks MD5Init(&(c->MD5)); 418245617Sbrooks break; 419245617Sbrooks case DIGEST_RIPEMD160: 420245617Sbrooks RIPEMD160_Init(&(c->RIPEMD160)); 421245617Sbrooks break; 422245617Sbrooks case DIGEST_SHA1: 423245617Sbrooks SHA1_Init(&(c->SHA1)); 424245617Sbrooks break; 425245617Sbrooks case DIGEST_SHA256: 426245617Sbrooks SHA256_Init(&(c->SHA256)); 427245617Sbrooks break; 428245617Sbrooks case DIGEST_SHA512: 429245617Sbrooks SHA512_Init(&(c->SHA512)); 430245617Sbrooks break; 431245617Sbrooks } 432245617Sbrooks} 433245617Sbrooks 434245617Sbrooksstatic void 435245617Sbrooksdigest_update(DIGEST_CTX *c, const unsigned char *data, size_t len) 436245617Sbrooks{ 437245617Sbrooks 438245617Sbrooks switch (digesttype) { 439245617Sbrooks case DIGEST_NONE: 440245617Sbrooks break; 441245617Sbrooks case DIGEST_MD5: 442245617Sbrooks MD5Update(&(c->MD5), data, len); 443245617Sbrooks break; 444245617Sbrooks case DIGEST_RIPEMD160: 445245617Sbrooks RIPEMD160_Update(&(c->RIPEMD160), data, len); 446245617Sbrooks break; 447245617Sbrooks case DIGEST_SHA1: 448245617Sbrooks SHA1_Update(&(c->SHA1), data, len); 449245617Sbrooks break; 450245617Sbrooks case DIGEST_SHA256: 451245617Sbrooks SHA256_Update(&(c->SHA256), data, len); 452245617Sbrooks break; 453245617Sbrooks case DIGEST_SHA512: 454245617Sbrooks SHA512_Update(&(c->SHA512), data, len); 455245617Sbrooks break; 456245617Sbrooks } 457245617Sbrooks} 458245617Sbrooks 459245617Sbrooksstatic char * 460245617Sbrooksdigest_end(DIGEST_CTX *c, char *buf) 461245617Sbrooks{ 462245617Sbrooks 463245617Sbrooks switch (digesttype) { 464245617Sbrooks case DIGEST_MD5: 465245617Sbrooks return (MD5End(&(c->MD5), buf)); 466245617Sbrooks case DIGEST_RIPEMD160: 467245617Sbrooks return (RIPEMD160_End(&(c->RIPEMD160), buf)); 468245617Sbrooks case DIGEST_SHA1: 469245617Sbrooks return (SHA1_End(&(c->SHA1), buf)); 470245617Sbrooks case DIGEST_SHA256: 471245617Sbrooks return (SHA256_End(&(c->SHA256), buf)); 472245617Sbrooks case DIGEST_SHA512: 473245617Sbrooks return (SHA512_End(&(c->SHA512), buf)); 474245617Sbrooks default: 475245617Sbrooks return (NULL); 476245617Sbrooks } 477245617Sbrooks} 478245617Sbrooks 479245312Sbrooks/* 480245312Sbrooks * parseid -- 481245312Sbrooks * parse uid or gid from arg into id, returning non-zero if successful 482245312Sbrooks */ 483245312Sbrooksstatic int 484245312Sbrooksparseid(const char *name, id_t *id) 48515069Sjulian{ 486245312Sbrooks char *ep; 48715069Sjulian errno = 0; 488245312Sbrooks *id = (id_t)strtoul(name, &ep, 10); 489245312Sbrooks if (errno || *ep != '\0') 490245312Sbrooks return (0); 491245312Sbrooks return (1); 49215069Sjulian} 49315069Sjulian 4941590Srgrimes/* 495245617Sbrooks * quiet_mktemp -- 496245617Sbrooks * mktemp implementation used mkstemp to avoid mktemp warnings. We 497245617Sbrooks * really do need mktemp semantics here as we will be creating a link. 498245617Sbrooks */ 499245617Sbrooksstatic char * 500245617Sbrooksquiet_mktemp(char *template) 501245617Sbrooks{ 502245617Sbrooks int fd; 503245617Sbrooks 504245617Sbrooks if ((fd = mkstemp(template)) == -1) 505245617Sbrooks return (NULL); 506245617Sbrooks close (fd); 507245617Sbrooks if (unlink(template) == -1) 508245617Sbrooks err(EX_OSERR, "unlink %s", template); 509245617Sbrooks return (template); 510245617Sbrooks} 511245617Sbrooks 512245617Sbrooks/* 513245617Sbrooks * do_link -- 514245617Sbrooks * make a hard link, obeying dorename if set 515245617Sbrooks * return -1 on failure 516245617Sbrooks */ 517245617Sbrooksstatic int 518245617Sbrooksdo_link(const char *from_name, const char *to_name, 519245617Sbrooks const struct stat *target_sb) 520245617Sbrooks{ 521245617Sbrooks char tmpl[MAXPATHLEN]; 522245617Sbrooks int ret; 523245617Sbrooks 524245617Sbrooks if (safecopy && target_sb != NULL) { 525245617Sbrooks (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); 526245617Sbrooks /* This usage is safe. */ 527245617Sbrooks if (quiet_mktemp(tmpl) == NULL) 528245617Sbrooks err(EX_OSERR, "%s: mktemp", tmpl); 529245617Sbrooks ret = link(from_name, tmpl); 530245617Sbrooks if (ret == 0) { 531245617Sbrooks if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == 532245617Sbrooks -1) { 533245617Sbrooks unlink(tmpl); 534245617Sbrooks err(EX_OSERR, "%s", to_name); 535245617Sbrooks } 536245617Sbrooks if (target_sb->st_flags & NOCHANGEBITS) 537245617Sbrooks (void)chflags(to_name, target_sb->st_flags & 538245617Sbrooks ~NOCHANGEBITS); 539304471Sbdrewery if (verbose) 540304471Sbdrewery printf("install: link %s -> %s\n", 541304471Sbdrewery from_name, to_name); 542245617Sbrooks ret = rename(tmpl, to_name); 543245617Sbrooks /* 544245617Sbrooks * If rename has posix semantics, then the temporary 545245617Sbrooks * file may still exist when from_name and to_name point 546245617Sbrooks * to the same file, so unlink it unconditionally. 547245617Sbrooks */ 548245617Sbrooks (void)unlink(tmpl); 549245617Sbrooks } 550245617Sbrooks return (ret); 551304471Sbdrewery } else { 552304471Sbdrewery if (verbose) 553304471Sbdrewery printf("install: link %s -> %s\n", 554304471Sbdrewery from_name, to_name); 555245617Sbrooks return (link(from_name, to_name)); 556304471Sbdrewery } 557245617Sbrooks} 558245617Sbrooks 559245617Sbrooks/* 560245617Sbrooks * do_symlink -- 561245617Sbrooks * Make a symbolic link, obeying dorename if set. Exit on failure. 562245617Sbrooks */ 563245617Sbrooksstatic void 564245617Sbrooksdo_symlink(const char *from_name, const char *to_name, 565245617Sbrooks const struct stat *target_sb) 566245617Sbrooks{ 567245617Sbrooks char tmpl[MAXPATHLEN]; 568245617Sbrooks 569245617Sbrooks if (safecopy && target_sb != NULL) { 570245617Sbrooks (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); 571245617Sbrooks /* This usage is safe. */ 572245617Sbrooks if (quiet_mktemp(tmpl) == NULL) 573245617Sbrooks err(EX_OSERR, "%s: mktemp", tmpl); 574245617Sbrooks 575245617Sbrooks if (symlink(from_name, tmpl) == -1) 576245617Sbrooks err(EX_OSERR, "symlink %s -> %s", from_name, tmpl); 577245617Sbrooks 578245617Sbrooks if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) { 579245617Sbrooks (void)unlink(tmpl); 580245617Sbrooks err(EX_OSERR, "%s", to_name); 581245617Sbrooks } 582245617Sbrooks if (target_sb->st_flags & NOCHANGEBITS) 583245617Sbrooks (void)chflags(to_name, target_sb->st_flags & 584245617Sbrooks ~NOCHANGEBITS); 585304471Sbdrewery if (verbose) 586304471Sbdrewery printf("install: symlink %s -> %s\n", 587304471Sbdrewery from_name, to_name); 588245617Sbrooks if (rename(tmpl, to_name) == -1) { 589245617Sbrooks /* Remove temporary link before exiting. */ 590245617Sbrooks (void)unlink(tmpl); 591245617Sbrooks err(EX_OSERR, "%s: rename", to_name); 592245617Sbrooks } 593245617Sbrooks } else { 594304471Sbdrewery if (verbose) 595304471Sbdrewery printf("install: symlink %s -> %s\n", 596304471Sbdrewery from_name, to_name); 597245617Sbrooks if (symlink(from_name, to_name) == -1) 598245617Sbrooks err(EX_OSERR, "symlink %s -> %s", from_name, to_name); 599245617Sbrooks } 600245617Sbrooks} 601245617Sbrooks 602245617Sbrooks/* 603245617Sbrooks * makelink -- 604245617Sbrooks * make a link from source to destination 605245617Sbrooks */ 606245617Sbrooksstatic void 607245617Sbrooksmakelink(const char *from_name, const char *to_name, 608245617Sbrooks const struct stat *target_sb) 609245617Sbrooks{ 610245617Sbrooks char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; 611245617Sbrooks struct stat to_sb; 612245617Sbrooks 613245617Sbrooks /* Try hard links first. */ 614245617Sbrooks if (dolink & (LN_HARD|LN_MIXED)) { 615245617Sbrooks if (do_link(from_name, to_name, target_sb) == -1) { 616245617Sbrooks if ((dolink & LN_HARD) || errno != EXDEV) 617245617Sbrooks err(EX_OSERR, "link %s -> %s", from_name, to_name); 618245617Sbrooks } else { 619245617Sbrooks if (stat(to_name, &to_sb)) 620245617Sbrooks err(EX_OSERR, "%s: stat", to_name); 621245617Sbrooks if (S_ISREG(to_sb.st_mode)) { 622245617Sbrooks /* 623245617Sbrooks * XXX: hard links to anything other than 624245617Sbrooks * plain files are not metalogged 625245617Sbrooks */ 626245617Sbrooks int omode; 627245617Sbrooks const char *oowner, *ogroup; 628245617Sbrooks char *offlags; 629245617Sbrooks char *dres; 630245617Sbrooks 631245617Sbrooks /* 632245617Sbrooks * XXX: use underlying perms, unless 633245617Sbrooks * overridden on command line. 634245617Sbrooks */ 635245617Sbrooks omode = mode; 636245617Sbrooks if (!haveopt_m) 637245617Sbrooks mode = (to_sb.st_mode & 0777); 638245617Sbrooks oowner = owner; 639245617Sbrooks if (!haveopt_o) 640245617Sbrooks owner = NULL; 641245617Sbrooks ogroup = group; 642245617Sbrooks if (!haveopt_g) 643245617Sbrooks group = NULL; 644245617Sbrooks offlags = fflags; 645245617Sbrooks if (!haveopt_f) 646245617Sbrooks fflags = NULL; 647245617Sbrooks dres = digest_file(from_name); 648245617Sbrooks metadata_log(to_name, "file", NULL, NULL, 649245617Sbrooks dres, to_sb.st_size); 650245617Sbrooks free(dres); 651245617Sbrooks mode = omode; 652245617Sbrooks owner = oowner; 653245617Sbrooks group = ogroup; 654245617Sbrooks fflags = offlags; 655245617Sbrooks } 656245617Sbrooks return; 657245617Sbrooks } 658245617Sbrooks } 659245617Sbrooks 660245617Sbrooks /* Symbolic links. */ 661245617Sbrooks if (dolink & LN_ABSOLUTE) { 662245617Sbrooks /* Convert source path to absolute. */ 663245617Sbrooks if (realpath(from_name, src) == NULL) 664245617Sbrooks err(EX_OSERR, "%s: realpath", from_name); 665245617Sbrooks do_symlink(src, to_name, target_sb); 666245617Sbrooks /* XXX: src may point outside of destdir */ 667245617Sbrooks metadata_log(to_name, "link", NULL, src, NULL, 0); 668245617Sbrooks return; 669245617Sbrooks } 670245617Sbrooks 671245617Sbrooks if (dolink & LN_RELATIVE) { 672318096Sbdrewery char *to_name_copy, *cp, *d, *s; 673245617Sbrooks 674245617Sbrooks /* Resolve pathnames. */ 675245617Sbrooks if (realpath(from_name, src) == NULL) 676245617Sbrooks err(EX_OSERR, "%s: realpath", from_name); 677245617Sbrooks 678245617Sbrooks /* 679245617Sbrooks * The last component of to_name may be a symlink, 680245617Sbrooks * so use realpath to resolve only the directory. 681245617Sbrooks */ 682318096Sbdrewery to_name_copy = strdup(to_name); 683318096Sbdrewery if (to_name_copy == NULL) 684318096Sbdrewery err(EX_OSERR, "%s: strdup", to_name); 685318096Sbdrewery cp = dirname(to_name_copy); 686245617Sbrooks if (realpath(cp, dst) == NULL) 687245617Sbrooks err(EX_OSERR, "%s: realpath", cp); 688245617Sbrooks /* .. and add the last component. */ 689245617Sbrooks if (strcmp(dst, "/") != 0) { 690245617Sbrooks if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst)) 691245617Sbrooks errx(1, "resolved pathname too long"); 692245617Sbrooks } 693318096Sbdrewery strcpy(to_name_copy, to_name); 694318096Sbdrewery cp = basename(to_name_copy); 695245617Sbrooks if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst)) 696245617Sbrooks errx(1, "resolved pathname too long"); 697318096Sbdrewery free(to_name_copy); 698245617Sbrooks 699245617Sbrooks /* Trim common path components. */ 700245617Sbrooks for (s = src, d = dst; *s == *d; s++, d++) 701245617Sbrooks continue; 702245617Sbrooks while (*s != '/') 703245617Sbrooks s--, d--; 704245617Sbrooks 705245617Sbrooks /* Count the number of directories we need to backtrack. */ 706245617Sbrooks for (++d, lnk[0] = '\0'; *d; d++) 707245617Sbrooks if (*d == '/') 708245617Sbrooks (void)strlcat(lnk, "../", sizeof(lnk)); 709245617Sbrooks 710245617Sbrooks (void)strlcat(lnk, ++s, sizeof(lnk)); 711245617Sbrooks 712245617Sbrooks do_symlink(lnk, to_name, target_sb); 713245617Sbrooks /* XXX: Link may point outside of destdir. */ 714245617Sbrooks metadata_log(to_name, "link", NULL, lnk, NULL, 0); 715245617Sbrooks return; 716245617Sbrooks } 717245617Sbrooks 718245617Sbrooks /* 719245617Sbrooks * If absolute or relative was not specified, try the names the 720245617Sbrooks * user provided. 721245617Sbrooks */ 722245617Sbrooks do_symlink(from_name, to_name, target_sb); 723245617Sbrooks /* XXX: from_name may point outside of destdir. */ 724245617Sbrooks metadata_log(to_name, "link", NULL, from_name, NULL, 0); 725245617Sbrooks} 726245617Sbrooks 727245617Sbrooks/* 7281590Srgrimes * install -- 7291590Srgrimes * build a path name and install the file 7301590Srgrimes */ 731200515Sdelphijstatic void 732102944Sdwmaloneinstall(const char *from_name, const char *to_name, u_long fset, u_int flags) 7331590Srgrimes{ 73477345Sru struct stat from_sb, temp_sb, to_sb; 735106967Speter struct timeval tvb[2]; 73677345Sru int devnull, files_match, from_fd, serrno, target; 73777345Sru int tempcopy, temp_fd, to_fd; 73877345Sru char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; 739245617Sbrooks char *digestresult; 7401590Srgrimes 74177345Sru files_match = 0; 742140817Sssouhlal from_fd = -1; 743140817Sssouhlal to_fd = -1; 74411356Sbde 7451590Srgrimes /* If try to install NULL file to a directory, fails. */ 7461590Srgrimes if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 747245617Sbrooks if (!dolink) { 748245617Sbrooks if (stat(from_name, &from_sb)) 749245617Sbrooks err(EX_OSERR, "%s", from_name); 750245617Sbrooks if (!S_ISREG(from_sb.st_mode)) { 751245617Sbrooks errno = EFTYPE; 752245617Sbrooks err(EX_OSERR, "%s", from_name); 753245617Sbrooks } 75411356Sbde } 7551590Srgrimes /* Build the target path. */ 7561590Srgrimes if (flags & DIRECTORY) { 7571590Srgrimes (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 7581590Srgrimes to_name, 75911356Sbde (p = strrchr(from_name, '/')) ? ++p : from_name); 7601590Srgrimes to_name = pathbuf; 7611590Srgrimes } 7621590Srgrimes devnull = 0; 7631590Srgrimes } else { 7641590Srgrimes devnull = 1; 7651590Srgrimes } 7661590Srgrimes 767291823Sbapt target = (lstat(to_name, &to_sb) == 0); 7681590Srgrimes 769245617Sbrooks if (dolink) { 770245617Sbrooks if (target && !safecopy) { 771245617Sbrooks if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1) 772245617Sbrooks err(EX_OSERR, "%s", to_name); 773245617Sbrooks if (to_sb.st_flags & NOCHANGEBITS) 774245617Sbrooks (void)chflags(to_name, 775245617Sbrooks to_sb.st_flags & ~NOCHANGEBITS); 776245617Sbrooks unlink(to_name); 777245617Sbrooks } 778245617Sbrooks makelink(from_name, to_name, target ? &to_sb : NULL); 779245617Sbrooks return; 780245617Sbrooks } 781245617Sbrooks 782291823Sbapt if (target && !S_ISREG(to_sb.st_mode) && !S_ISLNK(to_sb.st_mode)) { 78377345Sru errno = EFTYPE; 78477345Sru warn("%s", to_name); 78577345Sru return; 78677345Sru } 78777345Sru 78877345Sru /* Only copy safe if the target exists. */ 78977345Sru tempcopy = safecopy && target; 79077345Sru 79177345Sru if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) 79277345Sru err(EX_OSERR, "%s", from_name); 79377345Sru 79477345Sru /* If we don't strip, we can compare first. */ 795291823Sbapt if (docompare && !dostrip && target && S_ISREG(to_sb.st_mode)) { 79677345Sru if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 79711356Sbde err(EX_OSERR, "%s", to_name); 79877345Sru if (devnull) 79977345Sru files_match = to_sb.st_size == 0; 80077345Sru else 80177345Sru files_match = !(compare(from_fd, from_name, 80277345Sru (size_t)from_sb.st_size, to_fd, 803245617Sbrooks to_name, (size_t)to_sb.st_size, &digestresult)); 80477345Sru 80577345Sru /* Close "to" file unless we match. */ 80677345Sru if (!files_match) 80777345Sru (void)close(to_fd); 80811356Sbde } 80911356Sbde 81077345Sru if (!files_match) { 81177345Sru if (tempcopy) { 81277345Sru to_fd = create_tempfile(to_name, tempfile, 81377345Sru sizeof(tempfile)); 81477345Sru if (to_fd < 0) 81577345Sru err(EX_OSERR, "%s", tempfile); 81677345Sru } else { 81777345Sru if ((to_fd = create_newfile(to_name, target, 81877345Sru &to_sb)) < 0) 81977345Sru err(EX_OSERR, "%s", to_name); 82077345Sru if (verbose) 82177345Sru (void)printf("install: %s -> %s\n", 82277345Sru from_name, to_name); 8231590Srgrimes } 82477345Sru if (!devnull) 825245617Sbrooks digestresult = copy(from_fd, from_name, to_fd, 82677345Sru tempcopy ? tempfile : to_name, from_sb.st_size); 827245617Sbrooks else 828245617Sbrooks digestresult = NULL; 8291590Srgrimes } 83011356Sbde 83129379Speter if (dostrip) { 83277345Sru strip(tempcopy ? tempfile : to_name); 83329379Speter 83477345Sru /* 83577345Sru * Re-open our fd on the target, in case we used a strip 83677345Sru * that does not work in-place -- like GNU binutils strip. 83777345Sru */ 83877345Sru close(to_fd); 83977345Sru to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0); 84029379Speter if (to_fd < 0) 84177345Sru err(EX_OSERR, "stripping %s", to_name); 84229379Speter } 84329379Speter 8441590Srgrimes /* 84577345Sru * Compare the stripped temp file with the target. 84611356Sbde */ 847291823Sbapt if (docompare && dostrip && target && S_ISREG(to_sb.st_mode)) { 84877345Sru temp_fd = to_fd; 84911356Sbde 85077345Sru /* Re-open to_fd using the real target name. */ 85177345Sru if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 85277345Sru err(EX_OSERR, "%s", to_name); 85377345Sru 85477345Sru if (fstat(temp_fd, &temp_sb)) { 85577345Sru serrno = errno; 85677345Sru (void)unlink(tempfile); 85777345Sru errno = serrno; 85877345Sru err(EX_OSERR, "%s", tempfile); 85977345Sru } 86077345Sru 86177345Sru if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd, 862245617Sbrooks to_name, (size_t)to_sb.st_size, &digestresult) 863245617Sbrooks == 0) { 86477345Sru /* 86577345Sru * If target has more than one link we need to 86677345Sru * replace it in order to snap the extra links. 86777345Sru * Need to preserve target file times, though. 86877345Sru */ 86977345Sru if (to_sb.st_nlink != 1) { 870106967Speter tvb[0].tv_sec = to_sb.st_atime; 871106967Speter tvb[0].tv_usec = 0; 872106967Speter tvb[1].tv_sec = to_sb.st_mtime; 873106967Speter tvb[1].tv_usec = 0; 874106967Speter (void)utimes(tempfile, tvb); 87577345Sru } else { 87677345Sru files_match = 1; 87777345Sru (void)unlink(tempfile); 87811356Sbde } 87977345Sru (void) close(temp_fd); 88077345Sru } 881291823Sbapt } else if (dostrip) 882245617Sbrooks digestresult = digest_file(tempfile); 883245617Sbrooks 88477345Sru /* 88577345Sru * Move the new file into place if doing a safe copy 88677345Sru * and the files are different (or just not compared). 88777345Sru */ 88877345Sru if (tempcopy && !files_match) { 88977345Sru /* Try to turn off the immutable bits. */ 89077345Sru if (to_sb.st_flags & NOCHANGEBITS) 89177345Sru (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); 89277345Sru if (dobackup) { 89387724Sru if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name, 89487724Sru suffix) != strlen(to_name) + strlen(suffix)) { 89577345Sru unlink(tempfile); 89677345Sru errx(EX_OSERR, "%s: backup filename too long", 89777345Sru to_name); 89877345Sru } 89977345Sru if (verbose) 90077345Sru (void)printf("install: %s -> %s\n", to_name, backup); 901304471Sbdrewery if (unlink(backup) < 0 && errno != ENOENT) { 90211356Sbde serrno = errno; 903304471Sbdrewery if (to_sb.st_flags & NOCHANGEBITS) 904304471Sbdrewery (void)chflags(to_name, to_sb.st_flags); 90577345Sru unlink(tempfile); 90611356Sbde errno = serrno; 907304471Sbdrewery err(EX_OSERR, "unlink: %s", backup); 908304471Sbdrewery } 909304471Sbdrewery if (link(to_name, backup) < 0) { 910304471Sbdrewery serrno = errno; 911304471Sbdrewery unlink(tempfile); 912304471Sbdrewery if (to_sb.st_flags & NOCHANGEBITS) 913304471Sbdrewery (void)chflags(to_name, to_sb.st_flags); 914304471Sbdrewery errno = serrno; 915304471Sbdrewery err(EX_OSERR, "link: %s to %s", to_name, 91677345Sru backup); 91711356Sbde } 91811356Sbde } 91977345Sru if (verbose) 92077345Sru (void)printf("install: %s -> %s\n", from_name, to_name); 92177345Sru if (rename(tempfile, to_name) < 0) { 92277345Sru serrno = errno; 92377345Sru unlink(tempfile); 92477345Sru errno = serrno; 92577345Sru err(EX_OSERR, "rename: %s to %s", 92677345Sru tempfile, to_name); 92777345Sru } 92877345Sru 92977345Sru /* Re-open to_fd so we aren't hosed by the rename(2). */ 93077345Sru (void) close(to_fd); 93177345Sru if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 93277345Sru err(EX_OSERR, "%s", to_name); 93311356Sbde } 93411356Sbde 93511356Sbde /* 93677345Sru * Preserve the timestamp of the source file if necessary. 9371590Srgrimes */ 93877345Sru if (dopreserve && !files_match && !devnull) { 939106967Speter tvb[0].tv_sec = from_sb.st_atime; 940106967Speter tvb[0].tv_usec = 0; 941106967Speter tvb[1].tv_sec = from_sb.st_mtime; 942106967Speter tvb[1].tv_usec = 0; 943106967Speter (void)utimes(to_name, tvb); 9441590Srgrimes } 94577345Sru 94677345Sru if (fstat(to_fd, &to_sb) == -1) { 9471590Srgrimes serrno = errno; 9481590Srgrimes (void)unlink(to_name); 94911356Sbde errno = serrno; 95077345Sru err(EX_OSERR, "%s", to_name); 9511590Srgrimes } 9521590Srgrimes 9531590Srgrimes /* 95477345Sru * Set owner, group, mode for target; do the chown first, 95577345Sru * chown may lose the setuid bits. 95677345Sru */ 957245617Sbrooks if (!dounpriv && ((gid != (gid_t)-1 && gid != to_sb.st_gid) || 95877345Sru (uid != (uid_t)-1 && uid != to_sb.st_uid) || 959245617Sbrooks (mode != (to_sb.st_mode & ALLPERMS)))) { 96077345Sru /* Try to turn off the immutable bits. */ 96177345Sru if (to_sb.st_flags & NOCHANGEBITS) 96277345Sru (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS); 96377345Sru } 96477345Sru 965245617Sbrooks if (!dounpriv & 966245617Sbrooks (gid != (gid_t)-1 && gid != to_sb.st_gid) || 96777345Sru (uid != (uid_t)-1 && uid != to_sb.st_uid)) 96877345Sru if (fchown(to_fd, uid, gid) == -1) { 96977345Sru serrno = errno; 97077345Sru (void)unlink(to_name); 97177345Sru errno = serrno; 97277345Sru err(EX_OSERR,"%s: chown/chgrp", to_name); 97377345Sru } 97477345Sru 975245617Sbrooks if (mode != (to_sb.st_mode & ALLPERMS)) { 976246147Sbrooks if (fchmod(to_fd, 977246147Sbrooks dounpriv ? mode & (S_IRWXU|S_IRWXG|S_IRWXO) : mode)) { 97877345Sru serrno = errno; 97977345Sru (void)unlink(to_name); 98077345Sru errno = serrno; 98177345Sru err(EX_OSERR, "%s: chmod", to_name); 98277345Sru } 983245617Sbrooks } 98477345Sru 98577345Sru /* 9861590Srgrimes * If provided a set of flags, set them, otherwise, preserve the 9871590Srgrimes * flags, except for the dump flag. 98836586Speter * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just 98936586Speter * trying to turn off UF_NODUMP. If we're trying to set real flags, 990218909Sbrucec * then warn if the fs doesn't support it, otherwise fail. 9911590Srgrimes */ 992245617Sbrooks if (!dounpriv & !devnull && (flags & SETFLAGS || 993106246Sru (from_sb.st_flags & ~UF_NODUMP) != to_sb.st_flags) && 994106246Sru fchflags(to_fd, 9951590Srgrimes flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 99636586Speter if (flags & SETFLAGS) { 99736586Speter if (errno == EOPNOTSUPP) 99836586Speter warn("%s: chflags", to_name); 99936586Speter else { 100036586Speter serrno = errno; 100136586Speter (void)unlink(to_name); 100236586Speter errno = serrno; 100336586Speter err(EX_OSERR, "%s: chflags", to_name); 100436586Speter } 100536586Speter } 10061590Srgrimes } 10071590Srgrimes 10081590Srgrimes (void)close(to_fd); 100977345Sru if (!devnull) 101077345Sru (void)close(from_fd); 1011245617Sbrooks 1012245617Sbrooks metadata_log(to_name, "file", tvb, NULL, digestresult, to_sb.st_size); 1013245617Sbrooks free(digestresult); 10141590Srgrimes} 10151590Srgrimes 10161590Srgrimes/* 101711356Sbde * compare -- 101811356Sbde * compare two files; non-zero means files differ 101911356Sbde */ 1020200515Sdelphijstatic int 102187685Smarkmcompare(int from_fd, const char *from_name __unused, size_t from_len, 1022245617Sbrooks int to_fd, const char *to_name __unused, size_t to_len, 1023245617Sbrooks char **dresp) 102411356Sbde{ 102511356Sbde char *p, *q; 102611356Sbde int rv; 102718040Speter int done_compare; 1028245617Sbrooks DIGEST_CTX ctx; 102911356Sbde 103051705Sbillf rv = 0; 103177345Sru if (from_len != to_len) 103211356Sbde return 1; 103311356Sbde 103492611Sdes if (from_len <= MAX_CMP_SIZE) { 1035245617Sbrooks if (dresp != NULL) 1036245617Sbrooks digest_init(&ctx); 103718040Speter done_compare = 0; 103818039Speter if (trymmap(from_fd) && trymmap(to_fd)) { 1039245617Sbrooks p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, 1040245617Sbrooks from_fd, (off_t)0); 104121786Salex if (p == (char *)MAP_FAILED) 104218040Speter goto out; 1043245617Sbrooks q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, 1044245617Sbrooks to_fd, (off_t)0); 104521786Salex if (q == (char *)MAP_FAILED) { 104677345Sru munmap(p, from_len); 104718040Speter goto out; 104818040Speter } 104911356Sbde 105077345Sru rv = memcmp(p, q, from_len); 1051245617Sbrooks if (dresp != NULL) 1052245617Sbrooks digest_update(&ctx, p, from_len); 105377345Sru munmap(p, from_len); 105477345Sru munmap(q, from_len); 105518040Speter done_compare = 1; 105618040Speter } 105718040Speter out: 105818040Speter if (!done_compare) { 105918040Speter char buf1[MAXBSIZE]; 106018040Speter char buf2[MAXBSIZE]; 106118039Speter int n1, n2; 106218038Speter 106318039Speter rv = 0; 106418039Speter lseek(from_fd, 0, SEEK_SET); 106518039Speter lseek(to_fd, 0, SEEK_SET); 106618039Speter while (rv == 0) { 106718039Speter n1 = read(from_fd, buf1, sizeof(buf1)); 106818039Speter if (n1 == 0) 106918039Speter break; /* EOF */ 107018039Speter else if (n1 > 0) { 107118039Speter n2 = read(to_fd, buf2, n1); 107218039Speter if (n2 == n1) 107318039Speter rv = memcmp(buf1, buf2, n1); 107418039Speter else 107518039Speter rv = 1; /* out of sync */ 107618039Speter } else 107718039Speter rv = 1; /* read failure */ 1078245617Sbrooks digest_update(&ctx, buf1, n1); 107918039Speter } 108018039Speter lseek(from_fd, 0, SEEK_SET); 108118039Speter lseek(to_fd, 0, SEEK_SET); 108218038Speter } 108318039Speter } else 108418039Speter rv = 1; /* don't bother in this case */ 108518039Speter 1086245617Sbrooks if (dresp != NULL) { 1087245617Sbrooks if (rv == 0) 1088245617Sbrooks *dresp = digest_end(&ctx, NULL); 1089245617Sbrooks else 1090245617Sbrooks (void)digest_end(&ctx, NULL); 1091245617Sbrooks } 1092245617Sbrooks 109311356Sbde return rv; 109411356Sbde} 109511356Sbde 109611356Sbde/* 109777345Sru * create_tempfile -- 109877345Sru * create a temporary file based on path and open it 109977345Sru */ 1100200515Sdelphijstatic int 1101102944Sdwmalonecreate_tempfile(const char *path, char *temp, size_t tsize) 110277345Sru{ 110377345Sru char *p; 110477345Sru 110577345Sru (void)strncpy(temp, path, tsize); 110677345Sru temp[tsize - 1] = '\0'; 110777345Sru if ((p = strrchr(temp, '/')) != NULL) 110877345Sru p++; 110977345Sru else 111077345Sru p = temp; 111177345Sru (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); 111277345Sru temp[tsize - 1] = '\0'; 111377345Sru return (mkstemp(temp)); 111477345Sru} 111577345Sru 111677345Sru/* 111777345Sru * create_newfile -- 111877345Sru * create a new file, overwriting an existing one if necessary 111977345Sru */ 1120200515Sdelphijstatic int 1121102944Sdwmalonecreate_newfile(const char *path, int target, struct stat *sbp) 112277345Sru{ 112377345Sru char backup[MAXPATHLEN]; 1124107428Sfenner int saved_errno = 0; 1125107428Sfenner int newfd; 112677345Sru 112777345Sru if (target) { 112877345Sru /* 112977345Sru * Unlink now... avoid ETXTBSY errors later. Try to turn 113077345Sru * off the append/immutable bits -- if we fail, go ahead, 113177345Sru * it might work. 113277345Sru */ 113377345Sru if (sbp->st_flags & NOCHANGEBITS) 113477345Sru (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); 113577345Sru 113677345Sru if (dobackup) { 113787724Sru if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", 1138304471Sbdrewery path, suffix) != strlen(path) + strlen(suffix)) { 1139304471Sbdrewery saved_errno = errno; 1140304471Sbdrewery if (sbp->st_flags & NOCHANGEBITS) 1141304471Sbdrewery (void)chflags(path, sbp->st_flags); 1142304471Sbdrewery errno = saved_errno; 114377345Sru errx(EX_OSERR, "%s: backup filename too long", 114477345Sru path); 1145304471Sbdrewery } 114677345Sru (void)snprintf(backup, MAXPATHLEN, "%s%s", 114777345Sru path, suffix); 114877345Sru if (verbose) 114977345Sru (void)printf("install: %s -> %s\n", 115077345Sru path, backup); 1151304471Sbdrewery if (rename(path, backup) < 0) { 1152304471Sbdrewery saved_errno = errno; 1153304471Sbdrewery if (sbp->st_flags & NOCHANGEBITS) 1154304471Sbdrewery (void)chflags(path, sbp->st_flags); 1155304471Sbdrewery errno = saved_errno; 115677345Sru err(EX_OSERR, "rename: %s to %s", path, backup); 1157304471Sbdrewery } 115877345Sru } else 1159107428Sfenner if (unlink(path) < 0) 1160107428Sfenner saved_errno = errno; 116177345Sru } 116277345Sru 1163107428Sfenner newfd = open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); 1164107428Sfenner if (newfd < 0 && saved_errno != 0) 1165107428Sfenner errno = saved_errno; 1166107428Sfenner return newfd; 116777345Sru} 116877345Sru 116977345Sru/* 11701590Srgrimes * copy -- 11711590Srgrimes * copy from one file to another 11721590Srgrimes */ 1173245617Sbrooksstatic char * 1174102944Sdwmalonecopy(int from_fd, const char *from_name, int to_fd, const char *to_name, 1175102944Sdwmalone off_t size) 11761590Srgrimes{ 1177102944Sdwmalone int nr, nw; 11781590Srgrimes int serrno; 11791590Srgrimes char *p, buf[MAXBSIZE]; 118018040Speter int done_copy; 1181245617Sbrooks DIGEST_CTX ctx; 11821590Srgrimes 118377345Sru /* Rewind file descriptors. */ 118477345Sru if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) 118577345Sru err(EX_OSERR, "lseek: %s", from_name); 118677345Sru if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1) 118777345Sru err(EX_OSERR, "lseek: %s", to_name); 118877345Sru 1189245617Sbrooks digest_init(&ctx); 1190245617Sbrooks 11911590Srgrimes /* 11921590Srgrimes * Mmap and write if less than 8M (the limit is so we don't totally 11931590Srgrimes * trash memory on big files. This is really a minor hack, but it 11941590Srgrimes * wins some CPU back. 11951590Srgrimes */ 119618040Speter done_copy = 0; 119777345Sru if (size <= 8 * 1048576 && trymmap(from_fd) && 119877345Sru (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED, 119977345Sru from_fd, (off_t)0)) != (char *)MAP_FAILED) { 1200237988Skib nw = write(to_fd, p, size); 1201237988Skib if (nw != size) { 120218040Speter serrno = errno; 120318040Speter (void)unlink(to_name); 1204237988Skib if (nw >= 0) { 1205237988Skib errx(EX_OSERR, 1206237988Skib "short write to %s: %jd bytes written, %jd bytes asked to write", 1207237988Skib to_name, (uintmax_t)nw, (uintmax_t)size); 1208237988Skib } else { 1209237988Skib errno = serrno; 1210237988Skib err(EX_OSERR, "%s", to_name); 1211237988Skib } 121218040Speter } 1213245617Sbrooks digest_update(&ctx, p, size); 1214245617Sbrooks (void)munmap(p, size); 121518040Speter done_copy = 1; 121618040Speter } 121718040Speter if (!done_copy) { 1218245617Sbrooks while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { 12191590Srgrimes if ((nw = write(to_fd, buf, nr)) != nr) { 12201590Srgrimes serrno = errno; 12211590Srgrimes (void)unlink(to_name); 1222237988Skib if (nw >= 0) { 1223237988Skib errx(EX_OSERR, 1224237988Skib "short write to %s: %jd bytes written, %jd bytes asked to write", 1225237988Skib to_name, (uintmax_t)nw, 1226237988Skib (uintmax_t)size); 1227237988Skib } else { 1228237988Skib errno = serrno; 1229237988Skib err(EX_OSERR, "%s", to_name); 1230237988Skib } 12311590Srgrimes } 1232245617Sbrooks digest_update(&ctx, buf, nr); 1233245617Sbrooks } 12341590Srgrimes if (nr != 0) { 12351590Srgrimes serrno = errno; 12361590Srgrimes (void)unlink(to_name); 123711356Sbde errno = serrno; 123811356Sbde err(EX_OSERR, "%s", from_name); 12391590Srgrimes } 12401590Srgrimes } 1241245617Sbrooks return (digest_end(&ctx, NULL)); 12421590Srgrimes} 12431590Srgrimes 12441590Srgrimes/* 12451590Srgrimes * strip -- 12461590Srgrimes * use strip(1) to strip the target file 12471590Srgrimes */ 1248200515Sdelphijstatic void 1249102944Sdwmalonestrip(const char *to_name) 12501590Srgrimes{ 125196416Smarcel const char *stripbin; 12521590Srgrimes int serrno, status; 12531590Srgrimes 125440301Sdes switch (fork()) { 12551590Srgrimes case -1: 12561590Srgrimes serrno = errno; 12571590Srgrimes (void)unlink(to_name); 125811356Sbde errno = serrno; 125911356Sbde err(EX_TEMPFAIL, "fork"); 12601590Srgrimes case 0: 126196416Smarcel stripbin = getenv("STRIPBIN"); 126296416Smarcel if (stripbin == NULL) 126396416Smarcel stripbin = "strip"; 126496416Smarcel execlp(stripbin, stripbin, to_name, (char *)NULL); 126596437Sbde err(EX_OSERR, "exec(%s)", stripbin); 12661590Srgrimes default: 126716611Sbde if (wait(&status) == -1 || status) { 126896674Sfanf serrno = errno; 12691590Srgrimes (void)unlink(to_name); 127096674Sfanf errc(EX_SOFTWARE, serrno, "wait"); 127118525Simp /* NOTREACHED */ 127216611Sbde } 12731590Srgrimes } 12741590Srgrimes} 12751590Srgrimes 12761590Srgrimes/* 127718551Simp * install_dir -- 1278204111Suqs * build directory hierarchy 127918551Simp */ 1280200515Sdelphijstatic void 1281102944Sdwmaloneinstall_dir(char *path) 128218551Simp{ 1283102944Sdwmalone char *p; 128418551Simp struct stat sb; 128518551Simp int ch; 128618551Simp 128718551Simp for (p = path;; ++p) 128818551Simp if (!*p || (p != path && *p == '/')) { 128918551Simp ch = *p; 129018551Simp *p = '\0'; 129118551Simp if (stat(path, &sb)) { 129230809Sache if (errno != ENOENT || mkdir(path, 0755) < 0) { 129330795Sache err(EX_OSERR, "mkdir %s", path); 129418551Simp /* NOTREACHED */ 129577345Sru } else if (verbose) 129677345Sru (void)printf("install: mkdir %s\n", 129777345Sru path); 129830809Sache } else if (!S_ISDIR(sb.st_mode)) 129930809Sache errx(EX_OSERR, "%s exists but is not a directory", path); 130018551Simp if (!(*p = ch)) 130118551Simp break; 130218551Simp } 130318551Simp 1304245617Sbrooks if (!dounpriv) { 1305245617Sbrooks if ((gid != (gid_t)-1 || uid != (uid_t)-1) && 1306245617Sbrooks chown(path, uid, gid)) 1307245617Sbrooks warn("chown %u:%u %s", uid, gid, path); 1308245617Sbrooks /* XXXBED: should we do the chmod in the dounpriv case? */ 1309245617Sbrooks if (chmod(path, mode)) 1310245617Sbrooks warn("chmod %o %s", mode, path); 1311245617Sbrooks } 1312245617Sbrooks metadata_log(path, "dir", NULL, NULL, NULL, 0); 131318551Simp} 131418551Simp 131518551Simp/* 1316245617Sbrooks * metadata_log -- 1317245617Sbrooks * if metafp is not NULL, output mtree(8) full path name and settings to 1318245617Sbrooks * metafp, to allow permissions to be set correctly by other tools, 1319245617Sbrooks * or to allow integrity checks to be performed. 1320245617Sbrooks */ 1321245617Sbrooksstatic void 1322245617Sbrooksmetadata_log(const char *path, const char *type, struct timeval *tv, 1323245617Sbrooks const char *slink, const char *digestresult, off_t size) 1324245617Sbrooks{ 1325245617Sbrooks static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; 1326245617Sbrooks const char *p; 1327245617Sbrooks char *buf; 1328245617Sbrooks size_t destlen; 1329245617Sbrooks struct flock metalog_lock; 1330245617Sbrooks 1331245617Sbrooks if (!metafp) 1332245617Sbrooks return; 1333245617Sbrooks /* Buffer for strsvis(3). */ 1334245617Sbrooks buf = (char *)malloc(4 * strlen(path) + 1); 1335245617Sbrooks if (buf == NULL) { 1336245617Sbrooks warnx("%s", strerror(ENOMEM)); 1337245617Sbrooks return; 1338245617Sbrooks } 1339245617Sbrooks 1340245617Sbrooks /* Lock log file. */ 1341245617Sbrooks metalog_lock.l_start = 0; 1342245617Sbrooks metalog_lock.l_len = 0; 1343245617Sbrooks metalog_lock.l_whence = SEEK_SET; 1344245617Sbrooks metalog_lock.l_type = F_WRLCK; 1345245617Sbrooks if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { 1346245617Sbrooks warn("can't lock %s", metafile); 1347245617Sbrooks free(buf); 1348245617Sbrooks return; 1349245617Sbrooks } 1350245617Sbrooks 1351245617Sbrooks /* Remove destdir. */ 1352245617Sbrooks p = path; 1353245617Sbrooks if (destdir) { 1354245617Sbrooks destlen = strlen(destdir); 1355245617Sbrooks if (strncmp(p, destdir, destlen) == 0 && 1356245617Sbrooks (p[destlen] == '/' || p[destlen] == '\0')) 1357245617Sbrooks p += destlen; 1358245617Sbrooks } 1359245617Sbrooks while (*p && *p == '/') 1360245617Sbrooks p++; 1361245617Sbrooks strsvis(buf, p, VIS_OCTAL, extra); 1362245617Sbrooks p = buf; 1363245617Sbrooks /* Print details. */ 1364245617Sbrooks fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type); 1365245617Sbrooks if (owner) 1366245617Sbrooks fprintf(metafp, " uname=%s", owner); 1367245617Sbrooks if (group) 1368245617Sbrooks fprintf(metafp, " gname=%s", group); 1369245617Sbrooks fprintf(metafp, " mode=%#o", mode); 1370245617Sbrooks if (slink) { 1371245617Sbrooks strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */ 1372245617Sbrooks fprintf(metafp, " link=%s", buf); 1373245617Sbrooks } 1374245617Sbrooks if (*type == 'f') /* type=file */ 1375245617Sbrooks fprintf(metafp, " size=%lld", (long long)size); 1376245617Sbrooks if (tv != NULL && dopreserve) 1377245617Sbrooks fprintf(metafp, " time=%lld.%ld", 1378245617Sbrooks (long long)tv[1].tv_sec, (long)tv[1].tv_usec); 1379245617Sbrooks if (digestresult && digest) 1380245617Sbrooks fprintf(metafp, " %s=%s", digest, digestresult); 1381245617Sbrooks if (fflags) 1382245617Sbrooks fprintf(metafp, " flags=%s", fflags); 1383245617Sbrooks if (tags) 1384245617Sbrooks fprintf(metafp, " tags=%s", tags); 1385245617Sbrooks fputc('\n', metafp); 1386245617Sbrooks /* Flush line. */ 1387245617Sbrooks fflush(metafp); 1388245617Sbrooks 1389245617Sbrooks /* Unlock log file. */ 1390245617Sbrooks metalog_lock.l_type = F_UNLCK; 1391245617Sbrooks if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) 1392245617Sbrooks warn("can't unlock %s", metafile); 1393245617Sbrooks free(buf); 1394245617Sbrooks} 1395245617Sbrooks 1396245617Sbrooks/* 13971590Srgrimes * usage -- 13981590Srgrimes * print a usage message and die 13991590Srgrimes */ 1400200515Sdelphijstatic void 1401200465Sdelphijusage(void) 14021590Srgrimes{ 1403125553Sru (void)fprintf(stderr, 1404245617Sbrooks"usage: install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n" 1405245617Sbrooks" [-M log] [-D dest] [-h hash] [-T tags]\n" 1406245617Sbrooks" [-B suffix] [-l linkflags] [-N dbdir]\n" 1407245617Sbrooks" file1 file2\n" 1408245617Sbrooks" install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n" 1409245617Sbrooks" [-M log] [-D dest] [-h hash] [-T tags]\n" 1410245617Sbrooks" [-B suffix] [-l linkflags] [-N dbdir]\n" 1411245617Sbrooks" file1 ... fileN directory\n" 1412245617Sbrooks" install -dU [-vU] [-g group] [-m mode] [-N dbdir] [-o owner]\n" 1413245617Sbrooks" [-M log] [-D dest] [-h hash] [-T tags]\n" 1414245312Sbrooks" directory ...\n"); 141518525Simp exit(EX_USAGE); 141618525Simp /* NOTREACHED */ 14171590Srgrimes} 141818038Speter 141918038Speter/* 142018038Speter * trymmap -- 142118038Speter * return true (1) if mmap should be tried, false (0) if not. 142218038Speter */ 1423200515Sdelphijstatic int 1424102944Sdwmalonetrymmap(int fd) 142518038Speter{ 142632652Sbde/* 142732652Sbde * The ifdef is for bootstrapping - f_fstypename doesn't exist in 142832652Sbde * pre-Lite2-merge systems. 142932652Sbde */ 143032652Sbde#ifdef MFSNAMELEN 143118038Speter struct statfs stfs; 143218038Speter 1433245617Sbrooks if (fstatfs(fd, &stfs) != 0) 143432652Sbde return (0); 143532652Sbde if (strcmp(stfs.f_fstypename, "ufs") == 0 || 143632652Sbde strcmp(stfs.f_fstypename, "cd9660") == 0) 143732652Sbde return (1); 143832360Sjb#endif 143932652Sbde return (0); 144018038Speter} 1441