[BACK]Return to xinstall.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / xinstall

Annotation of src/usr.bin/xinstall/xinstall.c, Revision 1.44

1.44    ! moritz      1: /*     $OpenBSD: xinstall.c,v 1.43 2006/01/10 00:30:08 millert Exp $   */
1.2       deraadt     2: /*     $NetBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan Exp $    */
1.1       deraadt     3:
                      4: /*
                      5:  * Copyright (c) 1987, 1993
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
1.34      millert    16:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    17:  *    may be used to endorse or promote products derived from this software
                     18:  *    without specific prior written permission.
                     19:  *
                     20:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     21:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     22:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     23:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     24:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     25:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     26:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     27:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     28:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     29:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     30:  * SUCH DAMAGE.
                     31:  */
                     32:
                     33: #ifndef lint
                     34: static char copyright[] =
                     35: "@(#) Copyright (c) 1987, 1993\n\
                     36:        The Regents of the University of California.  All rights reserved.\n";
                     37: #endif /* not lint */
                     38:
                     39: #ifndef lint
                     40: #if 0
                     41: static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
                     42: #endif
1.44    ! moritz     43: static char rcsid[] = "$OpenBSD: xinstall.c,v 1.43 2006/01/10 00:30:08 millert Exp $";
1.1       deraadt    44: #endif /* not lint */
                     45:
                     46: #include <sys/param.h>
                     47: #include <sys/wait.h>
                     48: #include <sys/mman.h>
                     49: #include <sys/stat.h>
                     50:
                     51: #include <ctype.h>
1.4       millert    52: #include <err.h>
1.1       deraadt    53: #include <errno.h>
                     54: #include <fcntl.h>
                     55: #include <grp.h>
                     56: #include <paths.h>
                     57: #include <pwd.h>
                     58: #include <stdio.h>
                     59: #include <stdlib.h>
                     60: #include <string.h>
                     61: #include <unistd.h>
1.4       millert    62: #include <sysexits.h>
                     63: #include <utime.h>
1.1       deraadt    64:
                     65: #include "pathnames.h"
                     66:
1.19      millert    67: #define        DIRECTORY       0x01            /* Tell install it's a directory. */
                     68: #define        SETFLAGS        0x02            /* Tell install to set flags. */
                     69: #define NOCHANGEBITS   (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
                     70: #define BACKUP_SUFFIX  ".old"
                     71:
1.1       deraadt    72: struct passwd *pp;
                     73: struct group *gp;
1.19      millert    74: int dobackup, docompare, dodir, dopreserve, dostrip, safecopy;
1.1       deraadt    75: int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
1.4       millert    76: char pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
1.19      millert    77: char *suffix = BACKUP_SUFFIX;
1.1       deraadt    78: uid_t uid;
                     79: gid_t gid;
                     80:
1.31      millert    81: void   copy(int, char *, int, char *, off_t, int);
                     82: int    compare(int, const char *, size_t, int, const char *, size_t);
                     83: void   install(char *, char *, u_long, u_int);
                     84: void   install_dir(char *);
                     85: void   strip(char *);
                     86: void   usage(void);
                     87: int    create_newfile(char *, struct stat *);
                     88: int    create_tempfile(char *, char *, size_t);
                     89: int    file_write(int, char *, size_t, int *, int *, int);
1.33      tedu       90: void   file_flush(int, int);
1.1       deraadt    91:
                     92: int
