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