[BACK]Return to cp.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / bin / cp

Annotation of src/bin/cp/cp.c, Revision 1.3

1.1       cgd         1: /*
                      2:  * Copyright (c) 1988 The Regents of the University of California.
                      3:  * All rights reserved.
                      4:  *
                      5:  * This code is derived from software contributed to Berkeley by
                      6:  * David Hitz of Auspex Systems Inc.
                      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: char copyright[] =
                     39: "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
                     40:  All rights reserved.\n";
                     41: #endif /* not lint */
                     42:
                     43: #ifndef lint
                     44: static char sccsid[] = "@(#)cp.c       5.24 (Berkeley) 5/6/91";
1.3     ! cgd        45: static char rcsid[] = "$Header: cp.c,v 1.2 93/03/22 08:04:43 cgd Exp $";
1.1       cgd        46: #endif /* not lint */
                     47:
                     48: /*
                     49:  * cp copies source files to target files.
                     50:  *
                     51:  * The global PATH_T structures "to" and "from" always contain paths to the
                     52:  * current source and target files, respectively.  Since cp does not change
                     53:  * directories, these paths can be either absolute or dot-realative.
                     54:  *
                     55:  * The basic algorithm is to initialize "to" and "from", and then call the
                     56:  * recursive copy() function to do the actual work.  If "from" is a file,
                     57:  * copy copies the data.  If "from" is a directory, copy creates the
                     58:  * corresponding "to" directory, and calls itself recursively on all of
                     59:  * the entries in the "from" directory.
                     60:  */
                     61:
                     62: #include <sys/param.h>
                     63: #include <sys/stat.h>
                     64: #include <sys/time.h>
                     65: #include <dirent.h>
                     66: #include <fcntl.h>
                     67: #include <errno.h>
                     68: #include <unistd.h>
                     69: #include <stdio.h>
                     70: #include <stdlib.h>
                     71: #include <string.h>
                     72: #include "cp.h"
                     73:
                     74: PATH_T from = { from.p_path, "" };
                     75: PATH_T to = { to.p_path, "" };
                     76:
                     77: uid_t myuid;
                     78: int exit_val, myumask;
                     79: int iflag, pflag, orflag, rflag;
                     80: int (*statfcn)();
                     81: char *buf, *progname;
                     82:
                     83: main(argc, argv)
                     84:        int argc;
                     85:        char **argv;
                     86: {
                     87:        extern int optind;
                     88:        struct stat to_stat;
                     89:        register int c, r;
                     90:        int symfollow, lstat(), stat();
                     91:        char *old_to, *p;
                     92:
                     93:        /*
                     94:         * The utility cp(1) is used by mv(1) -- except for usage statements,
                     95:         * print the "called as" program name.
                     96:         */
                     97:        progname = (p = rindex(*argv,'/')) ? ++p : *argv;
                     98:
                     99:        symfollow = 0;
                    100:        while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
                    101:        switch ((char)c) {
                    102:                case 'f':
                    103:                        iflag = 0;
                    104:                        break;
                    105:                case 'h':
                    106:                        symfollow = 1;
                    107:                        break;
                    108:                case 'i':
                    109:                        iflag = isatty(fileno(stdin));
                    110:                        break;
                    111:                case 'p':
                    112:                        pflag = 1;
                    113:                        break;
                    114:                case 'R':
                    115:                        rflag = 1;
                    116:                        break;
                    117:                case 'r':
                    118:                        orflag = 1;
                    119:                        break;
                    120:                case '?':
                    121:                default:
                    122:                        usage();
                    123:                        break;
                    124:                }
                    125:        }
                    126:        argc -= optind;
                    127:        argv += optind;
                    128:
                    129:        if (argc < 2)
                    130:                usage();
                    131:
                    132:        if (rflag && orflag) {
                    133:                (void)fprintf(stderr,
                    134:                    "cp: the -R and -r options are mutually exclusive.\n");
                    135:                exit(1);
                    136:        }
                    137:
                    138:        buf = (char *)malloc(MAXBSIZE);
                    139:        if (!buf) {
                    140:                (void)fprintf(stderr, "%s: out of space.\n", progname);
                    141:                exit(1);
                    142:        }
                    143:
                    144:        myuid = getuid();
                    145:
                    146:        /* copy the umask for explicit mode setting */
                    147:        myumask = umask(0);
                    148:        (void)umask(myumask);
                    149:
                    150:        /* consume last argument first. */
                    151:        if (!path_set(&to, argv[--argc]))
                    152:                exit(1);
                    153:
                    154:        statfcn = symfollow || !rflag ? stat : lstat;
                    155:
                    156:        /*
                    157:         * Cp has two distinct cases:
                    158:         *
                    159:         * % cp [-rip] source target
                    160:         * % cp [-rip] source1 ... directory
                    161:         *
                    162:         * In both cases, source can be either a file or a directory.
                    163:         *
                    164:         * In (1), the target becomes a copy of the source. That is, if the
                    165:         * source is a file, the target will be a file, and likewise for
                    166:         * directories.
                    167:         *
                    168:         * In (2), the real target is not directory, but "directory/source".
                    169:         */
                    170:
                    171:        r = stat(to.p_path, &to_stat);
                    172:        if (r == -1 && errno != ENOENT) {
                    173:                error(to.p_path);
                    174:                exit(1);
                    175:        }
                    176:        if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
                    177:                /*
                    178:                 * Case (1).  Target is not a directory.
                    179:                 */
                    180:                if (argc > 1) {
                    181:                        usage();
                    182:                        exit(1);
                    183:                }
                    184:                if (!path_set(&from, *argv))
                    185:                        exit(1);
                    186:                copy();
                    187:        }
                    188:        else {
                    189:                /*
                    190:                 * Case (2).  Target is a directory.
                    191:                 */
                    192:                for (;; ++argv) {
                    193:                        if (!path_set(&from, *argv)) {
                    194:                                exit_val = 1;
                    195:                                continue;
                    196:                        }
                    197:                        old_to = path_append(&to, path_basename(&from), -1);
                    198:                        if (!old_to) {
                    199:                                exit_val = 1;
                    200:                                continue;
                    201:                        }
                    202:                        copy();
                    203:                        if (!--argc)
                    204:                                break;
                    205:                        path_restore(&to, old_to);
                    206:                }
                    207:        }
                    208:        exit(exit_val);
                    209: }
                    210:
                    211: /* copy file or directory at "from" to "to". */
                    212: copy()
                    213: {
                    214:        struct stat from_stat, to_stat;
                    215:        int dne, statval;
                    216:
                    217:        statval = statfcn(from.p_path, &from_stat);
                    218:        if (statval == -1) {
                    219:                error(from.p_path);
                    220:                return;
                    221:        }
                    222:
                    223:        /* not an error, but need to remember it happened */
                    224:        if (stat(to.p_path, &to_stat) == -1)
                    225:                dne = 1;
                    226:        else {
                    227:                if (to_stat.st_dev == from_stat.st_dev &&
                    228:                    to_stat.st_ino == from_stat.st_ino) {
                    229:                        (void)fprintf(stderr,
                    230:                            "%s: %s and %s are identical (not copied).\n",
                    231:                            progname, to.p_path, from.p_path);
                    232:                        exit_val = 1;
                    233:                        return;
                    234:                }
                    235:                dne = 0;
                    236:        }
                    237:
                    238:        switch(from_stat.st_mode & S_IFMT) {
                    239:        case S_IFLNK:
                    240:                copy_link(!dne);
                    241:                return;
                    242:        case S_IFDIR:
                    243:                if (!rflag && !orflag) {
                    244:                        (void)fprintf(stderr,
                    245:                            "%s: %s is a directory (not copied).\n",
                    246:                            progname, from.p_path);
                    247:                        exit_val = 1;
                    248:                        return;
                    249:                }
                    250:                if (dne) {
                    251:                        /*
                    252:                         * If the directory doesn't exist, create the new
                    253:                         * one with the from file mode plus owner RWX bits,
                    254:                         * modified by the umask.  Trade-off between being
                    255:                         * able to write the directory (if from directory is
                    256:                         * 555) and not causing a permissions race.  If the
                    257:                         * umask blocks owner writes cp fails.
                    258:                         */
                    259:                        if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
                    260:                                error(to.p_path);
                    261:                                return;
                    262:                        }
                    263:                }
                    264:                else if (!S_ISDIR(to_stat.st_mode) != S_IFDIR) {
                    265:                        (void)fprintf(stderr, "%s: %s: not a directory.\n",
                    266:                            progname, to.p_path);
                    267:                        return;
                    268:                }
                    269:                copy_dir();
                    270:                /*
                    271:                 * If not -p and directory didn't exist, set it to be the
                    272:                 * same as the from directory, umodified by the umask;
                    273:                 * arguably wrong, but it's been that way forever.
                    274:                 */
                    275:                if (pflag)
                    276:                        setfile(&from_stat, 0);
                    277:                else if (dne)
                    278:                        (void)chmod(to.p_path, from_stat.st_mode);
                    279:                return;
                    280:        case S_IFCHR:
                    281:        case S_IFBLK:
                    282:                if (rflag) {
                    283:                        copy_special(&from_stat, !dne);
                    284:                        return;
                    285:                }
                    286:                break;
                    287:        case S_IFIFO:
                    288:                if (rflag) {
                    289:                        copy_fifo(&from_stat, !dne);
                    290:                        return;
                    291:                }
                    292:                break;
                    293:        }
                    294:        copy_file(&from_stat, dne);
                    295: }
                    296:
                    297: copy_file(fs, dne)
                    298:        struct stat *fs;
                    299:        int dne;
                    300: {
                    301:        register int from_fd, to_fd, rcount, wcount;
                    302:        struct stat to_stat;
                    303:
                    304:        if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
                    305:                error(from.p_path);
                    306:                return;
                    307:        }
                    308:
                    309:        /*
                    310:         * If the file exists and we're interactive, verify with the user.
                    311:         * If the file DNE, set the mode to be the from file, minus setuid
                    312:         * bits, modified by the umask; arguably wrong, but it makes copying
                    313:         * executables work right and it's been that way forever.  (The
                    314:         * other choice is 666 or'ed with the execute bits on the from file
                    315:         * modified by the umask.)
                    316:         */
                    317:        if (!dne) {
                    318:                if (iflag) {
                    319:                        int checkch, ch;
                    320:
                    321:                        (void)fprintf(stderr, "overwrite %s? ", to.p_path);
                    322:                        checkch = ch = getchar();
                    323:                        while (ch != '\n' && ch != EOF)
                    324:                                ch = getchar();
                    325:                        if (checkch != 'y') {
                    326:                                (void)close(from_fd);
                    327:                                return;
                    328:                        }
                    329:                }
                    330:                to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
                    331:        } else
                    332:                to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
                    333:                    fs->st_mode & ~(S_ISUID|S_ISGID));
                    334:
                    335:        if (to_fd == -1) {
                    336:                error(to.p_path);
                    337:                (void)close(from_fd);
                    338:                return;
                    339:        }
                    340:
                    341:        while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
                    342:                wcount = write(to_fd, buf, rcount);
                    343:                if (rcount != wcount || wcount == -1) {
                    344:                        error(to.p_path);
                    345:                        break;
                    346:                }
                    347:        }
                    348:        if (rcount < 0)
                    349:                error(from.p_path);
                    350:        if (pflag)
                    351:                setfile(fs, to_fd);
                    352:        /*
                    353:         * If the source was setuid or setgid, lose the bits unless the
                    354:         * copy is owned by the same user and group.
                    355:         */
                    356:        else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
                    357:                if (fstat(to_fd, &to_stat))
                    358:                        error(to.p_path);
                    359: #define        RETAINBITS      (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
                    360:                else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
                    361:                    fs->st_mode & RETAINBITS & ~myumask))
                    362:                        error(to.p_path);
                    363:        (void)close(from_fd);
                    364:        if (close(to_fd))
                    365:                error(to.p_path);
                    366: }
                    367:
                    368: copy_dir()
                    369: {
                    370:        struct stat from_stat;
                    371:        struct dirent *dp, **dir_list;
                    372:        register int dir_cnt, i;
                    373:        char *old_from, *old_to;
                    374:
                    375:        dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
                    376:        if (dir_cnt == -1) {
                    377:                (void)fprintf(stderr, "%s: can't read directory %s.\n",
                    378:                    progname, from.p_path);
                    379:                exit_val = 1;
                    380:        }
                    381:
                    382:        /*
                    383:         * Instead of handling directory entries in the order they appear
                    384:         * on disk, do non-directory files before directory files.
                    385:         * There are two reasons to do directories last.  The first is
                    386:         * efficiency.  Files tend to be in the same cylinder group as
                    387:         * their parent, whereas directories tend not to be.  Copying files
                    388:         * all at once reduces seeking.  Second, deeply nested tree's
                    389:         * could use up all the file descriptors if we didn't close one
                    390:         * directory before recursivly starting on the next.
                    391:         */
                    392:        /* copy files */
                    393:        for (i = 0; i < dir_cnt; ++i) {
                    394:                dp = dir_list[i];
                    395:                if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
                    396:                    && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
                    397:                        goto done;
                    398:                old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
                    399:                if (!old_from) {
                    400:                        exit_val = 1;
                    401:                        goto done;
                    402:                }
                    403:
                    404:                if (statfcn(from.p_path, &from_stat) < 0) {
                    405:                        error(dp->d_name);
                    406:                        path_restore(&from, old_from);
                    407:                        goto done;
                    408:                }
                    409:                if (S_ISDIR(from_stat.st_mode)) {
                    410:                        path_restore(&from, old_from);
                    411:                        continue;
                    412:                }
                    413:                old_to = path_append(&to, dp->d_name, (int)dp->d_namlen);
                    414:                if (old_to) {
                    415:                        copy();
                    416:                        path_restore(&to, old_to);
                    417:                } else
                    418:                        exit_val = 1;
                    419:                path_restore(&from, old_from);
                    420: done:          dir_list[i] = NULL;
                    421:                (void)free((void *)dp);
                    422:        }
                    423:
                    424:        /* copy directories */
                    425:        for (i = 0; i < dir_cnt; ++i) {
                    426:                dp = dir_list[i];
                    427:                if (!dp)
                    428:                        continue;
                    429:                old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
                    430:                if (!old_from) {
                    431:                        exit_val = 1;
                    432:                        (void)free((void *)dp);
                    433:                        continue;
                    434:                }
                    435:                old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
                    436:                if (!old_to) {
                    437:                        exit_val = 1;
                    438:                        (void)free((void *)dp);
                    439:                        path_restore(&from, old_from);
                    440:                        continue;
                    441:                }
                    442:                copy();
                    443:                free((void *)dp);
                    444:                path_restore(&from, old_from);
                    445:                path_restore(&to, old_to);
                    446:        }
                    447:        free((void *)dir_list);
                    448: }
                    449:
                    450: copy_link(exists)
                    451:        int exists;
                    452: {
                    453:        int len;
                    454:        char link[MAXPATHLEN];
                    455:
                    456:        if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
                    457:                error(from.p_path);
                    458:                return;
                    459:        }
                    460:        link[len] = '\0';
                    461:        if (exists && unlink(to.p_path)) {
                    462:                error(to.p_path);
                    463:                return;
                    464:        }
                    465:        if (symlink(link, to.p_path)) {
                    466:                error(link);
                    467:                return;
                    468:        }
                    469: }
                    470:
                    471: copy_fifo(from_stat, exists)
                    472:        struct stat *from_stat;
                    473:        int exists;
                    474: {
                    475:        if (exists && unlink(to.p_path)) {
                    476:                error(to.p_path);
                    477:                return;
                    478:        }
                    479:        if (mkfifo(to.p_path, from_stat->st_mode)) {
                    480:                error(to.p_path);
                    481:                return;
                    482:        }
                    483:        if (pflag)
                    484:                setfile(from_stat, 0);
                    485: }
                    486:
                    487: copy_special(from_stat, exists)
                    488:        struct stat *from_stat;
                    489:        int exists;
                    490: {
                    491:        if (exists && unlink(to.p_path)) {
                    492:                error(to.p_path);
                    493:                return;
                    494:        }
                    495:        if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
                    496:                error(to.p_path);
                    497:                return;
                    498:        }
                    499:        if (pflag)
                    500:                setfile(from_stat, 0);
                    501: }
                    502:
                    503: setfile(fs, fd)
                    504:        register struct stat *fs;
                    505:        int fd;
                    506: {
                    507:        static struct timeval tv[2];
                    508:        char path[100];
                    509:
                    510:        fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
                    511:
                    512:        tv[0].tv_sec = fs->st_atime;
                    513:        tv[1].tv_sec = fs->st_mtime;
                    514:        if (utimes(to.p_path, tv)) {
                    515:                (void)snprintf(path, sizeof(path), "utimes: %s", to.p_path);
                    516:                error(path);
                    517:        }
                    518:        /*
                    519:         * Changing the ownership probably won't succeed, unless we're root
                    520:         * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
                    521:         * the mode; current BSD behavior is to remove all setuid bits on
                    522:         * chown.  If chown fails, lose setuid/setgid bits.
                    523:         */
                    524:        if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
                    525:            chown(to.p_path, fs->st_uid, fs->st_gid)) {
                    526:                if (errno != EPERM) {
                    527:                        (void)snprintf(path, sizeof(path),
                    528:                            "chown: %s", to.p_path);
                    529:                        error(path);
                    530:                }
                    531:                fs->st_mode &= ~(S_ISUID|S_ISGID);
                    532:        }
                    533:        if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
                    534:                (void)snprintf(path, sizeof(path), "chown: %s", to.p_path);
                    535:                error(path);
                    536:        }
                    537: }
                    538:
                    539: error(s)
                    540:        char *s;
                    541: {
                    542:        exit_val = 1;
                    543:        (void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno));
                    544: }
                    545:
                    546: usage()
                    547: {
                    548:        (void)fprintf(stderr,
                    549: "usage: cp [-Rfhip] src target;\n   or: cp [-Rfhip] src1 ... srcN directory\n");
                    550:        exit(1);
                    551: }

CVSweb <webmaster@jp.NetBSD.org>