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

1.3     ! deraadt     1: /*     $OpenBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan 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.
                     16:  * 3. All advertising materials mentioning features or use of this software
                     17:  *    must display the following acknowledgement:
                     18:  *     This product includes software developed by the University of
                     19:  *     California, Berkeley and its contributors.
                     20:  * 4. Neither the name of the University nor the names of its contributors
                     21:  *    may be used to endorse or promote products derived from this software
                     22:  *    without specific prior written permission.
                     23:  *
                     24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     34:  * SUCH DAMAGE.
                     35:  */
                     36:
                     37: #ifndef lint
                     38: static char copyright[] =
                     39: "@(#) Copyright (c) 1987, 1993\n\
                     40:        The Regents of the University of California.  All rights reserved.\n";
                     41: #endif /* not lint */
                     42:
                     43: #ifndef lint
                     44: #if 0
                     45: static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
                     46: #endif
1.3     ! deraadt    47: static char rcsid[] = "$OpenBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan Exp $";
1.1       deraadt    48: #endif /* not lint */
                     49:
                     50: #include <sys/param.h>
                     51: #include <sys/wait.h>
                     52: #include <sys/mman.h>
                     53: #include <sys/stat.h>
                     54:
                     55: #include <ctype.h>
                     56: #include <errno.h>
                     57: #include <fcntl.h>
                     58: #include <grp.h>
                     59: #include <paths.h>
                     60: #include <pwd.h>
                     61: #include <stdio.h>
                     62: #include <stdlib.h>
                     63: #include <string.h>
                     64: #include <unistd.h>
                     65: #include <err.h>
                     66:
                     67: #include "pathnames.h"
                     68:
                     69: struct passwd *pp;
                     70: struct group *gp;
                     71: int docopy, dodir, dostrip;
                     72: int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
                     73: char pathbuf[MAXPATHLEN];
                     74: uid_t uid;
                     75: gid_t gid;
                     76:
                     77: #define        DIRECTORY       0x01            /* Tell install it's a directory. */
                     78: #define        SETFLAGS        0x02            /* Tell install to set flags. */
                     79:
                     80: void   copy __P((int, char *, int, char *, off_t));
                     81: void   install __P((char *, char *, u_long, u_int));
                     82: void   install_dir __P((char *));
                     83: u_long string_to_flags __P((char **, u_long *, u_long *));
                     84: void   strip __P((char *));
                     85: void   usage __P((void));
                     86:
                     87: int
                     88: main(argc, argv)
                     89:        int argc;
                     90:        char *argv[];
                     91: {
                     92:        struct stat from_sb, to_sb;
                     93:        mode_t *set;
                     94:        u_long fset;
                     95:        u_int iflags;
                     96:        int ch, no_target;
                     97:        char *flags, *to_name, *group = NULL, *owner = NULL;
                     98:
                     99:        iflags = 0;
                    100:        while ((ch = getopt(argc, argv, "cf:g:m:o:sd")) != EOF)
                    101:                switch((char)ch) {
                    102:                case 'c':
                    103:                        docopy = 1;
                    104:                        break;
                    105:                case 'f':
                    106:                        flags = optarg;
                    107:                        if (string_to_flags(&flags, &fset, NULL))
                    108:                                errx(1, "%s: invalid flag", flags);
                    109:                        iflags |= SETFLAGS;
                    110:                        break;
                    111:                case 'g':
                    112:                        group = optarg;
                    113:                        break;
                    114:                case 'm':
                    115:                        if (!(set = setmode(optarg)))
                    116:                                errx(1, "%s: invalid file mode", optarg);
                    117:                        mode = getmode(set, 0);
                    118:                        break;
                    119:                case 'o':
                    120:                        owner = optarg;
                    121:                        break;
                    122:                case 's':
                    123:                        dostrip = 1;
                    124:                        break;
                    125:                case 'd':
                    126:                        dodir = 1;
                    127:                        break;
                    128:                case '?':
                    129:                default:
                    130:                        usage();
                    131:                }
                    132:        argc -= optind;
                    133:        argv += optind;
                    134:
                    135:        /* copy and strip options make no sense when creating directories */
                    136:        if ((docopy || dostrip) && dodir)
                    137:                usage();
                    138:
                    139:        /* must have at least two arguments, except when creating directories */
                    140:        if (argc < 2 && !dodir)
                    141:                usage();
                    142:
                    143:        /* get group and owner id's */
                    144:        if (group && !(gp = getgrnam(group)) && !isdigit(*group))
                    145:                errx(1, "unknown group %s", group);
                    146:        gid = (group) ? ((gp) ? gp->gr_gid : atoi(group)) : -1;
                    147:        if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner))
                    148:                errx(1, "unknown user %s", owner);
                    149:        uid = (owner) ? ((pp) ? pp->pw_uid : atoi(owner)) : -1;
                    150:
                    151:        if (dodir) {
                    152:                for (; *argv != NULL; ++argv)
                    153:                        install_dir(*argv);
                    154:                exit (0);
                    155:                /* NOTREACHED */
                    156:        }
                    157:
                    158:        no_target = stat(to_name = argv[argc - 1], &to_sb);
                    159:        if (!no_target && S_ISDIR(to_sb.st_mode)) {
                    160:                for (; *argv != to_name; ++argv)
                    161:                        install(*argv, to_name, fset, iflags | DIRECTORY);
                    162:                exit(0);
                    163:        }
                    164:
                    165:        /* can't do file1 file2 directory/file */
                    166:        if (argc != 2)
                    167:                usage();
                    168:
                    169:        if (!no_target) {
                    170:                if (stat(*argv, &from_sb))
                    171:                        err(1, "%s", *argv);
                    172:                if (!S_ISREG(to_sb.st_mode))
                    173:                        errx(1, "%s: %s", to_name, strerror(EFTYPE));
                    174:                if (to_sb.st_dev == from_sb.st_dev &&
                    175:                    to_sb.st_ino == from_sb.st_ino)
                    176:                        errx(1, "%s and %s are the same file", *argv, to_name);
                    177:                /*
                    178:                 * Unlink now... avoid ETXTBSY errors later.  Try and turn
                    179:                 * off the append/immutable bits -- if we fail, go ahead,
                    180:                 * it might work.
                    181:                 */
                    182: #define        NOCHANGEBITS    (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
                    183:                if (to_sb.st_flags & NOCHANGEBITS)
                    184:                        (void)chflags(to_name,
                    185:                            to_sb.st_flags & ~(NOCHANGEBITS));
                    186:                (void)unlink(to_name);
                    187:        }
                    188:        install(*argv, to_name, fset, iflags);
                    189:        exit(0);
                    190: }
                    191:
                    192: /*
                    193:  * install --
                    194:  *     build a path name and install the file
                    195:  */
                    196: void
                    197: install(from_name, to_name, fset, flags)
                    198:        char *from_name, *to_name;
                    199:        u_long fset;
                    200:        u_int flags;
                    201: {
                    202:        struct stat from_sb, to_sb;
                    203:        int devnull, from_fd, to_fd, serrno;
                    204:        char *p;
                    205:
                    206:        /* If try to install NULL file to a directory, fails. */
                    207:        if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
                    208:                if (stat(from_name, &from_sb))
                    209:                        err(1, "%s", from_name);
                    210:                if (!S_ISREG(from_sb.st_mode))
                    211:                        errx(1, "%s: %s", from_name, strerror(EFTYPE));
                    212:                /* Build the target path. */
                    213:                if (flags & DIRECTORY) {
                    214:                        (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
                    215:                            to_name,
                    216:                            (p = rindex(from_name, '/')) ? ++p : from_name);
                    217:                        to_name = pathbuf;
                    218:                }
                    219:                devnull = 0;
                    220:        } else {
                    221:                from_sb.st_flags = 0;   /* XXX */
                    222:                devnull = 1;
                    223:        }
                    224:
                    225:        /*
                    226:         * Unlink now... avoid ETXTBSY errors later.  Try and turn
                    227:         * off the append/immutable bits -- if we fail, go ahead,
                    228:         * it might work.
                    229:         */
                    230:        if (stat(to_name, &to_sb) == 0 &&
                    231:            to_sb.st_flags & (NOCHANGEBITS))
                    232:                (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
                    233:        (void)unlink(to_name);
                    234:
                    235:        /* Create target. */
                    236:        if ((to_fd = open(to_name,
                    237:            O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
                    238:                err(1, "%s", to_name);
                    239:        if (!devnull) {
                    240:                if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
                    241:                        (void)unlink(to_name);
                    242:                        err(1, "%s", from_name);
                    243:                }
                    244:                copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
                    245:                (void)close(from_fd);
                    246:        }
1.2       deraadt   247:
                    248:        if (dostrip) {
1.1       deraadt   249:                strip(to_name);
1.2       deraadt   250:
                    251:                /*
                    252:                 * Re-open our fd on the target, in case we used a strip
                    253:                 *  that does not work in-place -- like gnu binutils strip.
                    254:                 */
                    255:                close(to_fd);
                    256:                if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
                    257:                  err(1, "stripping %s", to_name);
                    258:        }
                    259:
1.1       deraadt   260:        /*
                    261:         * Set owner, group, mode for target; do the chown first,
                    262:         * chown may lose the setuid bits.
                    263:         */
                    264:        if ((gid != -1 || uid != -1) && fchown(to_fd, uid, gid)) {
                    265:                serrno = errno;
                    266:                (void)unlink(to_name);
                    267:                errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
                    268:        }
                    269:        if (fchmod(to_fd, mode)) {
                    270:                serrno = errno;
                    271:                (void)unlink(to_name);
                    272:                errx(1, "%s: chmod: %s", to_name, strerror(serrno));
                    273:        }
                    274:
                    275:        /*
                    276:         * If provided a set of flags, set them, otherwise, preserve the
                    277:         * flags, except for the dump flag.
                    278:         */
                    279:        if (fchflags(to_fd,
                    280:            flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
                    281:                serrno = errno;
                    282:                (void)unlink(to_name);
                    283:                errx(1, "%s: chflags: %s", to_name, strerror(serrno));
                    284:        }
                    285:
                    286:        (void)close(to_fd);
                    287:        if (!docopy && !devnull && unlink(from_name))
                    288:                err(1, "%s", from_name);
                    289: }
                    290:
                    291: /*
                    292:  * copy --
                    293:  *     copy from one file to another
                    294:  */
                    295: void
                    296: copy(from_fd, from_name, to_fd, to_name, size)
                    297:        register int from_fd, to_fd;
                    298:        char *from_name, *to_name;
                    299:        off_t size;
                    300: {
                    301:        register int nr, nw;
                    302:        int serrno;
                    303:        char *p, buf[MAXBSIZE];
1.3     ! deraadt   304:        volatile size_t siz;
1.1       deraadt   305:
                    306:        /*
                    307:         * Mmap and write if less than 8M (the limit is so we don't totally
                    308:         * trash memory on big files.  This is really a minor hack, but it
                    309:         * wins some CPU back.
                    310:         */
                    311:        if (size <= 8 * 1048576) {
                    312:                if ((p = mmap(NULL, (size_t)size, PROT_READ,
                    313:                    0, from_fd, (off_t)0)) == (char *)-1)
                    314:                        err(1, "%s", from_name);
1.3     ! deraadt   315:                siz = (size_t)size;
        !           316:                if (write(to_fd, p, siz) != siz)
1.1       deraadt   317:                        err(1, "%s", to_name);
                    318:        } else {
                    319:                while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
                    320:                        if ((nw = write(to_fd, buf, nr)) != nr) {
                    321:                                serrno = errno;
                    322:                                (void)unlink(to_name);
                    323:                                errx(1, "%s: %s",
                    324:                                    to_name, strerror(nw > 0 ? EIO : serrno));
                    325:                        }
                    326:                if (nr != 0) {
                    327:                        serrno = errno;
                    328:                        (void)unlink(to_name);
                    329:                        errx(1, "%s: %s", from_name, strerror(serrno));
                    330:                }
                    331:        }
                    332: }
                    333:
                    334: /*
                    335:  * strip --
                    336:  *     use strip(1) to strip the target file
                    337:  */
                    338: void
                    339: strip(to_name)
                    340:        char *to_name;
                    341: {
                    342:        int serrno, status;
                    343:
                    344:        switch (vfork()) {
                    345:        case -1:
                    346:                serrno = errno;
                    347:                (void)unlink(to_name);
                    348:                errx(1, "forks: %s", strerror(errno));
                    349:        case 0:
                    350:                execl(_PATH_STRIP, "strip", to_name, NULL);
                    351:                err(1, "%s", _PATH_STRIP);
                    352:        default:
                    353:                if (wait(&status) == -1 || status)
                    354:                        (void)unlink(to_name);
                    355:        }
                    356: }
                    357:
                    358: /*
                    359:  * install_dir --
                    360:  *     build directory heirarchy
                    361:  */
                    362: void
                    363: install_dir(path)
                    364:         char *path;
                    365: {
                    366:         register char *p;
                    367:         struct stat sb;
                    368:         int ch;
                    369:
                    370:         for (p = path;; ++p)
                    371:                 if (!*p || (p != path && *p  == '/')) {
                    372:                         ch = *p;
                    373:                         *p = '\0';
                    374:                         if (stat(path, &sb)) {
                    375:                                 if (errno != ENOENT || mkdir(path, 0777) < 0) {
                    376:                                        err(1, "%s", path);
                    377:                                        /* NOTREACHED */
                    378:                                 }
                    379:                         }
                    380:                         if (!(*p = ch))
                    381:                                break;
                    382:                 }
                    383:
                    384:        if (((gid != -1 || uid != -1) && chown(path, uid, gid)) ||
                    385:             chmod(path, mode)) {
                    386:                 warn("%s", path);
                    387:         }
                    388: }
                    389:
                    390: /*
                    391:  * usage --
                    392:  *     print a usage message and die
                    393:  */
                    394: void
                    395: usage()
                    396: {
                    397:        (void)fprintf(stderr, "\
                    398: usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\
                    399:        install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\
                    400:        install  -d   [-g group] [-m mode] [-o owner] directory ...\n");
                    401:        exit(1);
                    402: }