Home | History | Annotate | Line # | Download | only in xinstall
xinstall.c revision 1.62
      1 /*	$NetBSD: xinstall.c,v 1.62 2001/11/23 18:14:51 perry 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. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\
     39 	The Regents of the University of California.  All rights reserved.\n");
     40 #endif /* not lint */
     41 
     42 #ifndef lint
     43 #if 0
     44 static char sccsid[] = "@(#)xinstall.c	8.1 (Berkeley) 7/21/93";
     45 #else
     46 __RCSID("$NetBSD: xinstall.c,v 1.62 2001/11/23 18:14:51 perry Exp $");
     47 #endif
     48 #endif /* not lint */
     49 
     50 #include <sys/param.h>
     51 #include <sys/mman.h>
     52 #include <sys/stat.h>
     53 #include <sys/wait.h>
     54 
     55 #include <ctype.h>
     56 #include <err.h>
     57 #include <errno.h>
     58 #include <fcntl.h>
     59 #include <grp.h>
     60 #include <libgen.h>
     61 #include <paths.h>
     62 #include <pwd.h>
     63 #include <stdio.h>
     64 #include <stdlib.h>
     65 #include <string.h>
     66 #include <unistd.h>
     67 #include <vis.h>
     68 
     69 #include "pathnames.h"
     70 #include "stat_flags.h"
     71 
     72 #define STRIP_ARGS_MAX 32
     73 #define BACKUP_SUFFIX ".old"
     74 
     75 int	dobackup, docopy, dodir, dostrip, dolink, dopreserve, dorename,
     76 	    dounpriv;
     77 int	numberedbackup;
     78 int	mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
     79 char	pathbuf[MAXPATHLEN];
     80 uid_t	uid;
     81 gid_t	gid;
     82 char	*group, *owner, *fflags, *tags;
     83 FILE	*metafp;
     84 char	*metafile;
     85 u_long	fileflags;
     86 char	*stripArgs;
     87 char	*suffix = BACKUP_SUFFIX;
     88 
     89 #define LN_ABSOLUTE	0x01
     90 #define LN_RELATIVE	0x02
     91 #define LN_HARD		0x04
     92 #define LN_SYMBOLIC	0x08
     93 #define LN_MIXED	0x10
     94 
     95 #define	DIRECTORY	0x01		/* Tell install it's a directory. */
     96 #define	SETFLAGS	0x02		/* Tell install to set flags. */
     97 #define	HASUID		0x04		/* Tell install the uid was given */
     98 #define	HASGID		0x08		/* Tell install the gid was given */
     99 
    100 void	backup(const char *);
    101 void	copy(int, char *, int, char *, off_t);
    102 int	do_link(char *, char *);
    103 void	do_symlink(char *, char *);
    104 void	install(char *, char *, u_int);
    105 void	install_dir(char *, u_int);
    106 int	main(int, char *[]);
    107 void	makelink(char *, char *);
    108 void	metadata_log(const char *, const char *, struct timeval *, const char *);
    109 int	parseid(char *, id_t *);
    110 void	strip(char *);
    111 void	usage(void);
    112 char   *xbasename(char *);
    113 char   *xdirname(char *);
    114 
    115 int
    116 main(int argc, char *argv[])
    117 {
    118 	struct stat	from_sb, to_sb;
    119 	void		*set;
    120 	u_int		iflags;
    121 	int		ch, no_target;
    122 	char		*p, *to_name;
    123 
    124 	setprogname(argv[0]);
    125 
    126 	iflags = 0;
    127 	while ((ch = getopt(argc, argv, "cbB:df:g:l:m:M:o:prsS:T:U")) != -1)
    128 		switch((char)ch) {
    129 		case 'B':
    130 			suffix = optarg;
    131 			numberedbackup = 0;
    132 			{
    133 				/* Check if given suffix really generates
    134 				   different suffixes - catch e.g. ".%" */
    135 				char suffix_expanded0[FILENAME_MAX],
    136 				     suffix_expanded1[FILENAME_MAX];
    137 				(void)snprintf(suffix_expanded0, FILENAME_MAX,
    138 					       suffix, 0);
    139 				(void)snprintf(suffix_expanded1, FILENAME_MAX,
    140 					       suffix, 1);
    141 				if (strcmp(suffix_expanded0, suffix_expanded1)
    142 				    != 0)
    143 					numberedbackup = 1;
    144 			}
    145 			/* fall through; -B implies -b */
    146 			/*FALLTHROUGH*/
    147 		case 'b':
    148 			dobackup = 1;
    149 			break;
    150 		case 'c':
    151 			docopy = 1;
    152 			break;
    153 		case 'd':
    154 			dodir = 1;
    155 			break;
    156 		case 'f':
    157 			fflags = optarg;
    158 			break;
    159 		case 'g':
    160 			group = optarg;
    161 			break;
    162 		case 'l':
    163 			for (p = optarg; *p; p++)
    164 				switch (*p) {
    165 				case 's':
    166 					dolink &= ~(LN_HARD|LN_MIXED);
    167 					dolink |= LN_SYMBOLIC;
    168 					break;
    169 				case 'h':
    170 					dolink &= ~(LN_SYMBOLIC|LN_MIXED);
    171 					dolink |= LN_HARD;
    172 					break;
    173 				case 'm':
    174 					dolink &= ~(LN_SYMBOLIC|LN_HARD);
    175 					dolink |= LN_MIXED;
    176 					break;
    177 				case 'a':
    178 					dolink &= ~LN_RELATIVE;
    179 					dolink |= LN_ABSOLUTE;
    180 					break;
    181 				case 'r':
    182 					dolink &= ~LN_ABSOLUTE;
    183 					dolink |= LN_RELATIVE;
    184 					break;
    185 				default:
    186 					errx(1, "%c: invalid link type", *p);
    187 					/* NOTREACHED */
    188 				}
    189 			break;
    190 		case 'm':
    191 			if (!(set = setmode(optarg)))
    192 				errx(1, "%s: invalid file mode", optarg);
    193 			mode = getmode(set, 0);
    194 			free(set);
    195 			break;
    196 		case 'M':
    197 			metafile = optarg;
    198 			break;
    199 		case 'o':
    200 			owner = optarg;
    201 			break;
    202 		case 'p':
    203 			dopreserve = 1;
    204 			break;
    205 		case 'r':
    206 			dorename = 1;
    207 			break;
    208 		case 'S':
    209 			stripArgs = strdup(optarg);
    210 			if (stripArgs == NULL)
    211 				errx(1, "%s", strerror(ENOMEM));
    212 			/* fall through; -S implies -s */
    213 			/*FALLTHROUGH*/
    214 		case 's':
    215 			dostrip = 1;
    216 			break;
    217 		case 'T':
    218 			tags = optarg;
    219 			break;
    220 		case 'U':
    221 			dounpriv = 1;
    222 			break;
    223 		case '?':
    224 		default:
    225 			usage();
    226 		}
    227 	argc -= optind;
    228 	argv += optind;
    229 
    230 	/* strip and link options make no sense when creating directories */
    231 	if ((dostrip || dolink) && dodir)
    232 		usage();
    233 
    234 	/* strip and flags make no sense with links */
    235 	if ((dostrip || fflags) && dolink)
    236 		usage();
    237 
    238 	/* must have at least two arguments, except when creating directories */
    239 	if (argc < 2 && !dodir)
    240 		usage();
    241 
    242 	/* get group and owner id's */
    243 	if (group && !dounpriv) {
    244 		struct group *gp;
    245 
    246 		if ((gp = getgrnam(group)) != NULL)
    247 			gid = gp->gr_gid;
    248 		else if (! parseid(group, &gid))
    249 			errx(1, "unknown group %s", group);
    250 		iflags |= HASGID;
    251 	}
    252 	if (owner && !dounpriv) {
    253 		struct passwd *pp;
    254 
    255 		if ((pp = getpwnam(owner)) != NULL)
    256 			uid = pp->pw_uid;
    257 		else if (! parseid(owner, &uid))
    258 			errx(1, "unknown user %s", owner);
    259 		iflags |= HASUID;
    260 	}
    261 
    262 	if (fflags && !dounpriv) {
    263 		if (string_to_flags(&fflags, &fileflags, NULL))
    264 			errx(1, "%s: invalid flag", fflags);
    265 		iflags |= SETFLAGS;
    266 	}
    267 
    268 	if (metafile) {
    269 		if ((metafp = fopen(metafile, "a")) == NULL)
    270 			warn("open %s", metafile);
    271 	}
    272 
    273 	if (dodir) {
    274 		for (; *argv != NULL; ++argv)
    275 			install_dir(*argv, iflags);
    276 		exit (0);
    277 	}
    278 
    279 	no_target = stat(to_name = argv[argc - 1], &to_sb);
    280 	if (!no_target && S_ISDIR(to_sb.st_mode)) {
    281 		for (; *argv != to_name; ++argv)
    282 			install(*argv, to_name, iflags | DIRECTORY);
    283 		exit(0);
    284 	}
    285 
    286 	/* can't do file1 file2 directory/file */
    287 	if (argc != 2)
    288 		usage();
    289 
    290 	if (!no_target) {
    291 		/* makelink() handles checks for links */
    292 		if (!dolink) {
    293 			if (stat(*argv, &from_sb))
    294 				err(1, "%s", *argv);
    295 			if (!S_ISREG(to_sb.st_mode))
    296 				errx(1, "%s: not a regular file", to_name);
    297 			if (to_sb.st_dev == from_sb.st_dev &&
    298 			    to_sb.st_ino == from_sb.st_ino)
    299 				errx(1, "%s and %s are the same file", *argv,
    300 				    to_name);
    301 		}
    302 		/*
    303 		 * Unlink now... avoid ETXTBSY errors later.  Try and turn
    304 		 * off the append/immutable bits -- if we fail, go ahead,
    305 		 * it might work.
    306 		 */
    307 #define	NOCHANGEBITS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
    308 		if (to_sb.st_flags & NOCHANGEBITS)
    309 			(void)chflags(to_name,
    310 			    to_sb.st_flags & ~(NOCHANGEBITS));
    311 		if (dobackup)
    312 			backup(to_name);
    313 		else if (!dorename)
    314 			(void)unlink(to_name);
    315 	}
    316 	install(*argv, to_name, iflags);
    317 	exit(0);
    318 }
    319 
    320 /*
    321  * parseid --
    322  *	parse uid or gid from arg into id, returning non-zero if successful
    323  */
    324 int
    325 parseid(char *name, id_t *id)
    326 {
    327 	char	*ep;
    328 
    329 	errno = 0;
    330 	*id = (id_t)strtoul(name, &ep, 10);
    331 	if (errno || *ep != '\0')
    332 		return (0);
    333 	return (1);
    334 }
    335 
    336 /*
    337  * do_link --
    338  *	make a hard link, obeying dorename if set
    339  *	return -1 on failure
    340  */
    341 int
    342 do_link(char *from_name, char *to_name)
    343 {
    344 	char tmpl[MAXPATHLEN];
    345 	int ret;
    346 
    347 	if (dorename) {
    348 		(void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
    349 		    xdirname(to_name));
    350 		/* This usage is safe. The linker will bitch anyway. */
    351 		if (mktemp(tmpl) == NULL)
    352 			err(1, "%s", tmpl);
    353 		ret = link(from_name, tmpl);
    354 		if (ret == 0) {
    355 			ret = rename(tmpl, to_name);
    356 			if (ret < 0)
    357 				/* remove temporary link before exiting */
    358 				(void)unlink(tmpl);
    359 		}
    360 		return (ret);
    361 	} else
    362 		return (link(from_name, to_name));
    363 }
    364 
    365 /*
    366  * do_symlink --
    367  *	make a symbolic link, obeying dorename if set
    368  *	exit on failure
    369  */
    370 void
    371 do_symlink(char *from_name, char *to_name)
    372 {
    373 	char tmpl[MAXPATHLEN];
    374 
    375 	if (dorename) {
    376 		(void)snprintf(tmpl, sizeof(tmpl), "%s/inst.XXXXXX",
    377 		    xdirname(to_name));
    378 		/* This usage is safe. The linker will bitch anyway. */
    379 		if (mktemp(tmpl) == NULL)
    380 			err(1, "%s", tmpl);
    381 
    382 		if (symlink(from_name, tmpl) == -1)
    383 			err(1, "symlink %s -> %s", from_name, tmpl);
    384 		if (rename(tmpl, to_name) == -1) {
    385 			/* remove temporary link before exiting */
    386 			(void)unlink(tmpl);
    387 			err(1, "%s: rename", to_name);
    388 		}
    389 	} else {
    390 		if (symlink(from_name, to_name) == -1)
    391 			err(1, "symlink %s -> %s", from_name, to_name);
    392 	}
    393 }
    394 
    395 /*
    396  * makelink --
    397  *	make a link from source to destination
    398  */
    399 void
    400 makelink(char *from_name, char *to_name)
    401 {
    402 	char	src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
    403 
    404 	/* Try hard links first */
    405 	if (dolink & (LN_HARD|LN_MIXED)) {
    406 		if (do_link(from_name, to_name) == -1) {
    407 			if ((dolink & LN_HARD) || errno != EXDEV)
    408 				err(1, "link %s -> %s", from_name, to_name);
    409 		}
    410 		else {
    411 			metadata_log(to_name, "hlink", NULL, from_name);
    412 			return;
    413 		}
    414 	}
    415 
    416 	/* Symbolic links */
    417 	if (dolink & LN_ABSOLUTE) {
    418 		/* Convert source path to absolute */
    419 		if (realpath(from_name, src) == NULL)
    420 			err(1, "%s", from_name);
    421 		do_symlink(src, to_name);
    422 		metadata_log(to_name, "link", NULL, src);
    423 		return;
    424 	}
    425 
    426 	if (dolink & LN_RELATIVE) {
    427 		char *cp, *d, *s;
    428 
    429 		/* Resolve pathnames */
    430 		if (realpath(from_name, src) == NULL)
    431 			err(1, "%s", from_name);
    432 
    433 		/*
    434 		 * The last component of to_name may be a symlink,
    435 		 * so use realpath to resolve only the directory.
    436 		 */
    437 		cp = dirname(to_name);
    438 		if (realpath(cp, dst) == NULL)
    439 			err(1, "%s", cp);
    440 		/* .. and add the last component */
    441 		if (strcmp(dst, "/") != 0) {
    442 			if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
    443 				errx(1, "resolved pathname too long");
    444 		}
    445 		cp = xbasename(to_name);
    446 		if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
    447 			errx(1, "resolved pathname too long");
    448 
    449 		/* trim common path components */
    450 		for (s = src, d = dst; *s == *d; s++, d++)
    451 			continue;
    452 		while (*s != '/')
    453 			s--, d--;
    454 
    455 		/* count the number of directories we need to backtrack */
    456 		for (++d, lnk[0] = '\0'; *d; d++)
    457 			if (*d == '/')
    458 				(void)strcat(lnk, "../");
    459 
    460 		(void)strcat(lnk, ++s);
    461 
    462 		do_symlink(lnk, dst);
    463 		metadata_log(dst, "link", NULL, lnk);
    464 		return;
    465 	}
    466 
    467 	/*
    468 	 * If absolute or relative was not specified,
    469 	 * try the names the user provided
    470 	 */
    471 	do_symlink(from_name, to_name);
    472 	metadata_log(to_name, "link", NULL, from_name);
    473 }
    474 
    475 /*
    476  * install --
    477  *	build a path name and install the file
    478  */
    479 void
    480 install(char *from_name, char *to_name, u_int flags)
    481 {
    482 	struct stat	from_sb, to_sb;
    483 	struct timeval	tv[2];
    484 	int		devnull, from_fd, to_fd, serrno, tmpmode;
    485 	char		*p, tmpl[MAXPATHLEN], *oto_name;
    486 
    487 	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
    488 		if (!dolink) {
    489 			if (stat(from_name, &from_sb))
    490 				err(1, "%s", from_name);
    491 			if (!S_ISREG(from_sb.st_mode))
    492 				errx(1, "%s: not a regular file", from_name);
    493 		}
    494 		/* Build the target path. */
    495 		if (flags & DIRECTORY) {
    496 			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
    497 			    to_name,
    498 			    (p = strrchr(from_name, '/')) ? ++p : from_name);
    499 			to_name = pathbuf;
    500 		}
    501 		devnull = 0;
    502 	} else {
    503 		from_sb.st_flags = 0;	/* XXX */
    504 		devnull = 1;
    505 	}
    506 
    507 	/*
    508 	 * Unlink now... avoid ETXTBSY errors later.  Try and turn
    509 	 * off the append/immutable bits -- if we fail, go ahead,
    510 	 * it might work.
    511 	 */
    512 	if (stat(to_name, &to_sb) == 0 &&
    513 	    to_sb.st_flags & (NOCHANGEBITS))
    514 		(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
    515 	if (dorename) {
    516 		(void)snprintf(tmpl, sizeof(tmpl), "%sinst.XXXXXX",
    517 		    xdirname(to_name));
    518 		oto_name = to_name;
    519 		to_name = tmpl;
    520 	} else {
    521 		oto_name = NULL;	/* pacify gcc */
    522 		if (dobackup)
    523 			backup(to_name);
    524 		else
    525 			(void)unlink(to_name);
    526 	}
    527 
    528 	if (dolink) {
    529 		makelink(from_name, dorename ? oto_name : to_name);
    530 		return;
    531 	}
    532 
    533 	/* Create target. */
    534 	if (dorename) {
    535 		if ((to_fd = mkstemp(to_name)) == -1)
    536 			err(1, "%s", to_name);
    537 	} else {
    538 		if ((to_fd = open(to_name,
    539 		    O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
    540 			err(1, "%s", to_name);
    541 	}
    542 	if (!devnull) {
    543 		if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
    544 			(void)unlink(to_name);
    545 			err(1, "%s", from_name);
    546 		}
    547 		copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
    548 		(void)close(from_fd);
    549 	}
    550 
    551 	if (dostrip) {
    552 		strip(to_name);
    553 
    554 		/*
    555 		 * Re-open our fd on the target, in case we used a strip
    556 		 *  that does not work in-place -- like gnu binutils strip.
    557 		 */
    558 		close(to_fd);
    559 		if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
    560 		  err(1, "stripping %s", to_name);
    561 	}
    562 
    563 	/*
    564 	 * Set owner, group, mode for target; do the chown first,
    565 	 * chown may lose the setuid bits.
    566 	 */
    567 	if (!dounpriv &&
    568 	    (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) {
    569 		serrno = errno;
    570 		(void)unlink(to_name);
    571 		errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
    572 	}
    573 	tmpmode = mode;
    574 	if (dounpriv)
    575 		tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO;
    576 	if (fchmod(to_fd, tmpmode) == -1) {
    577 		serrno = errno;
    578 		(void)unlink(to_name);
    579 		errx(1, "%s: chmod: %s", to_name, strerror(serrno));
    580 	}
    581 
    582 	/*
    583 	 * Preserve the date of the source file.
    584 	 */
    585 	if (dopreserve) {
    586 #ifdef BSD4_4
    587 		TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec);
    588 		TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec);
    589 #else
    590 		tv[0].tv_sec = from_sb.st_atime;
    591 		tv[0].tv_usec = 0;
    592 		tv[1].tv_sec = from_sb.st_mtime;
    593 		tv[1].tv_usec = 0;
    594 #endif
    595 		if (!dounpriv && futimes(to_fd, tv) == -1)
    596 			warn("%s: futimes", to_name);
    597 	}
    598 
    599 	(void)close(to_fd);
    600 
    601 	if (dorename) {
    602 		if (rename(to_name, oto_name) == -1)
    603 			err(1, "%s: rename", to_name);
    604 		to_name = oto_name;
    605 	}
    606 
    607 	if (!docopy && !devnull && unlink(from_name))
    608 		err(1, "%s", from_name);
    609 
    610 	/*
    611 	 * If provided a set of flags, set them, otherwise, preserve the
    612 	 * flags, except for the dump flag.
    613 	 */
    614 	if (!dounpriv && chflags(to_name,
    615 	    flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1)
    616 	{
    617 		if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
    618 			warn("%s: chflags", to_name);
    619 	}
    620 
    621 	metadata_log(to_name, "file", tv, NULL);
    622 }
    623 
    624 /*
    625  * copy --
    626  *	copy from one file to another
    627  */
    628 void
    629 copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size)
    630 {
    631 	ssize_t	nr, nw;
    632 	int	serrno;
    633 	char	*p;
    634 	char	buf[MAXBSIZE];
    635 
    636 	/*
    637 	 * There's no reason to do anything other than close the file
    638 	 * now if it's empty, so let's not bother.
    639 	 */
    640 	if (size > 0) {
    641 
    642 		/*
    643 		 * Mmap and write if less than 8M (the limit is so we
    644 		 * don't totally trash memory on big files).  This is
    645 		 * really a minor hack, but it wins some CPU back.
    646 		 */
    647 
    648 		if (size <= 8 * 1048576) {
    649 			if ((p = mmap(NULL, (size_t)size, PROT_READ,
    650 			    MAP_FILE|MAP_SHARED, from_fd, (off_t)0))
    651 			    == MAP_FAILED) {
    652 				goto mmap_failed;
    653 			}
    654 #ifdef MADV_SEQUENTIAL
    655 			if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1
    656 			    && errno != EOPNOTSUPP)
    657 				warnx("madvise: %s", strerror(errno));
    658 #endif
    659 
    660 			if (write(to_fd, p, size) != size) {
    661 				serrno = errno;
    662 				(void)unlink(to_name);
    663 				errx(1, "%s: %s",
    664 				    to_name, strerror(serrno));
    665 			}
    666 		} else {
    667 mmap_failed:
    668 			while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
    669 				if ((nw = write(to_fd, buf, nr)) != nr) {
    670 					serrno = errno;
    671 					(void)unlink(to_name);
    672 					errx(1, "%s: %s", to_name,
    673 					    strerror(nw > 0 ? EIO : serrno));
    674 				}
    675 			}
    676 			if (nr != 0) {
    677 				serrno = errno;
    678 				(void)unlink(to_name);
    679 				errx(1, "%s: %s", from_name, strerror(serrno));
    680 			}
    681 		}
    682 	}
    683 }
    684 
    685 /*
    686  * strip --
    687  *	use strip(1) to strip the target file
    688  */
    689 void
    690 strip(char *to_name)
    691 {
    692 	int	serrno, status;
    693 	char	*stripprog;
    694 
    695 	switch (vfork()) {
    696 	case -1:
    697 		serrno = errno;
    698 		(void)unlink(to_name);
    699 		errx(1, "vfork: %s", strerror(serrno));
    700 		/*NOTREACHED*/
    701 	case 0:
    702 		stripprog = getenv("STRIP");
    703 		if (stripprog == NULL)
    704 			stripprog = _PATH_STRIP;
    705 
    706 		if (stripArgs) {
    707 			/*
    708 			 * build up a command line and let /bin/sh
    709 			 * parse the arguments
    710 			 */
    711 			char* cmd = (char*)malloc(sizeof(char)*
    712 						  (3+strlen(stripprog)+
    713 						     strlen(stripArgs)+
    714 						     strlen(to_name)));
    715 
    716 			if (cmd == NULL)
    717 				errx(1, "%s", strerror(ENOMEM));
    718 
    719 			sprintf(cmd, "%s %s %s", stripprog, stripArgs, to_name);
    720 
    721 			execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
    722 		} else
    723 			execlp(stripprog, "strip", to_name, NULL);
    724 
    725 		warn("%s", stripprog);
    726 		_exit(1);
    727 		/*NOTREACHED*/
    728 	default:
    729 		if (wait(&status) == -1 || status)
    730 			(void)unlink(to_name);
    731 	}
    732 }
    733 
    734 /*
    735  * backup --
    736  *	backup file "to_name" to to_name.suffix
    737  *	if suffix contains a "%", it's taken as a printf(3) pattern
    738  *	used for a numbered backup.
    739  */
    740 void
    741 backup(const char *to_name)
    742 {
    743 	char	bname[FILENAME_MAX];
    744 
    745 	if (numberedbackup) {
    746 		/* Do numbered backup */
    747 		int cnt;
    748 		char suffix_expanded[FILENAME_MAX];
    749 
    750 		cnt=0;
    751 		do {
    752 			(void)snprintf(suffix_expanded, FILENAME_MAX, suffix,
    753 			    cnt);
    754 			(void)snprintf(bname, FILENAME_MAX, "%s%s", to_name,
    755 			    suffix_expanded);
    756 			cnt++;
    757 		} while (access(bname, F_OK) == 0);
    758 	} else {
    759 		/* Do simple backup */
    760 		(void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix);
    761 	}
    762 
    763 	(void)rename(to_name, bname);
    764 }
    765 
    766 /*
    767  * install_dir --
    768  *	build directory hierarchy
    769  */
    770 void
    771 install_dir(char *path, u_int flags)
    772 {
    773         char		*p;
    774         struct stat	sb;
    775         int		ch;
    776 
    777         for (p = path;; ++p)
    778                 if (!*p || (p != path && *p  == '/')) {
    779                         ch = *p;
    780                         *p = '\0';
    781                         if (stat(path, &sb)) {
    782                                 if (errno != ENOENT || mkdir(path, 0777) < 0) {
    783 					err(1, "%s", path);
    784                                 }
    785                         }
    786                         if (!(*p = ch))
    787 				break;
    788                 }
    789 
    790 	if (!dounpriv && (
    791 	    ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1)
    792 	    || chmod(path, mode) == -1 )) {
    793                 warn("%s", path);
    794 	}
    795 	metadata_log(path, "dir", NULL, NULL);
    796 }
    797 
    798 /*
    799  * metadata_log --
    800  *	if metafp is not NULL, output mtree(8) full path name and settings to
    801  *	metafp, to allow permissions to be set correctly by other tools.
    802  */
    803 void
    804 metadata_log(const char *path, const char *type, struct timeval *tv,
    805 	const char *link)
    806 {
    807 	const char	extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
    808 	char		*buf;
    809 
    810 	if (!metafp)
    811 		return;
    812 	buf = (char *)malloc(4 * strlen(path) + 1);	/* buf for strsvis(3) */
    813 	if (buf == NULL) {
    814 		warnx("%s", strerror(ENOMEM));
    815 		return;
    816 	}
    817 	if (flock(fileno(metafp), LOCK_EX) == -1) {	/* lock log file */
    818 		warn("can't lock %s", metafile);
    819 		return;
    820 	}
    821 
    822 	strsvis(buf, path, VIS_CSTYLE, extra);		/* encode name */
    823 	fprintf(metafp, ".%s%s type=%s mode=%#o",	/* print details */
    824 	    buf[0] == '/' ? "" : "/", buf, type, mode);
    825 	if (link)
    826 		fprintf(metafp, " link=%s", link);
    827 	if (owner)
    828 		fprintf(metafp, " uname=%s", owner);
    829 	if (group)
    830 		fprintf(metafp, " gname=%s", group);
    831 	if (fflags)
    832 		fprintf(metafp, " flags=%s", fflags);
    833 	if (tags)
    834 		fprintf(metafp, " tags=%s", tags);
    835 	if (tv != NULL && dopreserve)
    836 		fprintf(metafp, " time=%ld.%ld", tv[1].tv_sec, tv[1].tv_usec);
    837 	fputc('\n', metafp);
    838 	fflush(metafp);					/* flush output */
    839 	if (flock(fileno(metafp), LOCK_UN) == -1) {	/* unlock log file */
    840 		warn("can't unlock %s", metafile);
    841 	}
    842 	free(buf);
    843 }
    844 
    845 /*
    846  * xbasename --
    847  *	libc basename(3) that returns a pointer to a static buffer
    848  *	instead of overwriting that passed-in string.
    849  */
    850 char *
    851 xbasename(char *path)
    852 {
    853 	static char tmp[MAXPATHLEN];
    854 
    855 	(void)strlcpy(tmp, path, sizeof(tmp));
    856 	return (basename(tmp));
    857 }
    858 
    859 /*
    860  * xdirname --
    861  *	libc dirname(3) that returns a pointer to a static buffer
    862  *	instead of overwriting that passed-in string.
    863  */
    864 char *
    865 xdirname(char *path)
    866 {
    867 	static char tmp[MAXPATHLEN];
    868 
    869 	(void)strlcpy(tmp, path, sizeof(tmp));
    870 	return (dirname(tmp));
    871 }
    872 
    873 /*
    874  * usage --
    875  *	print a usage message and die
    876  */
    877 void
    878 usage(void)
    879 {
    880 
    881 	(void)fprintf(stderr, "\
    882 usage: install [-Ubcprs] [-M log] [-T tags] [-B suffix] [-f flags] [-m mode]\n\
    883 	    [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 file2\n\
    884        install [-Ubcprs] [-M log] [-T tags] [-B suffix] [-f flags] [-m mode]\n\
    885 	    [-o owner] [-g group] [-l linkflags] [-S stripflags]\n\
    886 	    file1 ... fileN directory\n\
    887        install [-Up] [-M log] [-T tags] -d [-m mode]\n\
    888 	    [-o owner] [-g group] directory ...\n");
    889 	exit(1);
    890 }
    891