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