1.35      deraadt    93: main(int argc, char *argv[])
1.1       deraadt    94: {
                     95:        struct stat from_sb, to_sb;
1.41      otto       96:        void *set;
1.23      mickey     97:        u_int32_t fset;
1.1       deraadt    98:        u_int iflags;
                     99:        int ch, no_target;
                    100:        char *flags, *to_name, *group = NULL, *owner = NULL;
                    101:
                    102:        iflags = 0;
1.19      millert   103:        while ((ch = getopt(argc, argv, "BbCcdf:g:m:o:pSs")) != -1)
1.1       deraadt   104:                switch((char)ch) {
1.4       millert   105:                case 'C':
                    106:                        docompare = 1;
                    107:                        break;
1.19      millert   108:                case 'B':
                    109:                        suffix = optarg;
                    110:                        /* fall through; -B implies -b */
                    111:                case 'b':
                    112:                        dobackup = 1;
                    113:                        break;
1.1       deraadt   114:                case 'c':
1.4       millert   115:                        /* For backwards compatibility. */
1.1       deraadt   116:                        break;
                    117:                case 'f':
                    118:                        flags = optarg;
1.23      mickey    119:                        if (strtofflags(&flags, &fset, NULL))
1.4       millert   120:                                errx(EX_USAGE, "%s: invalid flag", flags);
1.1       deraadt   121:                        iflags |= SETFLAGS;
                    122:                        break;
                    123:                case 'g':
                    124:                        group = optarg;
                    125:                        break;
                    126:                case 'm':
                    127:                        if (!(set = setmode(optarg)))
1.4       millert   128:                                errx(EX_USAGE, "%s: invalid file mode", optarg);
1.1       deraadt   129:                        mode = getmode(set, 0);
1.16      deraadt   130:                        free(set);
1.1       deraadt   131:                        break;
                    132:                case 'o':
                    133:                        owner = optarg;
                    134:                        break;
1.4       millert   135:                case 'p':
                    136:                        docompare = dopreserve = 1;
                    137:                        break;
                    138:                case 'S':
                    139:                        safecopy = 1;
                    140:                        break;
1.1       deraadt   141:                case 's':
                    142:                        dostrip = 1;
                    143:                        break;
                    144:                case 'd':
                    145:                        dodir = 1;
                    146:                        break;
                    147:                case '?':
                    148:                default:
                    149:                        usage();
                    150:                }
                    151:        argc -= optind;
                    152:        argv += optind;
                    153:
1.4       millert   154:        /* some options make no sense when creating directories */
                    155:        if ((safecopy || docompare || dostrip) && dodir)
1.1       deraadt   156:                usage();
                    157:
                    158:        /* must have at least two arguments, except when creating directories */
                    159:        if (argc < 2 && !dodir)
                    160:                usage();
                    161:
1.4       millert   162:        /* need to make a temp copy so we can compare stripped version */
                    163:        if (docompare && dostrip)
                    164:                safecopy = 1;
                    165:
1.1       deraadt   166:        /* get group and owner id's */
                    167:        if (group && !(gp = getgrnam(group)) && !isdigit(*group))
1.4       millert   168:                errx(EX_NOUSER, "unknown group %s", group);
                    169:        gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : (gid_t)-1;
1.1       deraadt   170:        if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner))
1.4       millert   171:                errx(EX_NOUSER, "unknown user %s", owner);
                    172:        uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : (uid_t)-1;
1.1       deraadt   173:
                    174:        if (dodir) {
                    175:                for (; *argv != NULL; ++argv)
                    176:                        install_dir(*argv);
1.4       millert   177:                exit(EX_OK);
1.1       deraadt   178:                /* NOTREACHED */
                    179:        }
                    180:
                    181:        no_target = stat(to_name = argv[argc - 1], &to_sb);
                    182:        if (!no_target && S_ISDIR(to_sb.st_mode)) {
                    183:                for (; *argv != to_name; ++argv)
                    184:                        install(*argv, to_name, fset, iflags | DIRECTORY);
1.4       millert   185:                exit(EX_OK);
                    186:                /* NOTREACHED */
1.1       deraadt   187:        }
                    188:
                    189:        /* can't do file1 file2 directory/file */
                    190:        if (argc != 2)
1.32      millert   191:                errx(EX_OSERR, "Target: %s", argv[argc-1]);
1.1       deraadt   192:
                    193:        if (!no_target) {
                    194:                if (stat(*argv, &from_sb))
1.4       millert   195:                        err(EX_OSERR, "%s", *argv);
1.1       deraadt   196:                if (!S_ISREG(to_sb.st_mode))
1.4       millert   197:                        errx(EX_OSERR, "%s: %s", to_name, strerror(EFTYPE));
1.1       deraadt   198:                if (to_sb.st_dev == from_sb.st_dev &&
                    199:                    to_sb.st_ino == from_sb.st_ino)
1.4       millert   200:                        errx(EX_USAGE, "%s and %s are the same file", *argv, to_name);
1.1       deraadt   201:        }
                    202:        install(*argv, to_name, fset, iflags);
1.4       millert   203:        exit(EX_OK);
                    204:        /* NOTREACHED */
1.1       deraadt   205: }
                    206:
                    207: /*
                    208:  * install --
                    209:  *     build a path name and install the file
                    210:  */
                    211: void
