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

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