|
|
1.1 ! root 1: ! 2: /* ! 3: * cp source dest ! 4: * ! 5: * or ! 6: * ! 7: * cp source1 ... sourcen dest ! 8: * ! 9: * In the first case, dest must not be a directory (else we have a ! 10: * degenerate version of the second case). In the second case, ! 11: * dest must be a directory; the resulting objects will be ! 12: * named dest/source1 ... dest/sourcen. ! 13: * ! 14: * If any source is a directory, the tree structure under it ! 15: * will be (recursively) copied; this process will attempt to ! 16: * preserve any links that exist within the hierarchy. ! 17: */ ! 18: ! 19: #include <stdio.h> ! 20: #include <sys/types.h> ! 21: #include <sys/stat.h> ! 22: #include <sys/dir.h> ! 23: ! 24: #define BLKSIZE BUFSIZ ! 25: ! 26: /* Maximum file tree nesting before we start closing and re-opening */ ! 27: #define MAXLEVEL 8 ! 28: ! 29: /* Non-zero if stat buffer b refers to a directory, zero otherwise */ ! 30: #define isdir(b) (((b).st_mode & S_IFMT) == S_IFDIR) ! 31: ! 32: char *malloc(), *realloc(), *strcpy(), *strcat(); ! 33: int iamsu; ! 34: ! 35: /* Final component of the path name by which we were invoked */ ! 36: char *pgmname; ! 37: ! 38: main (argc, argv) ! 39: int argc; ! 40: char **argv; ! 41: { ! 42: int retcode; ! 43: char *dest, *destdir, *p, *q; ! 44: struct stat *argbufs, destbuf, statbuf, rootbuf; ! 45: int i; ! 46: ! 47: umask(0); ! 48: pgmname = argv[0]; ! 49: ! 50: /* ! 51: * Start validity checking ! 52: */ ! 53: ! 54: /* Check for too few arguments given */ ! 55: if (argc < 3) { ! 56: fprintf (stderr, "usage: %s f1 f2 or %s f1 ... fn d\n", ! 57: pgmname, pgmname); ! 58: return 1; ! 59: } ! 60: ! 61: /* ! 62: * If the last argument is a non-directory, we require ! 63: * argc == 3 (in the case of: cp f1 f2) ! 64: */ ! 65: dest = argv[argc-1]; ! 66: destbuf.st_mode = 0; /* Force isdir(destbuf) false */ ! 67: if ((stat (dest, &destbuf) < 0 || !isdir(destbuf)) && argc != 3) { ! 68: scream ("%s not a directory", dest); ! 69: return 1; ! 70: } ! 71: ! 72: /* ! 73: * Allocate storage for a stat buffer for each argument ! 74: * but the last. This will be used to check for trying to ! 75: * copy something into a subdirectory of itself. ! 76: */ ! 77: argbufs = (struct stat *) ! 78: malloc ((unsigned) (sizeof (struct stat) * (argc-2))); ! 79: if (argbufs == NULL) { ! 80: scream ("insufficient storage", ""); ! 81: return 1; ! 82: } ! 83: iamsu = (getuid() == 0); ! 84: ! 85: for (i = 0; i < argc-2; i++) { ! 86: if (stat (argv[i+1], &argbufs[i]) < 0) { ! 87: scream ("cannot access %s", argv[i+1]); ! 88: return 1; ! 89: } ! 90: } ! 91: ! 92: /* ! 93: * Now run back from the destination to the root directory, ! 94: * checking along the way for any matches with anything in ! 95: * the argbufs array. If we find any, we tried a forbidden ! 96: * operation. If the destination is not already a directory, ! 97: * we must start from its parent. Note that even in this case, ! 98: * we can't elide the test, because otherwise we wouldn't get ! 99: * ! 100: * cp . x ! 101: * ! 102: * This code also handles pathological but legal destinations ! 103: * properly; in particular note that the null string is a ! 104: * valid name for the current directory. Sigh. ! 105: * ! 106: * First figure out the name of the directory to check. ! 107: */ ! 108: destdir = malloc ((unsigned) (strlen (dest) + 2)); ! 109: if (destdir == NULL) { ! 110: scream ("insufficient storage", ""); ! 111: return 1; ! 112: } ! 113: strcpy (destdir, dest); ! 114: if (!isdir (destbuf)) { ! 115: p = q = destdir; ! 116: while (*q) { ! 117: if (*q++ == '/') ! 118: p = q - 1; ! 119: } ! 120: if (p == destdir) ! 121: strcpy (destdir, "."); ! 122: else ! 123: *p = '\0'; ! 124: } ! 125: ! 126: /* Now stat the root directory for later use */ ! 127: if (stat ("/", &rootbuf) < 0) { ! 128: scream ("cannot access root directory", ""); ! 129: return 1; ! 130: } ! 131: ! 132: /* Now run back from destdir to the root, checking against argbufs */ ! 133: do { ! 134: if (stat (destdir, &statbuf) < 0) { ! 135: scream ("cannot stat %s", destdir); ! 136: return 1; ! 137: } ! 138: for (i = 0; i < argc-2; i++) ! 139: if (argbufs[i].st_dev == statbuf.st_dev ! 140: && argbufs[i].st_ino == statbuf.st_ino) { ! 141: fprintf (stderr, "%s: %s contains %s\n", ! 142: pgmname, argv[i+1], dest); ! 143: return 1; ! 144: } ! 145: destdir = realloc (destdir, (unsigned) (strlen(destdir) + 4)); ! 146: if (destdir == NULL) { ! 147: scream ("insufficient storage", ""); ! 148: return 1; ! 149: } ! 150: strcat (destdir, "/.."); ! 151: } while (statbuf.st_dev != rootbuf.st_dev ! 152: || statbuf.st_ino != rootbuf.st_ino); ! 153: free (destdir); ! 154: ! 155: /* ! 156: * End of validity checking ! 157: */ ! 158: ! 159: if (isdir (destbuf)) { ! 160: retcode = 0; ! 161: for (i=1; i<argc-1; i++) { ! 162: ! 163: char *p, *q; ! 164: char *xdest; ! 165: ! 166: p = q = argv[i]; ! 167: while (*p) ! 168: if (*p++ == '/') ! 169: q = p; ! 170: xdest = malloc ((unsigned) ! 171: (strlen(dest) + strlen (q) + 2)); ! 172: if (xdest != NULL) { ! 173: strcpy (xdest, dest); ! 174: strcat (xdest, "/"); ! 175: strcat (xdest, q); ! 176: retcode |= copy (argv[i], &argbufs[i-1], xdest); ! 177: free (xdest); ! 178: } else { ! 179: scream ("no storage", ""); ! 180: retcode = 1; ! 181: } ! 182: } ! 183: } else ! 184: retcode = copy (argv[1], &argbufs[0], dest); ! 185: ! 186: return retcode; ! 187: } ! 188: ! 189: /* ! 190: * Copy a file or directory. The source file name is in "source", ! 191: * and the destination file name is in "dest". "srcbuf" points to ! 192: * a stat buffer for the source. ! 193: */ ! 194: int ! 195: copy (source, srcbuf, dest) ! 196: char *source, *dest; ! 197: struct stat *srcbuf; ! 198: { ! 199: int inf, outf, n; ! 200: char buffer[BLKSIZE]; ! 201: struct stat newbuf, destbuf; ! 202: static int level = 0; ! 203: static short firstyell = 1; ! 204: struct { ! 205: long actime, modtime; ! 206: } utimbuf; ! 207: long time(); ! 208: static struct linknode { ! 209: dev_t ln_dev; ! 210: ino_t ln_ino; ! 211: struct linknode *ln_next; ! 212: char *ln_name; /* variable */ ! 213: } *ln_head = NULL, *lnp; ! 214: ! 215: if (isdir (*srcbuf)) { ! 216: ! 217: char *srcname, *destname; ! 218: FILE *dirfile; ! 219: struct direct dir; ! 220: long dirloc; ! 221: int retcode = 0; ! 222: ! 223: /* ! 224: * Copy from a directory. This will have to copy the ! 225: * tree structure recursively. Thus, we will create ! 226: * a new directory of an appropriate name, then ! 227: * copy everything in source to elements of dest. ! 228: */ ! 229: if (stat (dest, &destbuf) < 0) { ! 230: if (mkdir (dest)) { ! 231: scream ("cannot create directory %s", dest); ! 232: return 1; ! 233: } ! 234: } else { ! 235: scream ("%s already exists", dest); ! 236: return 1; ! 237: } ! 238: ! 239: if ((dirfile = fopen (source, "r")) == NULL) { ! 240: scream ("cannot open %s", source); ! 241: return 1; ! 242: } ! 243: ! 244: srcname = malloc ((unsigned) (strlen(source) + DIRSIZ + 2)); ! 245: destname = malloc ((unsigned) (strlen(dest) + DIRSIZ + 2)); ! 246: if (srcname == NULL || destname == NULL) { ! 247: scream ("insufficient storage", ""); ! 248: fclose(dirfile); ! 249: return 1; ! 250: } ! 251: ! 252: while (fread ((char *) & dir, sizeof (struct direct), ! 253: 1, dirfile) == 1) ! 254: if (dir.d_ino != 0 && ! 255: strncmp (dir.d_name, ".", DIRSIZ) && ! 256: strncmp (dir.d_name, "..", DIRSIZ)) { ! 257: strcpy (srcname, source); ! 258: strcat (srcname, "/"); ! 259: strncat (srcname, dir.d_name, DIRSIZ); ! 260: strcpy (destname, dest); ! 261: strcat (destname, "/"); ! 262: strncat (destname, dir.d_name, DIRSIZ); ! 263: ! 264: if (level > MAXLEVEL) { ! 265: dirloc = ftell (dirfile); ! 266: fclose (dirfile); ! 267: dirfile = NULL; ! 268: } ! 269: ! 270: if (lstat (srcname, &newbuf) < 0) { ! 271: scream ("cannot stat %s", srcname); ! 272: if(dirfile != NULL) ! 273: fclose(dirfile); ! 274: return 1; ! 275: } ! 276: ! 277: level++; ! 278: retcode |= copy (srcname, &newbuf, destname); ! 279: level--; ! 280: ! 281: if (level > MAXLEVEL) { ! 282: if ((dirfile = fopen (source, "r")) ! 283: == NULL) { ! 284: scream ("cannot reopen %s", ! 285: source); ! 286: return 1; ! 287: } ! 288: if(fseek(dirfile, dirloc, 0) == EOF) { ! 289: scream("can't seek on %s", ! 290: source); ! 291: fclose(dirfile); ! 292: return(1); ! 293: } ! 294: } ! 295: } ! 296: ! 297: free (srcname); ! 298: free (destname); ! 299: if(iamsu) ! 300: chown(dest, srcbuf->st_uid, srcbuf->st_gid); ! 301: ! 302: utimbuf.actime = time ((long *) NULL); ! 303: utimbuf.modtime = srcbuf->st_mtime; ! 304: utime (dest, &utimbuf); ! 305: ! 306: if (chmod (dest, srcbuf->st_mode & 07777) < 0) { ! 307: scream ("cannot chmod for %s", dest); ! 308: retcode = 1; ! 309: } ! 310: fclose(dirfile); ! 311: return retcode; ! 312: } ! 313: ! 314: /* ! 315: * Here when src is not a directory ! 316: */ ! 317: if (stat (dest, &destbuf) >= 0 ! 318: && srcbuf->st_dev == destbuf.st_dev ! 319: && srcbuf->st_ino == destbuf.st_ino) { ! 320: scream ("cannot copy %s to itself", source); ! 321: return 1; ! 322: } ! 323: ! 324: /* ! 325: * Check if the source (which is known to be a file) has ! 326: * more than one link. If not, all is easy. If so, we may ! 327: * be able to create a link rather than copying the source. ! 328: */ ! 329: if (srcbuf->st_nlink > 1) { ! 330: ! 331: /* Hunt through the table of linked items for a match */ ! 332: lnp = ln_head; ! 333: while (lnp != NULL && ! 334: (lnp->ln_dev != srcbuf->st_dev ! 335: || lnp->ln_ino != srcbuf->st_ino)) { ! 336: lnp = lnp->ln_next; ! 337: } ! 338: ! 339: /* If match found, try a link */ ! 340: if (lnp != NULL) ! 341: if (link (lnp->ln_name, dest)) { ! 342: scream ("cannot create %s link", dest); ! 343: return 1; ! 344: } else ! 345: return 0; ! 346: ! 347: /* No match found -- build a table entry */ ! 348: lnp = (struct linknode *) malloc (sizeof (struct linknode)); ! 349: ! 350: if (lnp == NULL) { ! 351: if (firstyell) { ! 352: scream ("link table too large", ""); ! 353: firstyell = 0; ! 354: } ! 355: } else if ((lnp->ln_name = malloc ((unsigned) (strlen(dest)+1))) ! 356: == NULL) { ! 357: if (firstyell) { ! 358: scream ("link names too large", ""); ! 359: firstyell = 0; ! 360: } ! 361: free ((char *) lnp); ! 362: } else { ! 363: lnp->ln_dev = srcbuf->st_dev; ! 364: lnp->ln_ino = srcbuf->st_ino; ! 365: strcpy (lnp->ln_name, dest); ! 366: lnp->ln_next = ln_head; ! 367: ln_head = lnp; ! 368: } ! 369: } ! 370: ! 371: /* Now copy the file, whether or not there was a link */ ! 372: switch(srcbuf->st_mode & S_IFMT){ ! 373: case S_IFLNK:{ ! 374: int len; ! 375: char buf[1024]; ! 376: len = readlink(source, buf, sizeof buf); ! 377: if(len<=0){ ! 378: scream("can't read link for %s", source); ! 379: return 1; ! 380: } ! 381: if(symlink(buf, dest)!=0){ ! 382: scream("can't write link for %s", dest); ! 383: return 1; ! 384: } ! 385: break; ! 386: } ! 387: case S_IFCHR: ! 388: case S_IFBLK: ! 389: if(mknod(dest, srcbuf->st_mode, srcbuf->st_rdev)!=0){ ! 390: scream("can't mknod %s", dest); ! 391: return 1; ! 392: } ! 393: break; ! 394: default: ! 395: if ((outf = creat (dest, srcbuf->st_mode & 07777)) < 0) { ! 396: scream ("cannot create %s", dest); ! 397: return 1; ! 398: } ! 399: if ((inf = open (source, 0)) < 0) { ! 400: scream ("cannot open %s", source); ! 401: close(outf); ! 402: return 1; ! 403: } ! 404: while ((n = read (inf, buffer, BLKSIZE)) > 0) { ! 405: if (write (outf, buffer, n) != n) { ! 406: scream ("output error on %s", dest); ! 407: close (inf); ! 408: close (outf); ! 409: return 1; ! 410: } ! 411: } ! 412: if (n < 0) { ! 413: scream ("input error on %s", source); ! 414: close (inf); ! 415: close (outf); ! 416: return 1; ! 417: } ! 418: if (close (outf) < 0) { ! 419: scream ("cannot close %s", dest); ! 420: close (inf); ! 421: return 1; ! 422: } ! 423: if (close (inf) < 0) { ! 424: scream ("cannot close %s", source); ! 425: return 1; ! 426: } ! 427: break; ! 428: } ! 429: utimbuf.actime = srcbuf->st_atime; ! 430: utimbuf.modtime = srcbuf->st_mtime; ! 431: utime (dest, &utimbuf); ! 432: if(iamsu) ! 433: chown(dest, srcbuf->st_uid, srcbuf->st_gid); ! 434: return 0; ! 435: } ! 436: ! 437: int ! 438: mkdir(d) ! 439: char *d; ! 440: { ! 441: int status, pid, w; ! 442: /*w = open(".", 0); ! 443: printf("%d\t%s\n", w, d); ! 444: close(w);*/ ! 445: switch (pid=fork()) { ! 446: case 0: ! 447: execl ("/bin/mkdir", "mkdir", d, 0); ! 448: /* No break */ ! 449: case -1: ! 450: return 1; ! 451: ! 452: default: ! 453: do w = wait (&status); ! 454: while (w != pid && w > 0); ! 455: if (w == pid) ! 456: return status; ! 457: return w; ! 458: } ! 459: } ! 460: ! 461: scream (s1, s2) ! 462: char *s1, *s2; ! 463: { ! 464: fprintf (stderr, "%s: ", pgmname); ! 465: fprintf (stderr, s1, s2); ! 466: putc ('\n', stderr); ! 467: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.