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

1.1       deraadt     1: /*     $NetBSD: xinstall.c,v 1.8 1995/08/10 04:20:57 ghudson Exp $     */
                      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
                     46: static char rcsid[] = "$NetBSD: xinstall.c,v 1.8 1995/08/10 04:20:57 ghudson Exp $";
                     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:        }
                    246:        if (dostrip)
                    247:                strip(to_name);
                    248:        /*
                    249:         * Set owner, group, mode for target; do the chown first,
                    250:         * chown may lose the setuid bits.
                    251:         */
                    252:        if ((gid != -1 || uid != -1) && fchown(to_fd, uid, gid)) {
                    253:                serrno = errno;
                    254:                (void)unlink(to_name);
                    255:                errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
                    256:        }
                    257:        if (fchmod(to_fd, mode)) {
                    258:                serrno = errno;
                    259:                (void)unlink(to_name);
                    260:                errx(1, "%s: chmod: %s", to_name, strerror(serrno));
                    261:        }
                    262:
                    263:        /*
                    264:         * If provided a set of flags, set them, otherwise, preserve the
                    265:         * flags, except for the dump flag.
                    266:         */
                    267:        if (fchflags(to_fd,
                    268:            flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
                    269:                serrno = errno;
                    270:                (void)unlink(to_name);
                    271:                errx(1, "%s: chflags: %s", to_name, strerror(serrno));
                    272:        }
                    273:
                    274:        (void)close(to_fd);
                    275:        if (!docopy && !devnull && unlink(from_name))
                    276:                err(1, "%s", from_name);
                    277: }
                    278:
                    279: /*
                    280:  * copy --
                    281:  *     copy from one file to another
                    282:  */
                    283: void
                    284: copy(from_fd, from_name, to_fd, to_name, size)
                    285:        register int from_fd, to_fd;
                    286:        char *from_name, *to_name;
                    287:        off_t size;
                    288: {
                    289:        register int nr, nw;
                    290:        int serrno;
                    291:        char *p, buf[MAXBSIZE];
                    292:
                    293:        /*
                    294:         * Mmap and write if less than 8M (the limit is so we don't totally
                    295:         * trash memory on big files.  This is really a minor hack, but it
                    296:         * wins some CPU back.
                    297:         */
                    298:        if (size <= 8 * 1048576) {
                    299:                if ((p = mmap(NULL, (size_t)size, PROT_READ,
                    300:                    0, from_fd, (off_t)0)) == (char *)-1)
                    301:                        err(1, "%s", from_name);
                    302:                if (write(to_fd, p, size) != size)
                    303:                        err(1, "%s", to_name);
                    304:        } else {
                    305:                while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
                    306:                        if ((nw = write(to_fd, buf, nr)) != nr) {
                    307:                                serrno = errno;
                    308:                                (void)unlink(to_name);
                    309:                                errx(1, "%s: %s",
                    310:                                    to_name, strerror(nw > 0 ? EIO : serrno));
                    311:                        }
                    312:                if (nr != 0) {
                    313:                        serrno = errno;
                    314:                        (void)unlink(to_name);
                    315:                        errx(1, "%s: %s", from_name, strerror(serrno));
                    316:                }
                    317:        }
                    318: }
                    319:
                    320: /*
                    321:  * strip --
                    322:  *     use strip(1) to strip the target file
                    323:  */
                    324: void
                    325: strip(to_name)
                    326:        char *to_name;
                    327: {
                    328:        int serrno, status;
                    329:
                    330:        switch (vfork()) {
                    331:        case -1:
                    332:                serrno = errno;
                    333:                (void)unlink(to_name);
                    334:                errx(1, "forks: %s", strerror(errno));
                    335:        case 0:
                    336:                execl(_PATH_STRIP, "strip", to_name, NULL);
                    337:                err(1, "%s", _PATH_STRIP);
                    338:        default:
                    339:                if (wait(&status) == -1 || status)
                    340:                        (void)unlink(to_name);
                    341:        }
                    342: }
                    343:
                    344: /*
                    345:  * install_dir --
                    346:  *     build directory heirarchy
                    347:  */
                    348: void
                    349: install_dir(path)
                    350:         char *path;
                    351: {
                    352:         register char *p;
                    353:         struct stat sb;
                    354:         int ch;
                    355:
                    356:         for (p = path;; ++p)
                    357:                 if (!*p || (p != path && *p  == '/')) {
                    358:                         ch = *p;
                    359:                         *p = '\0';
                    360:                         if (stat(path, &sb)) {
                    361:                                 if (errno != ENOENT || mkdir(path, 0777) < 0) {
                    362:                                        err(1, "%s", path);
                    363:                                        /* NOTREACHED */
                    364:                                 }
                    365:                         }
                    366:                         if (!(*p = ch))
                    367:                                break;
                    368:                 }
                    369:
                    370:        if (((gid != -1 || uid != -1) && chown(path, uid, gid)) ||
                    371:             chmod(path, mode)) {
                    372:                 warn("%s", path);
                    373:         }
                    374: }
                    375:
                    376: /*
                    377:  * usage --
                    378:  *     print a usage message and die
                    379:  */
                    380: void
                    381: usage()
                    382: {
                    383:        (void)fprintf(stderr, "\
                    384: usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\
                    385:        install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\
                    386:        install  -d   [-g group] [-m mode] [-o owner] directory ...\n");
                    387:        exit(1);
                    388: }