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

1.1       cgd         1: /*
1.10    ! mycroft     2:  * Copyright (c) 1988, 1993, 1994
        !             3:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         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
1.10    ! mycroft    38: static char copyright[] =
        !            39: "@(#) Copyright (c) 1988, 1993, 1994\n\
        !            40:        The Regents of the University of California.  All rights reserved.\n";
1.1       cgd        41: #endif /* not lint */
                     42:
                     43: #ifndef lint
1.10    ! mycroft    44: /*static char sccsid[] = "from: @(#)cp.c       8.2 (Berkeley) 4/1/94";*/
        !            45: static char *rcsid = "$Id: $";
1.1       cgd        46: #endif /* not lint */
                     47:
                     48: /*
1.10    ! mycroft    49:  * Cp copies source files to target files.
1.1       cgd        50:  *
1.10    ! mycroft    51:  * The global PATH_T structure "to" always contains the path to the
        !            52:  * current target file.  Since fts(3) does not change directories,
        !            53:  * this path can be either absolute or dot-relative.
1.1       cgd        54:  *
1.10    ! mycroft    55:  * The basic algorithm is to initialize "to" and use fts(3) to traverse
        !            56:  * the file hierarchy rooted in the argument list.  A trivial case is the
        !            57:  * case of 'cp file1 file2'.  The more interesting case is the case of
        !            58:  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
        !            59:  * path (relative to the root of the traversal) is appended to dir (stored
        !            60:  * in "to") to form the final target path.
1.1       cgd        61:  */
                     62:
                     63: #include <sys/param.h>
                     64: #include <sys/stat.h>
1.5       mycroft    65: #include <sys/mman.h>
1.1       cgd        66: #include <sys/time.h>
1.10    ! mycroft    67:
1.1       cgd        68: #include <dirent.h>
1.10    ! mycroft    69: #include <err.h>
        !            70: #include <errno.h>
1.1       cgd        71: #include <fcntl.h>
1.10    ! mycroft    72: #include <fts.h>
1.1       cgd        73: #include <stdio.h>
                     74: #include <stdlib.h>
                     75: #include <string.h>
1.10    ! mycroft    76: #include <unistd.h>
        !            77:
1.5       mycroft    78: #include "extern.h"
1.1       cgd        79:
1.10    ! mycroft    80: #define        STRIP_TRAILING_SLASH(p) {                                       \
        !            81:         while ((p).p_end > (p).p_path && (p).p_end[-1] == '/')         \
        !            82:                 *--(p).p_end = 0;                                      \
        !            83: }
1.5       mycroft    84:
1.1       cgd        85: PATH_T to = { to.p_path, "" };
                     86:
                     87: uid_t myuid;
1.10    ! mycroft    88: int Rflag, iflag, pflag, rflag;
        !            89: int myumask;
        !            90:
        !            91: enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
1.1       cgd        92:
1.10    ! mycroft    93: int copy __P((char *[], enum op, int));
        !            94: int mastercmp __P((const FTSENT **, const FTSENT **));
        !            95:
        !            96: int
1.1       cgd        97: main(argc, argv)
                     98:        int argc;
