Home | History | Annotate | Line # | Download | only in xinstall
xinstall.c revision 1.107
      1 /*	$NetBSD: xinstall.c,v 1.107 2009/04/14 08:54:59 lukem Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1987, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #if HAVE_NBTOOL_CONFIG_H
     33 #include "nbtool_config.h"
     34 #else
     35 #define HAVE_FUTIMES 1
     36 #define HAVE_STRUCT_STAT_ST_FLAGS 1
     37 #endif
     38 
     39 #include <sys/cdefs.h>
     40 #if defined(__COPYRIGHT) && !defined(lint)
     41 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\
     42  The Regents of the University of California.  All rights reserved.");
     43 #endif /* not lint */
     44 
     45 #if defined(__RCSID) && !defined(lint)
     46 #if 0
     47 static char sccsid[] = "@(#)xinstall.c	8.1 (Berkeley) 7/21/93";
     48 #else
     49 __RCSID("$NetBSD: xinstall.c,v 1.107 2009/04/14 08:54:59 lukem Exp $");
     50 #endif
     51 #endif /* not lint */
     52 
     53 #define __MKTEMP_OK__	/* All uses of mktemp have been checked */
     54 #include <sys/param.h>
     55 #include <sys/mman.h>
     56 #include <sys/stat.h>
     57 #include <sys/wait.h>
     58 #include <sys/time.h>
     59 
     60 #include <ctype.h>
     61 #include <err.h>
     62 #include <errno.h>
     63 #include <fcntl.h>
     64 #include <grp.h>
     65 #include <libgen.h>
     66 #include <paths.h>
     67 #include <pwd.h>
     68 #include <stdio.h>
     69 #include <stdlib.h>
     70 #include <string.h>
     71 #include <unistd.h>
     72 #include <util.h>
     73 #include <vis.h>
     74 
     75 #include <md5.h>
     76 #include <rmd160.h>
     77 #include <sha1.h>
     78 
     79 #include "pathnames.h"
     80 #include "mtree.h"
     81 
     82 #define STRIP_ARGS_MAX 32
     83 #define BACKUP_SUFFIX ".old"
     84 
     85 int	dobackup, dodir, dostrip, dolink, dopreserve, dorename, dounpriv;
     86 int	haveopt_f, haveopt_g, haveopt_m, haveopt_o;
     87 int	numberedbackup;
     88 int	mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
     89 char	pathbuf[MAXPATHLEN];
     90 id_t	uid = -1, gid = -1;
     91 char	*group, *owner, *fflags, *tags;
     92 FILE	*metafp;
     93 char	*metafile;
     94 u_long	fileflags;
     95 char	*stripArgs;
     96 char	*afterinstallcmd;
     97 const char *suffix = BACKUP_SUFFIX;
     98 char	*destdir;
     99 
    100 enum {
    101 	DIGEST_NONE = 0,
    102 	DIGEST_MD5,
    103 	DIGEST_RMD160,
    104 	DIGEST_SHA1,
    105 } digesttype = DIGEST_NONE;
    106 char	*digest;
    107 
    108 #define LN_ABSOLUTE	0x01
    109 #define LN_RELATIVE	0x02
    110 #define LN_HARD		0x04
    111 #define LN_SYMBOLIC	0x08
    112 #define LN_MIXED	0x10
    113 
    114 #define	DIRECTORY	0x01		/* Tell install it's a directory. */
    115 #define	SETFLAGS	0x02		/* Tell install to set flags. */
    116 #define	HASUID		0x04		/* Tell install the uid was given */
    117 #define	HASGID		0x08		/* Tell install the gid was given */
    118 
    119 void	afterinstall(const char *, const char *, int);
    120 void	backup(const char *);
    121 char   *copy(int, char *, int, char *, off_t);
    122 int	do_link(char *, char *);
    123 void	do_symlink(char *, char *);
    124 void	install(char *, char *, u_int);
    125 void	install_dir(char *, u_int);
    126 int	main(int, char *[]);
    127 void	makelink(char *, char *);
    128 void	metadata_log(const char *, const char *, struct timeval *,
    129 	    const char *, const char *, off_t);
    130 int	parseid(char *, id_t *);
    131 void	strip(char *);
    132 void	usage(void);
    133 char   *xbasename(char *);
    134 char   *xdirname(char *);
    135 
    136 int
    137 main(int argc, char *argv[])
    138 {
    139 	struct stat	from_sb, to_sb;
    140 	void		*set;
    141 	u_int		iflags;
    142 	int		ch, no_target;
    143 	char		*p, *to_name;
    144 
    145 	setprogname(argv[0]);
    146 
    147 	iflags = 0;
    148 	while ((ch = getopt(argc, argv, "a:cbB:dD:f:g:h:l:m:M:N:o:prsS:T:U"))
    149 	    != -1)
    150 		switch((char)ch) {
    151 		case 'a':
    152 			afterinstallcmd = strdup(optarg);
    153 			if (afterinstallcmd == NULL)
    154 				errx(1, "%s", strerror(ENOMEM));
    155 			break;
    156 		case 'B':
    157 			suffix = optarg;
    158 			numberedbackup = 0;
    159 			{
    160 				/* Check if given suffix really generates
    161 				   different suffixes - catch e.g. ".%" */
    162 				char suffix_expanded0[FILENAME_MAX],
    163 				     suffix_expanded1[FILENAME_MAX];
    164 				(void)snprintf(suffix_expanded0, FILENAME_MAX,
    165 					       suffix, 0);
    166 				(void)snprintf(suffix_expanded1, FILENAME_MAX,
    167 					       suffix, 1);
    168 				if (strcmp(suffix_expanded0, suffix_expanded1)
    169 				    != 0)
    170 					numberedbackup = 1;
    171 			}
    172 			/* fall through; -B implies -b */
    173 			/*FALLTHROUGH*/
    174 		case 'b':
    175 			dobackup = 1;
    176 			break;
    177 		case 'c':
    178 			/* ignored; was "docopy" which is now the default. */
    179 			break;
    180 		case 'd':
    181 			dodir = 1;
    182 			break;
    183 		case 'D':
    184 			destdir = optarg;
    185 			break;
    186 #if ! HAVE_NBTOOL_CONFIG_H
    187 		case 'f':
    188 			haveopt_f = 1;
    189 			fflags = optarg;
    190 			break;
    191 #endif
    192 		case 'g':
    193 			haveopt_g = 1;
    194 			group = optarg;
    195 			break;
    196 		case 'h':
    197 			digest = optarg;
    198 			break;
    199 		case 'l':
    200 			for (p = optarg; *p; p++)
    201 				switch (*p) {
    202 				case 's':
    203 					dolink &= ~(LN_HARD|LN_MIXED);
    204 					dolink |= LN_SYMBOLIC;
    205 					break;
    206 				case 'h':
    207 					dolink &= ~(LN_SYMBOLIC|LN_MIXED);
    208 					dolink |= LN_HARD;
    209 					break;
    210 				case 'm':
    211 					dolink &= ~(LN_SYMBOLIC|LN_HARD);
    212 					dolink |= LN_MIXED;
    213 					break;
    214 				case 'a':
    215 					dolink &= ~LN_RELATIVE;
    216 					dolink |= LN_ABSOLUTE;
    217 					break;
    218 				case 'r':
    219 					dolink &= ~LN_ABSOLUTE;
    220 					dolink |= LN_RELATIVE;
    221 					break;
    222 				default:
    223 					errx(1, "%c: invalid link type", *p);
    224 					/* NOTREACHED */
    225 				}
    226 			break;
    227 		case 'm':
    228 			haveopt_m = 1;
    229 			if (!(set = setmode(optarg)))
    230 				err(1, "Cannot set file mode `%s'", optarg);
    231 			mode = getmode(set, 0);
    232 			free(set);
    233 			break;
    234 		case 'M':
    235 			metafile = optarg;
    236 			break;
    237 		case 'N':
    238 			if (! setup_getid(optarg))
    239 				errx(1,
    240 			    "Unable to use user and group databases in `%s'",
    241 				    optarg);
    242 			break;
    243 		case 'o':
    244 			haveopt_o = 1;
    245 			owner = optarg;
    246 			break;
    247 		case 'p':
    248 			dopreserve = 1;
    249 			break;
    250 		case 'r':
    251 			dorename = 1;
    252 			break;
    253 		case 'S':
    254 			stripArgs = strdup(optarg);
    255 			if (stripArgs == NULL)
    256 				errx(1, "%s", strerror(ENOMEM));
    257 			/* fall through; -S implies -s */
    258 			/*FALLTHROUGH*/
    259 		case 's':
    260 			dostrip = 1;
    261 			break;
    262 		case 'T':
    263 			tags = optarg;
    264 			break;
    265 		case 'U':
    266 			dounpriv = 1;
    267 			break;
    268 		case '?':
    269 		default:
    270 			usage();
    271 		}
    272 	argc -= optind;
    273 	argv += optind;
    274 
    275 	/* strip and link options make no sense when creating directories */
    276 	if ((dostrip || dolink) && dodir)
    277 		usage();
    278 
    279 	/* strip and flags make no sense with links */
    280 	if ((dostrip || fflags) && dolink)
    281 		usage();
    282 
    283 	/* must have at least two arguments, except when creating directories */
    284 	if (argc < 2 && !dodir)
    285 		usage();
    286 
    287 	if (digest) {
    288 		if (0) {
    289 		} else if (strcmp(digest, "none") == 0) {
    290 			digesttype = DIGEST_NONE;
    291 		} else if (strcmp(digest, "md5") == 0) {
    292 			digesttype = DIGEST_MD5;
    293 		} else if (strcmp(digest, "rmd160") == 0) {
    294 			digesttype = DIGEST_RMD160;
    295 		} else if (strcmp(digest, "sha1") == 0) {
    296 			digesttype = DIGEST_SHA1;
    297 		} else {
    298 			warnx("unknown digest `%s'", digest);
    299 			usage();
    300 		}
    301 	}
    302 
    303 	/* get group and owner id's */
    304 	if (group && !dounpriv) {
    305 		if (gid_from_group(group, &gid) == -1 && ! parseid(group, &gid))
    306 			errx(1, "unknown group %s", group);
    307 		iflags |= HASGID;
    308 	}
    309 	if (owner && !dounpriv) {
    310 		if (uid_from_user(owner, &uid) == -1 && ! parseid(owner, &uid))
    311 			errx(1, "unknown user %s", owner);
    312 		iflags |= HASUID;
    313 	}
    314 
    315 #if ! HAVE_NBTOOL_CONFIG_H
    316 	if (fflags && !dounpriv) {
    317 		if (string_to_flags(&fflags, &fileflags, NULL))
    318 			errx(1, "%s: invalid flag", fflags);
    319 		/* restore fflags since string_to_flags() changed it */
    320 		fflags = flags_to_string(fileflags, "-");
    321 		iflags |= SETFLAGS;
    322 	}
    323 #endif
    324 
    325 	if (metafile) {
    326 		if ((metafp = fopen(metafile, "a")) == NULL)
    327 			warn("open %s", metafile);
    328 	} else
    329 		digesttype = DIGEST_NONE;
    330 
    331 	if (dodir) {
    332 		for (; *argv != NULL; ++argv)
    333 			install_dir(*argv, iflags);
    334 		exit (0);
    335 	}
    336 
    337 	no_target = stat(to_name = argv[argc - 1], &to_sb);
    338 	if (!no_target && S_ISDIR(to_sb.st_mode)) {
    339 		for (; *argv != to_name; ++argv)
    340 			install(*argv, to_name, iflags | DIRECTORY);
    341 		exit(0);
    342 	}
    343 
    344 	/* can't do file1 file2 directory/file */
    345 	if (argc != 2) {
    346 		errx(EXIT_FAILURE, "the last argument (%s) "
    347 		    "must name an existing directory", argv[argc - 1]);
    348 		/* NOTREACHED */
    349 	}
    350 
    351 	if (!no_target) {
    352 		/* makelink() handles checks for links */
    353 		if (!dolink) {
    354 			if (stat(*argv, &from_sb))
    355 				err(1, "%s: stat", *argv);
    356 			if (!S_ISREG(to_sb.st_mode))
    357 				errx(1, "%s: not a regular file", to_name);
    358 			if (to_sb.st_dev == from_sb.st_dev &&
    359 			    to_sb.st_ino == from_sb.st_ino)
    360 				errx(1, "%s and %s are the same file", *argv,
    361 				    to_name);
    362 		}
    363 		/*
    364 		 * Unlink now... avoid ETXTBSY errors later.  Try and turn
    365 		 * off the append/immutable bits -- if we fail, go ahead,
    366 		 * it might work.
    367 		 */
    368 #if ! HAVE_NBTOOL_CONFIG_H
    369 #define	NOCHANGEBITS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
    370 		if (to_sb.st_flags & NOCHANGEBITS)
    371 			(void)chflags(to_name,
    372 			    to_sb.st_flags & ~(NOCHANGEBITS));
    373 #endif
    374 		if (dobackup)
    375 			backup(to_name);
    376 		else if (!dorename)
    377 			(void)unlink(to_name);
    378 	}
    379 	install(*argv, to_name, iflags);
    380 	exit(0);
    381 }
    382 
    383 /*
    384  * parseid --
    385  *	parse uid or gid from arg into id, returning non-zero if successful
    386  */
    387 int
    388 parseid(char *name, id_t *id)
    389 {
    390 	char	*ep;
    391 
    392 	errno = 0;
    393 	*id = (id_t)strtoul(name, &ep, 10);
    394 	if (errno || *ep != '\0')
    395 		return (0);
    396 	return (1);
    397 }
    398 
    399 /*
    400  * do_link --
    401  *	make a hard link, obeying dorename if set
    402  *	return -1 on failure
    403  */
    404 int
    405 do_link(char *from_name, char *to_name)
    406 {
    407 	char tmpl[MAXPATHLEN];
    408 	int ret;
    409 
    410 	if (dorename) {
    411 		(void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
    412 		    xdirname(to_name));
    413 		/* This usage is safe. */
    414 		if (mktemp(tmpl) == NULL)
    415 			err(1, "%s: mktemp", tmpl);
    416 		ret = link(from_name, tmpl);
    417 		if (ret == 0) {
    418 			ret = rename(tmpl, to_name);
    419 			/* If rename has posix semantics, then the temporary
    420 			 * file may still exist when from_name and to_name point
    421 			 * to the same file, so unlink it unconditionally.
    422 			 */
    423 			(void)unlink(tmpl);
    424 		}
    425 		return (ret);
    426 	} else
    427 		return (link(from_name, to_name));
    428 }
    429 
    430 /*
    431  * do_symlink --
    432  *	make a symbolic link, obeying dorename if set
    433  *	exit on failure
    434  */
    435 void
    436 do_symlink(char *from_name, char *to_name)
    437 {
    438 	char tmpl[MAXPATHLEN];
    439 
    440 	if (dorename) {
    441 		(void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
    442 		    xdirname(to_name));
    443 		/* This usage is safe. */
    444 		if (mktemp(tmpl) == NULL)
    445 			err(1, "%s: mktemp", tmpl);
    446 
    447 		if (symlink(from_name, tmpl) == -1)
    448 			err(1, "symlink %s -> %s", from_name, tmpl);
    449 		if (rename(tmpl, to_name) == -1) {
    450 			/* remove temporary link before exiting */
    451 			(void)unlink(tmpl);
    452 			err(1, "%s: rename", to_name);
    453 		}
    454 	} else {
    455 		if (symlink(from_name, to_name) == -1)
    456 			err(1, "symlink %s -> %s", from_name, to_name);
    457 	}
    458 }
    459 
    460 /*
    461  * makelink --
    462  *	make a link from source to destination
    463  */
    464 void
    465 makelink(char *from_name, char *to_name)
    466 {
    467 	char	src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
    468 	struct stat	to_sb;
    469 
    470 	/* Try hard links first */
    471 	if (dolink & (LN_HARD|LN_MIXED)) {
    472 		if (do_link(from_name, to_name) == -1) {
    473 			if ((dolink & LN_HARD) || errno != EXDEV)
    474 				err(1, "link %s -> %s", from_name, to_name);
    475 		} else {
    476 			if (stat(to_name, &to_sb))
    477 				err(1, "%s: stat", to_name);
    478 			if (S_ISREG(to_sb.st_mode)) {
    479 					/* XXX: hard links to anything
    480 					 * other than plain files are not
    481 					 * metalogged
    482 					 */
    483 				int omode;
    484 				char *oowner, *ogroup, *offlags;
    485 				char *dres;
    486 
    487 					/* XXX: use underlying perms,
    488 					 * unless overridden on command line.
    489 					 */
    490 				omode = mode;
    491 				if (!haveopt_m)
    492 					mode = (to_sb.st_mode & 0777);
    493 				oowner = owner;
    494 				if (!haveopt_o)
    495 					owner = NULL;
    496 				ogroup = group;
    497 				if (!haveopt_g)
    498 					group = NULL;
    499 				offlags = fflags;
    500 				if (!haveopt_f)
    501 					fflags = NULL;
    502 				switch (digesttype) {
    503 				case DIGEST_MD5:
    504 					dres = MD5File(from_name, NULL);
    505 					break;
    506 				case DIGEST_RMD160:
    507 					dres = RMD160File(from_name, NULL);
    508 					break;
    509 				case DIGEST_SHA1:
    510 					dres = SHA1File(from_name, NULL);
    511 					break;
    512 				default:
    513 					dres = NULL;
    514 				}
    515 				metadata_log(to_name, "file", NULL, NULL,
    516 				    dres, to_sb.st_size);
    517 				free(dres);
    518 				mode = omode;
    519 				owner = oowner;
    520 				group = ogroup;
    521 				fflags = offlags;
    522 			}
    523 			return;
    524 		}
    525 	}
    526 
    527 	/* Symbolic links */
    528 	if (dolink & LN_ABSOLUTE) {
    529 		/* Convert source path to absolute */
    530 		if (realpath(from_name, src) == NULL)
    531 			err(1, "%s: realpath", from_name);
    532 		do_symlink(src, to_name);
    533 			/* XXX: src may point outside of destdir */
    534 		metadata_log(to_name, "link", NULL, src, NULL, 0);
    535 		return;
    536 	}
    537 
    538 	if (dolink & LN_RELATIVE) {
    539 		char *cp, *d, *s;
    540 
    541 		/* Resolve pathnames */
    542 		if (realpath(from_name, src) == NULL)
    543 			err(1, "%s: realpath", from_name);
    544 
    545 		/*
    546 		 * The last component of to_name may be a symlink,
    547 		 * so use realpath to resolve only the directory.
    548 		 */
    549 		cp = xdirname(to_name);
    550 		if (realpath(cp, dst) == NULL)
    551 			err(1, "%s: realpath", cp);
    552 		/* .. and add the last component */
    553 		if (strcmp(dst, "/") != 0) {
    554 			if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
    555 				errx(1, "resolved pathname too long");
    556 		}
    557 		cp = xbasename(to_name);
    558 		if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
    559 			errx(1, "resolved pathname too long");
    560 
    561 		/* trim common path components */
    562 		for (s = src, d = dst; *s == *d; s++, d++)
    563 			continue;
    564 		while (*s != '/')
    565 			s--, d--;
    566 
    567 		/* count the number of directories we need to backtrack */
    568 		for (++d, lnk[0] = '\0'; *d; d++)
    569 			if (*d == '/')
    570 				(void)strlcat(lnk, "../", sizeof(lnk));
    571 
    572 		(void)strlcat(lnk, ++s, sizeof(lnk));
    573 
    574 		do_symlink(lnk, to_name);
    575 			/* XXX: lnk may point outside of destdir */
    576 		metadata_log(to_name, "link", NULL, lnk, NULL, 0);
    577 		return;
    578 	}
    579 
    580 	/*
    581 	 * If absolute or relative was not specified,
    582 	 * try the names the user provided
    583 	 */
    584 	do_symlink(from_name, to_name);
    585 		/* XXX: from_name may point outside of destdir */
    586 	metadata_log(to_name, "link", NULL, from_name, NULL, 0);
    587 }
    588 
    589 /*
    590  * install --
    591  *	build a path name and install the file
    592  */
    593 void
    594 install(char *from_name, char *to_name, u_int flags)
    595 {
    596 	struct stat	from_sb;
    597 #if ! HAVE_NBTOOL_CONFIG_H
    598 	struct stat	to_sb;
    599 #endif
    600 	struct timeval	tv[2];
    601 	int		devnull, from_fd, to_fd, serrno, tmpmode;
    602 	char		*p, tmpl[MAXPATHLEN], *oto_name, *digestresult;
    603 
    604 	if (!dolink) {
    605 			/* ensure that from_sb & tv are sane if !dolink */
    606 		if (stat(from_name, &from_sb))
    607 			err(1, "%s: stat", from_name);
    608 #if BSD4_4 && !HAVE_NBTOOL_CONFIG_H
    609 		TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec);
    610 		TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec);
    611 #else
    612 		tv[0].tv_sec = from_sb.st_atime;
    613 		tv[0].tv_usec = 0;
    614 		tv[1].tv_sec = from_sb.st_mtime;
    615 		tv[1].tv_usec = 0;
    616 #endif
    617 	}
    618 
    619 	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
    620 		if (!dolink) {
    621 			if (!S_ISREG(from_sb.st_mode))
    622 				errx(1, "%s: not a regular file", from_name);
    623 		}
    624 		/* Build the target path. */
    625 		if (flags & DIRECTORY) {
    626 			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
    627 			    to_name,
    628 			    (p = strrchr(from_name, '/')) ? ++p : from_name);
    629 			to_name = pathbuf;
    630 		}
    631 		devnull = 0;
    632 	} else {
    633 #if HAVE_STRUCT_STAT_ST_FLAGS
    634 		from_sb.st_flags = 0;	/* XXX */
    635 #endif
    636 		devnull = 1;
    637 	}
    638 
    639 	/*
    640 	 * Unlink now... avoid ETXTBSY errors later.  Try and turn
    641 	 * off the append/immutable bits -- if we fail, go ahead,
    642 	 * it might work.
    643 	 */
    644 #if ! HAVE_NBTOOL_CONFIG_H
    645 	if (stat(to_name, &to_sb) == 0 &&
    646 	    to_sb.st_flags & (NOCHANGEBITS))
    647 		(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
    648 #endif
    649 	if (dorename) {
    650 		(void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
    651 		    xdirname(to_name));
    652 		oto_name = to_name;
    653 		to_name = tmpl;
    654 	} else {
    655 		oto_name = NULL;	/* pacify gcc */
    656 		if (dobackup)
    657 			backup(to_name);
    658 		else
    659 			(void)unlink(to_name);
    660 	}
    661 
    662 	if (dolink) {
    663 		makelink(from_name, dorename ? oto_name : to_name);
    664 		return;
    665 	}
    666 
    667 	/* Create target. */
    668 	if (dorename) {
    669 		if ((to_fd = mkstemp(to_name)) == -1)
    670 			err(1, "%s: mkstemp", to_name);
    671 	} else {
    672 		if ((to_fd = open(to_name,
    673 		    O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
    674 			err(1, "%s: open", to_name);
    675 	}
    676 	digestresult = NULL;
    677 	if (!devnull) {
    678 		if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
    679 			(void)unlink(to_name);
    680 			err(1, "%s: open", from_name);
    681 		}
    682 		digestresult =
    683 		    copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
    684 		(void)close(from_fd);
    685 	}
    686 
    687 	if (dostrip) {
    688 		strip(to_name);
    689 
    690 		/*
    691 		 * Re-open our fd on the target, in case we used a strip
    692 		 *  that does not work in-place -- like gnu binutils strip.
    693 		 */
    694 		close(to_fd);
    695 		if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
    696 			err(1, "stripping %s", to_name);
    697 	}
    698 
    699 	if (afterinstallcmd != NULL) {
    700 		afterinstall(afterinstallcmd, to_name, 1);
    701 
    702 		/*
    703 		 * Re-open our fd on the target, in case we used an
    704 		 * after-install command that does not work in-place
    705 		 */
    706 		close(to_fd);
    707 		if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
    708 			err(1, "running after install command on %s", to_name);
    709 	}
    710 
    711 	/*
    712 	 * Set owner, group, mode for target; do the chown first,
    713 	 * chown may lose the setuid bits.
    714 	 */
    715 	if (!dounpriv &&
    716 	    (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) {
    717 		serrno = errno;
    718 		(void)unlink(to_name);
    719 		errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
    720 	}
    721 	tmpmode = mode;
    722 	if (dounpriv)
    723 		tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO;
    724 	if (fchmod(to_fd, tmpmode) == -1) {
    725 		serrno = errno;
    726 		(void)unlink(to_name);
    727 		errx(1, "%s: chmod: %s", to_name, strerror(serrno));
    728 	}
    729 
    730 	/*
    731 	 * Preserve the date of the source file.
    732 	 */
    733 	if (dopreserve) {
    734 #if HAVE_FUTIMES
    735 		if (futimes(to_fd, tv) == -1)
    736 			warn("%s: futimes", to_name);
    737 #else
    738 		if (utimes(to_name, tv) == -1)
    739 			warn("%s: utimes", to_name);
    740 #endif
    741 	}
    742 
    743 	(void)close(to_fd);
    744 
    745 	if (dorename) {
    746 		if (rename(to_name, oto_name) == -1)
    747 			err(1, "%s: rename", to_name);
    748 		to_name = oto_name;
    749 	}
    750 
    751 	/*
    752 	 * If provided a set of flags, set them, otherwise, preserve the
    753 	 * flags, except for the dump flag.
    754 	 */
    755 #if ! HAVE_NBTOOL_CONFIG_H
    756 	if (!dounpriv && chflags(to_name,
    757 	    flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1)
    758 	{
    759 		if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
    760 			warn("%s: chflags", to_name);
    761 	}
    762 #endif
    763 
    764 	metadata_log(to_name, "file", tv, NULL, digestresult,
    765 	    (devnull ? 0 : from_sb.st_size));
    766 	free(digestresult);
    767 }
    768 
    769 /*
    770  * copy --
    771  *	copy from one file to another
    772  */
    773 char *
    774 copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size)
    775 {
    776 	ssize_t	nr, nw;
    777 	int	serrno;
    778 	u_char	*p;
    779 	u_char	buf[MAXBSIZE];
    780 	MD5_CTX		ctxMD5;
    781 	RMD160_CTX	ctxRMD160;
    782 	SHA1_CTX	ctxSHA1;
    783 
    784 	switch (digesttype) {
    785 	case DIGEST_MD5:
    786 		MD5Init(&ctxMD5);
    787 		break;
    788 	case DIGEST_RMD160:
    789 		RMD160Init(&ctxRMD160);
    790 		break;
    791 	case DIGEST_SHA1:
    792 		SHA1Init(&ctxSHA1);
    793 		break;
    794 	case DIGEST_NONE:
    795 	default:
    796 		break;
    797 	}
    798 	/*
    799 	 * There's no reason to do anything other than close the file
    800 	 * now if it's empty, so let's not bother.
    801 	 */
    802 	if (size > 0) {
    803 
    804 		/*
    805 		 * Mmap and write if less than 8M (the limit is so we
    806 		 * don't totally trash memory on big files).  This is
    807 		 * really a minor hack, but it wins some CPU back.
    808 		 */
    809 
    810 		if (size <= 8 * 1048576) {
    811 			if ((p = mmap(NULL, (size_t)size, PROT_READ,
    812 			    MAP_FILE|MAP_SHARED, from_fd, (off_t)0))
    813 			    == MAP_FAILED) {
    814 				goto mmap_failed;
    815 			}
    816 #if defined(MADV_SEQUENTIAL) && !defined(__APPLE__)
    817 			if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1
    818 			    && errno != EOPNOTSUPP)
    819 				warnx("madvise: %s", strerror(errno));
    820 #endif
    821 
    822 			if (write(to_fd, p, size) != size) {
    823 				serrno = errno;
    824 				(void)unlink(to_name);
    825 				errx(1, "%s: write: %s",
    826 				    to_name, strerror(serrno));
    827 			}
    828 			switch (digesttype) {
    829 			case DIGEST_MD5:
    830 				MD5Update(&ctxMD5, p, size);
    831 				break;
    832 			case DIGEST_RMD160:
    833 				RMD160Update(&ctxRMD160, p, size);
    834 				break;
    835 			case DIGEST_SHA1:
    836 				SHA1Update(&ctxSHA1, p, size);
    837 				break;
    838 			default:
    839 				break;
    840 			}
    841 			(void)munmap(p, size);
    842 		} else {
    843  mmap_failed:
    844 			while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
    845 				if ((nw = write(to_fd, buf, nr)) != nr) {
    846 					serrno = errno;
    847 					(void)unlink(to_name);
    848 					errx(1, "%s: write: %s", to_name,
    849 					    strerror(nw > 0 ? EIO : serrno));
    850 				}
    851 				switch (digesttype) {
    852 				case DIGEST_MD5:
    853 					MD5Update(&ctxMD5, buf, nr);
    854 					break;
    855 				case DIGEST_RMD160:
    856 					RMD160Update(&ctxRMD160, buf, nr);
    857 					break;
    858 				case DIGEST_SHA1:
    859 					SHA1Update(&ctxSHA1, buf, nr);
    860 					break;
    861 				default:
    862 					break;
    863 				}
    864 			}
    865 			if (nr != 0) {
    866 				serrno = errno;
    867 				(void)unlink(to_name);
    868 				errx(1, "%s: read: %s", from_name, strerror(serrno));
    869 			}
    870 		}
    871 	}
    872 	switch (digesttype) {
    873 	case DIGEST_MD5:
    874 		return MD5End(&ctxMD5, NULL);
    875 	case DIGEST_RMD160:
    876 		return RMD160End(&ctxRMD160, NULL);
    877 	case DIGEST_SHA1:
    878 		return SHA1End(&ctxSHA1, NULL);
    879 	default:
    880 		return NULL;
    881 	}
    882 }
    883 
    884 /*
    885  * strip --
    886  *	use strip(1) to strip the target file
    887  */
    888 void
    889 strip(char *to_name)
    890 {
    891 	static const char exec_failure[] = ": exec of strip failed: ";
    892 	int	serrno, status;
    893 	const char * volatile stripprog, *progname;
    894 	char *cmd;
    895 
    896 	if ((stripprog = getenv("STRIP")) == NULL) {
    897 #ifdef TARGET_STRIP
    898 		stripprog = TARGET_STRIP;
    899 #else
    900 		stripprog = _PATH_STRIP;
    901 #endif
    902 	}
    903 
    904 	cmd = NULL;
    905 
    906 	if (stripArgs) {
    907 		/*
    908 		 * Build up a command line and let /bin/sh
    909 		 * parse the arguments.
    910 		 */
    911 		int ret = asprintf(&cmd, "%s %s %s", stripprog, stripArgs,
    912 		    to_name);
    913 
    914 		if (ret == -1 || cmd == NULL)
    915 			err(1, "asprintf failed");
    916 	}
    917 
    918 	switch (vfork()) {
    919 	case -1:
    920 		serrno = errno;
    921 		(void)unlink(to_name);
    922 		errx(1, "vfork: %s", strerror(serrno));
    923 		/*NOTREACHED*/
    924 	case 0:
    925 
    926 		if (stripArgs)
    927 			execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
    928 		else
    929 			execlp(stripprog, "strip", to_name, NULL);
    930 
    931 		progname = getprogname();
    932 		write(STDERR_FILENO, progname, strlen(progname));
    933 		write(STDERR_FILENO, exec_failure, strlen(exec_failure));
    934 		write(STDERR_FILENO, stripprog, strlen(stripprog));
    935 		write(STDERR_FILENO, "\n", 1);
    936 		_exit(1);
    937 		/*NOTREACHED*/
    938 	default:
    939 		if (wait(&status) == -1 || status)
    940 			(void)unlink(to_name);
    941 	}
    942 
    943 	free(cmd);
    944 }
    945 
    946 /*
    947  * afterinstall --
    948  *	run provided command on the target file or directory after it's been
    949  *	installed and stripped, but before permissions are set or it's renamed
    950  */
    951 void
    952 afterinstall(const char *command, const char *to_name, int errunlink)
    953 {
    954 	int	serrno, status;
    955 	char	*cmd;
    956 
    957 	switch (vfork()) {
    958 	case -1:
    959 		serrno = errno;
    960 		if (errunlink)
    961 			(void)unlink(to_name);
    962 		errx(1, "vfork: %s", strerror(serrno));
    963 		/*NOTREACHED*/
    964 	case 0:
    965 		/*
    966 		 * build up a command line and let /bin/sh
    967 		 * parse the arguments
    968 		 */
    969 		cmd = (char*)malloc(sizeof(char)*
    970 					  (2+strlen(command)+
    971 					     strlen(to_name)));
    972 
    973 		if (cmd == NULL)
    974 			errx(1, "%s", strerror(ENOMEM));
    975 
    976 		sprintf(cmd, "%s %s", command, to_name);
    977 
    978 		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
    979 
    980 		warn("%s: exec of after install command", command);
    981 		_exit(1);
    982 		/*NOTREACHED*/
    983 	default:
    984 		if ((wait(&status) == -1 || status) && errunlink)
    985 			(void)unlink(to_name);
    986 	}
    987 }
    988 
    989 /*
    990  * backup --
    991  *	backup file "to_name" to to_name.suffix
    992  *	if suffix contains a "%", it's taken as a printf(3) pattern
    993  *	used for a numbered backup.
    994  */
    995 void
    996 backup(const char *to_name)
    997 {
    998 	char	bname[FILENAME_MAX];
    999 
   1000 	if (numberedbackup) {
   1001 		/* Do numbered backup */
   1002 		int cnt;
   1003 		char suffix_expanded[FILENAME_MAX];
   1004 
   1005 		cnt=0;
   1006 		do {
   1007 			(void)snprintf(suffix_expanded, FILENAME_MAX, suffix,
   1008 			    cnt);
   1009 			(void)snprintf(bname, FILENAME_MAX, "%s%s", to_name,
   1010 			    suffix_expanded);
   1011 			cnt++;
   1012 		} while (access(bname, F_OK) == 0);
   1013 	} else {
   1014 		/* Do simple backup */
   1015 		(void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix);
   1016 	}
   1017 
   1018 	(void)rename(to_name, bname);
   1019 }
   1020 
   1021 /*
   1022  * install_dir --
   1023  *	build directory hierarchy
   1024  */
   1025 void
   1026 install_dir(char *path, u_int flags)
   1027 {
   1028         char		*p;
   1029         struct stat	sb;
   1030         int		ch;
   1031 
   1032         for (p = path;; ++p)
   1033                 if (!*p || (p != path && *p  == '/')) {
   1034                         ch = *p;
   1035                         *p = '\0';
   1036                         if (stat(path, &sb)) {
   1037                                 if (errno != ENOENT || mkdir(path, 0777) < 0) {
   1038 					err(1, "%s: mkdir", path);
   1039                                 }
   1040                         }
   1041 			else if (!S_ISDIR(sb.st_mode)) {
   1042 				errx(1, "%s exists but is not a directory", path);
   1043 			}
   1044                         if (!(*p = ch))
   1045 				break;
   1046                 }
   1047 
   1048 	if (afterinstallcmd != NULL)
   1049 		afterinstall(afterinstallcmd, path, 0);
   1050 
   1051 	if (!dounpriv && (
   1052 	    ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1)
   1053 	    || chmod(path, mode) == -1 )) {
   1054                 warn("%s: chown/chmod", path);
   1055 	}
   1056 	metadata_log(path, "dir", NULL, NULL, NULL, 0);
   1057 }
   1058 
   1059 /*
   1060  * metadata_log --
   1061  *	if metafp is not NULL, output mtree(8) full path name and settings to
   1062  *	metafp, to allow permissions to be set correctly by other tools,
   1063  *	or to allow integrity checks to be performed.
   1064  */
   1065 void
   1066 metadata_log(const char *path, const char *type, struct timeval *tv,
   1067 	const char *slink, const char *digestresult, off_t size)
   1068 {
   1069 	static const char	extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
   1070 	const char	*p;
   1071 	char		*buf;
   1072 	size_t		destlen;
   1073 	struct flock	metalog_lock;
   1074 
   1075 	if (!metafp)
   1076 		return;
   1077 	buf = (char *)malloc(4 * strlen(path) + 1);	/* buf for strsvis(3) */
   1078 	if (buf == NULL) {
   1079 		warnx("%s", strerror(ENOMEM));
   1080 		return;
   1081 	}
   1082 							/* lock log file */
   1083 	metalog_lock.l_start = 0;
   1084 	metalog_lock.l_len = 0;
   1085 	metalog_lock.l_whence = SEEK_SET;
   1086 	metalog_lock.l_type = F_WRLCK;
   1087 	if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
   1088 		warn("can't lock %s", metafile);
   1089 		free(buf);
   1090 		return;
   1091 	}
   1092 
   1093 	p = path;					/* remove destdir */
   1094 	if (destdir) {
   1095 		destlen = strlen(destdir);
   1096 		if (strncmp(p, destdir, destlen) == 0 &&
   1097 		    (p[destlen] == '/' || p[destlen] == '\0'))
   1098 			p += destlen;
   1099 	}
   1100 	while (*p && *p == '/')				/* remove leading /s */
   1101 		p++;
   1102 	strsvis(buf, p, VIS_CSTYLE, extra);		/* encode name */
   1103 	p = buf;
   1104 							/* print details */
   1105 	fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type);
   1106 	if (owner)
   1107 		fprintf(metafp, " uname=%s", owner);
   1108 	if (group)
   1109 		fprintf(metafp, " gname=%s", group);
   1110 	fprintf(metafp, " mode=%#o", mode);
   1111 	if (slink) {
   1112 		strsvis(buf, slink, VIS_CSTYLE, extra);	/* encode link */
   1113 		fprintf(metafp, " link=%s", buf);
   1114 	}
   1115 	if (*type == 'f') /* type=file */
   1116 		fprintf(metafp, " size=%lld", (long long)size);
   1117 	if (tv != NULL && dopreserve)
   1118 		fprintf(metafp, " time=%lld.%ld",
   1119 			(long long)tv[1].tv_sec, (long)tv[1].tv_usec);
   1120 	if (digestresult && digest)
   1121 		fprintf(metafp, " %s=%s", digest, digestresult);
   1122 	if (fflags)
   1123 		fprintf(metafp, " flags=%s", fflags);
   1124 	if (tags)
   1125 		fprintf(metafp, " tags=%s", tags);
   1126 	fputc('\n', metafp);
   1127 	fflush(metafp);					/* flush output */
   1128 							/* unlock log file */
   1129 	metalog_lock.l_type = F_UNLCK;
   1130 	if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
   1131 		warn("can't unlock %s", metafile);
   1132 	}
   1133 	free(buf);
   1134 }
   1135 
   1136 /*
   1137  * xbasename --
   1138  *	libc basename(3) that returns a pointer to a static buffer
   1139  *	instead of overwriting that passed-in string.
   1140  */
   1141 char *
   1142 xbasename(char *path)
   1143 {
   1144 	static char tmp[MAXPATHLEN];
   1145 
   1146 	(void)strlcpy(tmp, path, sizeof(tmp));
   1147 	return (basename(tmp));
   1148 }
   1149 
   1150 /*
   1151  * xdirname --
   1152  *	libc dirname(3) that returns a pointer to a static buffer
   1153  *	instead of overwriting that passed-in string.
   1154  */
   1155 char *
   1156 xdirname(char *path)
   1157 {
   1158 	static char tmp[MAXPATHLEN];
   1159 
   1160 	(void)strlcpy(tmp, path, sizeof(tmp));
   1161 	return (dirname(tmp));
   1162 }
   1163 
   1164 /*
   1165  * usage --
   1166  *	print a usage message and die
   1167  */
   1168 void
   1169 usage(void)
   1170 {
   1171 	const char *prog;
   1172 
   1173 	prog = getprogname();
   1174 
   1175 	(void)fprintf(stderr,
   1176 "usage: %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n"
   1177 "           [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group] \n"
   1178 "           [-l linkflags] [-h hash] [-S stripflags] file1 file2\n"
   1179 "       %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n"
   1180 "           [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group]\n"
   1181 "           [-l linkflags] [-h hash] [-S stripflags] file1 ... fileN directory\n"
   1182 "       %s -d [-Up] [-M log] [-D dest] [-T tags] [-a aftercmd] [-m mode]\n"
   1183 "           [-N dbdir] [-o owner] [-g group] directory ...\n",
   1184 	    prog, prog, prog);
   1185 	exit(1);
   1186 }
   1187