1.35      deraadt   212: install(char *from_name, char *to_name, u_long fset, u_int flags)
1.1       deraadt   213: {
                    214:        struct stat from_sb, to_sb;
1.4       millert   215:        struct utimbuf utb;
                    216:        int devnull, from_fd, to_fd, serrno, files_match = 0;
1.1       deraadt   217:        char *p;
                    218:
1.4       millert   219:        (void)memset((void *)&from_sb, 0, sizeof(from_sb));
                    220:        (void)memset((void *)&to_sb, 0, sizeof(to_sb));
                    221:
1.1       deraadt   222:        /* If try to install NULL file to a directory, fails. */
                    223:        if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
                    224:                if (stat(from_name, &from_sb))
1.4       millert   225:                        err(EX_OSERR, "%s", from_name);
1.1       deraadt   226:                if (!S_ISREG(from_sb.st_mode))
1.4       millert   227:                        errx(EX_OSERR, "%s: %s", from_name, strerror(EFTYPE));
1.1       deraadt   228:                /* Build the target path. */
                    229:                if (flags & DIRECTORY) {
                    230:                        (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
                    231:                            to_name,
1.11      millert   232:                            (p = strrchr(from_name, '/')) ? ++p : from_name);
1.1       deraadt   233:                        to_name = pathbuf;
                    234:                }
                    235:                devnull = 0;
                    236:        } else {
                    237:                devnull = 1;
                    238:        }
                    239:
1.4       millert   240:        if (stat(to_name, &to_sb) == 0) {
                    241:                /* Only compare against regular files. */
                    242:                if (docompare && !S_ISREG(to_sb.st_mode)) {
                    243:                        docompare = 0;
                    244:                        warnx("%s: %s", to_name, strerror(EFTYPE));
                    245:                }
                    246:        } else if (docompare) {
                    247:                /* File does not exist so silently ignore compare flag. */
                    248:                docompare = 0;
                    249:        }
                    250:
                    251:        if (safecopy) {
                    252:                to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));
                    253:                if (to_fd < 0)
                    254:                        err(EX_OSERR, "%s", tempfile);
                    255:        } else if (docompare && !dostrip) {
                    256:                if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
                    257:                        err(EX_OSERR, "%s", to_name);
                    258:        } else {
                    259:                if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
                    260:                        err(EX_OSERR, "%s", to_name);
                    261:        }
                    262:
