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