[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.77

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