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, ×tamp_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, ×tamp_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