|
|
1.1 ! root 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 are permitted provided ! 9: * that: (1) source distributions retain this entire copyright notice and ! 10: * comment, and (2) distributions including binaries display the following ! 11: * acknowledgement: ``This product includes software developed by the ! 12: * University of California, Berkeley and its contributors'' in the ! 13: * documentation or other materials provided with the distribution and in ! 14: * all advertising materials mentioning features or use of this software. ! 15: * Neither the name of the University nor the names of its contributors may ! 16: * be used to endorse or promote products derived from this software without ! 17: * specific prior written permission. ! 18: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 19: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 20: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 21: */ ! 22: ! 23: #ifndef lint ! 24: char copyright[] = ! 25: "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ ! 26: All rights reserved.\n"; ! 27: #endif /* not lint */ ! 28: ! 29: #ifndef lint ! 30: static char sccsid[] = "@(#)cp.c 5.20 (Berkeley) 6/29/90"; ! 31: #endif /* not lint */ ! 32: ! 33: /* ! 34: * cp copies source files to target files. ! 35: * ! 36: * The global PATH_T structures "to" and "from" always contain paths to the ! 37: * current source and target files, respectively. Since cp does not change ! 38: * directories, these paths can be either absolute or dot-realative. ! 39: * ! 40: * The basic algorithm is to initialize "to" and "from", and then call the ! 41: * recursive copy() function to do the actual work. If "from" is a file, ! 42: * copy copies the data. If "from" is a directory, copy creates the ! 43: * corresponding "to" directory, and calls itself recursively on all of ! 44: * the entries in the "from" directory. ! 45: */ ! 46: ! 47: #include <sys/param.h> ! 48: #include <sys/stat.h> ! 49: #include <sys/file.h> ! 50: #include <sys/dir.h> ! 51: #include <sys/time.h> ! 52: #include <stdio.h> ! 53: #include <errno.h> ! 54: #include <stdlib.h> ! 55: #include <string.h> ! 56: ! 57: #define type(st) ((st).st_mode & S_IFMT) ! 58: ! 59: typedef struct { ! 60: char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path. */ ! 61: char *p_end; /* pointer to NULL at end of path. */ ! 62: } PATH_T; ! 63: ! 64: PATH_T from = { "", from.p_path }; ! 65: PATH_T to = { "", to.p_path }; ! 66: ! 67: uid_t myuid; ! 68: int exit_val, myumask; ! 69: int iflag, pflag, orflag, rflag; ! 70: int (*statfcn)(); ! 71: char *buf, *pname; ! 72: char *path_append(), *path_basename(); ! 73: ! 74: main(argc, argv) ! 75: int argc; ! 76: char **argv; ! 77: { ! 78: extern int optind; ! 79: struct stat to_stat; ! 80: register int c, r; ! 81: int symfollow, lstat(), stat(); ! 82: char *old_to, *p; ! 83: ! 84: /* ! 85: * cp is used by mv(1) -- except for usage statements, print ! 86: * the "called as" program name. ! 87: */ ! 88: pname = (p = rindex(*argv,'/')) ? ++p : *argv; ! 89: ! 90: symfollow = 0; ! 91: while ((c = getopt(argc, argv, "Rfhipr")) != EOF) { ! 92: switch ((char)c) { ! 93: case 'f': ! 94: iflag = 0; ! 95: break; ! 96: case 'h': ! 97: symfollow = 1; ! 98: break; ! 99: case 'i': ! 100: iflag = isatty(fileno(stdin)); ! 101: break; ! 102: case 'p': ! 103: pflag = 1; ! 104: break; ! 105: case 'R': ! 106: rflag = 1; ! 107: break; ! 108: case 'r': ! 109: orflag = 1; ! 110: break; ! 111: case '?': ! 112: default: ! 113: usage(); ! 114: break; ! 115: } ! 116: } ! 117: argc -= optind; ! 118: argv += optind; ! 119: ! 120: if (argc < 2) ! 121: usage(); ! 122: ! 123: if (rflag && orflag) { ! 124: (void)fprintf(stderr, ! 125: "cp: the -R and -r options are mutually exclusive.\n"); ! 126: exit(1); ! 127: } ! 128: ! 129: buf = (char *)malloc(MAXBSIZE); ! 130: if (!buf) { ! 131: (void)fprintf(stderr, "%s: out of space.\n", pname); ! 132: exit(1); ! 133: } ! 134: ! 135: myuid = getuid(); ! 136: ! 137: /* copy the umask for explicit mode setting */ ! 138: myumask = umask(0); ! 139: (void)umask(myumask); ! 140: ! 141: /* consume last argument first. */ ! 142: if (!path_set(&to, argv[--argc])) ! 143: exit(exit_val); ! 144: ! 145: statfcn = symfollow || !rflag ? stat : lstat; ! 146: ! 147: /* ! 148: * Cp has two distinct cases: ! 149: * ! 150: * Case (1) $ cp [-rip] source target ! 151: * ! 152: * Case (2) $ cp [-rip] source1 ... directory ! 153: * ! 154: * In both cases, source can be either a file or a directory. ! 155: * ! 156: * In (1), the target becomes a copy of the source. That is, if the ! 157: * source is a file, the target will be a file, and likewise for ! 158: * directories. ! 159: * ! 160: * In (2), the real target is not directory, but "directory/source". ! 161: */ ! 162: ! 163: r = stat(to.p_path, &to_stat); ! 164: if (r == -1 && errno != ENOENT) { ! 165: error(to.p_path); ! 166: exit(1); ! 167: } ! 168: if (r == -1 || type(to_stat) != S_IFDIR) { ! 169: /* ! 170: * Case (1). Target is not a directory. ! 171: */ ! 172: if (argc > 1) { ! 173: usage(); ! 174: exit(1); ! 175: } ! 176: if (!path_set(&from, *argv)) ! 177: exit(exit_val); ! 178: copy(); ! 179: } ! 180: else { ! 181: /* ! 182: * Case (2). Target is a directory. ! 183: */ ! 184: for (;; ++argv) { ! 185: if (!path_set(&from, *argv)) ! 186: continue; ! 187: old_to = path_append(&to, path_basename(&from), -1); ! 188: if (!old_to) ! 189: continue; ! 190: copy(); ! 191: if (!--argc) ! 192: break; ! 193: path_restore(&to, old_to); ! 194: } ! 195: } ! 196: exit(exit_val); ! 197: } ! 198: ! 199: /* copy file or directory at "from" to "to". */ ! 200: copy() ! 201: { ! 202: struct stat from_stat, to_stat; ! 203: int dne, statval; ! 204: ! 205: statval = statfcn(from.p_path, &from_stat); ! 206: if (statval == -1) { ! 207: error(from.p_path); ! 208: return; ! 209: } ! 210: ! 211: /* not an error, but need to remember it happened */ ! 212: if (stat(to.p_path, &to_stat) == -1) ! 213: dne = 1; ! 214: else { ! 215: if (to_stat.st_dev == from_stat.st_dev && ! 216: to_stat.st_ino == from_stat.st_ino) { ! 217: (void)fprintf(stderr, ! 218: "%s: %s and %s are identical (not copied).\n", ! 219: pname, to.p_path, from.p_path); ! 220: exit_val = 1; ! 221: return; ! 222: } ! 223: dne = 0; ! 224: } ! 225: ! 226: switch(type(from_stat)) { ! 227: case S_IFLNK: ! 228: copy_link(!dne); ! 229: return; ! 230: case S_IFDIR: ! 231: if (!rflag && !orflag) { ! 232: (void)fprintf(stderr, ! 233: "%s: %s is a directory (not copied).\n", ! 234: pname, from.p_path); ! 235: exit_val = 1; ! 236: return; ! 237: } ! 238: if (dne) { ! 239: /* ! 240: * If the directory doesn't exist, create the new ! 241: * one with the from file mode plus owner RWX bits, ! 242: * modified by the umask. Trade-off between being ! 243: * able to write the directory (if from directory is ! 244: * 555) and not causing a permissions race. If the ! 245: * umask blocks owner writes cp fails. ! 246: */ ! 247: if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) { ! 248: error(to.p_path); ! 249: return; ! 250: } ! 251: } ! 252: else if (type(to_stat) != S_IFDIR) { ! 253: (void)fprintf(stderr, "%s: %s: not a directory.\n", ! 254: pname, to.p_path); ! 255: return; ! 256: } ! 257: copy_dir(); ! 258: /* ! 259: * If not -p and directory didn't exist, set it to be the ! 260: * same as the from directory, umodified by the umask; ! 261: * arguably wrong, but it's been that way forever. ! 262: */ ! 263: if (pflag) ! 264: setfile(&from_stat, 0); ! 265: else if (dne) ! 266: (void)chmod(to.p_path, from_stat.st_mode); ! 267: return; ! 268: case S_IFCHR: ! 269: case S_IFBLK: ! 270: if (rflag) { ! 271: copy_special(&from_stat, !dne); ! 272: return; ! 273: } ! 274: break; ! 275: case S_IFIFO: ! 276: if (rflag) { ! 277: copy_fifo(&from_stat, !dne); ! 278: return; ! 279: } ! 280: break; ! 281: } ! 282: copy_file(&from_stat, dne); ! 283: } ! 284: ! 285: copy_file(fs, dne) ! 286: struct stat *fs; ! 287: int dne; ! 288: { ! 289: register int from_fd, to_fd, rcount, wcount; ! 290: struct stat to_stat; ! 291: ! 292: if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) { ! 293: error(from.p_path); ! 294: return; ! 295: } ! 296: ! 297: /* ! 298: * If the file exists and we're interactive, verify with the user. ! 299: * If the file DNE, set the mode to be the from file, minus setuid ! 300: * bits, modified by the umask; arguably wrong, but it makes copying ! 301: * executables work right and it's been that way forever. (The ! 302: * other choice is 666 or'ed with the execute bits on the from file ! 303: * modified by the umask.) ! 304: */ ! 305: if (!dne) { ! 306: if (iflag) { ! 307: int checkch, ch; ! 308: ! 309: (void)fprintf(stderr, "overwrite %s? ", to.p_path); ! 310: checkch = ch = getchar(); ! 311: while (ch != '\n' && ch != EOF) ! 312: ch = getchar(); ! 313: if (checkch != 'y') { ! 314: (void)close(from_fd); ! 315: return; ! 316: } ! 317: } ! 318: to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0); ! 319: } else ! 320: to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC, ! 321: fs->st_mode & ~(S_ISUID|S_ISGID)); ! 322: ! 323: if (to_fd == -1) { ! 324: error(to.p_path); ! 325: (void)close(from_fd); ! 326: return; ! 327: } ! 328: ! 329: while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { ! 330: wcount = write(to_fd, buf, rcount); ! 331: if (rcount != wcount || wcount == -1) { ! 332: error(to.p_path); ! 333: break; ! 334: } ! 335: } ! 336: if (rcount < 0) ! 337: error(from.p_path); ! 338: if (pflag) ! 339: setfile(fs, to_fd); ! 340: /* ! 341: * If the source was setuid or setgid, lose the bits unless the ! 342: * copy is owned by the same user and group. ! 343: */ ! 344: else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) ! 345: if (fstat(to_fd, &to_stat)) ! 346: error(to.p_path); ! 347: #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) ! 348: else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, ! 349: fs->st_mode & RETAINBITS & ~myumask)) ! 350: error(to.p_path); ! 351: (void)close(from_fd); ! 352: (void)close(to_fd); ! 353: } ! 354: ! 355: copy_dir() ! 356: { ! 357: struct stat from_stat; ! 358: struct direct *dp, **dir_list; ! 359: register int dir_cnt, i; ! 360: char *old_from, *old_to; ! 361: ! 362: dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL); ! 363: if (dir_cnt == -1) { ! 364: (void)fprintf(stderr, "%s: can't read directory %s.\n", ! 365: pname, from.p_path); ! 366: exit_val = 1; ! 367: } ! 368: ! 369: /* ! 370: * Instead of handling directory entries in the order they appear ! 371: * on disk, do non-directory files before directory files. ! 372: * There are two reasons to do directories last. The first is ! 373: * efficiency. Files tend to be in the same cylinder group as ! 374: * their parent, whereas directories tend not to be. Copying files ! 375: * all at once reduces seeking. Second, deeply nested tree's ! 376: * could use up all the file descriptors if we didn't close one ! 377: * directory before recursivly starting on the next. ! 378: */ ! 379: /* copy files */ ! 380: for (i = 0; i < dir_cnt; ++i) { ! 381: dp = dir_list[i]; ! 382: if (dp->d_namlen <= 2 && dp->d_name[0] == '.' ! 383: && (dp->d_name[1] == NULL || dp->d_name[1] == '.')) ! 384: goto done; ! 385: old_from = path_append(&from, dp->d_name, (int)dp->d_namlen); ! 386: if (!old_from) ! 387: goto done; ! 388: ! 389: if (statfcn(from.p_path, &from_stat) < 0) { ! 390: error(dp->d_name); ! 391: path_restore(&from, old_from); ! 392: goto done; ! 393: } ! 394: if (type(from_stat) == S_IFDIR) { ! 395: path_restore(&from, old_from); ! 396: continue; ! 397: } ! 398: old_to = path_append(&to, dp->d_name, (int)dp->d_namlen); ! 399: if (old_to) { ! 400: copy(); ! 401: path_restore(&to, old_to); ! 402: } ! 403: path_restore(&from, old_from); ! 404: done: dir_list[i] = NULL; ! 405: (void)free((void *)dp); ! 406: } ! 407: ! 408: /* copy directories */ ! 409: for (i = 0; i < dir_cnt; ++i) { ! 410: dp = dir_list[i]; ! 411: if (!dp) ! 412: continue; ! 413: old_from = path_append(&from, dp->d_name, (int) dp->d_namlen); ! 414: if (!old_from) { ! 415: (void)free((void *)dp); ! 416: continue; ! 417: } ! 418: old_to = path_append(&to, dp->d_name, (int) dp->d_namlen); ! 419: if (!old_to) { ! 420: (void)free((void *)dp); ! 421: path_restore(&from, old_from); ! 422: continue; ! 423: } ! 424: copy(); ! 425: free((void *)dp); ! 426: path_restore(&from, old_from); ! 427: path_restore(&to, old_to); ! 428: } ! 429: free((void *)dir_list); ! 430: } ! 431: ! 432: copy_link(exists) ! 433: int exists; ! 434: { ! 435: int len; ! 436: char link[MAXPATHLEN]; ! 437: ! 438: if ((len = readlink(from.p_path, link, sizeof(link))) == -1) { ! 439: error(from.p_path); ! 440: return; ! 441: } ! 442: link[len] = '\0'; ! 443: if (exists && unlink(to.p_path)) { ! 444: error(to.p_path); ! 445: return; ! 446: } ! 447: if (symlink(link, to.p_path)) { ! 448: error(link); ! 449: return; ! 450: } ! 451: } ! 452: ! 453: copy_fifo(from_stat, exists) ! 454: struct stat *from_stat; ! 455: int exists; ! 456: { ! 457: if (exists && unlink(to.p_path)) { ! 458: error(to.p_path); ! 459: return; ! 460: } ! 461: if (mkfifo(to.p_path, from_stat->st_mode)) { ! 462: error(to.p_path); ! 463: return; ! 464: } ! 465: if (pflag) ! 466: setfile(from_stat, 0); ! 467: } ! 468: ! 469: copy_special(from_stat, exists) ! 470: struct stat *from_stat; ! 471: int exists; ! 472: { ! 473: if (exists && unlink(to.p_path)) { ! 474: error(to.p_path); ! 475: return; ! 476: } ! 477: if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { ! 478: error(to.p_path); ! 479: return; ! 480: } ! 481: if (pflag) ! 482: setfile(from_stat, 0); ! 483: } ! 484: ! 485: setfile(fs, fd) ! 486: register struct stat *fs; ! 487: int fd; ! 488: { ! 489: static struct timeval tv[2]; ! 490: ! 491: fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; ! 492: ! 493: tv[0].tv_sec = fs->st_atime; ! 494: tv[1].tv_sec = fs->st_mtime; ! 495: if (utimes(to.p_path, tv)) ! 496: error(to.p_path); ! 497: /* ! 498: * Changing the ownership probably won't succeed, unless we're root ! 499: * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting ! 500: * the mode; current BSD behavior is to remove all setuid bits on ! 501: * chown. If chown fails, lose setuid/setgid bits. ! 502: */ ! 503: if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : ! 504: chown(to.p_path, fs->st_uid, fs->st_gid)) { ! 505: if (errno != EPERM) ! 506: error(to.p_path); ! 507: fs->st_mode &= ~(S_ISUID|S_ISGID); ! 508: } ! 509: if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) ! 510: error(to.p_path); ! 511: } ! 512: ! 513: ismember(gid) ! 514: gid_t gid; ! 515: { ! 516: register int cnt; ! 517: static int ngroups, groups[NGROUPS]; ! 518: ! 519: if (!ngroups) { ! 520: ngroups = getgroups(NGROUPS, groups); ! 521: if (ngroups == -1) { ! 522: ngroups = 0; ! 523: exit_val = 1; ! 524: (void)fprintf(stderr, "%s: %s\n", ! 525: pname, strerror(errno)); ! 526: return(0); ! 527: } ! 528: } ! 529: for (cnt = ngroups; cnt--;) ! 530: if (gid == groups[cnt]) ! 531: return(1); ! 532: return(0); ! 533: } ! 534: ! 535: error(s) ! 536: char *s; ! 537: { ! 538: exit_val = 1; ! 539: (void)fprintf(stderr, "%s: %s: %s\n", pname, s, strerror(errno)); ! 540: } ! 541: ! 542: /******************************************************************** ! 543: * Path Manipulation Routines. ! 544: ********************************************************************/ ! 545: ! 546: /* ! 547: * These functions manipulate paths in PATH_T structures. ! 548: * ! 549: * They eliminate multiple slashes in paths when they notice them, and keep ! 550: * the path non-slash terminated. ! 551: * ! 552: * Both path_set() and path_append() return 0 if the requested name ! 553: * would be too long. ! 554: */ ! 555: ! 556: #define STRIP_TRAILING_SLASH(p) { \ ! 557: while ((p)->p_end > (p)->p_path && (p)->p_end[-1] == '/') \ ! 558: *--(p)->p_end = 0; \ ! 559: } ! 560: ! 561: /* ! 562: * Move specified string into path. Convert "" to "." to handle BSD ! 563: * semantics for a null path. Strip trailing slashes. ! 564: */ ! 565: path_set(p, string) ! 566: register PATH_T *p; ! 567: char *string; ! 568: { ! 569: if (strlen(string) > MAXPATHLEN) { ! 570: (void)fprintf(stderr, "%s: %s: name too long.\n", ! 571: pname, string); ! 572: exit_val = 1; ! 573: return(0); ! 574: } ! 575: ! 576: (void)strcpy(p->p_path, string); ! 577: p->p_end = p->p_path + strlen(p->p_path); ! 578: ! 579: if (p->p_path == p->p_end) { ! 580: *p->p_end++ = '.'; ! 581: *p->p_end = 0; ! 582: } ! 583: ! 584: STRIP_TRAILING_SLASH(p); ! 585: return(1); ! 586: } ! 587: ! 588: /* ! 589: * Append specified string to path, inserting '/' if necessary. Return a ! 590: * pointer to the old end of path for restoration. ! 591: */ ! 592: char * ! 593: path_append(p, name, len) ! 594: register PATH_T *p; ! 595: char *name; ! 596: int len; ! 597: { ! 598: char *old; ! 599: ! 600: old = p->p_end; ! 601: if (len == -1) ! 602: len = strlen(name); ! 603: ! 604: /* ! 605: * The final "+ 1" accounts for the '/' between old path and name. ! 606: */ ! 607: if ((len + p->p_end - p->p_path + 1) > MAXPATHLEN) { ! 608: (void)fprintf(stderr, ! 609: "%s: %s/%s: name too long.\n", pname, p->p_path, name); ! 610: exit_val = 1; ! 611: return(0); ! 612: } ! 613: ! 614: /* ! 615: * This code should always be executed, since paths shouldn't ! 616: * end in '/'. ! 617: */ ! 618: if (p->p_end[-1] != '/') { ! 619: *p->p_end++ = '/'; ! 620: *p->p_end = 0; ! 621: } ! 622: ! 623: (void)strncat(p->p_end, name, len); ! 624: p->p_end += len; ! 625: *p->p_end = 0; ! 626: ! 627: STRIP_TRAILING_SLASH(p); ! 628: return(old); ! 629: } ! 630: ! 631: /* ! 632: * Restore path to previous value. (As returned by path_append.) ! 633: */ ! 634: path_restore(p, old) ! 635: PATH_T *p; ! 636: char *old; ! 637: { ! 638: p->p_end = old; ! 639: *p->p_end = 0; ! 640: } ! 641: ! 642: /* ! 643: * Return basename of path. (Like basename(1).) ! 644: */ ! 645: char * ! 646: path_basename(p) ! 647: PATH_T *p; ! 648: { ! 649: char *basename; ! 650: ! 651: basename = rindex(p->p_path, '/'); ! 652: return(basename ? ++basename : p->p_path); ! 653: } ! 654: ! 655: usage() ! 656: { ! 657: (void)fprintf(stderr, ! 658: "usage: cp [-Rfhip] src target;\n or: cp [-Rfhip] src1 ... srcN directory\n"); ! 659: exit(1); ! 660: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.