xinstall.c revision 18482
189857Sobrien/*
2130561Sobrien * Copyright (c) 1987, 1993
389857Sobrien *	The Regents of the University of California.  All rights reserved.
489857Sobrien *
5104834Sobrien * Redistribution and use in source and binary forms, with or without
689857Sobrien * modification, are permitted provided that the following conditions
7104834Sobrien * are met:
8104834Sobrien * 1. Redistributions of source code must retain the above copyright
9104834Sobrien *    notice, this list of conditions and the following disclaimer.
10104834Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1189857Sobrien *    notice, this list of conditions and the following disclaimer in the
12104834Sobrien *    documentation and/or other materials provided with the distribution.
13104834Sobrien * 3. All advertising materials mentioning features or use of this software
14104834Sobrien *    must display the following acknowledgement:
15104834Sobrien *	This product includes software developed by the University of
1689857Sobrien *	California, Berkeley and its contributors.
17104834Sobrien * 4. Neither the name of the University nor the names of its contributors
18104834Sobrien *    may be used to endorse or promote products derived from this software
19104834Sobrien *    without specific prior written permission.
2089857Sobrien *
2189857Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2289857Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2389857Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2489857Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2589857Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2689857Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2789857Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28104834Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2989857Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3089857Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3189857Sobrien * SUCH DAMAGE.
3289857Sobrien */
3389857Sobrien
3489857Sobrien#ifndef lint
3589857Sobrienstatic const char copyright[] =
3689857Sobrien"@(#) Copyright (c) 1987, 1993\n\
37130561Sobrien	The Regents of the University of California.  All rights reserved.\n";
3889857Sobrien#endif /* not lint */
3989857Sobrien
4089857Sobrien#ifndef lint
4189857Sobrien/*static char sccsid[] = "From: @(#)xinstall.c	8.1 (Berkeley) 7/21/93";*/
42104834Sobrienstatic const char rcsid[] =
43104834Sobrien	"$Id: xinstall.c,v 1.12 1996/09/05 07:54:08 peter Exp $";
4489857Sobrien#endif /* not lint */
4589857Sobrien
4689857Sobrien/*-
4789857Sobrien * Todo:
4889857Sobrien * o for -C, compare original files except in -s case.
4989857Sobrien * o for -C, don't change anything if nothing needs be changed.  In
5089857Sobrien *   particular, don't toggle the immutable flags just to allow null
5189857Sobrien *   attribute changes and don't clear the dump flag.  (I think inode
5289857Sobrien *   ctimes are not updated for null attribute changes, but this is a
5389857Sobrien *   bug.)
5489857Sobrien * o independent of -C, if a copy must be made, then copy to a tmpfile,
5589857Sobrien *   set all attributes except the immutable flags, then rename, then
5689857Sobrien *   set the immutable flags.  It's annoying that the immutable flags
5789857Sobrien *   defeat the atomicicity of rename - it seems that there must be
5889857Sobrien * o a window where the target is not immutable.
5989857Sobrien */
6089857Sobrien
6189857Sobrien#include <sys/param.h>
6289857Sobrien#include <sys/wait.h>
6389857Sobrien#include <sys/mman.h>
6489857Sobrien#include <sys/stat.h>
6589857Sobrien#include <sys/mount.h>
6689857Sobrien
6789857Sobrien#include <ctype.h>
6889857Sobrien#include <err.h>
69130561Sobrien#include <errno.h>
7089857Sobrien#include <fcntl.h>
7189857Sobrien#include <grp.h>
7289857Sobrien#include <paths.h>
7389857Sobrien#include <pwd.h>
7489857Sobrien#include <stdio.h>
7589857Sobrien#include <stdlib.h>
7689857Sobrien#include <string.h>
7789857Sobrien#include <sysexits.h>
7889857Sobrien#include <unistd.h>
7989857Sobrien#include <utime.h>
8089857Sobrien
8189857Sobrien#include "pathnames.h"
8289857Sobrien
8389857Sobrienint debug, docompare, docopy, dopreserve, dostrip, verbose;
8489857Sobrienint mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
8589857Sobrienchar *group, *owner, pathbuf[MAXPATHLEN];
8689857Sobrienchar pathbuf2[MAXPATHLEN];
8789857Sobrien
8889857Sobrien#define	DIRECTORY	0x01		/* Tell install it's a directory. */
89130561Sobrien#define	SETFLAGS	0x02		/* Tell install to set flags. */
9089857Sobrien#define	NOCHANGEBITS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
9189857Sobrien
9289857Sobrienvoid	copy __P((int, char *, int, char *, off_t));
9389857Sobrienint	compare __P((int, const char *, int, const char *,
9489857Sobrien		     const struct stat *, const struct stat *));
9589857Sobrienvoid	install __P((char *, char *, u_long, u_int));
9689857Sobrienu_long	string_to_flags __P((char **, u_long *, u_long *));
9789857Sobrienvoid	strip __P((char *));
9889857Sobrienvoid	usage __P((void));
9989857Sobrienint	trymmap __P((int));
10089857Sobrien
10189857Sobrien#define ALLOW_NUMERIC_IDS 1
102130561Sobrien#ifdef ALLOW_NUMERIC_IDS
103130561Sobrien
10489857Sobrienuid_t	uid;
10589857Sobriengid_t	gid;
10689857Sobrien
10789857Sobrienuid_t	resolve_uid __P((char *));
10889857Sobriengid_t	resolve_gid __P((char *));
10989857Sobrienu_long	numeric_id __P((char *, char *));
11089857Sobrien
11189857Sobrien#else
11289857Sobrien
11389857Sobrienstruct passwd *pp;
11489857Sobrienstruct group *gp;
11589857Sobrien
11689857Sobrien#endif /* ALLOW_NUMERIC_IDS */
11789857Sobrien
11889857Sobrienint
11989857Sobrienmain(argc, argv)
12089857Sobrien	int argc;
12189857Sobrien	char *argv[];
12289857Sobrien{
12389857Sobrien	struct stat from_sb, to_sb;
12489857Sobrien	mode_t *set;
12589857Sobrien	u_long fset;
12689857Sobrien	u_int iflags;
12789857Sobrien	int ch, no_target;
128104834Sobrien	char *flags, *to_name;
12989857Sobrien
13089857Sobrien	iflags = 0;
13189857Sobrien	while ((ch = getopt(argc, argv, "Ccdf:g:m:o:psv")) != EOF)
13289857Sobrien		switch((char)ch) {
13389857Sobrien		case 'C':
134130561Sobrien			docompare = docopy = 1;
135130561Sobrien			break;
13689857Sobrien		case 'c':
13789857Sobrien			docopy = 1;
13889857Sobrien			break;
13989857Sobrien		case 'D':
14089857Sobrien			debug++;
14189857Sobrien			break;
14289857Sobrien		case 'f':
14389857Sobrien			flags = optarg;
14489857Sobrien			if (string_to_flags(&flags, &fset, NULL))
14589857Sobrien				errx(EX_USAGE, "%s: invalid flag", flags);
14689857Sobrien			iflags |= SETFLAGS;
14789857Sobrien			break;
14889857Sobrien		case 'g':
14989857Sobrien			group = optarg;
15089857Sobrien			break;
15189857Sobrien		case 'm':
15289857Sobrien			if (!(set = setmode(optarg)))
15389857Sobrien				errx(EX_USAGE, "invalid file mode: %s",
15489857Sobrien				     optarg);
15589857Sobrien			mode = getmode(set, 0);
15689857Sobrien			break;
15789857Sobrien		case 'o':
15889857Sobrien			owner = optarg;
15989857Sobrien			break;
16089857Sobrien		case 'p':
16189857Sobrien			docompare = docopy = dopreserve = 1;
16289857Sobrien			break;
16389857Sobrien		case 's':
16489857Sobrien			dostrip = 1;
16589857Sobrien			break;
16689857Sobrien		case 'v':
16789857Sobrien			verbose = 1;
16889857Sobrien			break;
16989857Sobrien		case '?':
17089857Sobrien		default:
17189857Sobrien			usage();
17289857Sobrien		}
17389857Sobrien	argc -= optind;
17489857Sobrien	argv += optind;
17589857Sobrien	if (argc < 2)
17689857Sobrien		usage();
17789857Sobrien
17889857Sobrien#ifdef ALLOW_NUMERIC_IDS
17989857Sobrien
18089857Sobrien	if (owner)
18189857Sobrien		uid = resolve_uid(owner);
18289857Sobrien	if (group)
18389857Sobrien		gid = resolve_gid(group);
18489857Sobrien
18589857Sobrien#else
18689857Sobrien
18789857Sobrien	/* get group and owner id's */
18889857Sobrien	if (owner && !(pp = getpwnam(owner)))
18989857Sobrien		errx(EX_NOUSER, "unknown user %s", owner);
19089857Sobrien	if (group && !(gp = getgrnam(group)))
19189857Sobrien		errx(EX_NOUSER, "unknown group %s", group);
19289857Sobrien
19389857Sobrien#endif /* ALLOW_NUMERIC_IDS */
19489857Sobrien
19589857Sobrien	no_target = stat(to_name = argv[argc - 1], &to_sb);
19689857Sobrien	if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
19789857Sobrien		for (; *argv != to_name; ++argv)
19889857Sobrien			install(*argv, to_name, fset, iflags | DIRECTORY);
19989857Sobrien		exit(0);
20089857Sobrien	}
20189857Sobrien
20289857Sobrien	/* can't do file1 file2 directory/file */
20389857Sobrien	if (argc != 2)
20489857Sobrien		usage();
20589857Sobrien
206130561Sobrien	if (!no_target) {
207130561Sobrien		if (stat(*argv, &from_sb))
208130561Sobrien			err(EX_OSERR, "%s", *argv);
209130561Sobrien		if (!S_ISREG(to_sb.st_mode)) {
210130561Sobrien			errno = EFTYPE;
211130561Sobrien			err(EX_OSERR, "%s", to_name);
21289857Sobrien		}
21389857Sobrien		if (to_sb.st_dev == from_sb.st_dev &&
21489857Sobrien		    to_sb.st_ino == from_sb.st_ino)
21589857Sobrien			errx(EX_USAGE,
21689857Sobrien			    "%s and %s are the same file", *argv, to_name);
21789857Sobrien/*
21889857Sobrien * XXX - It's not at all clear why this code was here, since it completely
21989857Sobrien * duplicates code install().  The version in install() handles the -C flag
22089857Sobrien * correctly, so we'll just disable this for now.
22189857Sobrien */
22289857Sobrien#if 0
22389857Sobrien		/*
22489857Sobrien		 * Unlink now... avoid ETXTBSY errors later.  Try and turn
22589857Sobrien		 * off the append/immutable bits -- if we fail, go ahead,
22689857Sobrien		 * it might work.
22789857Sobrien		 */
22889857Sobrien		if (to_sb.st_flags & NOCHANGEBITS)
22989857Sobrien			(void)chflags(to_name,
23089857Sobrien			    to_sb.st_flags & ~(NOCHANGEBITS));
23189857Sobrien		(void)unlink(to_name);
23289857Sobrien#endif
23389857Sobrien	}
23489857Sobrien	install(*argv, to_name, fset, iflags);
23589857Sobrien	exit(0);
23689857Sobrien}
23789857Sobrien
23889857Sobrien#ifdef ALLOW_NUMERIC_IDS
239130561Sobrien
24089857Sobrienuid_t
24189857Sobrienresolve_uid(s)
24289857Sobrien	char *s;
24389857Sobrien{
24489857Sobrien	struct passwd *pw;
24589857Sobrien
24689857Sobrien	return ((pw = getpwnam(s)) == NULL) ?
24789857Sobrien		(uid_t) numeric_id(s, "user") : pw->pw_uid;
24889857Sobrien}
24989857Sobrien
25089857Sobriengid_t
25189857Sobrienresolve_gid(s)
25289857Sobrien	char *s;
25389857Sobrien{
25489857Sobrien	struct group *gr;
25589857Sobrien
25689857Sobrien	return ((gr = getgrnam(s)) == NULL) ?
25789857Sobrien		(gid_t) numeric_id(s, "group") : gr->gr_gid;
25889857Sobrien}
25989857Sobrien
26089857Sobrienu_long
26189857Sobriennumeric_id(name, type)
26289857Sobrien	char *name, *type;
26389857Sobrien{
26489857Sobrien	u_long val;
26589857Sobrien	char *ep;
26689857Sobrien
267130561Sobrien	/*
268130561Sobrien	 * XXX
26989857Sobrien	 * We know that uid_t's and gid_t's are unsigned longs.
27089857Sobrien	 */
27189857Sobrien	errno = 0;
272130561Sobrien	val = strtoul(name, &ep, 10);
27389857Sobrien	if (errno)
27489857Sobrien		err(EX_NOUSER, "%s", name);
27589857Sobrien	if (*ep != '\0')
27689857Sobrien		errx(EX_NOUSER, "unknown %s %s", type, name);
27789857Sobrien	return (val);
27889857Sobrien}
27989857Sobrien
28089857Sobrien#endif /* ALLOW_NUMERIC_IDS */
28189857Sobrien
28289857Sobrien/*
28389857Sobrien * install --
28489857Sobrien *	build a path name and install the file
28589857Sobrien */
28689857Sobrienvoid
28789857Sobrieninstall(from_name, to_name, fset, flags)
28889857Sobrien	char *from_name, *to_name;
28989857Sobrien	u_long fset;
290130561Sobrien	u_int flags;
291130561Sobrien{
29289857Sobrien	struct stat from_sb, to_sb;
29389857Sobrien	int devnull, from_fd, to_fd, serrno;
29489857Sobrien	char *p, *old_to_name = 0;
29589857Sobrien
29689857Sobrien	if (debug >= 2 && !docompare)
29789857Sobrien		fprintf(stderr, "install: invoked without -C for %s to %s\n",
29889857Sobrien			from_name, to_name);
29989857Sobrien
30089857Sobrien	/* If try to install NULL file to a directory, fails. */
30189857Sobrien	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
30289857Sobrien		if (stat(from_name, &from_sb))
30389857Sobrien			err(EX_OSERR, "%s", from_name);
30489857Sobrien		if (!S_ISREG(from_sb.st_mode)) {
30589857Sobrien			errno = EFTYPE;
30689857Sobrien			err(EX_OSERR, "%s", from_name);
30789857Sobrien		}
30889857Sobrien		/* Build the target path. */
30989857Sobrien		if (flags & DIRECTORY) {
31089857Sobrien			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
311130561Sobrien			    to_name,
31289857Sobrien			    (p = strrchr(from_name, '/')) ? ++p : from_name);
31389857Sobrien			to_name = pathbuf;
31489857Sobrien		}
31589857Sobrien		devnull = 0;
31689857Sobrien	} else {
31789857Sobrien		from_sb.st_flags = 0;	/* XXX */
31889857Sobrien		devnull = 1;
319130561Sobrien	}
32089857Sobrien
32189857Sobrien	if (docompare) {
32289857Sobrien		old_to_name = to_name;
32389857Sobrien		/*
32489857Sobrien		 * Make a new temporary file in the same file system
32589857Sobrien		 * (actually, in in the same directory) as the target so
32689857Sobrien		 * that the temporary file can be renamed to the target.
32789857Sobrien		 */
328130561Sobrien		snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name);
32989857Sobrien		p = strrchr(pathbuf2, '/');
33089857Sobrien		p = (p == NULL ? pathbuf2 : p + 1);
33189857Sobrien		snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX");
33289857Sobrien		to_fd = mkstemp(pathbuf2);
33389857Sobrien		if (to_fd < 0)
334130561Sobrien			/* XXX should fall back to not comparing. */
335130561Sobrien			err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name);
33689857Sobrien		to_name = pathbuf2;
33789857Sobrien	} else {
33889857Sobrien		/*
33989857Sobrien		 * Unlink now... avoid errors later.  Try to turn off the
34089857Sobrien		 * append/immutable bits -- if we fail, go ahead, it might
34189857Sobrien		 * work.
34289857Sobrien		 */
34389857Sobrien		if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS)
34489857Sobrien			(void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
34589857Sobrien		unlink(to_name);
346130561Sobrien
34789857Sobrien		/* Create target. */
34889857Sobrien		to_fd = open(to_name,
34989857Sobrien			     O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
35089857Sobrien		if (to_fd < 0)
351130561Sobrien			err(EX_OSERR, "%s", to_name);
35289857Sobrien	}
35389857Sobrien
35489857Sobrien	if (!devnull) {
35589857Sobrien		if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
35689857Sobrien			serrno = errno;
35789857Sobrien			(void)unlink(to_name);
35889857Sobrien			errno = serrno;
35989857Sobrien			err(EX_OSERR, "%s", from_name);
36089857Sobrien		}
36189857Sobrien		copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
36289857Sobrien		(void)close(from_fd);
36389857Sobrien	}
36489857Sobrien
36589857Sobrien	if (dostrip)
36689857Sobrien		strip(to_name);
367130561Sobrien
36889857Sobrien	/*
36989857Sobrien	 * Unfortunately, because we strip the installed file and not the
37089857Sobrien	 * original one, it is impossible to do the comparison without
37189857Sobrien	 * first laboriously copying things over and then comparing.
37289857Sobrien	 * It may be possible to better optimize the !dostrip case, however.
37389857Sobrien	 * For further study.
37489857Sobrien	 */
37589857Sobrien	if (docompare) {
37689857Sobrien		struct stat old_sb, new_sb, timestamp_sb;
37789857Sobrien		int old_fd;
37889857Sobrien		struct utimbuf utb;
37989857Sobrien
380104834Sobrien		old_fd = open(old_to_name, O_RDONLY, 0);
381104834Sobrien		if (old_fd < 0 && errno == ENOENT)
38289857Sobrien			goto different;
38389857Sobrien		if (old_fd < 0)
38489857Sobrien			err(EX_OSERR, "%s", old_to_name);
38589857Sobrien		fstat(old_fd, &old_sb);
386130561Sobrien		if (old_sb.st_flags & NOCHANGEBITS)
387104834Sobrien			(void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS);
38889857Sobrien		fstat(to_fd, &new_sb);
38989857Sobrien		if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb,
39089857Sobrien			    &new_sb)) {
39189857Sobriendifferent:
39289857Sobrien			if (debug != 0)
39389857Sobrien				fprintf(stderr,
39489857Sobrien					"install: renaming for %s: %s to %s\n",
39589857Sobrien					from_name, to_name, old_to_name);
39689857Sobrien			if (dopreserve && stat(from_name, &timestamp_sb) == 0) {
39789857Sobrien				utb.actime = from_sb.st_atime;
39889857Sobrien				utb.modtime = from_sb.st_mtime;
39989857Sobrien				(void)utime(to_name, &utb);
40089857Sobrien			}
40189857Sobrienmoveit:
40289857Sobrien			if (verbose) {
40389857Sobrien				printf("install: %s -> %s\n",
40489857Sobrien					from_name, old_to_name);
40589857Sobrien			}
40689857Sobrien			if (rename(to_name, old_to_name) < 0) {
40789857Sobrien				serrno = errno;
40889857Sobrien				unlink(to_name);
40989857Sobrien				unlink(old_to_name);
41089857Sobrien				errno = serrno;
41189857Sobrien				err(EX_OSERR, "rename: %s to %s", to_name,
41289857Sobrien				    old_to_name);
41389857Sobrien			}
41489857Sobrien			close(old_fd);
41589857Sobrien		} else {
41689857Sobrien			if (old_sb.st_nlink != 1) {
417130561Sobrien				/*
41889857Sobrien				 * Replace the target, although it hasn't
41989857Sobrien				 * changed, to snap the extra links.  But
42089857Sobrien				 * preserve the target file times.
421130561Sobrien				 */
42289857Sobrien				if (fstat(old_fd, &timestamp_sb) == 0) {
42389857Sobrien					utb.actime = timestamp_sb.st_atime;
42489857Sobrien					utb.modtime = timestamp_sb.st_mtime;
425130561Sobrien					(void)utime(to_name, &utb);
426130561Sobrien				}
427130561Sobrien				goto moveit;
42889857Sobrien			}
42989857Sobrien			if (unlink(to_name) < 0)
43089857Sobrien				err(EX_OSERR, "unlink: %s", to_name);
431130561Sobrien			close(to_fd);
43289857Sobrien			to_fd = old_fd;
43389857Sobrien		}
43489857Sobrien		to_name = old_to_name;
43589857Sobrien	}
43689857Sobrien
43789857Sobrien	/*
438130561Sobrien	 * Set owner, group, mode for target; do the chown first,
43989857Sobrien	 * chown may lose the setuid bits.
44089857Sobrien	 */
44189857Sobrien	if ((group || owner) &&
44289857Sobrien#ifdef ALLOW_NUMERIC_IDS
44389857Sobrien	    fchown(to_fd, owner ? uid : -1, group ? gid : -1)) {
44489857Sobrien#else
44589857Sobrien	    fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
44689857Sobrien#endif
44789857Sobrien		serrno = errno;
44889857Sobrien		(void)unlink(to_name);
44989857Sobrien		errno = serrno;
45089857Sobrien		err(EX_OSERR,"%s: chown/chgrp", to_name);
45189857Sobrien	}
45289857Sobrien	if (fchmod(to_fd, mode)) {
45389857Sobrien		serrno = errno;
45489857Sobrien		(void)unlink(to_name);
45589857Sobrien		errno = serrno;
45689857Sobrien		err(EX_OSERR, "%s: chmod", to_name);
45789857Sobrien	}
458130561Sobrien
45989857Sobrien	/*
46089857Sobrien	 * If provided a set of flags, set them, otherwise, preserve the
46189857Sobrien	 * flags, except for the dump flag.
46289857Sobrien	 */
46389857Sobrien	if (fchflags(to_fd,
46489857Sobrien	    flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
465130561Sobrien		serrno = errno;
46689857Sobrien		(void)unlink(to_name);
46789857Sobrien		errno = serrno;
46889857Sobrien		err(EX_OSERR, "%s: chflags", to_name);
46989857Sobrien	}
47089857Sobrien
47189857Sobrien	(void)close(to_fd);
47289857Sobrien	if (!docopy && !devnull && unlink(from_name))
47389857Sobrien		err(EX_OSERR, "%s", from_name);
47489857Sobrien}
47589857Sobrien
47689857Sobrien/*
47789857Sobrien * compare --
478130561Sobrien *	compare two files; non-zero means files differ
47989857Sobrien */
48089857Sobrienint
48189857Sobriencompare(int from_fd, const char *from_name, int to_fd, const char *to_name,
48289857Sobrien	const struct stat *from_sb, const struct stat *to_sb)
48389857Sobrien{
48489857Sobrien	char *p, *q;
48589857Sobrien	int rv;
48689857Sobrien	size_t tsize;
48789857Sobrien	int done_compare;
48889857Sobrien
48989857Sobrien	if (from_sb->st_size != to_sb->st_size)
49089857Sobrien		return 1;
49189857Sobrien
49289857Sobrien	tsize = (size_t)from_sb->st_size;
49389857Sobrien
49489857Sobrien	if (tsize <= 8 * 1024 * 1024) {
49589857Sobrien		done_compare = 0;
49689857Sobrien		if (trymmap(from_fd) && trymmap(to_fd)) {
49789857Sobrien			p = mmap(NULL, tsize, PROT_READ, 0, from_fd, (off_t)0);
49889857Sobrien			if ((long)p == -1)
499130561Sobrien				goto out;
50089857Sobrien			q = mmap(NULL, tsize, PROT_READ, 0, to_fd, (off_t)0);
50189857Sobrien			if ((long)q == -1) {
50289857Sobrien				munmap(p, tsize);
50389857Sobrien				goto out;
504130561Sobrien			}
50589857Sobrien
50689857Sobrien			rv = memcmp(p, q, tsize);
507130561Sobrien			munmap(p, tsize);
508130561Sobrien			munmap(q, tsize);
509130561Sobrien			done_compare = 1;
510130561Sobrien		}
511130561Sobrien	out:
512130561Sobrien		if (!done_compare) {
513130561Sobrien			char buf1[MAXBSIZE];
514130561Sobrien			char buf2[MAXBSIZE];
515130561Sobrien			int n1, n2;
516130561Sobrien
517130561Sobrien			rv = 0;
518130561Sobrien			lseek(from_fd, 0, SEEK_SET);
519130561Sobrien			lseek(to_fd, 0, SEEK_SET);
520130561Sobrien			while (rv == 0) {
521130561Sobrien				n1 = read(from_fd, buf1, sizeof(buf1));
522130561Sobrien				if (n1 == 0)
523130561Sobrien					break;		/* EOF */
524130561Sobrien				else if (n1 > 0) {
525130561Sobrien					n2 = read(to_fd, buf2, n1);
526130561Sobrien					if (n2 == n1)
527130561Sobrien						rv = memcmp(buf1, buf2, n1);
528130561Sobrien					else
529130561Sobrien						rv = 1;	/* out of sync */
530130561Sobrien				} else
531130561Sobrien					rv = 1;		/* read failure */
532130561Sobrien			}
533130561Sobrien			lseek(from_fd, 0, SEEK_SET);
534130561Sobrien			lseek(to_fd, 0, SEEK_SET);
535130561Sobrien		}
536130561Sobrien	} else
537130561Sobrien		rv = 1;	/* don't bother in this case */
538130561Sobrien
539130561Sobrien	return rv;
540130561Sobrien}
541130561Sobrien
542130561Sobrien/*
543130561Sobrien * copy --
544130561Sobrien *	copy from one file to another
545130561Sobrien */
546130561Sobrienvoid
547130561Sobriencopy(from_fd, from_name, to_fd, to_name, size)
548130561Sobrien	register int from_fd, to_fd;
549130561Sobrien	char *from_name, *to_name;
550130561Sobrien	off_t size;
551130561Sobrien{
552130561Sobrien	register int nr, nw;
553130561Sobrien	int serrno;
554130561Sobrien	char *p, buf[MAXBSIZE];
555130561Sobrien	int done_copy;
556130561Sobrien
557130561Sobrien	/*
558130561Sobrien	 * Mmap and write if less than 8M (the limit is so we don't totally
559130561Sobrien	 * trash memory on big files.  This is really a minor hack, but it
560130561Sobrien	 * wins some CPU back.
561130561Sobrien	 */
562130561Sobrien	done_copy = 0;
563130561Sobrien	if (size <= 8 * 1048576 && trymmap(from_fd)) {
564130561Sobrien		if ((p = mmap(NULL, (size_t)size, PROT_READ,
565130561Sobrien				0, from_fd, (off_t)0)) == (char *)-1)
566130561Sobrien			goto out;
567130561Sobrien		if ((nw = write(to_fd, p, size)) != size) {
568130561Sobrien			serrno = errno;
569130561Sobrien			(void)unlink(to_name);
570130561Sobrien			errno = nw > 0 ? EIO : serrno;
57189857Sobrien			err(EX_OSERR, "%s", to_name);
57289857Sobrien		}
57389857Sobrien		done_copy = 1;
574130561Sobrien	out:
57589857Sobrien	}
576130561Sobrien	if (!done_copy) {
57789857Sobrien		while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
57889857Sobrien			if ((nw = write(to_fd, buf, nr)) != nr) {
579130561Sobrien				serrno = errno;
58089857Sobrien				(void)unlink(to_name);
581130561Sobrien				errno = nw > 0 ? EIO : serrno;
58289857Sobrien				err(EX_OSERR, "%s", to_name);
58389857Sobrien			}
58489857Sobrien		if (nr != 0) {
58589857Sobrien			serrno = errno;
58689857Sobrien			(void)unlink(to_name);
58789857Sobrien			errno = serrno;
58889857Sobrien			err(EX_OSERR, "%s", from_name);
589130561Sobrien		}
590130561Sobrien	}
591130561Sobrien}
592130561Sobrien
593130561Sobrien/*
594130561Sobrien * strip --
595130561Sobrien *	use strip(1) to strip the target file
596130561Sobrien */
597130561Sobrienvoid
598130561Sobrienstrip(to_name)
599130561Sobrien	char *to_name;
600130561Sobrien{
60189857Sobrien	int serrno, status;
60289857Sobrien
603130561Sobrien	switch (vfork()) {
60489857Sobrien	case -1:
605130561Sobrien		serrno = errno;
606130561Sobrien		(void)unlink(to_name);
607130561Sobrien		errno = serrno;
608130561Sobrien		err(EX_TEMPFAIL, "fork");
60989857Sobrien	case 0:
610130561Sobrien		execlp("strip", "strip", to_name, NULL);
611130561Sobrien		err(EX_OSERR, "exec(strip)");
612130561Sobrien	default:
613130561Sobrien		if (wait(&status) == -1 || status) {
61489857Sobrien			(void)unlink(to_name);
615130561Sobrien			exit(EX_SOFTWARE);
616130561Sobrien		}
617130561Sobrien	}
618130561Sobrien}
619130561Sobrien
620130561Sobrien/*
62189857Sobrien * usage --
622130561Sobrien *	print a usage message and die
623130561Sobrien */
62489857Sobrienvoid
62589857Sobrienusage()
626130561Sobrien{
62789857Sobrien	(void)fprintf(stderr,
62889857Sobrien"usage: install [-CcDps] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n");
62989857Sobrien	exit(1);
63089857Sobrien}
63189857Sobrien
63289857Sobrien/*
63389857Sobrien * trymmap --
63489857Sobrien *	return true (1) if mmap should be tried, false (0) if not.
63589857Sobrien */
63689857Sobrienint
63789857Sobrientrymmap(fd)
63889857Sobrien	int fd;
63989857Sobrien{
64089857Sobrien	struct statfs stfs;
64189857Sobrien
64289857Sobrien	if (fstatfs(fd, &stfs) < 0)
64389857Sobrien		return 0;
64489857Sobrien	switch(stfs.f_type) {
64589857Sobrien	case MOUNT_UFS:		/* should be safe.. */
64689857Sobrien	case MOUNT_CD9660:	/* should be safe.. */
64789857Sobrien		return 1;
64889857Sobrien	}
64989857Sobrien	return 0;
65089857Sobrien}
65189857Sobrien