xref: /dragonfly/usr.bin/xinstall/xinstall.c (revision 7b1120e5)
1 /*
2  * Copyright (c) 1987, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1987, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)xinstall.c	8.1 (Berkeley) 7/21/93
31  */
32 
33 #include <sys/param.h>
34 #include <sys/mman.h>
35 #include <sys/mount.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <grp.h>
44 #include <libgen.h>
45 #include <paths.h>
46 #include <pwd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sysexits.h>
51 #include <unistd.h>
52 #include <utime.h>
53 #include <vis.h>
54 
55 #include "mtree.h"
56 
57 /* Bootstrap aid - this doesn't exist in most older releases */
58 #ifndef MAP_FAILED
59 #define MAP_FAILED ((void *)-1)	/* from <sys/mman.h> */
60 #endif
61 #ifndef UF_NOHISTORY
62 #define UF_NOHISTORY	0
63 #endif
64 
65 #define MAX_CMP_SIZE	(16 * 1024 * 1024)
66 
67 #define	LN_ABSOLUTE	0x01
68 #define	LN_RELATIVE	0x02
69 #define	LN_HARD		0x04
70 #define	LN_SYMBOLIC	0x08
71 #define	LN_MIXED	0x10
72 
73 #define	DIRECTORY	0x01		/* Tell install it's a directory. */
74 #define	SETFLAGS	0x02		/* Tell install to set flags. */
75 #define	NOCHANGEBITS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
76 #define	BACKUP_SUFFIX	".old"
77 
78 static gid_t gid;
79 static uid_t uid;
80 static int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv,
81     nommap, safecopy, verbose;
82 static int haveopt_f, haveopt_g, haveopt_m, haveopt_o;
83 static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
84 static const char *group, *owner;
85 static const char *suffix = BACKUP_SUFFIX;
86 static char *destdir, *fflags;
87 
88 static int	compare(int, const char *, size_t, int, const char *, size_t);
89 static void	copy(int, const char *, int, const char *, off_t);
90 static int	create_newfile(const char *, int, struct stat *);
91 static int	create_tempfile(const char *, char *, size_t);
92 static int	do_link(const char *, const char *, const struct stat *);
93 static void	do_symlink(const char *, const char *, const struct stat *);
94 static void	makelink(const char *, const char *, const struct stat *);
95 static void	install(const char *, const char *, u_long, u_long, u_int);
96 static void	install_dir(char *);
97 static int	parseid(const char *, id_t *);
98 static void	strip(const char *);
99 static int	trymmap(int);
100 static void	usage(void);
101 
102 int
103 main(int argc, char *argv[])
104 {
105 	struct stat from_sb, to_sb;
106 	mode_t *set;
107 	u_long fset;
108 	u_long fclr;
109 	int ch, no_target;
110 	u_int iflags;
111 	char *p;
112 	const char *to_name;
113 
114 	fclr = 0;
115 	fset = 0;
116 	iflags = 0;
117 	group = NULL;
118 	owner = NULL;
119 
120 	while ((ch = getopt(argc, argv, "B:bCcD:df:g:L:l:M:m:N:o:pSsUv")) != -1)
121 		switch((char)ch) {
122 		case 'B':
123 			suffix = optarg;
124 			/* FALLTHROUGH */
125 		case 'b':
126 			dobackup = 1;
127 			break;
128 		case 'C':
129 			docompare = 1;
130 			break;
131 		case 'c':
132 			/* For backwards compatibility. */
133 			break;
134 		case 'D':
135 			destdir = optarg;
136 			break;
137 		case 'd':
138 			dodir = 1;
139 			break;
140 		case 'f':
141 			haveopt_f = 1;
142 			fflags = optarg;
143 			break;
144 		case 'g':
145 			haveopt_g = 1;
146 			group = optarg;
147 			break;
148 		case 'l':
149 			for (p = optarg; *p; p++)
150 				switch (*p) {
151 				case 's':
152 					dolink &= ~(LN_HARD|LN_MIXED);
153 					dolink |= LN_SYMBOLIC;
154 					break;
155 				case 'h':
156 					dolink &= ~(LN_SYMBOLIC|LN_MIXED);
157 					dolink |= LN_HARD;
158 					break;
159 				case 'm':
160 					dolink &= ~(LN_SYMBOLIC|LN_HARD);
161 					dolink |= LN_MIXED;
162 					break;
163 				case 'a':
164 					dolink &= ~LN_RELATIVE;
165 					dolink |= LN_ABSOLUTE;
166 					break;
167 				case 'r':
168 					dolink &= ~LN_ABSOLUTE;
169 					dolink |= LN_RELATIVE;
170 					break;
171 				default:
172 					errx(EXIT_FAILURE, "%c: invalid link type", *p);
173 					/* NOTREACHED */
174 				}
175 			break;
176 		case 'M':
177 			nommap = 1;
178 			break;
179 		case 'm':
180 			haveopt_m = 1;
181 			if (!(set = setmode(optarg)))
182 				errx(EX_USAGE, "invalid file mode: %s",
183 				     optarg);
184 			mode = getmode(set, 0);
185 			free(set);
186 			break;
187 		case 'L':
188 			/* -L kept for compatibility with pre-5.4 DragonFly */
189 			warnx("Option -L is deprecated, use -N instead");
190 			/* FALLTHROUGH */
191 		case 'N':
192 			if (!setup_getid(optarg))
193 				err(EX_OSERR, "Unable to use user and group "
194 				    "databases in `%s'", optarg);
195 			break;
196 		case 'o':
197 			haveopt_o = 1;
198 			owner = optarg;
199 			break;
200 		case 'p':
201 			docompare = dopreserve = 1;
202 			break;
203 		case 'S':
204 			safecopy = 1;
205 			break;
206 		case 's':
207 			dostrip = 1;
208 			break;
209 		case 'U':
210 			dounpriv = 1;
211 			break;
212 		case 'v':
213 			verbose = 1;
214 			break;
215 		case '?':
216 		default:
217 			usage();
218 		}
219 	argc -= optind;
220 	argv += optind;
221 
222 	/* some options make no sense when creating directories */
223 	if (dostrip && dodir) {
224 		warnx("-d and -s may not be specified together");
225 		usage();
226 	}
227 
228 	if (getenv("DONTSTRIP") != NULL) {
229 		warnx("DONTSTRIP set - will not strip installed binaries");
230 		dostrip = 0;
231 	}
232 
233 	/* must have at least two arguments, except when creating directories */
234 	if (argc == 0 || (argc == 1 && !dodir))
235 		usage();
236 
237 	/* need to make a temp copy so we can compare stripped version */
238 	if (docompare && dostrip)
239 		safecopy = 1;
240 
241 	/* get group and owner id's */
242 	if (group != NULL && !dounpriv) {
243 		if (gid_from_group(group, &gid) == -1) {
244 			id_t id;
245 			if (!parseid(group, &id))
246 				errx(1, "unknown group %s", group);
247 			gid = id;
248 		}
249 	} else
250 		gid = (gid_t)-1;
251 
252 	if (owner != NULL && !dounpriv) {
253 		if (uid_from_user(owner, &uid) == -1) {
254 			id_t id;
255 			if (!parseid(owner, &id))
256 				errx(1, "unknown user %s", owner);
257 			uid = id;
258 		}
259 	} else
260 		uid = (uid_t)-1;
261 
262 	if (fflags != NULL && !dounpriv) {
263 		if (strtofflags(&fflags, &fset, &fclr))
264 			errx(EX_USAGE, "%s: invalid flag", fflags);
265 		iflags |= SETFLAGS;
266 	}
267 
268 	if (dodir) {
269 		for (; *argv != NULL; ++argv)
270 			install_dir(*argv);
271 		exit(EX_OK);
272 		/* NOTREACHED */
273 	}
274 
275 	to_name = argv[argc - 1];
276 	no_target = stat(to_name, &to_sb);
277 	if (!no_target && S_ISDIR(to_sb.st_mode)) {
278 		if (dolink & LN_SYMBOLIC) {
279 			if (lstat(to_name, &to_sb) != 0)
280 				err(EX_OSERR, "%s vanished", to_name);
281 			if (S_ISLNK(to_sb.st_mode)) {
282 				if (argc != 2) {
283 					errno = ENOTDIR;
284 					err(EX_USAGE, "%s", to_name);
285 				}
286 				install(*argv, to_name, fset, fclr, iflags);
287 				exit(EX_OK);
288 			}
289 		}
290 		for (; *argv != to_name; ++argv)
291 			install(*argv, to_name, fset, fclr, iflags | DIRECTORY);
292 		exit(EX_OK);
293 		/* NOTREACHED */
294 	}
295 
296 	/* can't do file1 file2 directory/file */
297 	if (argc != 2) {
298 		if (no_target)
299 			warnx("target directory `%s' does not exist",
300 			    argv[argc - 1]);
301 		else
302 			warnx("target `%s' is not a directory",
303 			    argv[argc - 1]);
304 		usage();
305 	}
306 
307 	if (!no_target && !dolink) {
308 		if (stat(*argv, &from_sb))
309 			err(EX_OSERR, "%s", *argv);
310 		if (!S_ISREG(to_sb.st_mode)) {
311 			errno = EFTYPE;
312 			err(EX_OSERR, "%s", to_name);
313 		}
314 		if (to_sb.st_dev == from_sb.st_dev &&
315 		    to_sb.st_ino == from_sb.st_ino)
316 			errx(EX_USAGE,
317 			    "%s and %s are the same file", *argv, to_name);
318 	}
319 	install(*argv, to_name, fset, fclr, iflags);
320 	exit(EX_OK);
321 	/* NOTREACHED */
322 }
323 
324 /*
325  * parseid --
326  *	parse uid or gid from arg into id, returning non-zero if successful
327  */
328 static int
329 parseid(const char *name, id_t *id)
330 {
331 	char	*ep;
332 	errno = 0;
333 	*id = (id_t)strtoul(name, &ep, 10);
334 	if (errno || *ep != '\0')
335 		return (0);
336 	return (1);
337 }
338 
339 /*
340  * quiet_mktemp --
341  *	mktemp implementation used mkstemp to avoid mktemp warnings.  We
342  *	really do need mktemp semantics here as we will be creating a link.
343  */
344 static char *
345 quiet_mktemp(char *template)
346 {
347 	int fd;
348 
349 	if ((fd = mkstemp(template)) == -1)
350 		return (NULL);
351 	close (fd);
352 	if (unlink(template) == -1)
353 		err(EX_OSERR, "unlink %s", template);
354 	return (template);
355 }
356 
357 /*
358  * do_link --
359  *	make a hard link, obeying dorename if set
360  *	return -1 on failure
361  */
362 static int
363 do_link(const char *from_name, const char *to_name,
364     const struct stat *target_sb)
365 {
366 	char tmpl[MAXPATHLEN];
367 	int ret;
368 
369 	if (safecopy && target_sb != NULL) {
370 		(void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
371 		/* This usage is safe. */
372 		if (quiet_mktemp(tmpl) == NULL)
373 			err(EX_OSERR, "%s: mktemp", tmpl);
374 		ret = link(from_name, tmpl);
375 		if (ret == 0) {
376 			if (target_sb->st_mode & S_IFDIR && rmdir(to_name) ==
377 			    -1) {
378 				unlink(tmpl);
379 				err(EX_OSERR, "%s", to_name);
380 			}
381 			if (target_sb->st_flags & NOCHANGEBITS)
382 				(void)chflags(to_name, target_sb->st_flags &
383 				     ~NOCHANGEBITS);
384 			if (verbose)
385 				printf("install: link %s -> %s\n",
386 				    from_name, to_name);
387 			ret = rename(tmpl, to_name);
388 			/*
389 			 * If rename has posix semantics, then the temporary
390 			 * file may still exist when from_name and to_name point
391 			 * to the same file, so unlink it unconditionally.
392 			 */
393 			(void)unlink(tmpl);
394 		}
395 		return (ret);
396 	} else {
397 		if (verbose)
398 			printf("install: link %s -> %s\n",
399 			    from_name, to_name);
400 		return (link(from_name, to_name));
401 	}
402 }
403 
404 /*
405  * do_symlink --
406  *	Make a symbolic link, obeying dorename if set. Exit on failure.
407  */
408 static void
409 do_symlink(const char *from_name, const char *to_name,
410     const struct stat *target_sb)
411 {
412 	char tmpl[MAXPATHLEN];
413 
414 	if (safecopy && target_sb != NULL) {
415 		(void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
416 		/* This usage is safe. */
417 		if (quiet_mktemp(tmpl) == NULL)
418 			err(EX_OSERR, "%s: mktemp", tmpl);
419 
420 		if (symlink(from_name, tmpl) == -1)
421 			err(EX_OSERR, "symlink %s -> %s", from_name, tmpl);
422 
423 		if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) {
424 			(void)unlink(tmpl);
425 			err(EX_OSERR, "%s", to_name);
426 		}
427 		if (target_sb->st_flags & NOCHANGEBITS)
428 			(void)chflags(to_name, target_sb->st_flags &
429 			     ~NOCHANGEBITS);
430 		if (verbose)
431 			printf("install: symlink %s -> %s\n",
432 			    from_name, to_name);
433 		if (rename(tmpl, to_name) == -1) {
434 			/* Remove temporary link before exiting. */
435 			(void)unlink(tmpl);
436 			err(EX_OSERR, "%s: rename", to_name);
437 		}
438 	} else {
439 		if (verbose)
440 			printf("install: symlink %s -> %s\n",
441 			    from_name, to_name);
442 		if (symlink(from_name, to_name) == -1)
443 			err(EX_OSERR, "symlink %s -> %s", from_name, to_name);
444 	}
445 }
446 
447 /*
448  * makelink --
449  *	make a link from source to destination
450  */
451 static void
452 makelink(const char *from_name, const char *to_name,
453     const struct stat *target_sb)
454 {
455 	char	src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
456 	struct stat	to_sb;
457 
458 	/* Try hard links first. */
459 	if (dolink & (LN_HARD|LN_MIXED)) {
460 		if (do_link(from_name, to_name, target_sb) == -1) {
461 			if ((dolink & LN_HARD) || errno != EXDEV)
462 				err(EX_OSERR, "link %s -> %s", from_name, to_name);
463 		} else {
464 			if (stat(to_name, &to_sb))
465 				err(EX_OSERR, "%s: stat", to_name);
466 			if (S_ISREG(to_sb.st_mode)) {
467 				/*
468 				 * XXX: hard links to anything other than
469 				 * plain files are not metalogged
470 				 */
471 				int omode;
472 				const char *oowner, *ogroup;
473 				char *offlags;
474 
475 				/*
476 				 * XXX: use underlying perms, unless
477 				 * overridden on command line.
478 				 */
479 				omode = mode;
480 				if (!haveopt_m)
481 					mode = (to_sb.st_mode & 0777);
482 				oowner = owner;
483 				if (!haveopt_o)
484 					owner = NULL;
485 				ogroup = group;
486 				if (!haveopt_g)
487 					group = NULL;
488 				offlags = fflags;
489 				if (!haveopt_f)
490 					fflags = NULL;
491 				mode = omode;
492 				owner = oowner;
493 				group = ogroup;
494 				fflags = offlags;
495 			}
496 			return;
497 		}
498 	}
499 
500 	/* Symbolic links. */
501 	if (dolink & LN_ABSOLUTE) {
502 		/* Convert source path to absolute. */
503 		if (realpath(from_name, src) == NULL)
504 			err(EX_OSERR, "%s: realpath", from_name);
505 		do_symlink(src, to_name, target_sb);
506 		/* XXX: src may point outside of destdir */
507 		return;
508 	}
509 
510 	if (dolink & LN_RELATIVE) {
511 		char *to_name_copy, *cp, *d, *s;
512 
513 		if (*from_name != '/') {
514 			/* this is already a relative link */
515 			do_symlink(from_name, to_name, target_sb);
516 			/* XXX: from_name may point outside of destdir. */
517 			return;
518 		}
519 
520 		/* Resolve pathnames. */
521 		if (realpath(from_name, src) == NULL)
522 			err(EX_OSERR, "%s: realpath", from_name);
523 
524 		/*
525 		 * The last component of to_name may be a symlink,
526 		 * so use realpath to resolve only the directory.
527 		 */
528 		to_name_copy = strdup(to_name);
529 		if (to_name_copy == NULL)
530 			err(EX_OSERR, "%s: strdup", to_name);
531 		cp = dirname(to_name_copy);
532 		if (realpath(cp, dst) == NULL)
533 			err(EX_OSERR, "%s: realpath", cp);
534 		/* .. and add the last component. */
535 		if (strcmp(dst, "/") != 0) {
536 			if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
537 				errx(1, "resolved pathname too long");
538 		}
539 		strcpy(to_name_copy, to_name);
540 		cp = basename(to_name_copy);
541 		if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
542 			errx(1, "resolved pathname too long");
543 		free(to_name_copy);
544 
545 		/* Trim common path components. */
546 		for (s = src, d = dst; *s == *d; s++, d++)
547 			continue;
548 		while (*s != '/')
549 			s--, d--;
550 
551 		/* Count the number of directories we need to backtrack. */
552 		for (++d, lnk[0] = '\0'; *d; d++)
553 			if (*d == '/')
554 				(void)strlcat(lnk, "../", sizeof(lnk));
555 
556 		(void)strlcat(lnk, ++s, sizeof(lnk));
557 
558 		do_symlink(lnk, to_name, target_sb);
559 		/* XXX: Link may point outside of destdir. */
560 		return;
561 	}
562 
563 	/*
564 	 * If absolute or relative was not specified, try the names the
565 	 * user provided.
566 	 */
567 	do_symlink(from_name, to_name, target_sb);
568 	/* XXX: from_name may point outside of destdir. */
569 }
570 
571 /*
572  * install --
573  *	build a path name and install the file
574  */
575 static void
576 install(const char *from_name, const char *to_name, u_long fset, u_long fclr,
577 	u_int flags)
578 {
579 	struct stat from_sb, temp_sb, to_sb;
580 	struct utimbuf utb;
581 	int devnull, files_match, from_fd, serrno, target;
582 	int tempcopy, temp_fd, to_fd;
583 	u_long nfset;
584 	char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
585 
586 	files_match = 0;
587 	from_fd = -1;
588 	to_fd = -1;
589 
590 	/* If try to install NULL file to a directory, fails. */
591 	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
592 		if (!dolink) {
593 			if (stat(from_name, &from_sb))
594 				err(EX_OSERR, "%s", from_name);
595 			if (!S_ISREG(from_sb.st_mode)) {
596 				errno = EFTYPE;
597 				err(EX_OSERR, "%s", from_name);
598 			}
599 		}
600 		/* Build the target path. */
601 		if (flags & DIRECTORY) {
602 			(void)snprintf(pathbuf, sizeof(pathbuf), "%s%s%s",
603 			    to_name,
604 			    to_name[strlen(to_name) - 1] == '/' ? "" : "/",
605 			    (p = strrchr(from_name, '/')) ? ++p : from_name);
606 			to_name = pathbuf;
607 		}
608 		devnull = 0;
609 	} else {
610 		devnull = 1;
611 	}
612 
613 	target = (lstat(to_name, &to_sb) == 0);
614 
615 	if (dolink) {
616 		if (target && !safecopy) {
617 			if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1)
618 				err(EX_OSERR, "%s", to_name);
619 			if (to_sb.st_flags & NOCHANGEBITS)
620 				(void)chflags(to_name,
621 				    to_sb.st_flags & ~NOCHANGEBITS);
622 			unlink(to_name);
623 		}
624 		makelink(from_name, to_name, target ? &to_sb : NULL);
625 		return;
626 	}
627 
628 	if (target && !S_ISREG(to_sb.st_mode) && !S_ISLNK(to_sb.st_mode)) {
629 		errno = EFTYPE;
630 		warn("%s", to_name);
631 		return;
632 	}
633 
634 	/* Only copy safe if the target exists. */
635 	tempcopy = safecopy && target;
636 
637 	if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0)
638 		err(EX_OSERR, "%s", from_name);
639 
640 	/* If we don't strip, we can compare first. */
641 	if (docompare && !dostrip && target && S_ISREG(to_sb.st_mode)) {
642 		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
643 			err(EX_OSERR, "%s", to_name);
644 		if (devnull)
645 			files_match = to_sb.st_size == 0;
646 		else
647 			files_match = !(compare(from_fd, from_name,
648 			    (size_t)from_sb.st_size, to_fd,
649 			    to_name, (size_t)to_sb.st_size));
650 
651 		/* Close "to" file unless we match. */
652 		if (!files_match)
653 			(void)close(to_fd);
654 	}
655 
656 	if (!files_match) {
657 		if (tempcopy) {
658 			to_fd = create_tempfile(to_name, tempfile,
659 			    sizeof(tempfile));
660 			if (to_fd < 0)
661 				err(EX_OSERR, "%s", tempfile);
662 		} else {
663 			if ((to_fd = create_newfile(to_name, target,
664 			    &to_sb)) < 0)
665 				err(EX_OSERR, "%s", to_name);
666 			if (verbose)
667 				(void)printf("install: %s -> %s\n",
668 				    from_name, to_name);
669 		}
670 		if (!devnull)
671 			copy(from_fd, from_name, to_fd,
672 			     tempcopy ? tempfile : to_name, from_sb.st_size);
673 	}
674 
675 	if (dostrip) {
676 		strip(tempcopy ? tempfile : to_name);
677 
678 		/*
679 		 * Re-open our fd on the target, in case we used a strip
680 		 * that does not work in-place -- like GNU binutils strip.
681 		 */
682 		close(to_fd);
683 		to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0);
684 		if (to_fd < 0)
685 			err(EX_OSERR, "stripping %s", to_name);
686 	}
687 
688 	/*
689 	 * Compare the stripped temp file with the target.
690 	 */
691 	if (docompare && dostrip && target && S_ISREG(to_sb.st_mode)) {
692 		temp_fd = to_fd;
693 
694 		/* Re-open to_fd using the real target name. */
695 		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
696 			err(EX_OSERR, "%s", to_name);
697 
698 		if (fstat(temp_fd, &temp_sb)) {
699 			serrno = errno;
700 			(void)unlink(tempfile);
701 			errno = serrno;
702 			err(EX_OSERR, "%s", tempfile);
703 		}
704 
705 		if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
706 			    to_name, (size_t)to_sb.st_size) == 0) {
707 			/*
708 			 * If target has more than one link we need to
709 			 * replace it in order to snap the extra links.
710 			 * Need to preserve target file times, though.
711 			 */
712 			if (to_sb.st_nlink != 1) {
713 				utb.actime = to_sb.st_atime;
714 				utb.modtime = to_sb.st_mtime;
715 				utime(tempfile, &utb);
716 			} else {
717 				files_match = 1;
718 				(void)unlink(tempfile);
719 			}
720 			(void) close(temp_fd);
721 		}
722 	}
723 
724 	/*
725 	 * Move the new file into place if doing a safe copy
726 	 * and the files are different (or just not compared).
727 	 */
728 	if (tempcopy && !files_match) {
729 		/* Try to turn off the immutable bits. */
730 		if (to_sb.st_flags & NOCHANGEBITS)
731 			(void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
732 		if (dobackup) {
733 			if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
734 			    suffix) != strlen(to_name) + strlen(suffix)) {
735 				unlink(tempfile);
736 				errx(EX_OSERR, "%s: backup filename too long",
737 				    to_name);
738 			}
739 			if (verbose)
740 				(void)printf("install: %s -> %s\n", to_name, backup);
741 			if (rename(to_name, backup) < 0) {
742 				serrno = errno;
743 				unlink(tempfile);
744 				errno = serrno;
745 				err(EX_OSERR, "rename: %s to %s", to_name,
746 				     backup);
747 			}
748 		}
749 		if (verbose)
750 			(void)printf("install: %s -> %s\n", from_name, to_name);
751 		if (rename(tempfile, to_name) < 0) {
752 			serrno = errno;
753 			unlink(tempfile);
754 			errno = serrno;
755 			err(EX_OSERR, "rename: %s to %s",
756 			    tempfile, to_name);
757 		}
758 
759 		/* Re-open to_fd so we aren't hosed by the rename(2). */
760 		(void) close(to_fd);
761 		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
762 			err(EX_OSERR, "%s", to_name);
763 	}
764 
765 	/*
766 	 * Preserve the timestamp of the source file if necessary.
767 	 */
768 	if (dopreserve && !files_match && !devnull) {
769 		utb.actime = from_sb.st_atime;
770 		utb.modtime = from_sb.st_mtime;
771 		utime(to_name, &utb);
772 	}
773 
774 	if (fstat(to_fd, &to_sb) == -1) {
775 		serrno = errno;
776 		(void)unlink(to_name);
777 		errno = serrno;
778 		err(EX_OSERR, "%s", to_name);
779 	}
780 
781 	/*
782 	 * Set owner, group, mode for target; do the chown first,
783 	 * chown may lose the setuid bits.
784 	 */
785 	if (!dounpriv && ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
786 	    (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
787 	    (mode != to_sb.st_mode))) {
788 		/* Try to turn off the immutable bits. */
789 		if (to_sb.st_flags & NOCHANGEBITS)
790 			(void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
791 	}
792 
793 	if (!dounpriv && (
794 	    (gid != (gid_t)-1 && gid != to_sb.st_gid) ||
795 	    (uid != (uid_t)-1 && uid != to_sb.st_uid)))
796 		if (fchown(to_fd, uid, gid) == -1) {
797 			serrno = errno;
798 			(void)unlink(to_name);
799 			errno = serrno;
800 			err(EX_OSERR,"%s: chown/chgrp", to_name);
801 		}
802 
803 	if (mode != to_sb.st_mode) {
804 		if (fchmod(to_fd,
805 		     dounpriv ? mode & (S_IRWXU|S_IRWXG|S_IRWXO) : mode)) {
806 			serrno = errno;
807 			(void)unlink(to_name);
808 			errno = serrno;
809 			err(EX_OSERR, "%s: chmod", to_name);
810 		}
811 	}
812 
813 	/*
814 	 * If provided a set of flags, set them, otherwise, preserve the
815 	 * flags, except for the dump and history flags.  The dump flag
816 	 * is left clear on the target while the history flag from when
817 	 * the target was created (which is inherited from the target's
818 	 * parent directory) is retained.
819 	 */
820 	if (flags & SETFLAGS) {
821 		nfset = (to_sb.st_flags | fset) & ~fclr;
822 	} else {
823 		nfset = (from_sb.st_flags & ~(UF_NODUMP | UF_NOHISTORY)) |
824 			(to_sb.st_flags & UF_NOHISTORY);
825 	}
826 
827 	/*
828 	 * NFS does not support flags.  Ignore EOPNOTSUPP flags if we're just
829 	 * trying to turn off UF_NODUMP.  If we're trying to set real flags,
830 	 * then warn if the fs doesn't support it, otherwise fail.
831 	 */
832 	if (!dounpriv && !devnull && fchflags(to_fd, nfset)) {
833 		if (flags & SETFLAGS) {
834 			if (errno == EOPNOTSUPP)
835 				warn("%s: chflags", to_name);
836 			else {
837 				serrno = errno;
838 				(void)unlink(to_name);
839 				errno = serrno;
840 				err(EX_OSERR, "%s: chflags", to_name);
841 			}
842 		}
843 	}
844 
845 	(void)close(to_fd);
846 	if (!devnull)
847 		(void)close(from_fd);
848 }
849 
850 /*
851  * compare --
852  *	compare two files; non-zero means files differ
853  */
854 static int
855 compare(int from_fd, const char *from_name __unused, size_t from_len,
856 	int to_fd, const char *to_name __unused, size_t to_len)
857 {
858 	char *p, *q;
859 	int rv;
860 	int done_compare;
861 
862 	rv = 0;
863 	if (from_len != to_len)
864 		return 1;
865 
866 	if (from_len <= MAX_CMP_SIZE) {
867 		done_compare = 0;
868 		if (trymmap(from_fd) && trymmap(to_fd)) {
869 			p = mmap(NULL, from_len, PROT_READ, MAP_SHARED,
870 			    from_fd, (off_t)0);
871 			if (p == (char *)MAP_FAILED)
872 				goto out;
873 			q = mmap(NULL, from_len, PROT_READ, MAP_SHARED,
874 			    to_fd, (off_t)0);
875 			if (q == (char *)MAP_FAILED) {
876 				munmap(p, from_len);
877 				goto out;
878 			}
879 
880 			rv = memcmp(p, q, from_len);
881 			munmap(p, from_len);
882 			munmap(q, from_len);
883 			done_compare = 1;
884 		}
885 	out:
886 		if (!done_compare) {
887 			char buf1[MAXBSIZE];
888 			char buf2[MAXBSIZE];
889 			int n1, n2;
890 
891 			rv = 0;
892 			lseek(from_fd, 0, SEEK_SET);
893 			lseek(to_fd, 0, SEEK_SET);
894 			while (rv == 0) {
895 				n1 = read(from_fd, buf1, sizeof(buf1));
896 				if (n1 == 0)
897 					break;		/* EOF */
898 				else if (n1 > 0) {
899 					n2 = read(to_fd, buf2, n1);
900 					if (n2 == n1)
901 						rv = memcmp(buf1, buf2, n1);
902 					else
903 						rv = 1;	/* out of sync */
904 				} else
905 					rv = 1;		/* read failure */
906 			}
907 			lseek(from_fd, 0, SEEK_SET);
908 			lseek(to_fd, 0, SEEK_SET);
909 		}
910 	} else
911 		rv = 1;	/* don't bother in this case */
912 
913 	return rv;
914 }
915 
916 /*
917  * create_tempfile --
918  *	create a temporary file based on path and open it
919  */
920 static int
921 create_tempfile(const char *path, char *temp, size_t tsize)
922 {
923 	char *p;
924 
925 	(void)strncpy(temp, path, tsize);
926 	temp[tsize - 1] = '\0';
927 	if ((p = strrchr(temp, '/')) != NULL)
928 		p++;
929 	else
930 		p = temp;
931 	(void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p);
932 	temp[tsize - 1] = '\0';
933 	return (mkstemp(temp));
934 }
935 
936 /*
937  * create_newfile --
938  *	create a new file, overwriting an existing one if necessary
939  */
940 static int
941 create_newfile(const char *path, int target, struct stat *sbp)
942 {
943 	char backup[MAXPATHLEN];
944 
945 	if (target) {
946 		/*
947 		 * Unlink now... avoid ETXTBSY errors later.  Try to turn
948 		 * off the append/immutable bits -- if we fail, go ahead,
949 		 * it might work.
950 		 */
951 		if (sbp->st_flags & NOCHANGEBITS)
952 			(void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
953 
954 		if (dobackup) {
955 			if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s",
956 			    path, suffix) != strlen(path) + strlen(suffix))
957 				errx(EX_OSERR, "%s: backup filename too long",
958 				    path);
959 			(void)snprintf(backup, MAXPATHLEN, "%s%s",
960 			    path, suffix);
961 			if (verbose)
962 				(void)printf("install: %s -> %s\n",
963 				    path, backup);
964 			if (rename(path, backup) < 0)
965 				err(EX_OSERR, "rename: %s to %s", path, backup);
966 		} else
967 			unlink(path);
968 	}
969 
970 	return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
971 }
972 
973 /*
974  * copy --
975  *	copy from one file to another
976  */
977 static void
978 copy(int from_fd, const char *from_name, int to_fd,
979      const char *to_name, off_t size)
980 {
981 	int nr, nw;
982 	int serrno;
983 	char *p;
984 	char buf[MAXBSIZE];
985 	int done_copy;
986 
987 	/* Rewind file descriptors. */
988 	if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
989 		err(EX_OSERR, "lseek: %s", from_name);
990 	if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
991 		err(EX_OSERR, "lseek: %s", to_name);
992 
993 	/*
994 	 * Mmap and write if less than 8M (the limit is so we don't totally
995 	 * trash memory on big files.  This is really a minor hack, but it
996 	 * wins some CPU back.
997 	 */
998 	done_copy = 0;
999 	if (size <= 8 * 1048576 && trymmap(from_fd) &&
1000 	    (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
1001 		    from_fd, (off_t)0)) != (char *)MAP_FAILED) {
1002 		nw = write(to_fd, p, size);
1003 		if (nw != size) {
1004 			serrno = errno;
1005 			(void)unlink(to_name);
1006 			errno = nw > 0 ? EIO : serrno;
1007 			err(EX_OSERR, "%s", to_name);
1008 		}
1009 		done_copy = 1;
1010 	}
1011 	if (!done_copy) {
1012 		while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
1013 			if ((nw = write(to_fd, buf, nr)) != nr) {
1014 				serrno = errno;
1015 				(void)unlink(to_name);
1016 				errno = nw > 0 ? EIO : serrno;
1017 				err(EX_OSERR, "%s", to_name);
1018 			}
1019 		}
1020 		if (nr != 0) {
1021 			serrno = errno;
1022 			(void)unlink(to_name);
1023 			errno = serrno;
1024 			err(EX_OSERR, "%s", from_name);
1025 		}
1026 	}
1027 }
1028 
1029 /*
1030  * strip --
1031  *	use strip(1) to strip the target file
1032  */
1033 static void
1034 strip(const char *to_name)
1035 {
1036 	const char *stripbin;
1037 	int serrno, status;
1038 
1039 	switch (fork()) {
1040 	case -1:
1041 		serrno = errno;
1042 		(void)unlink(to_name);
1043 		errno = serrno;
1044 		err(EX_TEMPFAIL, "fork");
1045 	case 0:
1046 		stripbin = getenv("STRIPBIN");
1047 		if (stripbin == NULL)
1048 			stripbin = "strip";
1049 		execlp(stripbin, stripbin, to_name, NULL);
1050 		err(EX_OSERR, "exec(%s)", stripbin);
1051 	default:
1052 		if (wait(&status) == -1 || status) {
1053 			serrno = errno;
1054 			(void)unlink(to_name);
1055 			errc(EX_SOFTWARE, serrno, "wait");
1056 			/* NOTREACHED */
1057 		}
1058 	}
1059 }
1060 
1061 /*
1062  * When doing a concurrent make -j N multiple install's can race the mkdir.
1063  */
1064 static
1065 int
1066 mkdir_race(const char *path, int nmode)
1067 {
1068 	int res;
1069 	struct stat sb;
1070 
1071 	res = mkdir(path, nmode);
1072 	if (res < 0 && errno == EEXIST) {
1073 		if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
1074 			return(0);
1075 		res = mkdir(path, nmode);
1076 	}
1077 	return (res);
1078 }
1079 
1080 /*
1081  * install_dir --
1082  *	build directory hierarchy
1083  */
1084 static void
1085 install_dir(char *path)
1086 {
1087 	char *p;
1088 	struct stat sb;
1089 	int ch;
1090 
1091 	for (p = path;; ++p)
1092 		if (!*p || (p != path && *p  == '/')) {
1093 			ch = *p;
1094 			*p = '\0';
1095 			if (stat(path, &sb)) {
1096 				if (errno != ENOENT ||
1097 				    mkdir_race(path, 0755) < 0) {
1098 					err(EX_OSERR, "mkdir %s", path);
1099 					/* NOTREACHED */
1100 				} else if (verbose)
1101 					(void)printf("install: mkdir %s\n",
1102 						     path);
1103 			} else if (!S_ISDIR(sb.st_mode))
1104 				errx(EX_OSERR, "%s exists but is not a directory", path);
1105 			if (!(*p = ch))
1106 				break;
1107 		}
1108 
1109 	if (!dounpriv) {
1110 		if ((gid != (gid_t)-1 || uid != (uid_t)-1) &&
1111 		    chown(path, uid, gid))
1112 			warn("chown %u:%u %s", uid, gid, path);
1113 		/* XXXBED: should we do the chmod in the dounpriv case? */
1114 		if (chmod(path, mode))
1115 			warn("chmod %o %s", mode, path);
1116 	}
1117 }
1118 
1119 /*
1120  * usage --
1121  *	print a usage message and die
1122  */
1123 static void
1124 usage(void)
1125 {
1126 	fprintf(stderr,
1127 "usage: install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n"
1128 "               [-D dest] [-h hash]\n"
1129 "               [-B suffix] [-l linkflags] [-N dbdir]\n"
1130 "               file1 file2\n"
1131 "       install [-bCcpSsUv] [-B suffix] [-D dest] [-f flags] [-g group]\n"
1132 "               [-N dbdir] [-m mode] [-o owner] file1 ... fileN directory\n"
1133 "       install -d [-lUv] [-D dest] [-g group] [-m mode] [-N dbdir] [-o owner]\n"
1134 "               directory ...\n");
1135 	exit(EX_USAGE);
1136 	/* NOTREACHED */
1137 }
1138 
1139 /*
1140  * trymmap --
1141  *	return true (1) if mmap should be tried, false (0) if not.
1142  */
1143 static int
1144 trymmap(int fd)
1145 {
1146 /*
1147  * The ifdef is for bootstrapping - f_fstypename doesn't exist in
1148  * pre-Lite2-merge systems.
1149  */
1150 #ifdef MFSNAMELEN
1151 	struct statfs stfs;
1152 
1153 	if (nommap || fstatfs(fd, &stfs) != 0)
1154 		return (0);
1155 	if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
1156 	    strcmp(stfs.f_fstypename, "cd9660") == 0)
1157 		return (1);
1158 #endif
1159 	return (0);
1160 }
1161