1.1       deraadt   263:        if (!devnull) {
                    264:                if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
1.4       millert   265:                        serrno = errno;
                    266:                        (void)unlink(safecopy ? tempfile : to_name);
                    267:                        errx(EX_OSERR, "%s: %s", from_name, strerror(serrno));
                    268:                }
                    269:
                    270:                if (docompare && !safecopy) {
                    271:                        files_match = !(compare(from_fd, from_name,
                    272:                                        (size_t)from_sb.st_size, to_fd,
                    273:                                        to_name, (size_t)to_sb.st_size));
                    274:
                    275:                        /* Truncate "to" file for copy unless we match */
                    276:                        if (!files_match) {
                    277:                                (void)close(to_fd);
                    278:                                if ((to_fd = create_newfile(to_name, &to_sb)) < 0)
                    279:                                        err(EX_OSERR, "%s", to_name);
                    280:                        }
1.1       deraadt   281:                }
1.4       millert   282:                if (!files_match)
                    283:                        copy(from_fd, from_name, to_fd,
1.17      millert   284:                             safecopy ? tempfile : to_name, from_sb.st_size,
                    285:                             ((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size));
1.1       deraadt   286:        }
1.2       deraadt   287:
                    288:        if (dostrip) {
1.4       millert   289:                strip(safecopy ? tempfile : to_name);
1.2       deraadt   290:
                    291:                /*
                    292:                 * Re-open our fd on the target, in case we used a strip
                    293:                 *  that does not work in-place -- like gnu binutils strip.
                    294:                 */
                    295:                close(to_fd);
1.4       millert   296:                if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY,
                    297:                     0)) < 0)
                    298:                        err(EX_OSERR, "stripping %s", to_name);
                    299:        }
                    300:
                    301:        /*
                    302:         * Compare the (possibly stripped) temp file to the target.
                    303:         */
                    304:        if (safecopy && docompare) {
                    305:                int temp_fd = to_fd;
                    306:                struct stat temp_sb;
                    307:
                    308:                /* Re-open to_fd using the real target name. */
                    309:                if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
                    310:                        err(EX_OSERR, "%s", to_name);
                    311:
                    312:                if (fstat(temp_fd, &temp_sb)) {
                    313:                        serrno = errno;
                    314:                        (void)unlink(tempfile);
                    315:                        errx(EX_OSERR, "%s: %s", tempfile, strerror(serrno));
                    316:                }
                    317:
                    318:                if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
                    319:                            to_name, (size_t)to_sb.st_size) == 0) {
                    320:                        /*
                    321:                         * If target has more than one link we need to
                    322:                         * replace it in order to snap the extra links.
                    323:                         * Need to preserve target file times, though.
                    324:                         */
                    325:                        if (to_sb.st_nlink != 1) {
                    326:                                utb.actime = to_sb.st_atime;
                    327:                                utb.modtime = to_sb.st_mtime;
                    328:                                (void)utime(tempfile, &utb);
                    329:                        } else {
                    330:                                files_match = 1;
                    331:                                (void)unlink(tempfile);
                    332:                        }
                    333:                        (void) close(temp_fd);
                    334:                }
                    335:        }
                    336:
                    337:        /*
                    338:         * Move the new file into place if doing a safe copy
                    339:         * and the files are different (or just not compared).
                    340:         */
                    341:        if (safecopy && !files_match) {
                    342:                /* Try to turn off the immutable bits. */
                    343:                if (to_sb.st_flags & (NOCHANGEBITS))
                    344:                        (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
1.19      millert   345:                if (dobackup) {
                    346:                        char backup[MAXPATHLEN];
                    347:                        (void)snprintf(backup, MAXPATHLEN, "%s%s", to_name,
                    348:                            suffix);
1.28      heko      349:                        /* It is ok for the target file not to exist. */
                    350:                        if (rename(to_name, backup) < 0 && errno != ENOENT) {
1.19      millert   351:                                serrno = errno;
                    352:                                unlink(tempfile);
                    353:                                errx(EX_OSERR, "rename: %s to %s: %s", to_name,
                    354:                                     backup, strerror(serrno));
                    355:                        }
                    356:                }
1.4       millert   357:                if (rename(tempfile, to_name) < 0 ) {
                    358:                        serrno = errno;
                    359:                        unlink(tempfile);
                    360:                        errx(EX_OSERR, "rename: %s to %s: %s", tempfile,
                    361:                             to_name, strerror(serrno));
                    362:                }
                    363:
                    364:                /* Re-open to_fd so we aren't hosed by the rename(2). */
                    365:                (void) close(to_fd);
                    366:                if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
                    367:                        err(EX_OSERR, "%s", to_name);
                    368:        }
                    369:
                    370:        /*
1.30      millert   371:         * Preserve the timestamp of the source file if necessary.
1.4       millert   372:         */
                    373:        if (dopreserve && !files_match) {
                    374:                utb.actime = from_sb.st_atime;
                    375:                utb.modtime = from_sb.st_mtime;
                    376:                (void)utime(to_name, &utb);
1.2       deraadt   377:        }
                    378:
1.1       deraadt   379:        /*
                    380:         * Set owner, group, mode for target; do the chown first,
                    381:         * chown may lose the setuid bits.
                    382:         */
1.4       millert   383:        if ((gid != (gid_t)-1 || uid != (uid_t)-1) && fchown(to_fd, uid, gid)) {
1.1       deraadt   384:                serrno = errno;
                    385:                (void)unlink(to_name);
1.4       millert   386:                errx(EX_OSERR, "%s: chown/chgrp: %s", to_name, strerror(serrno));
1.1       deraadt   387:        }
                    388:        if (fchmod(to_fd, mode)) {
                    389:                serrno = errno;
                    390:                (void)unlink(to_name);
1.4       millert   391:                errx(EX_OSERR, "%s: chmod: %s", to_name, strerror(serrno));
1.1       deraadt   392:        }
                    393:
                    394:        /*
                    395:         * If provided a set of flags, set them, otherwise, preserve the
                    396:         * flags, except for the dump flag.
                    397:         */
                    398:        if (fchflags(to_fd,
                    399:            flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
1.12      millert   400:                if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
                    401:                        warnx("%s: chflags: %s", to_name, strerror(errno));
1.1       deraadt   402:        }
                    403:
                    404:        (void)close(to_fd);
1.18      millert   405:        if (!devnull)
                    406:                (void)close(from_fd);
1.1       deraadt   407: }
                    408:
                    409: /*
                    410:  * copy --
                    411:  *     copy from one file to another
                    412:  */
                    413: void
1.35      deraadt   414: copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size,
                    415:     int sparse)
