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