xinstall.c revision 87724
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. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35
36__FBSDID("$FreeBSD: head/usr.bin/xinstall/xinstall.c 87724 2001-12-12 08:49:51Z ru $");
37
38#ifndef lint
39static const char copyright[] =
40"@(#) Copyright (c) 1987, 1993\n\
41	The Regents of the University of California.  All rights reserved.\n";
42#endif
43
44#ifndef lint
45static const char sccsid[] = "From: @(#)xinstall.c	8.1 (Berkeley) 7/21/93";
46#endif
47
48#include <sys/param.h>
49#include <sys/mman.h>
50#include <sys/mount.h>
51#include <sys/stat.h>
52#include <sys/wait.h>
53
54#include <ctype.h>
55#include <err.h>
56#include <errno.h>
57#include <fcntl.h>
58#include <grp.h>
59#include <paths.h>
60#include <pwd.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <sysexits.h>
65#include <unistd.h>
66#include <utime.h>
67
68#include "pathnames.h"
69
70/* Bootstrap aid - this doesn't exist in most older releases */
71#ifndef MAP_FAILED
72#define MAP_FAILED ((void *)-1)	/* from <sys/mman.h> */
73#endif
74
75#define	DIRECTORY	0x01		/* Tell install it's a directory. */
76#define	SETFLAGS	0x02		/* Tell install to set flags. */
77#define	NOCHANGEBITS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
78#define	BACKUP_SUFFIX	".old"
79
80struct passwd *pp;
81struct group *gp;
82gid_t gid;
83uid_t uid;
84int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose;
85mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
86const char *suffix = BACKUP_SUFFIX;
87
88void	copy __P((int, const char *, int, const char *, off_t));
89int	compare __P((int, const char *, size_t, int, const char *, size_t));
90int	create_newfile __P((const char *, int, struct stat *));
91int	create_tempfile __P((const char *, char *, size_t));
92void	install __P((const char *, const char *, u_long, u_int));
93void	install_dir __P((char *));
94u_long	numeric_id __P((const char *, const char *));
95void	strip __P((const char *));
96int	trymmap __P((int));
97void	usage __P((void));
98
99int
100main(argc, argv)
101	int argc;
102	char *argv[];
103{
104	struct stat from_sb, to_sb;
105	mode_t *set;
106	u_long fset;
107	int ch, no_target;
108	u_int iflags;
109	char *flags;
110	const char *group, *owner, *to_name;
111
112	iflags = 0;
113	group = owner = NULL;
114	while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1)
115		switch((char)ch) {
116		case 'B':
117			suffix = optarg;
118			/* FALLTHROUGH */
119		case 'b':
120			dobackup = 1;
121			break;
122		case 'C':
123			docompare = 1;
124			break;
125		case 'c':
126			/* For backwards compatibility. */
127			break;
128		case 'd':
129			dodir = 1;
130			break;
131		case 'f':
132			flags = optarg;
133			if (strtofflags(&flags, &fset, NULL))
134				errx(EX_USAGE, "%s: invalid flag", flags);
135			iflags |= SETFLAGS;
136			break;
137		case 'g':
138			group = optarg;
139			break;
140		case 'M':
141			nommap = 1;
142			break;
143		case 'm':
144			if (!(set = setmode(optarg)))
145				errx(EX_USAGE, "invalid file mode: %s",
146				     optarg);
147			mode = getmode(set, 0);
148			free(set);
149			break;
150		case 'o':
151			owner = optarg;
152			break;
153		case 'p':
154			docompare = dopreserve = 1;
155			break;
156		case 'S':
157			safecopy = 1;
158			break;
159		case 's':
160			dostrip = 1;
161			break;
162		case 'v':
163			verbose = 1;
164			break;
165		case '?':
166		default:
167			usage();
168		}
169	argc -= optind;
170	argv += optind;
171
172	/* some options make no sense when creating directories */
173	if ((safecopy || dostrip) && dodir)
174		usage();
175
176	/*
177	 * Older versions allowed -d -C combo.  Issue a warning
178	 * for now, but turn this into an error before 4.5-RELEASE.
179	 */
180	if (docompare && dodir)
181		warnx("the -d and -C options may not be specified together");
182
183	/* must have at least two arguments, except when creating directories */
184	if (argc < 2 && !dodir)
185		usage();
186
187	/* need to make a temp copy so we can compare stripped version */
188	if (docompare && dostrip)
189		safecopy = 1;
190
191	/* get group and owner id's */
192	if (group != NULL) {
193		if ((gp = getgrnam(group)) != NULL)
194			gid = gp->gr_gid;
195		else
196			gid = (gid_t)numeric_id(group, "group");
197	} else
198		gid = (gid_t)-1;
199
200	if (owner != NULL) {
201		if ((pp = getpwnam(owner)) != NULL)
202			uid = pp->pw_uid;
203		else
204			uid = (uid_t)numeric_id(owner, "user");
205	} else
206		uid = (uid_t)-1;
207
208	if (dodir) {
209		for (; *argv != NULL; ++argv)
210			install_dir(*argv);
211		exit(EX_OK);
212		/* NOTREACHED */
213	}
214
215	no_target = stat(to_name = argv[argc - 1], &to_sb);
216	if (!no_target && S_ISDIR(to_sb.st_mode)) {
217		for (; *argv != to_name; ++argv)
218			install(*argv, to_name, fset, iflags | DIRECTORY);
219		exit(EX_OK);
220		/* NOTREACHED */
221	}
222
223	/* can't do file1 file2 directory/file */
224	if (argc != 2)
225		usage();
226
227	if (!no_target) {
228		if (stat(*argv, &from_sb))
229			err(EX_OSERR, "%s", *argv);
230		if (!S_ISREG(to_sb.st_mode)) {
231			errno = EFTYPE;
232			err(EX_OSERR, "%s", to_name);
233		}
234		if (to_sb.st_dev == from_sb.st_dev &&
235		    to_sb.st_ino == from_sb.st_ino)
236			errx(EX_USAGE,
237			    "%s and %s are the same file", *argv, to_name);
238	}
239	install(*argv, to_name, fset, iflags);
240	exit(EX_OK);
241	/* NOTREACHED */
242}
243
244u_long
245numeric_id(name, type)
246	const char *name, *type;
247{
248	u_long val;
249	char *ep;
250
251	/*
252	 * XXX
253	 * We know that uid_t's and gid_t's are unsigned longs.
254	 */
255	errno = 0;
256	val = strtoul(name, &ep, 10);
257	if (errno)
258		err(EX_NOUSER, "%s", name);
259	if (*ep != '\0')
260		errx(EX_NOUSER, "unknown %s %s", type, name);
261	return (val);
262}
263
264/*
265 * install --
266 *	build a path name and install the file
267 */
268void
269install(from_name, to_name, fset, flags)
270	const char *from_name, *to_name;
271	u_long fset;
272	u_int flags;
273{
274	struct stat from_sb, temp_sb, to_sb;
275	struct utimbuf utb;
276	int devnull, files_match, from_fd, serrno, target;
277	int tempcopy, temp_fd, to_fd;
278	char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
279
280	files_match = 0;
281
282	/* If try to install NULL file to a directory, fails. */
283	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
284		if (stat(from_name, &from_sb))
285			err(EX_OSERR, "%s", from_name);
286		if (!S_ISREG(from_sb.st_mode)) {
287			errno = EFTYPE;
288			err(EX_OSERR, "%s", from_name);
289		}
290		/* Build the target path. */
291		if (flags & DIRECTORY) {
292			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
293			    to_name,
294			    (p = strrchr(from_name, '/')) ? ++p : from_name);
295			to_name = pathbuf;
296		}
297		devnull = 0;
298	} else {
299		devnull = 1;
300	}
301
302	target = stat(to_name, &to_sb) == 0;
303
304	/* Only install to regular files. */
305	if (target && !S_ISREG(to_sb.st_mode)) {
306		errno = EFTYPE;
307		warn("%s", to_name);
308		return;
309	}
310
311	/* Only copy safe if the target exists. */
312	tempcopy = safecopy && target;
313
314	if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0)
315		err(EX_OSERR, "%s", from_name);
316
317	/* If we don't strip, we can compare first. */
318	if (docompare && !dostrip && target) {
319		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
320			err(EX_OSERR, "%s", to_name);
321		if (devnull)
322			files_match = to_sb.st_size == 0;
323		else
324			files_match = !(compare(from_fd, from_name,
325			    (size_t)from_sb.st_size, to_fd,
326			    to_name, (size_t)to_sb.st_size));
327
328		/* Close "to" file unless we match. */
329		if (!files_match)
330			(void)close(to_fd);
331	}
332
333	if (!files_match) {
334		if (tempcopy) {
335			to_fd = create_tempfile(to_name, tempfile,
336			    sizeof(tempfile));
337			if (to_fd < 0)
338				err(EX_OSERR, "%s", tempfile);
339		} else {
340			if ((to_fd = create_newfile(to_name, target,
341			    &to_sb)) < 0)
342				err(EX_OSERR, "%s", to_name);
343			if (verbose)
344				(void)printf("install: %s -> %s\n",
345				    from_name, to_name);
346		}
347		if (!devnull)
348			copy(from_fd, from_name, to_fd,
349			     tempcopy ? tempfile : to_name, from_sb.st_size);
350	}
351
352	if (dostrip) {
353		strip(tempcopy ? tempfile : to_name);
354
355		/*
356		 * Re-open our fd on the target, in case we used a strip
357		 * that does not work in-place -- like GNU binutils strip.
358		 */
359		close(to_fd);
360		to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0);
361		if (to_fd < 0)
362			err(EX_OSERR, "stripping %s", to_name);
363	}
364
365	/*
366	 * Compare the stripped temp file with the target.
367	 */
368	if (docompare && dostrip && target) {
369		temp_fd = to_fd;
370
371		/* Re-open to_fd using the real target name. */
372		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
373			err(EX_OSERR, "%s", to_name);
374
375		if (fstat(temp_fd, &temp_sb)) {
376			serrno = errno;
377			(void)unlink(tempfile);
378			errno = serrno;
379			err(EX_OSERR, "%s", tempfile);
380		}
381
382		if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
383			    to_name, (size_t)to_sb.st_size) == 0) {
384			/*
385			 * If target has more than one link we need to
386			 * replace it in order to snap the extra links.
387			 * Need to preserve target file times, though.
388			 */
389			if (to_sb.st_nlink != 1) {
390				utb.actime = to_sb.st_atime;
391				utb.modtime = to_sb.st_mtime;
392				(void)utime(tempfile, &utb);
393			} else {
394				files_match = 1;
395				(void)unlink(tempfile);
396			}
397			(void) close(temp_fd);
398		}
399	}
400
401	/*
402	 * Move the new file into place if doing a safe copy
403	 * and the files are different (or just not compared).
404	 */
405	if (tempcopy && !files_match) {
406		/* Try to turn off the immutable bits. */
407		if (to_sb.st_flags & NOCHANGEBITS)
408			(void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
409		if (dobackup) {
410			if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
411			    suffix) != strlen(to_name) + strlen(suffix)) {
412				unlink(tempfile);
413				errx(EX_OSERR, "%s: backup filename too long",
414				    to_name);
415			}
416			if (verbose)
417				(void)printf("install: %s -> %s\n", to_name, backup);
418			if (rename(to_name, backup) < 0) {
419				serrno = errno;
420				unlink(tempfile);
421				errno = serrno;
422				err(EX_OSERR, "rename: %s to %s", to_name,
423				     backup);
424			}
425		}
426		if (verbose)
427			(void)printf("install: %s -> %s\n", from_name, to_name);
428		if (rename(tempfile, to_name) < 0) {
429			serrno = errno;
430			unlink(tempfile);
431			errno = serrno;
432			err(EX_OSERR, "rename: %s to %s",
433			    tempfile, to_name);
434		}
435
436		/* Re-open to_fd so we aren't hosed by the rename(2). */
437		(void) close(to_fd);
438		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
439			err(EX_OSERR, "%s", to_name);
440	}
441
442	/*
443	 * Preserve the timestamp of the source file if necessary.
444	 */
445	if (dopreserve && !files_match && !devnull) {
446		utb.actime = from_sb.st_atime;
447		utb.modtime = from_sb.st_mtime;
448		(void)utime(to_name, &utb);
449	}
450
451	if (fstat(to_fd, &to_sb) == -1) {
452		serrno = errno;
453		(void)unlink(to_name);
454		errno = serrno;
455		err(EX_OSERR, "%s", to_name);
456	}
457
458	/*
459	 * Set owner, group, mode for target; do the chown first,
460	 * chown may lose the setuid bits.
461	 */
462	if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
463	    (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
464	    (mode != to_sb.st_mode)) {
465		/* Try to turn off the immutable bits. */
466		if (to_sb.st_flags & NOCHANGEBITS)
467			(void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
468	}
469
470	if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
471	    (uid != (uid_t)-1 && uid != to_sb.st_uid))
472		if (fchown(to_fd, uid, gid) == -1) {
473			serrno = errno;
474			(void)unlink(to_name);
475			errno = serrno;
476			err(EX_OSERR,"%s: chown/chgrp", to_name);
477		}
478
479	if (mode != to_sb.st_mode)
480		if (fchmod(to_fd, mode)) {
481			serrno = errno;
482			(void)unlink(to_name);
483			errno = serrno;
484			err(EX_OSERR, "%s: chmod", to_name);
485		}
486
487	/*
488	 * If provided a set of flags, set them, otherwise, preserve the
489	 * flags, except for the dump flag.
490	 * NFS does not support flags.  Ignore EOPNOTSUPP flags if we're just
491	 * trying to turn off UF_NODUMP.  If we're trying to set real flags,
492	 * then warn if the the fs doesn't support it, otherwise fail.
493	 */
494	if (!devnull && fchflags(to_fd,
495	    flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
496		if (flags & SETFLAGS) {
497			if (errno == EOPNOTSUPP)
498				warn("%s: chflags", to_name);
499			else {
500				serrno = errno;
501				(void)unlink(to_name);
502				errno = serrno;
503				err(EX_OSERR, "%s: chflags", to_name);
504			}
505		}
506	}
507
508	(void)close(to_fd);
509	if (!devnull)
510		(void)close(from_fd);
511}
512
513/*
514 * compare --
515 *	compare two files; non-zero means files differ
516 */
517int
518compare(int from_fd, const char *from_name __unused, size_t from_len,
519	int to_fd, const char *to_name __unused, size_t to_len)
520{
521	char *p, *q;
522	int rv;
523	int done_compare;
524
525	rv = 0;
526	if (from_len != to_len)
527		return 1;
528
529	if (from_len <= 8 * 1024 * 1024) {
530		done_compare = 0;
531		if (trymmap(from_fd) && trymmap(to_fd)) {
532			p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
533			if (p == (char *)MAP_FAILED)
534				goto out;
535			q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
536			if (q == (char *)MAP_FAILED) {
537				munmap(p, from_len);
538				goto out;
539			}
540
541			rv = memcmp(p, q, from_len);
542			munmap(p, from_len);
543			munmap(q, from_len);
544			done_compare = 1;
545		}
546	out:
547		if (!done_compare) {
548			char buf1[MAXBSIZE];
549			char buf2[MAXBSIZE];
550			int n1, n2;
551
552			rv = 0;
553			lseek(from_fd, 0, SEEK_SET);
554			lseek(to_fd, 0, SEEK_SET);
555			while (rv == 0) {
556				n1 = read(from_fd, buf1, sizeof(buf1));
557				if (n1 == 0)
558					break;		/* EOF */
559				else if (n1 > 0) {
560					n2 = read(to_fd, buf2, n1);
561					if (n2 == n1)
562						rv = memcmp(buf1, buf2, n1);
563					else
564						rv = 1;	/* out of sync */
565				} else
566					rv = 1;		/* read failure */
567			}
568			lseek(from_fd, 0, SEEK_SET);
569			lseek(to_fd, 0, SEEK_SET);
570		}
571	} else
572		rv = 1;	/* don't bother in this case */
573
574	return rv;
575}
576
577/*
578 * create_tempfile --
579 *	create a temporary file based on path and open it
580 */
581int
582create_tempfile(path, temp, tsize)
583	const char *path;
584	char *temp;
585	size_t tsize;
586{
587	char *p;
588
589	(void)strncpy(temp, path, tsize);
590	temp[tsize - 1] = '\0';
591	if ((p = strrchr(temp, '/')) != NULL)
592		p++;
593	else
594		p = temp;
595	(void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p);
596	temp[tsize - 1] = '\0';
597	return (mkstemp(temp));
598}
599
600/*
601 * create_newfile --
602 *	create a new file, overwriting an existing one if necessary
603 */
604int
605create_newfile(path, target, sbp)
606	const char *path;
607	int target;
608	struct stat *sbp;
609{
610	char backup[MAXPATHLEN];
611
612	if (target) {
613		/*
614		 * Unlink now... avoid ETXTBSY errors later.  Try to turn
615		 * off the append/immutable bits -- if we fail, go ahead,
616		 * it might work.
617		 */
618		if (sbp->st_flags & NOCHANGEBITS)
619			(void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
620
621		if (dobackup) {
622			if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s",
623			    path, suffix) != strlen(path) + strlen(suffix))
624				errx(EX_OSERR, "%s: backup filename too long",
625				    path);
626			(void)snprintf(backup, MAXPATHLEN, "%s%s",
627			    path, suffix);
628			if (verbose)
629				(void)printf("install: %s -> %s\n",
630				    path, backup);
631			if (rename(path, backup) < 0)
632				err(EX_OSERR, "rename: %s to %s", path, backup);
633		} else
634			(void)unlink(path);
635	}
636
637	return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
638}
639
640/*
641 * copy --
642 *	copy from one file to another
643 */
644void
645copy(from_fd, from_name, to_fd, to_name, size)
646	register int from_fd, to_fd;
647	const char *from_name, *to_name;
648	off_t size;
649{
650	register int nr, nw;
651	int serrno;
652	char *p, buf[MAXBSIZE];
653	int done_copy;
654
655	/* Rewind file descriptors. */
656	if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
657		err(EX_OSERR, "lseek: %s", from_name);
658	if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
659		err(EX_OSERR, "lseek: %s", to_name);
660
661	/*
662	 * Mmap and write if less than 8M (the limit is so we don't totally
663	 * trash memory on big files.  This is really a minor hack, but it
664	 * wins some CPU back.
665	 */
666	done_copy = 0;
667	if (size <= 8 * 1048576 && trymmap(from_fd) &&
668	    (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
669		    from_fd, (off_t)0)) != (char *)MAP_FAILED) {
670		if ((nw = write(to_fd, p, size)) != size) {
671			serrno = errno;
672			(void)unlink(to_name);
673			errno = nw > 0 ? EIO : serrno;
674			err(EX_OSERR, "%s", to_name);
675		}
676		done_copy = 1;
677	}
678	if (!done_copy) {
679		while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
680			if ((nw = write(to_fd, buf, nr)) != nr) {
681				serrno = errno;
682				(void)unlink(to_name);
683				errno = nw > 0 ? EIO : serrno;
684				err(EX_OSERR, "%s", to_name);
685			}
686		if (nr != 0) {
687			serrno = errno;
688			(void)unlink(to_name);
689			errno = serrno;
690			err(EX_OSERR, "%s", from_name);
691		}
692	}
693}
694
695/*
696 * strip --
697 *	use strip(1) to strip the target file
698 */
699void
700strip(to_name)
701	const char *to_name;
702{
703	int serrno, status;
704
705	switch (fork()) {
706	case -1:
707		serrno = errno;
708		(void)unlink(to_name);
709		errno = serrno;
710		err(EX_TEMPFAIL, "fork");
711	case 0:
712		execlp("strip", "strip", to_name, (char *)NULL);
713		err(EX_OSERR, "exec(strip)");
714	default:
715		if (wait(&status) == -1 || status) {
716			(void)unlink(to_name);
717			exit(EX_SOFTWARE);
718			/* NOTREACHED */
719		}
720	}
721}
722
723/*
724 * install_dir --
725 *	build directory heirarchy
726 */
727void
728install_dir(path)
729	char *path;
730{
731	register char *p;
732	struct stat sb;
733	int ch;
734
735	for (p = path;; ++p)
736		if (!*p || (p != path && *p  == '/')) {
737			ch = *p;
738			*p = '\0';
739			if (stat(path, &sb)) {
740				if (errno != ENOENT || mkdir(path, 0755) < 0) {
741					err(EX_OSERR, "mkdir %s", path);
742					/* NOTREACHED */
743				} else if (verbose)
744					(void)printf("install: mkdir %s\n",
745						     path);
746			} else if (!S_ISDIR(sb.st_mode))
747				errx(EX_OSERR, "%s exists but is not a directory", path);
748			if (!(*p = ch))
749				break;
750 		}
751
752	if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
753		warn("chown %u:%u %s", uid, gid, path);
754	if (chmod(path, mode))
755		warn("chmod %o %s", mode, path);
756}
757
758/*
759 * usage --
760 *	print a usage message and die
761 */
762void
763usage()
764{
765	(void)fprintf(stderr, "\
766usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\
767               [-o owner] file1 file2\n\
768       install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\
769               [-o owner] file1 ... fileN directory\n\
770       install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n");
771	exit(EX_USAGE);
772	/* NOTREACHED */
773}
774
775/*
776 * trymmap --
777 *	return true (1) if mmap should be tried, false (0) if not.
778 */
779int
780trymmap(fd)
781	int fd;
782{
783/*
784 * The ifdef is for bootstrapping - f_fstypename doesn't exist in
785 * pre-Lite2-merge systems.
786 */
787#ifdef MFSNAMELEN
788	struct statfs stfs;
789
790	if (nommap || fstatfs(fd, &stfs) != 0)
791		return (0);
792	if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
793	    strcmp(stfs.f_fstypename, "cd9660") == 0)
794		return (1);
795#endif
796	return (0);
797}
798