1.1       deraadt   416: {
1.29      mpech     417:        ssize_t nr, nw;
1.1       deraadt   418:        int serrno;
                    419:        char *p, buf[MAXBSIZE];
                    420:
1.7       millert   421:        /* Rewind file descriptors. */
                    422:        if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
                    423:                err(EX_OSERR, "lseek: %s", from_name);
                    424:        if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
                    425:                err(EX_OSERR, "lseek: %s", to_name);
                    426:
1.1       deraadt   427:        /*
                    428:         * Mmap and write if less than 8M (the limit is so we don't totally
                    429:         * trash memory on big files.  This is really a minor hack, but it
1.17      millert   430:         * wins some CPU back.  Sparse files need special treatment.
1.1       deraadt   431:         */
1.17      millert   432:        if (!sparse && size <= 8 * 1048576) {
                    433:                volatile size_t siz;
                    434:
1.19      millert   435:                if ((p = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE,
1.38      grange    436:                    from_fd, (off_t)0)) == MAP_FAILED) {
1.6       millert   437:                        serrno = errno;
                    438:                        (void)unlink(to_name);
                    439:                        errx(EX_OSERR, "%s: %s", from_name, strerror(serrno));
                    440:                }
1.39      mickey    441:                if (size)
                    442:                        madvise(p, size, MADV_SEQUENTIAL);
1.3       deraadt   443:                siz = (size_t)size;
1.6       millert   444:                if ((nw = write(to_fd, p, siz)) != siz) {
                    445:                        serrno = errno;
                    446:                        (void)unlink(to_name);
                    447:                        errx(EX_OSERR, "%s: %s",
                    448:                            to_name, strerror(nw > 0 ? EIO : serrno));
                    449:                }
1.4       millert   450:                (void) munmap(p, (size_t)size);
1.1       deraadt   451:        } else {
1.17      millert   452:                int sz, rem, isem = 1;
                    453:                struct stat sb;
                    454:
                    455:                /*
                    456:                 * Pass the blocksize of the file being written to the write
                    457:                 * routine.  if the size is zero, use the default S_BLKSIZE.
                    458:                 */
                    459:                if (fstat(to_fd, &sb) != 0 || sb.st_blksize == 0)
                    460:                        sz = S_BLKSIZE;
                    461:                else
                    462:                        sz = sb.st_blksize;
1.20      millert   463:                rem = sz;
1.17      millert   464:
                    465:                while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
                    466:                        if (sparse)
                    467:                                nw = file_write(to_fd, buf, nr, &rem, &isem, sz);
                    468:                        else
                    469:                                nw = write(to_fd, buf, nr);
                    470:                        if (nw != nr) {
1.1       deraadt   471:                                serrno = errno;
                    472:                                (void)unlink(to_name);
1.4       millert   473:                                errx(EX_OSERR, "%s: %s",
1.1       deraadt   474:                                    to_name, strerror(nw > 0 ? EIO : serrno));
                    475:                        }
1.17      millert   476:                }
1.33      tedu      477:                if (sparse)
                    478:                        file_flush(to_fd, isem);
1.1       deraadt   479:                if (nr != 0) {
                    480:                        serrno = errno;
                    481:                        (void)unlink(to_name);
1.4       millert   482:                        errx(EX_OSERR, "%s: %s", from_name, strerror(serrno));
1.1       deraadt   483:                }
                    484:        }
                    485: }
                    486:
                    487: /*
1.4       millert   488:  * compare --
                    489:  *     compare two files; non-zero means files differ
                    490:  */
                    491: int
1.35      deraadt   492: compare(int from_fd, const char *from_name, size_t from_len, int to_fd,
                    493:     const char *to_name, size_t to_len)