1.10    ! mycroft    99:        char *argv[];
1.1       cgd       100: {
1.10    ! mycroft   101:        struct stat to_stat, tmp_stat;
        !           102:        enum op type;
        !           103:        int Hflag, Lflag, Pflag, ch, fts_options, r;
        !           104:        char *target;
        !           105:
        !           106:        Hflag = Lflag = Pflag = Rflag = 0;
        !           107:        while ((ch = getopt(argc, argv, "HLPRfipr")) != EOF)
        !           108:                switch (ch) {
        !           109:                case 'H':
        !           110:                        Hflag = 1;
        !           111:                        Lflag = Pflag = 0;
        !           112:                        break;
        !           113:                case 'L':
        !           114:                        Lflag = 1;
        !           115:                        Hflag = Pflag = 0;
        !           116:                        break;
        !           117:                case 'P':
        !           118:                        Pflag = 1;
        !           119:                        Hflag = Lflag = 0;
        !           120:                        break;
        !           121:                case 'R':
        !           122:                        Rflag = 1;
        !           123:                        break;
1.1       cgd       124:                case 'f':
                    125:                        iflag = 0;
                    126:                        break;
                    127:                case 'i':
                    128:                        iflag = isatty(fileno(stdin));
                    129:                        break;
                    130:                case 'p':
                    131:                        pflag = 1;
                    132:                        break;
1.10    ! mycroft   133:                case 'r':
1.1       cgd       134:                        rflag = 1;
                    135:                        break;
                    136:                case '?':
                    137:                default:
                    138:                        usage();
                    139:                        break;
                    140:                }
                    141:        argc -= optind;
                    142:        argv += optind;
                    143:
                    144:        if (argc < 2)
                    145:                usage();
                    146:
1.10    ! mycroft   147:        fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
        !           148:        if (rflag) {
        !           149:                if (Rflag)
        !           150:                        errx(1,
        !           151:                    "the -R and -r options may not be specified together.");
        !           152:                if (Hflag || Lflag || Pflag)
        !           153:                        errx(1,
        !           154:        "the -H, -L, and -P options may not be specified with the -r option.");
        !           155:                fts_options &= ~FTS_PHYSICAL;
        !           156:                fts_options |= FTS_LOGICAL;
        !           157:        }
        !           158:        if (Rflag) {
        !           159:                if (Hflag)
        !           160:                        fts_options |= FTS_COMFOLLOW;
        !           161:                if (Lflag) {
        !           162:                        fts_options &= ~FTS_PHYSICAL;
        !           163:                        fts_options |= FTS_LOGICAL;
        !           164:                }
        !           165:        } else {
        !           166:                fts_options &= ~FTS_PHYSICAL;
        !           167:                fts_options |= FTS_LOGICAL;
1.1       cgd       168:        }
                    169:
                    170:        myuid = getuid();
                    171:
1.10    ! mycroft   172:        /* Copy the umask for explicit mode setting. */
1.1       cgd       173:        myumask = umask(0);
                    174:        (void)umask(myumask);
                    175:
1.10    ! mycroft   176:        /* Save the target base in "to". */
        !           177:        target = argv[--argc];
        !           178:        if (strlen(target) > MAXPATHLEN)
        !           179:                errx(1, "%s: name too long", target);
        !           180:        (void)strcpy(to.p_path, target);
        !           181:        to.p_end = to.p_path + strlen(to.p_path);
        !           182:         if (to.p_path == to.p_end) {
        !           183:                *to.p_end++ = '.';
        !           184:                *to.p_end = 0;
        !           185:        }
        !           186:         STRIP_TRAILING_SLASH(to);
        !           187:        to.target_end = to.p_end;
        !           188:
        !           189:        /* Set end of argument list for fts(3). */
        !           190:        argv[argc] = NULL;
        !           191:
1.1       cgd       192:        /*
                    193:         * Cp has two distinct cases:
                    194:         *
1.10    ! mycroft   195:         * cp [-R] source target
        !           196:         * cp [-R] source1 ... sourceN directory
1.1       cgd       197:         *
                    198:         * In both cases, source can be either a file or a directory.
                    199:         *
                    200:         * In (1), the target becomes a copy of the source. That is, if the
                    201:         * source is a file, the target will be a file, and likewise for
                    202:         * directories.
                    203:         *
                    204:         * In (2), the real target is not directory, but "directory/source".
                    205:         */
                    206:        r = stat(to.p_path, &to_stat);
1.10    ! mycroft   207:        if (r == -1 && errno != ENOENT)
        !           208:                err(1, "%s", to.p_path);
1.1       cgd       209:        if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
                    210:                /*
                    211:                 * Case (1).  Target is not a directory.
1.10    ! mycroft   212:                 */
1.1       cgd       213:                if (argc > 1) {
                    214:                        usage();
                    215:                        exit(1);
                    216:                }
1.10    ! mycroft   217:                /*
        !           218:                 * Need to detect the case:
        !           219:                 *      cp -R dir foo
        !           220:                 * Where dir is a directory and foo does not exist, where
        !           221:                 * we want pathname concatenations turned on but not for
        !           222:                 * the initial mkdir().
        !           223:                 */
        !           224:                if (r == -1) {
        !           225:                        if (rflag || (Rflag && (Lflag || Hflag)))
        !           226:                                stat(*argv, &tmp_stat);
        !           227:                        else
        !           228:                                lstat(*argv, &tmp_stat);
        !           229:
        !           230:                        if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
        !           231:                                type = DIR_TO_DNE;
        !           232:                        else
        !           233:                                type = FILE_TO_FILE;
        !           234:                } else
        !           235:                        type = FILE_TO_FILE;
        !           236:        } else
1.1       cgd       237:                /*
                    238:                 * Case (2).  Target is a directory.
                    239:                 */
1.10    ! mycroft   240:                type = FILE_TO_DIR;
        !           241:
        !           242:        exit (copy(argv, type, fts_options));
1.1       cgd       243: }
                    244:
1.10    ! mycroft   245: int
        !           246: copy(argv, type, fts_options)
        !           247:        char *argv[];
        !           248:        enum op type;
        !           249:        int fts_options;
