|
|
1.1 root 1: /*
2: * reccp [-z] source dest
3: *
4: * or
5: *
6: * reccp [-z] source1 ... sourcen dest
7: *
8: * In the first case, dest must not be a directory (else we have a
9: * degenerate version of the second case). In the second case,
10: * dest must be a directory; the resulting objects will be
11: * named dest/source1 ... dest/sourcen.
12: *
13: * If any source is a directory, the tree structure under it
14: * will be (recursively) copied; this process will attempt to
15: * preserve any links that exist within the hierarchy.
16: *
17: * This version of reccp copies symbolic links as symbolic links.
18: *
19: * -z ==> copy blocks of zeros as holes.
20: */
21:
22: #include <stdio.h>
23: #include <sys/types.h>
24: #include <sys/stat.h>
25: #include <signal.h>
26:
27: #define FTW_more_to_come 1
28: #include <ftw.h>
29: /* rest of struct FTW: */
30: int obase; /* added to give basename offset of output file */
31: };
32: /* #include "ftw.body" */
33: /*
34: * Struct FTW (whose definition starts at the end of ftw.h) must
35: * must include at least the integers quit, base, and level.
36: */
37:
38: #define FTW_PATHLEN0 1000
39: #define FTW_PATHINC 1000
40: #ifndef S_IFLNK
41: #define lstat stat
42: #endif
43: #ifdef S_IFSOCK
44: #include <sys/dir.h>
45: #else
46: #include "ndir.h"
47: #endif
48: #ifndef ENOMEM
49: #include <errno.h>
50: #endif
51:
52: extern int errno;
53:
54: /*
55: * Each generation of ftw1 (the real ftw) allocates one copy, R, of the
56: * following structure; it passes a pointer to this structure when it
57: * recursively invokes itself. These structures are chained together,
58: * so that if it becomes necessary to recycle file descriptors, then
59: * the oldest descriptor (the one at the shallowest depth still open)
60: * can be recycled.
61: */
62:
63: struct FTW_rec {
64: struct FTW_rec *prev;
65: long here; /* seek to here when reopening at this level */
66: DIR *fd; /* file descriptor at this level */
67: };
68:
69: /*
70: * One instance, T, of the following structure is allocated by ftw; a
71: * pointer to it is passed to all generations of ftw1 (the real ftw).
72: * T could often be a global variable, but this way the parameter fn
73: * can invoke ftw for an independent tree walk.
74: * Component T->path points to storage for the object path-names;
75: * this storage may be relocated by realloc if T->path needs to be
76: * more than T->pathlast characters long.
77: * T->path[T->pathnext] is the next free character in the pathnames.
78: * T->depth = parameter depth to ftw. T->lastout is the deepest level at
79: * which a file descriptor has been recycled.
80: */
81:
82: struct FTW_top {
83: int (*fn)();
84: char *path;
85: unsigned pathlast, pathnext;
86: int lastout;
87: int depth;
88: };
89:
90: int
91: ftw (path, fn, depth)
92: char *path;
93: int (*fn)();
94: int depth;
95: {
96: struct FTW_top T;
97: struct FTW_rec R;
98: struct FTW S;
99: int rc;
100: char *malloc(), *strcpy();
101:
102: T.depth = depth;
103: T.lastout = -1;
104: T.fn = fn;
105: S.quit = 0;
106: S.level = -1;
107:
108: /* initialize S.base, T.pathnext... */
109: {
110: register char c, *p, *q;
111: for (p = q = path; c = *p; p++) if (c == '/') q = p + 1;
112: S.base = q - path;
113: T.pathnext = p - path;
114: }
115:
116: T.pathlast = T.pathnext + FTW_PATHLEN0;
117: T.path = malloc(T.pathlast);
118: if (!T.path) { errno = ENOMEM; return -1; }
119: strcpy(T.path, path);
120: rc = ftw_1_(&R, &T, 0, &S);
121: free(T.path);
122: return rc;
123: }
124:
125: int
126: ftw_1_ (R, T, level, S1)
127: register struct FTW_rec *R;
128: register struct FTW_top *T;
129: int level;
130: struct FTW *S1;
131: {
132: int rc, n;
133: DIR *fd;
134: struct direct *dirp;
135: char *component, *path;
136: struct stat sb;
137: struct FTW_rec mr;
138: unsigned nextsave;
139: struct FTW S;
140: char *realloc();
141: long lseek();
142:
143: mr.prev = R;
144: path = T->path;
145: S.level = level;
146: S.quit = 0;
147: S.base = S1->base;
148:
149: /* Try to get file status. If unsuccessful, errno will say why. */
150: if (lstat(path, &sb) < 0) {
151: badstat: rc = -1;
152: if (errno == EACCES) {
153: rc = (*T->fn) (path, &sb, FTW_NS, &S);
154: S1->quit = S.quit;
155: }
156: return rc;
157: };
158:
159: /*
160: * The stat succeeded, so we know the object exists.
161: * If not a directory, call the user function and return.
162: */
163: #ifdef S_IFLNK
164: if ((sb.st_mode & S_IFMT) == S_IFLNK) {
165: rc = (*T->fn) (path, &sb, FTW_SL, &S);
166: S1->quit = S.quit;
167: if (rc || S.quit == FTW_SKR) return rc;
168: if (S.quit != FTW_FOLLOW) return 0;
169: S1->quit = S.quit = 0;
170: if (stat(path, &sb) < 0) goto badstat;
171: }
172: #endif
173:
174: if ((sb.st_mode & S_IFMT) != S_IFDIR) {
175: rc = (*T->fn) (path, &sb, FTW_F, &S);
176: S1->quit = S.quit;
177: return rc;
178: }
179:
180: /*
181: * The object was a directory.
182: *
183: * Open a file to read the directory
184: */
185: mr.fd = fd = opendir(path);
186:
187: /*
188: * Call the user function, telling it whether
189: * the directory can be read. If it can't be read
190: * call the user function or indicate an error,
191: * depending on the reason it couldn't be read.
192: */
193: if (!fd) {
194: rc = -1;
195: if (errno == EACCES) {
196: rc = (*T->fn) (path, &sb, FTW_DNR, &S);
197: S1->quit = S.quit;
198: }
199: return rc;
200: }
201:
202: /* We could read the directory. Call user function. */
203: rc = (*T->fn) (path, &sb, FTW_D, &S);
204: if (rc != 0)
205: return rc;
206: if (S.quit == FTW_SKD) return 0;
207: if (S.quit == FTW_SKR) {S1->quit = FTW_SKR; return 0;}
208:
209: /* Make sure path is big enough to hold generated pathnames. */
210:
211: n = nextsave = T->pathnext;
212: if (n + MAXNAMLEN + 1 >= T->pathlast) {
213: T->pathlast += FTW_PATHINC;
214: path = T->path = realloc(T->path, T->pathlast);
215: if (!path) {
216: (void) closedir(fd);
217: errno = ENOMEM;
218: return -1;
219: }
220: }
221:
222: /* Create a prefix to which we will append component names */
223:
224: if (n > 0 && path[n-1] != '/') path[n++] = '/';
225: component = path + n;
226:
227: /*
228: * Read the directory one component at a time.
229: * We must ignore "." and "..", but other than that,
230: * just create a path name and call self to check it out.
231: */
232: while (dirp = readdir(fd)) {
233: if (dirp->d_ino != 0
234: && strcmp (dirp->d_name, ".") != 0
235: && strcmp (dirp->d_name, "..") != 0) {
236: int i;
237: struct FTW_rec *pr;
238:
239: /* Append the component name to the working path */
240: strcpy(component, dirp->d_name);
241: T->pathnext = n + strlen(dirp->d_name);
242:
243: /*
244: * If we are about to exceed our depth,
245: * remember where we are and close the file.
246: */
247: if (level - T->lastout >= T->depth) {
248: pr = &mr;
249: i = T->lastout++;
250: while (++i < level) pr = pr->prev;
251: pr->here = telldir(pr->fd);
252: closedir(pr->fd);
253: }
254:
255: /*
256: * Do a recursive call to process the file.
257: */
258: S.quit = 0;
259: S.base = n;
260: rc = ftw_1_(&mr, T, level+1, &S);
261: if (rc != 0 || S.quit == FTW_SKR) {
262: if (level > T->lastout) closedir(fd);
263: T->pathnext = nextsave;
264: return rc;
265: }
266:
267: /*
268: * If we closed the file, try to reopen it.
269: */
270: if (level <= T->lastout) {
271: char c = path[nextsave];
272: path[nextsave] = 0;
273: T->lastout = level - 1;
274: mr.fd = fd = opendir(path);
275: if (!fd) return -1;
276: path[nextsave] = c;
277: seekdir(fd, mr.here);
278: }
279: }
280: }
281: T->pathnext = nextsave;
282: path[nextsave] = 0;
283:
284: /*
285: * We got out of the subdirectory loop. Call the user
286: * function again at the end and clean up.
287: */
288:
289: rc = (*T->fn) (path, &sb, FTW_DP, &S);
290: closedir(fd);
291: S1->quit = S.quit;
292: return rc;
293: }
294:
295: #define BLKSIZE BUFSIZ
296: char zero[BLKSIZE]; /* keep as zero */
297: int zflag;
298:
299: /* destination path-name buffer initial value, increment */
300: #define PATHLEN0 1000
301: #define PATHINC 1000
302: #define IHTABLEN 287
303: #define SBUFLEN 1000
304:
305: /* Non-zero if stat buffer b refers to a directory, zero otherwise */
306: #define isdir(b) (((b).st_mode & S_IFMT) == S_IFDIR)
307:
308: char *malloc(), *realloc(), *strcpy(), *strcat();
309: int copy();
310:
311: char symbuf[SBUFLEN]; /* Buffer for symbolic link names */
312: int firstyell = 1;
313: int destlen; /* length of destination (last arg) */
314: int iamsu;
315: char *dest;
316: char *destdir; /* Storage for output pathname -- handled like
317: * the source pathname in ftw, subject to being
318: * relocated by realloc.
319: */
320: unsigned dlast, dnext; /* Offsets of next available, last+1 chars of
321: * output pathname.
322: */
323:
324: /* Final component of the path name by which we were invoked */
325: char *pgmname;
326:
327: struct inodetab { /* for keeping track of links */
328: struct inodetab *nextint;
329: ino_t i_ino;
330: dev_t i_dev;
331: char iname[1];
332: };
333:
334: struct inodetab *ihtab[IHTABLEN]; /* hash table for remembering links */
335:
336: int retcode;
337:
338: main(argc, argv)
339: int argc;
340: char **argv;
341: {
342: char *p, *q;
343: struct stat *argbufs, destbuf, statbuf, rootbuf;
344: int i;
345:
346: umask(0);
347: pgmname = argv[0];
348:
349: if (argc > 1 && !strcmp(argv[1],"-z")) {
350: zflag = 1;
351: --argc;
352: ++argv;
353: }
354:
355: /*
356: * Start validity checking
357: */
358:
359: /* Check for too few arguments given */
360: if (argc < 3) {
361: fprintf(stderr,
362: "usage: %s [-z] f1 f2 or %s [-z] f1 ... fn d\n",
363: pgmname, pgmname);
364: return 1;
365: }
366:
367: /*
368: * If the last argument is a non-directory, we require
369: * argc == 3 (in the case of: cp f1 f2)
370: */
371: dest = argv[argc-1];
372: destbuf.st_mode = 0; /* Force isdir(destbuf) false */
373: if ((stat (dest, &destbuf) < 0 || !isdir(destbuf)) && argc != 3) {
374: scream ("%s not a directory", dest);
375: return 1;
376: }
377:
378: /*
379: * Allocate storage for a stat buffer for each argument
380: * but the last. This will be used to check for trying to
381: * copy something into a subdirectory of itself.
382: */
383: argbufs = (struct stat *)
384: malloc ((unsigned) (sizeof (struct stat) * (argc-2)));
385: if (argbufs == NULL) {
386: scream ("insufficient storage", "");
387: return 1;
388: }
389: iamsu = (getuid() == 0);
390:
391: for (i = 0; i < argc-2; i++) {
392: if (lstat (argv[i+1], &argbufs[i]) < 0) {
393: scream ("cannot access %s", argv[i+1]);
394: return 1;
395: }
396: }
397:
398: /*
399: * Now run back from the destination to the root directory,
400: * checking along the way for any matches with anything in
401: * the argbufs array. If we find any, we tried a forbidden
402: * operation. If the destination is not already a directory,
403: * we must start from its parent. Note that even in this case,
404: * we can't elide the test, because otherwise we wouldn't get
405: *
406: * cp . x
407: *
408: * This code also handles pathological but legal destinations
409: * properly; in particular note that the null string is a
410: * valid name for the current directory. Sigh.
411: *
412: * First figure out the name of the directory to check.
413: */
414: dnext = destlen = strlen(dest);
415: dlast = destlen + 2;
416: if (dlast < PATHLEN0) dlast = PATHLEN0;
417: destdir = malloc(dlast);
418: if (!destdir) { scream ("no storage", ""); return 1; }
419: strcpy (destdir, dest);
420: if (!isdir (destbuf)) {
421: p = q = destdir;
422: while (*q) {
423: if (*q++ == '/')
424: p = q - 1;
425: }
426: if (p == destdir) {
427: strcpy (destdir, ".");
428: dnext = 1;
429: }
430: else {
431: *p = '\0';
432: dnext = p - destdir;
433: }
434: }
435:
436: /* Now stat the root directory for later use */
437: if (stat ("/", &rootbuf) < 0) {
438: scream ("cannot access root directory", "");
439: return 1;
440: }
441:
442: /* Now run back from destdir to the root, checking against argbufs */
443: do {
444: if (stat (destdir, &statbuf) < 0) {
445: scream ("cannot stat %s", destdir);
446: return 1;
447: }
448: for (i = 0; i < argc-2; i++)
449: if (argbufs[i].st_dev == statbuf.st_dev
450: && argbufs[i].st_ino == statbuf.st_ino) {
451: fprintf (stderr, "%s: %s contains %s\n",
452: pgmname, argv[i+1], dest);
453: return 1;
454: }
455: dcheck(3, "initial arg checking");
456: strcpy(destdir+dnext, "/..");
457: dnext += 3;
458: } while (statbuf.st_dev != rootbuf.st_dev
459: || statbuf.st_ino != rootbuf.st_ino);
460:
461: /*
462: * End of validity checking
463: */
464:
465: strcpy(destdir, dest);
466: for (i = 0; i < IHTABLEN; i++) ihtab[i] = 0;
467: if (isdir(destbuf)) {
468: destdir[destlen] = '/';
469: dnext = destlen + 1;
470: for (i = 1; i < argc-1; i++) {
471:
472: char *p, *q;
473:
474: p = q = argv[i];
475: while (*p)
476: if (*p++ == '/')
477: q = p;
478: dnext = destlen + (p - q) + 1;
479:
480: p = destdir + destlen;
481: *p = '/';
482: strcpy(p+1, q);
483: retcode |= ftw(argv[i], copy, 12);
484: }
485: }
486:
487: else {
488: dnext = destlen;
489: retcode = ftw(argv[1], copy, 12);
490: }
491:
492: return retcode;
493: }
494:
495: int
496: copy(source, srcbuf, code, S)
497: char *source;
498: struct stat *srcbuf;
499: int code;
500: struct FTW *S;
501: {
502: char *s;
503: int rc, slen;
504:
505: switch(code) {
506:
507: case FTW_F: /* nondirectory */
508:
509: dcheck(MAXNAMLEN+1,source);
510: if (S->level)
511: strcpy(destdir + dnext, source + S->base);
512: return copy1(source, srcbuf, destdir);
513:
514: case FTW_D: /* new directory */
515:
516: dcheck(MAXNAMLEN+1,source);
517: S->obase = dnext;
518: s = destdir+dnext;
519: if (S->level) {
520: for (source += S->base; *s = *source;)
521: { s++, source++; }
522: }
523: dnext = s - destdir;
524: if (mkdir(destdir,0700)) {
525: scream("can't mkdir %s", destdir);
526: return 1;
527: }
528: *s++ = '/'; *s = 0;
529: ++dnext;
530: return 0;
531:
532: case FTW_DNR: /* unreadable directory */
533:
534: scream("can't read %s", source);
535: return 1;
536:
537: case FTW_NS: /* unstatable file */
538:
539: scream("can't stat %s", source);
540: return 1;
541:
542: case FTW_DP: /* end of directory */
543:
544: destdir[dnext-1] = 0;
545: dnext = S->obase;
546: if (chmod(destdir, (int)srcbuf->st_mode))
547: scream("can't chmod %s", destdir);
548: if(iamsu && chown(destdir, srcbuf->st_uid,
549: srcbuf->st_gid))
550: scream("can't chown %s", destdir);
551: return settime(srcbuf, destdir);
552:
553: #ifdef S_IFLNK
554: case FTW_SL: /* symbolic link */
555:
556: slen = readlink(source, symbuf, SBUFLEN);
557: if (slen >= SBUFLEN) {
558: scream("Symbolic path to %s too long.",
559: source);
560: return 1;
561: }
562: dcheck(MAXNAMLEN+1,source);
563: symbuf[slen] = 0;
564: if (S->level) strcpy(destdir + dnext,
565: source + S->base);
566: rc = 0;
567: umask(~(int)srcbuf->st_mode);
568: if (symlink(symbuf,destdir)) {
569: fprintf(stderr,
570: "%s: can't symbolically link %s to %s\n",
571: pgmname, destdir, symbuf);
572: rc = 1;
573: }
574: umask(0);
575: return rc;
576: #endif
577:
578: default: /* bug */
579: fprintf(stderr,"%s: unknown code = %d from ftw\n",
580: pgmname, code);
581: exit(1);
582: }
583: }
584:
585: /*
586: * Copy a file. The source file name is in "source",
587: * and the destination file name is in "dest". "srcbuf" points to
588: * a stat buffer for the source.
589: */
590:
591: int
592: copy1(source, srcbuf, dest)
593: char *source, *dest;
594: struct stat *srcbuf;
595: {
596: int bsize, inf, outf, n, type, zsize;
597: char buffer[BLKSIZE];
598: struct stat destbuf;
599: long lseek();
600:
601: if (stat (dest, &destbuf) >= 0
602: && srcbuf->st_dev == destbuf.st_dev
603: && srcbuf->st_ino == destbuf.st_ino) {
604: scream ("cannot copy %s to itself", source);
605: return 1;
606: }
607:
608: switch(type = srcbuf->st_mode & S_IFMT) {
609:
610: case S_IFCHR: /* character special */
611: case S_IFBLK: /* block special */
612: if (!iamsu) {
613: scream("can't copy special file %s", source);
614: return 1;
615: }
616: if (n = linkchk(srcbuf,destdir)) return n-1;
617: if (mknod(destdir, (int) srcbuf->st_mode,
618: (int) srcbuf->st_rdev)) {
619: scream("can't create %s", destdir);
620: return 1;
621: }
622: goto finish;
623:
624: case S_IFREG: /* regular file */
625: if (n = linkchk(srcbuf,destdir)) return n-1;
626: break;
627:
628: default:
629: fprintf(stderr,
630: "%s: unknown file type 0x%x for %s\n",
631: pgmname, type, source);
632: return 1;
633: }
634:
635: /* Now copy the file, whether or not there was a link */
636:
637: if ((outf = creat (dest, (int) (srcbuf->st_mode & 0777))) < 0) {
638: scream ("cannot create %s", dest);
639: return 1;
640: }
641: if ((inf = open (source, 0)) < 0) {
642: scream ("cannot open %s", source);
643: close(outf);
644: return 1;
645: }
646: if (zflag) {
647: fstat(outf, &destbuf);
648: bsize = BSIZE(destbuf.st_dev);
649: if (bsize > BLKSIZE) {
650: fprintf(stderr, "%s: BSIZE(%s) > %d\n", pgmname,
651: dest, BLKSIZE);
652: return 1;
653: }
654: }
655: else bsize = BLKSIZE;
656: zsize = 0;
657: while ((n = read (inf, buffer, bsize)) > 0) {
658: if (zflag && (n == bsize) && !memcmp(buffer, zero, bsize)) {
659: if (lseek(outf, (long)bsize, 1) < 0) {
660: scream("lseek (for block of 0's) failed on %s",
661: dest);
662: goto endit;
663: }
664: zsize = bsize;
665: }
666: else if (write (outf, buffer, n) != n) {
667: scream ("output error on %s", dest);
668: endit:
669: close (inf);
670: close (outf);
671: return 1;
672: }
673: else zsize = 0;
674: }
675: if (n < 0) {
676: scream ("input error on %s", source);
677: goto endit;
678: }
679: if (zsize && (lseek(outf, (long)-zsize, 1) < 0
680: || write(outf,zero,zsize) != zsize)) {
681: scream("error writing trailing 0's from %s", source);
682: goto endit;
683: }
684: if (close (outf) < 0) {
685: scream ("cannot close %s", dest);
686: close (inf);
687: return 1;
688: }
689: if (close (inf) < 0) {
690: scream ("cannot close %s", source);
691: return 1;
692: }
693:
694: finish:
695: if(iamsu && chown(dest, srcbuf->st_uid, srcbuf->st_gid)) {
696: scream("can't chown %s", dest);
697: return 1;
698: }
699: return settime(srcbuf, dest);
700: }
701:
702: scream (s1, s2)
703: char *s1, *s2;
704: {
705: fprintf (stderr, "%s: ", pgmname);
706: fprintf (stderr, s1, s2);
707: putc ('\n', stderr);
708: retcode |= 1;
709: }
710:
711: dcheck(len, source)
712: int len;
713: char *source;
714: {
715: int dnext1 = dnext + len;
716: if (dnext1 >= dlast) {
717: dlast += PATHINC;
718: destdir = realloc(destdir, dlast);
719: if (!destdir) {
720: scream("malloc failed on %s",source);
721: exit(1);
722: }
723: }
724: }
725:
726: int
727: linkchk(sb,d)
728: struct stat *sb;
729: char *d;
730: {
731: register struct inodetab *p, *p0;
732: struct stat buf;
733:
734: if (sb->st_nlink < 2) return 0;
735:
736: p0 = (struct inodetab *)
737: (((sb->st_dev + sb->st_ino) % IHTABLEN) + ihtab);
738: while ((p = p0->nextint) &&
739: (p->i_ino != sb->st_ino || p->i_dev != sb->st_dev)) p0 = p;
740:
741: if (p) {/* If this is a link to a file already seen... */
742: if (link(p->iname, d)) {
743:
744: /* unlink d and try again, unless d is a directory */
745:
746: if (lstat(d,&buf)
747: || (buf.st_mode & S_IFMT) == S_IFDIR
748: || unlink(d)
749: || link(p->iname,d)) {
750: fprintf(stderr,"%s: can't link %s to %s\n",
751: pgmname, p->iname, d);
752: return 2;
753: }
754: }
755: return 1;
756: }
757:
758: /* This file has more than one link to it -- add it to the list */
759: /* of files that may be seen later. */
760:
761: p = (struct inodetab *)
762: malloc((unsigned) (sizeof(struct inodetab) + strlen(d)));
763: if (!p) {
764: if (firstyell) {
765: firstyell = 0;
766: scream("storage for links exhausted with %s", d);
767: }
768: return 0;
769: }
770:
771: p0->nextint = p;
772: p->nextint = 0;
773: p->i_ino = sb->st_ino;
774: p->i_dev = sb->st_dev;
775: strcpy(p->iname, d);
776: return 0;
777: }
778:
779: settime(srcbuf, dest) /* set time and mode of dest */
780: struct stat *srcbuf;
781: char *dest;
782: {
783: time_t utimbuf[2];
784: int m, rc = 0;
785:
786: utimbuf[0] = srcbuf->st_atime;
787: utimbuf[1] = srcbuf->st_mtime;
788: utime (dest, utimbuf);
789:
790: m = srcbuf->st_mode;
791: #ifdef S_ISVTX
792: if (m & S_ISVTX && !iamsu) {
793: scream("can't set save-text bit of %s", dest);
794: m &= ~S_ISVTX;
795: }
796: if ((m & (S_ISUID | S_ISGID | S_ISVTX)) && (rc = chmod(dest,m)))
797: #else
798: if ((m & (S_ISUID | S_ISGID)) && (rc = chmod(dest,m)))
799: #endif
800: scream("can't set mode of %s", dest);
801: return rc;
802: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.