1.4       millert   494: {
                    495:        caddr_t p1, p2;
1.21      millert   496:        size_t length, remainder;
                    497:        off_t from_off, to_off;
1.4       millert   498:        int dfound;
                    499:
                    500:        if (from_len != to_len)
                    501:                return(1);
                    502:
                    503:        /*
                    504:         * Compare the two files being careful not to mmap
                    505:         * more than 8M at a time.
                    506:         */
1.21      millert   507:        from_off = to_off = (off_t)0;
1.4       millert   508:        remainder = from_len;
                    509:        do {
                    510:                length = MIN(remainder, 8 * 1048576);
                    511:                remainder -= length;
                    512:
1.22      mickey    513:                if ((p1 = mmap(NULL, length, PROT_READ, MAP_PRIVATE,
1.38      grange    514:                    from_fd, from_off)) == MAP_FAILED)
1.4       millert   515:                        err(EX_OSERR, "%s", from_name);
1.22      mickey    516:                if ((p2 = mmap(NULL, length, PROT_READ, MAP_PRIVATE,
1.38      grange    517:                    to_fd, to_off)) == MAP_FAILED)
1.4       millert   518:                        err(EX_OSERR, "%s", to_name);
1.39      mickey    519:                if (length) {
                    520:                        madvise(p1, length, MADV_SEQUENTIAL);
                    521:                        madvise(p2, length, MADV_SEQUENTIAL);
                    522:                }
1.4       millert   523:
                    524:                dfound = memcmp(p1, p2, length);
                    525:
                    526:                (void) munmap(p1, length);
                    527:                (void) munmap(p2, length);
1.21      millert   528:
                    529:                from_off += length;
                    530:                to_off += length;
1.4       millert   531:
1.7       millert   532:        } while (!dfound && remainder > 0);
1.4       millert   533:
                    534:        return(dfound);
                    535: }
                    536:
                    537: /*
1.1       deraadt   538:  * strip --
                    539:  *     use strip(1) to strip the target file
                    540:  */
                    541: void
1.35      deraadt   542: strip(char *to_name)
1.1       deraadt   543: {
                    544:        int serrno, status;
1.26      millert   545:        char * volatile path_strip;
1.18      millert   546:
1.13      millert   547:        if (issetugid() || (path_strip = getenv("STRIP")) == NULL)
                    548:                path_strip = _PATH_STRIP;
1.1       deraadt   549:
                    550:        switch (vfork()) {
                    551:        case -1:
                    552:                serrno = errno;
                    553:                (void)unlink(to_name);
1.4       millert   554:                errx(EX_TEMPFAIL, "forks: %s", strerror(serrno));
1.1       deraadt   555:        case 0:
1.44    ! moritz    556:                execl(path_strip, "strip", "--", to_name, (char *)NULL);
1.13      millert   557:                warn("%s", path_strip);
1.9       deraadt   558:                _exit(EX_OSERR);
1.1       deraadt   559:        default:
1.14      millert   560:                if (wait(&status) == -1 || !WIFEXITED(status))
1.1       deraadt   561:                        (void)unlink(to_name);
                    562:        }
                    563: }
                    564:
                    565: /*
                    566:  * install_dir --
1.42      jsg       567:  *     build directory hierarchy
1.1       deraadt   568:  */
                    569: void
1.35      deraadt   570: install_dir(char *path)
1.1       deraadt   571: {
1.29      mpech     572:        char *p;
1.8       imp       573:        struct stat sb;
                    574:        int ch;
                    575:
                    576:        for (p = path;; ++p)
                    577:                if (!*p || (p != path && *p  == '/')) {
                    578:                        ch = *p;
                    579:                        *p = '\0';
                    580:                        if (stat(path, &sb)) {
                    581:                                if (errno != ENOENT || mkdir(path, 0777) < 0) {
1.4       millert   582:                                        err(EX_OSERR, "%s", path);
1.1       deraadt   583:                                        /* NOTREACHED */
1.8       imp       584:                                }
                    585:                        }
                    586:                        if (!(*p = ch))
1.1       deraadt   587:                                break;
1.8       imp       588:                }
1.1       deraadt   589:
1.4       millert   590:        if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) ||
1.8       imp       591:            chmod(path, mode)) {
                    592:                warn("%s", path);
                    593:        }
1.1       deraadt   594: }
                    595:
                    596: /*
                    597:  * usage --
                    598:  *     print a usage message and die
                    599:  */
                    600: void
