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>