1.1       cgd       250: {
1.10    ! mycroft   251:        struct stat to_stat;
        !           252:        FTS *ftsp;
        !           253:        FTSENT *curr;
        !           254:        int base, dne, nlen, rval;
        !           255:        char *p;
1.1       cgd       256:
1.10    ! mycroft   257:        if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
        !           258:                err(1, NULL);
        !           259:        for (rval = 0; (curr = fts_read(ftsp)) != NULL;) {
        !           260:                switch (curr->fts_info) {
        !           261:                case FTS_NS:
        !           262:                case FTS_ERR:
        !           263:                        warnx("%s: %s",
        !           264:                            curr->fts_path, strerror(curr->fts_errno));
        !           265:                        rval = 1;
        !           266:                        continue;
        !           267:                case FTS_DC:                    /* Warn, continue. */
        !           268:                        warnx("%s: directory causes a cycle", curr->fts_path);
        !           269:                        rval = 1;
        !           270:                        continue;
        !           271:                case FTS_DP:                    /* Ignore, continue. */
        !           272:                        continue;
        !           273:                }
        !           274:
        !           275:                /*
        !           276:                 * If we are in case (2) or (3) above, we need to append the
        !           277:                  * source name to the target name.
        !           278:                  */
        !           279:                if (type != FILE_TO_FILE) {
        !           280:                        if ((curr->fts_namelen +
        !           281:                            to.target_end - to.p_path + 1) > MAXPATHLEN) {
        !           282:                                warnx("%s/%s: name too long (not copied)",
        !           283:                                    to.p_path, curr->fts_name);
        !           284:                                rval = 1;
        !           285:                                continue;
        !           286:                        }
1.1       cgd       287:
1.10    ! mycroft   288:                        /*
        !           289:                         * Need to remember the roots of traversals to create
        !           290:                         * correct pathnames.  If there's a directory being
        !           291:                         * copied to a non-existent directory, e.g.
        !           292:                         *      cp -R a/dir noexist
        !           293:                         * the resulting path name should be noexist/foo, not
        !           294:                         * noexist/dir/foo (where foo is a file in dir), which
        !           295:                         * is the case where the target exists.
        !           296:                         *
        !           297:                         * Also, check for "..".  This is for correct path
        !           298:                         * concatentation for paths ending in "..", e.g.
        !           299:                         *      cp -R .. /tmp
        !           300:                         * Paths ending in ".." are changed to ".".  This is
        !           301:                         * tricky, but seems the easiest way to fix the problem.
        !           302:                         *
        !           303:                         * XXX
        !           304:                         * Since the first level MUST be FTS_ROOTLEVEL, base
        !           305:                         * is always initialized.
        !           306:                         */
        !           307:                        if (curr->fts_level == FTS_ROOTLEVEL)
        !           308:                                if (type != DIR_TO_DNE) {
        !           309:                                        p = strrchr(curr->fts_path, '/');
        !           310:                                        base = (p == NULL) ? 0 :
        !           311:                                            (int)(p - curr->fts_path + 1);
        !           312:
        !           313:                                        if (!strcmp(&curr->fts_path[base],
        !           314:                                            ".."))
        !           315:                                                base += 1;
        !           316:                                } else
        !           317:                                        base = curr->fts_pathlen;
        !           318:
        !           319:                        if (to.target_end[-1] != '/') {
        !           320:                                *to.target_end = '/';
        !           321:                                *(to.target_end + 1) = 0;
        !           322:                        }
        !           323:                        p = &curr->fts_path[base];
        !           324:                        nlen = curr->fts_pathlen - base;
1.8       jtc       325:
1.10    ! mycroft   326:                        (void)strncat(to.target_end + 1, p, nlen);
        !           327:                        to.p_end = to.target_end + nlen + 1;
        !           328:                        *to.p_end = 0;
        !           329:                        STRIP_TRAILING_SLASH(to);
        !           330:                }
        !           331:
        !           332:                /* Not an error but need to remember it happened */
        !           333:                if (stat(to.p_path, &to_stat) == -1)
        !           334:                        dne = 1;
        !           335:                else {
        !           336:                        if (to_stat.st_dev == curr->fts_statp->st_dev &&
        !           337:                            to_stat.st_ino == curr->fts_statp->st_ino) {
        !           338:                                warnx("%s and %s are identical (not copied).",
        !           339:                                    to.p_path, curr->fts_path);
        !           340:                                rval = 1;
        !           341:                                if (S_ISDIR(curr->fts_statp->st_mode))
        !           342:                                        (void)fts_set(ftsp, curr, FTS_SKIP);
        !           343:                                continue;
        !           344:                        }
        !           345:                        if (!S_ISDIR(curr->fts_statp->st_mode) &&
        !           346:                            S_ISDIR(to_stat.st_mode)) {
        !           347:                                warnx("cannot overwrite directory %s with non-directory %s.",
        !           348:                                    to.p_path, curr->fts_path);
        !           349:                                rval = 1;
        !           350:                                continue;
        !           351:                        }
        !           352:                        dne = 0;
1.1       cgd       353:                }
                    354:
1.10    ! mycroft   355:                switch (curr->fts_statp->st_mode & S_IFMT) {
        !           356:                case S_IFLNK:
        !           357:                        if (copy_link(curr, !dne))
        !           358:                                rval = 1;
        !           359:                        break;
        !           360:                case S_IFDIR:
        !           361:                        if (!Rflag && !rflag) {
        !           362:                                warnx("%s is a directory (not copied).",
        !           363:                                    curr->fts_path);
        !           364:                                (void)fts_set(ftsp, curr, FTS_SKIP);
        !           365:                                rval = 1;
        !           366:                                break;
        !           367:                        }
1.1       cgd       368:                        /*
                    369:                         * If the directory doesn't exist, create the new
                    370:                         * one with the from file mode plus owner RWX bits,
                    371:                         * modified by the umask.  Trade-off between being
                    372:                         * able to write the directory (if from directory is
                    373:                         * 555) and not causing a permissions race.  If the
1.10    ! mycroft   374:                         * umask blocks owner writes, we fail..
1.1       cgd       375:                         */
1.10    ! mycroft   376:                        if (dne) {
        !           377:                                if (mkdir(to.p_path,
        !           378:                                    curr->fts_statp->st_mode | S_IRWXU) < 0)
        !           379:                                        err(1, "%s", to.p_path);
        !           380:                        } else if (!S_ISDIR(to_stat.st_mode)) {
        !           381:                                errno = ENOTDIR;
        !           382:                                err(1, "%s: %s", to.p_path);
1.1       cgd       383:                        }
1.10    ! mycroft   384:                        /*
        !           385:                         * If not -p and directory didn't exist, set it to be
        !           386:                         * the same as the from directory, umodified by the
        !           387:                          * umask; arguably wrong, but it's been that way
        !           388:                          * forever.
        !           389:                         */
        !           390:                        if (pflag && setfile(curr->fts_statp, 0))
        !           391:                                rval = 1;
        !           392:                        else if (dne)
        !           393:                                (void)chmod(to.p_path,
        !           394:                                    curr->fts_statp->st_mode);
        !           395:                        break;
        !           396:                case S_IFBLK:
        !           397:                case S_IFCHR:
        !           398:                        if (Rflag) {
        !           399:                                if (copy_special(curr->fts_statp, !dne))
        !           400:                                        rval = 1;
        !           401:                        } else
        !           402:                                if (copy_file(curr, dne))
        !           403:                                        rval = 1;
        !           404:                        break;
        !           405:                case S_IFIFO:
        !           406:                        if (Rflag)
        !           407:                                if (copy_fifo(curr->fts_statp, !dne))
        !           408:                                        rval = 1;
        !           409:                        else
        !           410:                                if (copy_file(curr, dne))
        !           411:                                        rval = 1;
        !           412:                        break;
        !           413:                default:
        !           414:                        if (copy_file(curr, dne))
        !           415:                                rval = 1;
        !           416:                        break;
1.1       cgd       417:                }
                    418:        }
1.10    ! mycroft   419:        if (errno)
        !           420:                err(1, "fts_read");
        !           421:        return (rval);
1.1       cgd       422: }
                    423:
1.10    ! mycroft   424: /*
        !           425:  * mastercmp --
        !           426:  *     The comparison function for the copy order.  The order is to copy
        !           427:  *     non-directory files before directory files.  The reason for this
        !           428:  *     is because files tend to be in the same cylinder group as their
        !           429:  *     parent directory, whereas directories tend not to be.  Copying the
        !           430:  *     files first reduces seeking.
        !           431:  */
        !           432: int
        !           433: mastercmp(a, b)
        !           434:        const FTSENT **a, **b;
1.1       cgd       435: {
1.10    ! mycroft   436:        int a_info, b_info;
1.1       cgd       437:
1.10    ! mycroft   438:        a_info = (*a)->fts_info;
        !           439:        if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
        !           440:                return (0);
        !           441:        b_info = (*b)->fts_info;
        !           442:        if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
        !           443:                return (0);
        !           444:        if (a_info == FTS_D)
        !           445:                return (-1);
        !           446:        if (b_info == FTS_D)
        !           447:                return (1);
        !           448:        return (0);
1.1       cgd       449: }

CVSweb <webmaster@jp.NetBSD.org>