1.35      deraadt   601: usage(void)
1.1       deraadt   602: {
                    603:        (void)fprintf(stderr, "\
1.40      jmc       604: usage: install [-bCcdpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n            source [...] target [...]\n");
1.4       millert   605:        exit(EX_USAGE);
                    606:        /* NOTREACHED */
                    607: }
                    608:
                    609: /*
                    610:  * create_tempfile --
                    611:  *     create a temporary file based on path and open it
                    612:  */
                    613: int
1.35      deraadt   614: create_tempfile(char *path, char *temp, size_t tsize)
1.4       millert   615: {
                    616:        char *p;
                    617:
1.43      millert   618:        strlcpy(temp, path, tsize);
                    619:        if ((p = strrchr(temp, '/')) != NULL)
1.4       millert   620:                p++;
                    621:        else
                    622:                p = temp;
1.43      millert   623:        *p = '\0';
                    624:        strlcat(p, "INS@XXXXXXXXXX", tsize);
1.4       millert   625:
                    626:        return(mkstemp(temp));
                    627: }
                    628:
                    629: /*
                    630:  * create_newfile --
1.30      millert   631:  *     create a new file, overwriting an existing one if necessary
1.4       millert   632:  */
                    633: int
1.35      deraadt   634: create_newfile(char *path, struct stat *sbp)
1.4       millert   635: {
1.19      millert   636:        char backup[MAXPATHLEN];
                    637:
1.4       millert   638:        /*
                    639:         * Unlink now... avoid ETXTBSY errors later.  Try and turn
                    640:         * off the append/immutable bits -- if we fail, go ahead,
                    641:         * it might work.
                    642:         */
                    643:        if (sbp->st_flags & (NOCHANGEBITS))
                    644:                (void)chflags(path, sbp->st_flags & ~(NOCHANGEBITS));
                    645:
1.19      millert   646:        if (dobackup) {
                    647:                (void)snprintf(backup, MAXPATHLEN, "%s%s", path, suffix);
1.28      heko      648:                /* It is ok for the target file not to exist. */
                    649:                if (rename(path, backup) < 0 && errno != ENOENT)
                    650:                        err(EX_OSERR, "rename: %s to %s (errno %d)", path, backup, errno);
1.19      millert   651:        } else
                    652:                (void)unlink(path);
1.4       millert   653:
                    654:        return(open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
1.17      millert   655: }
                    656:
                    657: /*
                    658:  * file_write()
                    659:  *     Write/copy a file (during copy or archive extract). This routine knows
                    660:  *     how to copy files with lseek holes in it. (Which are read as file
                    661:  *     blocks containing all 0's but do not have any file blocks associated
                    662:  *     with the data). Typical examples of these are files created by dbm
                    663:  *     variants (.pag files). While the file size of these files are huge, the
                    664:  *     actual storage is quite small (the files are sparse). The problem is
                    665:  *     the holes read as all zeros so are probably stored on the archive that
                    666:  *     way (there is no way to determine if the file block is really a hole,
                    667:  *     we only know that a file block of all zero's can be a hole).
                    668:  *     At this writing, no major archive format knows how to archive files
                    669:  *     with holes. However, on extraction (or during copy, -rw) we have to
                    670:  *     deal with these files. Without detecting the holes, the files can
                    671:  *     consume a lot of file space if just written to disk. This replacement
                    672:  *     for write when passed the basic allocation size of a file system block,
                    673:  *     uses lseek whenever it detects the input data is all 0 within that
                    674:  *     file block. In more detail, the strategy is as follows:
                    675:  *     While the input is all zero keep doing an lseek. Keep track of when we
                    676:  *     pass over file block boundries. Only write when we hit a non zero
                    677:  *     input. once we have written a file block, we continue to write it to
                    678:  *     the end (we stop looking at the input). When we reach the start of the
                    679:  *     next file block, start checking for zero blocks again. Working on file
                    680:  *     block boundries significantly reduces the overhead when copying files
                    681:  *     that are NOT very sparse. This overhead (when compared to a write) is
                    682:  *     almost below the measurement resolution on many systems. Without it,
                    683:  *     files with holes cannot be safely copied. It does has a side effect as
                    684:  *     it can put holes into files that did not have them before, but that is
                    685:  *     not a problem since the file contents are unchanged (in fact it saves
                    686:  *     file space). (Except on paging files for diskless clients. But since we
                    687:  *     cannot determine one of those file from here, we ignore them). If this
                    688:  *     ever ends up on a system where CTG files are supported and the holes
                    689:  *     are not desired, just do a conditional test in those routines that
                    690:  *     call file_write() and have it call write() instead. BEFORE CLOSING THE
                    691:  *     FILE, make sure to call file_flush() when the last write finishes with
                    692:  *     an empty block. A lot of file systems will not create an lseek hole at
                    693:  *     the end. In this case we drop a single 0 at the end to force the
                    694:  *     trailing 0's in the file.
                    695:  *     ---Parameters---
                    696:  *     rem: how many bytes left in this file system block
                    697:  *     isempt: have we written to the file block yet (is it empty)
                    698:  *     sz: basic file block allocation size
                    699:  *     cnt: number of bytes on this write
                    700:  *     str: buffer to write
                    701:  * Return:
                    702:  *     number of bytes written, -1 on write (or lseek) error.
                    703:  */
                    704:
                    705: int
1.35      deraadt   706: file_write(int fd, char *str, size_t cnt, int *rem, int *isempt, int sz)
1.17      millert   707: {
1.29      mpech     708:        char *pt;
                    709:        char *end;
                    710:        size_t wcnt;
                    711:        char *st = str;
1.17      millert   712:
                    713:        /*
                    714:         * while we have data to process
                    715:         */
                    716:        while (cnt) {
                    717:                if (!*rem) {
                    718:                        /*
                    719:                         * We are now at the start of file system block again
                    720:                         * (or what we think one is...). start looking for
                    721:                         * empty blocks again
                    722:                         */
                    723:                        *isempt = 1;
                    724:                        *rem = sz;
                    725:                }
                    726:
                    727:                /*
                    728:                 * only examine up to the end of the current file block or
                    729:                 * remaining characters to write, whatever is smaller
                    730:                 */
                    731:                wcnt = MIN(cnt, *rem);
                    732:                cnt -= wcnt;
                    733:                *rem -= wcnt;
                    734:                if (*isempt) {
                    735:                        /*
                    736:                         * have not written to this block yet, so we keep
                    737:                         * looking for zero's
                    738:                         */
                    739:                        pt = st;
                    740:                        end = st + wcnt;
                    741:
                    742:                        /*
                    743:                         * look for a zero filled buffer
                    744:                         */
                    745:                        while ((pt < end) && (*pt == '\0'))
                    746:                                ++pt;
                    747:
                    748:                        if (pt == end) {
                    749:                                /*
                    750:                                 * skip, buf is empty so far
                    751:                                 */
                    752:                                if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) {
                    753:                                        warn("lseek");
                    754:                                        return(-1);
                    755:                                }
                    756:                                st = pt;
                    757:                                continue;
                    758:                        }
                    759:                        /*
                    760:                         * drat, the buf is not zero filled
                    761:                         */
                    762:                        *isempt = 0;
                    763:                }
                    764:
                    765:                /*
                    766:                 * have non-zero data in this file system block, have to write
                    767:                 */
                    768:                if (write(fd, st, wcnt) != wcnt) {
                    769:                        warn("write");
                    770:                        return(-1);
                    771:                }
                    772:                st += wcnt;
                    773:        }
                    774:        return(st - str);
1.33      tedu      775: }
                    776:
                    777: /*
                    778:  * file_flush()
                    779:  *     when the last file block in a file is zero, many file systems will not
                    780:  *     let us create a hole at the end. To get the last block with zeros, we
                    781:  *     write the last BYTE with a zero (back up one byte and write a zero).
                    782:  */
                    783: void
                    784: file_flush(int fd, int isempt)
                    785: {
                    786:        static char blnk[] = "\0";
                    787:
                    788:        /*
                    789:         * silly test, but make sure we are only called when the last block is
                    790:         * filled with all zeros.
                    791:         */
                    792:        if (!isempt)
                    793:                return;
                    794:
                    795:        /*
                    796:         * move back one byte and write a zero
                    797:         */
                    798:        if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
                    799:                warn("Failed seek on file");
                    800:                return;
                    801:        }
                    802:
                    803:        if (write(fd, blnk, 1) < 0)
                    804:                warn("Failed write to file");
                    805:        return;
1.1       deraadt   806: }