|
|
1.1 root 1: /*
2: * cpdir.c
3: * DON"T USE THIS SOURCE! MAY REBOOT THE SYSTEM ON BIG CPDIRS!
4: * Copy hierarchies in a file system, preserving structure.
5: * Needs directory stuff in headers: cc -D__KERNEL__ cpdir.c
6: * Define SLOW for 'block at a time copying' - not recommended.
7: */
8:
9: /*
10: * Known bug: if dir1/a and dir1/b are linked, and dir2/b exists,
11: * cpdir -u dir1 dir2 creates new dir2/a not linked to dir2/b.
12: */
13:
14: #include <stdio.h>
15: #include <stdlib.h>
16: #include <string.h>
17: #include <errno.h>
18: #include <signal.h>
19: #include <sys/types.h>
20: #include <sys/stat.h>
21: #include <sys/uproc.h>
22: #include <ctype.h>
23: #include <access.h>
24: #include <canon.h>
25:
26: extern long lseek();
27:
28: /* Avoid conflicts with headers, bogus nachos. */
29: #undef hash
30: #undef DIRBUF
31:
32: /* Manifest constants. */
33: #define VERSION "1.2"
34: #define CPBUFSIZ (50*BUFSIZ) /* copy buffer */
35: #define SDSIZ (sizeof(struct direct))
36: #define MAXINT 32767
37: #define SOURCE 0
38: #define TARGET 1
39: #define DEV0 ((dev_t)0)
40: #define INODE0 ((ino_t)0)
41: #define ROOTUID 0
42: #define HASHSIZE 37
43: #define TRUE (0==0)
44: #define FALSE (0==1)
45:
46: /* Macros. */
47: #define hash(ino) ((ino)%HASHSIZE)
48:
49: /* Type definitions. */
50: typedef char bool;
51:
52: typedef union {
53: struct direct dbuf;
54: char cbuf[SDSIZ + 1];
55: } DIRBUF;
56: #define db_ino dbuf.d_ino
57: #define db_name dbuf.d_name
58:
59: /* Link tables keep track of whether links have been properly preserved. */
60: typedef struct link {
61: ino_t l_ino;
62: dev_t l_dev;
63: dev_t l_tdev;
64: struct link *l_next;
65: uint_t l_nlink;
66: char l_name[];
67: } LINK;
68:
69: typedef struct str {
70: struct str *s_next;
71: char s_str[];
72: } STR;
73:
74:
75: /*
76: * Global variables.
77: */
78: dev_t dir2_dev;
79: ino_t dir2_ino;
80: dev_t src_dev;
81: dev_t tgt_dev;
82: struct stat srcstat;
83: struct stat tgtstat;
84: LINK *srctab[HASHSIZE];
85: LINK *tgttab[HASHSIZE];
86: STR *strtab[HASHSIZE];
87:
88: bool aflag;
89: bool dflag;
90: bool eflag;
91: bool sflag;
92: bool tflag;
93: bool uflag;
94: bool vflag;
95: bool wflag;
96: bool root;
97: bool splitmsg;
98: bool errprefix;
99: bool dir1slash;
100: bool dir2slash;
101:
102: int rlimit = MAXINT;
103: int rlevel = 1;
104: int exitval;
105: int numsuppress;
106: int broken;
107: int srclinks;
108: int tgtlinks;
109: int srcsize;
110: int tgtsize;
111: int dir1len;
112: int dir2len;
113: int interrupted; /* For handling SIGINT and SIGQUIT */
114:
115: short uid;
116: short gid;
117:
118: char *dir1;
119: char *dir2;
120: char *target;
121: char *source;
122:
123: /*
124: * Error message and other strings.
125: */
126: char *fmt1 = "%s\n";
127: char *fmt1a = "%-72s\r";
128: char *fmt2 = "%s: %s\n";
129: char *fmt3 = "%s: %s%s\n";
130: char *fmt4 = "%s: %s%s%s\n";
131: char *devbound = "crossing device boundary";
132: char *writeerr = "write error";
133: char *readerr = "read error";
134: char *supressd = "suppressed";
135: char *linked = "linked";
136: char *notdir = "not a directory";
137: char *nopermit = "permission denied";
138: char *nomkdir = "cannot make directory";
139: char *nounlink = "cannot unlink ";
140: char *nolink = "cannot link ";
141: char *nocreate = "cannot create";
142: char *nofind = "cannot find";
143: char *noopen = "cannot open";
144:
145: /*
146: * Functions returning non_int.
147: */
148: char *tmalloc();
149: bool dirchecks();
150: bool suppress();
151: LINK *linklocate();
152: LINK *linkinstall();
153: bool linkattempt();
154: bool tgtunlink();
155: char *concat();
156: char *parent();
157: bool isslash();
158:
159: main(ac, av) int ac; char *av[];
160: {
161: ac = 0;
162: aarghh(av);
163: init();
164: cpdir();
165: report();
166: if (aflag)
167: printf("%72s\r", "");
168: return exitval;
169: }
170:
171: aarghh(av)
172: register char *av[];
173: {
174: register char *cp;
175:
176: for (;;) {
177: if ((cp = *++av) == NULL)
178: usage();
179: if (*cp++ != '-')
180: break;
181: while (*cp)
182: switch (*cp++) {
183: case 'a':
184: aflag = TRUE;
185: break;
186: case 'd':
187: dflag = TRUE;
188: break;
189: case 'e':
190: eflag = TRUE;
191: break;
192: case 'r':
193: if (!isdigit(*cp)) {
194: rlimit = 1;
195: break;
196: }
197: *av = cp;
198: rlimit = new_atoi(av);
199: cp = *av;
200: break;
201: case 's':
202: sflag = TRUE;
203: strinstall(cp);
204: ++numsuppress;
205: goto NEXTARG;
206: break;
207: case 't':
208: tflag = wflag = eflag = TRUE;
209: break;
210: case 'u':
211: uflag = TRUE;
212: break;
213: case 'v':
214: vflag = wflag = TRUE;
215: break;
216: case 'V':
217: fprintf(stderr, "cpdir: V%s\n", VERSION);
218: break;
219: default:
220: usage();
221: }
222: NEXTARG:;
223: }
224:
225: if ((dir1=*av++) == NULL || (dir2=*av++) == NULL || *av != NULL)
226: usage();
227: }
228:
229: /*
230: * Actions: Initialize certain flags and the name buffers source and target.
231: * Verify dir1 and dir2 permissions. Dir2 is made if necessary.
232: * Side Effects: On return srcstat contains the status of dir1. If dir2
233: * exists tgtstat contains its status. In the case that tflag is set and dir2
234: * does not exist, tgt_dev and tgtstat.st_dev are the device of target's
235: * parent, the device that target would be created on except for tflag.
236: */
237: init()
238: {
239: struct stat outstat;
240: struct stat errstat;
241:
242: uid = getuid();
243: gid = getgid();
244: root = (uid == ROOTUID);
245: if (root)
246: umask(0);
247:
248: errprefix = !isatty(fileno(stdin));
249: fstat(fileno(stdout), &outstat);
250: fstat(fileno(stderr), &errstat);
251: splitmsg = (outstat.st_ino != errstat.st_ino) ||
252: (outstat.st_dev != errstat.st_dev);
253:
254: dir1len = strlen(dir1);
255: srcsize = (dir1len + 1);
256: source = tmalloc(srcsize);
257: strcpy(source, dir1);
258: dir1slash = isslash(dir1);
259:
260: dir2len = strlen(dir2);
261: tgtsize = dir2len + 1;
262: target = tmalloc(tgtsize);
263: strcpy(target, dir2);
264: dir2slash = isslash(dir2);
265:
266: /*
267: * We must check boundary conditions before we call dirchecks().
268: * These are: source exists and is a directory, and if target does not
269: * exist that we have write and search permission on its parent and
270: * tgt_dev contains the device number of the parent.
271: */
272: if (stat(dir1, &srcstat) < 0)
273: fatal(fmt2, dir1, nofind);
274: if ((srcstat.st_mode & S_IFMT) != S_IFDIR)
275: fatal(fmt2, dir1, notdir);
276: if (access(dir2, 0) < 0) {
277: register char *cp;
278: cp = parent(dir2);
279: if (stat(cp, &tgtstat) < 0)
280: fatal(fmt2, dir2, "cannot find parent");
281: if (access(cp, AWRITE|ASRCH) < 0)
282: fatal(fmt2, dir2, nomkdir);
283: tgt_dev = tgtstat.st_dev;
284: }
285: if (!dirchecks())
286: exit(1);
287:
288: src_dev = srcstat.st_dev;
289: dir2_dev = tgt_dev = tgtstat.st_dev;
290: if (access(target, 0) == 0)
291: dir2_ino = tgtstat.st_ino;
292: }
293:
294:
295: /*
296: * Assume:
297: * Source is a directory we can read and search. Srcstat contains its status.
298: * Target is a directory we can write and search. Tgtstat contains its status.
299: * Src_dev and tgt_dev are the devices of the parents of source and target.
300: * If tflag is on target may not exist. In this case tgtstat.st_dev
301: * contains the device where target would have been created if tflag were off.
302: */
303: cpdir()
304: {
305: register int n;
306: register int fd;
307: register DIRBUF *dbp;
308: DIRBUF dirbuf;
309: long address;
310: dev_t sdev;
311: dev_t tdev;
312: struct stat locsrcstat;
313: struct stat loctgtstat;
314:
315: /*
316: * Check for circular copy.
317: */
318: if (srcstat.st_ino == dir2_ino &&
319: srcstat.st_dev == dir2_dev) {
320: if (wflag)
321: printf(fmt2, source,
322: "not copied to avoid circular copy");
323: return;
324: }
325:
326: /*
327: * Open source, and prepare dirbuf.
328: */
329: if ((fd = open(source, 0)) < 0) {
330: errprint(fmt2, source, noopen);
331: return;
332: }
333: dbp = &dirbuf;
334: dbp->cbuf[SDSIZ] = '\0';
335:
336: /*
337: * Save srcstat, src_dev, tgtstat, tgt_dev to restore before exit.
338: */
339: locsrcstat = srcstat;
340: loctgtstat = tgtstat;
341: tdev = tgt_dev;
342: sdev = src_dev;
343:
344: /*
345: * Detect the crossing of device boundaries.
346: */
347: if (tgt_dev != tgtstat.st_dev) {
348: if (wflag)
349: printf(fmt2, target, devbound);
350: tgt_dev = tgtstat.st_dev;
351: }
352: if (src_dev != srcstat.st_dev) {
353: if (wflag)
354: printf(fmt2, source, devbound);
355: src_dev = srcstat.st_dev;
356: }
357:
358: /*
359: * Loop through the directory source.
360: */
361: while ((n = read(fd, dbp->cbuf, SDSIZ)) > 0) {
362: if (n != SDSIZ) {
363: errprint(fmt2, source, readerr);
364: close(fd);
365: goto OUT;
366: }
367:
368: canino(dbp->db_ino);
369: if (dbp->db_ino == INODE0 ||
370: !strcmp(dbp->db_name, ".") ||
371: !strcmp(dbp->db_name, ".."))
372: continue;
373:
374: grow(dbp->db_name);
375: if (sflag && suppress()) {
376: if (vflag)
377: printf(fmt2, source, supressd);
378: shrink();
379: continue;
380: }
381: if (stat(source, &srcstat) < 0) {
382: errprint(fmt2, source, nofind);
383: shrink();
384: continue;
385: }
386:
387: switch (srcstat.st_mode & S_IFMT) {
388: default:
389: errprint(fmt2, source, "unknown file type");
390: shrink();
391: continue;
392: case S_IFCHR:
393: case S_IFBLK:
394: cpnode();
395: shrink();
396: continue;
397: case S_IFPIP:
398: srcstat.st_rdev = DEV0;
399: cpnode();
400: shrink();
401: continue;
402: case S_IFREG:
403: cpfile();
404: shrink();
405: continue;
406: case S_IFDIR:
407: if (rlevel == rlimit) {
408: if (vflag)
409: printf(fmt2, source, supressd);
410: shrink();
411: continue;
412: }
413: if (!dirchecks()) {
414: shrink();
415: continue;
416: }
417:
418: address = lseek(fd, 0L, 1);
419: close(fd);
420: ++rlevel;
421: cpdir();
422: --rlevel;
423: shrink();
424: if ((fd = open(source, 0)) < 0) {
425: errprint(fmt2, source,
426: "cannot reopen, copy incomplete");
427: goto OUT;
428: }
429: lseek(fd, address, 0);
430: continue;
431: }
432: }
433: close(fd);
434:
435: OUT:
436: vprintf();
437: srcstat = locsrcstat;
438: tgtstat = loctgtstat;
439: tgt_dev = tdev;
440: src_dev = sdev;
441: adjust();
442: }
443:
444: /*
445: * Assume source names the file to be copied, srcstat contains its status,
446: * and target is the file to copy to. Target may or may not exist.
447: */
448: cpfile()
449: {
450: #ifndef SLOW
451: register char *ip;
452: #endif
453: register int n;
454: register int i;
455: register int fd1;
456: register int fd2;
457: register fsize_t size;
458: #ifndef SLOW
459: register char *wp; /* write pointer */
460: register int wflag; /* write flag */
461: static char buf[CPBUFSIZ]; /* copy buffer */
462: #else
463: static char buf[BUFSIZ];
464: #endif
465: int intflag, hupflag;
466: LINK *lp;
467:
468: if (access(source, AREAD) < 0) {
469: errprint(fmt2, source, noopen);
470: return;
471: }
472:
473: if (uflag && stat(target, &tgtstat) == 0)
474: if (srcstat.st_mtime <= tgtstat.st_mtime) {
475: /*
476: * Target exists and is not older, do not update.
477: * But watch out for links and keep the link counts sane.
478: */
479: if (vflag)
480: printf(fmt2, target, "no update");
481: if (srcstat.st_nlink > 1) {
482: if ((lp = linklocate(SOURCE)) == NULL)
483: linkinstall(SOURCE);
484: else if (lp->l_nlink == 0)
485: linkpurge(SOURCE, lp);
486: }
487: if (tgtstat.st_nlink > 1) {
488: if ((lp = linklocate(TARGET)) == NULL)
489: linkinstall(TARGET);
490: else if (lp->l_nlink == 0)
491: linkpurge(TARGET, lp);
492: }
493: return;
494: }
495:
496: if (!tgtunlink())
497: return;
498: if (srcstat.st_nlink > 1)
499: if (linkattempt())
500: return;
501:
502: if (tflag) {
503: vprintf();
504: return;
505: }
506:
507: intflag = catch(SIGINT);
508: hupflag = catch(SIGHUP);
509: if ((fd1 = open(source, 0)) < 0) {
510: errprint(fmt2, source, noopen);
511: return;
512: }
513: if ((fd2 = creat(target, 0)) < 0) {
514: close(fd1);
515: errprint(fmt2, target, nocreate);
516: return;
517: }
518:
519: size = srcstat.st_size;
520: #ifndef SLOW
521: while ((n = read(fd1, buf, sizeof(buf))) > 0) {
522: #else
523: while ((n = read(fd1, buf, BUFSIZ)) > 0) {
524: #endif
525: /*
526: * Check for blocks of zeroes (holes in a sparse file).
527: * However, a block of zeroes at the end of a file must be
528: * written so the file has correct length.
529: */
530: #ifndef SLOW
531: wp = ip = buf;
532: size -= n;
533: while ((n-BUFSIZ) > 0 || ((n-BUFSIZ) == 0 && size != 0)) {
534: n -= BUFSIZ;
535: wflag = FALSE;
536: ip = wp;
537: for (i = 0; i < BUFSIZ; ++i)
538: if (*ip++ != '\0') {
539: wflag = TRUE;
540: break;
541: }
542: if (wflag) {
543: if (write(fd2, wp, BUFSIZ) < BUFSIZ) {
544: errprint(fmt2, target, writeerr);
545: close(fd1);
546: close(fd2);
547: return FALSE;
548: }
549: } else {
550: lseek(fd2, (long)BUFSIZ, 1);
551: }
552: wp += BUFSIZ;
553: }
554: if (write(fd2, wp, n) < n) {
555: errprint(fmt2, target, writeerr);
556: close(fd1);
557: close(fd2);
558: return FALSE;
559: }
560: #else
561: if ((size -= n) == (fsize_t)0)
562: goto WRITE;
563: for (i = 0; i < n; ++i)
564: if (buf[i] != '\0')
565: goto WRITE;
566: lseek(fd2, (long)BUFSIZ, 1);
567: continue;
568:
569: WRITE:
570: if (write(fd2, buf, n) < n) {
571: errprint(fmt2, target, writeerr);
572: close(fd1);
573: close(fd2);
574: return FALSE;
575: }
576: #endif
577: }
578: close(fd1);
579: close(fd2);
580: adjust();
581: if (intflag)
582: signal(SIGINT, SIG_DFL);
583: if (hupflag)
584: signal(SIGHUP, SIG_DFL);
585: if (interrupted)
586: exit(1);
587:
588: if (n < 0)
589: errprint(fmt2, source, readerr);
590: else
591: vprintf();
592: }
593:
594: /*
595: * Copy special nodes and named pipes.
596: */
597: cpnode()
598: {
599: if (!root) {
600: if (vflag)
601: printf(fmt2, source, "not the super-user");
602: return;
603: }
604: if (!tgtunlink())
605: return;
606: if (srcstat.st_nlink > 1)
607: if (linkattempt())
608: return;
609: if (!tflag) {
610: if (mknod(target, srcstat.st_mode, srcstat.st_rdev) < 0) {
611: errprint(fmt2, target, "cannot make node");
612: return;
613: }
614: adjust();
615: }
616: if (vflag)
617: printf(fmt2, target, "copied node");
618: else if (aflag)
619: printf(fmt1a, target);
620: }
621:
622:
623: /*
624: * Grow each of target and source by appending '/' and cp.
625: */
626: grow(cp)
627: register char *cp;
628: {
629: register int a;
630: register int b;
631:
632: a = strlen(cp) + 2;
633:
634: b = strlen(source);
635: if (a+b > srcsize) {
636: if ((source = realloc(source, a+b)) == NULL)
637: nomemory();
638: srcsize = a+b;
639: }
640: if (rlevel > 1 || !dir1slash)
641: source[b++] = '/';
642: strcpy(source+b, cp);
643:
644: b = strlen(target);
645: if (a+b > tgtsize) {
646: if ((target = realloc(target, a+b)) == NULL)
647: nomemory();
648: tgtsize = a+b;
649: }
650: if (rlevel > 1 || !dir2slash)
651: target[b++] = '/';
652: strcpy(target+b, cp);
653: }
654:
655: /*
656: * Shrink source and target down by the last pathname component.
657: */
658: shrink()
659: {
660: if (rlevel == 1 && dir1slash)
661: source[dir1len] = '\0';
662: else
663: *rindex(source, '/') = '\0';
664: if (rlevel == 1 && dir2slash)
665: target[dir2len] = '\0';
666: else
667: *rindex(target, '/') = '\0';
668: }
669:
670:
671: /*
672: * Returns a value equal to atoi(*cpp). Positive decimal integers only.
673: * Leaves *cpp pointing to the character that terminated the digit string.
674: */
675: new_atoi(cpp)
676: char **cpp;
677: {
678: register int sum = 0;
679: register int c;
680: register char *cp;
681:
682: sum = 0;
683: cp = *cpp;
684: while (isdigit(c = *cp++)) {
685: sum *= 10;
686: sum -= '0';
687: sum += c;
688: }
689: *cpp = cp-1;
690: return sum;
691: }
692:
693: errprint(arg)
694: char *arg[];
695: {
696: register char *format;
697:
698: format = (errprefix) ? "cpdir: %r" : "%r";
699: fprintf(stderr, format, &arg);
700: if (wflag && splitmsg)
701: printf(format, &arg);
702: if (!eflag)
703: exit(1);
704: exitval = 1;
705: }
706:
707: fatal(arg)
708: char *arg[];
709: {
710: errprint("%r", &arg);
711: exit(1);
712: }
713:
714: /*
715: * Interface to malloc to check for bad returns.
716: */
717: char *
718: tmalloc(n)
719: int n;
720: {
721: register char *cp;
722: if ((cp = malloc(n)) == NULL)
723: nomemory();
724: return cp;
725: }
726:
727: /*
728: * Assume: source is a dir, srcstat has its status, we have write and search
729: * permission on target's parent directory, tgt_dev is the device of target's
730: * parent.
731: * Actions: Check permissions on source. Check permissions on target if it
732: * exists. If non-extant and tflag is off, make it.
733: * Side Effects: On return, if target exists, tgtstat contains its status. If
734: * it does not exist, tgtstat.st_dev is the device it would have been created
735: * on if tflag were off.
736: * Return TRUE if everything Aok, FALSE otherwise.
737: */
738: bool
739: dirchecks()
740: {
741: if (access(source, AREAD | ASRCH) < 0) {
742: errprint(fmt2, source, nopermit);
743: return FALSE;
744: }
745:
746: if (stat(target, &tgtstat) == 0) {
747: if ((tgtstat.st_mode & S_IFMT) != S_IFDIR) {
748: errprint(fmt3, source, "target is ", notdir);
749: return FALSE;
750: }
751: if (access(target, AWRITE | ASRCH) < 0) {
752: errprint(fmt2, target, nopermit);
753: return FALSE;
754: }
755: return TRUE;
756: }
757:
758: if (tflag) {
759: tgtstat.st_dev = tgt_dev;
760: return TRUE;
761: }
762:
763: if (mkdir(target, 0700)) {
764: errprint(fmt2, target, nomkdir);
765: return FALSE;
766: }
767:
768: if (stat(target, &tgtstat) < 0) {
769: errprint(fmt2, target, "made but cannot stat");
770: return FALSE;
771: }
772: return TRUE;
773: }
774:
775: strinstall(cp)
776: register char *cp;
777: {
778: register STR *sp;
779: register int n;
780:
781: n = strhash(cp);
782: for (sp = strtab[n]; sp != NULL; sp = sp->s_next)
783: if (strcmp(cp, sp->s_str) == 0)
784: return;
785: sp = (STR *) tmalloc(sizeof(STR) + strlen(cp) + 1);
786: sp->s_next = strtab[n];
787: strtab[n] = sp;
788: strcpy(sp->s_str, cp);
789: }
790:
791: bool
792: suppress()
793: {
794: register char *cp;
795: register STR *sp;
796: register STR **spp;
797:
798: cp = source + dir1len;
799: if (!dir1slash)
800: ++cp;
801: spp = strtab + strhash(cp);
802: if ((sp = *spp) == NULL)
803: return FALSE;
804: while (sp != NULL) {
805: if (strcmp(cp, sp->s_str) == 0) {
806: *spp = sp->s_next;
807: free(sp);
808: if (--numsuppress == 0)
809: sflag = FALSE;
810: return TRUE;
811: }
812: spp = &(sp->s_next);
813: sp = *spp;
814: }
815: return FALSE;
816: }
817:
818: strhash(cp)
819: register uchar_t *cp;
820: {
821: register uint_t sum = 0;
822: for (sum = 0; *cp != '\0'; sum += *cp++)
823: ;
824: return sum % HASHSIZE;
825: }
826:
827: /*
828: * Look for a LINK entry in the source or target link table.
829: * If found, decrement the link count field and return a pointer to the LINK.
830: * If not found, return NULL.
831: */
832: LINK *
833: linklocate(flag)
834: int flag;
835: {
836: register LINK *lp;
837: register struct stat *stp;
838:
839: if (flag == SOURCE) {
840: stp = &srcstat;
841: lp = srctab[hash(stp->st_ino)];
842: } else {
843: stp = &tgtstat;
844: lp = tgttab[hash(stp->st_ino)];
845: }
846:
847: for ( ; lp != NULL; lp = lp->l_next) {
848: if (lp->l_ino != stp->st_ino)
849: continue;
850: if (lp->l_dev != stp->st_dev)
851: continue;
852: --(lp->l_nlink);
853: return lp;
854: }
855: return NULL;
856: }
857:
858: /*
859: * Create a new LINK table entry and add it to the source or target LINK table.
860: * Initialize its link count to nlinks - 1.
861: */
862: LINK *
863: linkinstall(flag)
864: int flag;
865: {
866: register LINK **lpp;
867: register LINK *lp;
868: register struct stat *stp;
869:
870: if (flag == SOURCE) {
871: ++srclinks;
872: stp = &srcstat;
873: lpp = hash(stp->st_ino) + srctab;
874: }
875: else {
876: ++tgtlinks;
877: stp = &tgtstat;
878: lpp = hash(stp->st_ino) + tgttab;
879: }
880:
881: lp = (LINK *) tmalloc(sizeof(LINK) + strlen(target) - dir2len + 1);
882: if (!dir2slash)
883: strcpy(lp->l_name, target + dir2len + 1);
884: else
885: strcpy(lp->l_name, target + dir2len);
886: lp->l_dev = stp->st_dev;
887: lp->l_ino = stp->st_ino;
888: lp->l_tdev = tgt_dev;
889: lp->l_nlink = stp->st_nlink - 1;
890: lp->l_next = *lpp;
891: *lpp = lp;
892: return lp;
893: }
894:
895: /*
896: * Purge LINK entry lp from the source or target LINK table.
897: */
898: linkpurge(flag, lp)
899: int flag;
900: register LINK *lp;
901: {
902: register LINK *lp1;
903: register LINK **lpp;
904:
905: if (flag == SOURCE) {
906: --srclinks;
907: lpp = hash(srcstat.st_ino) + srctab;
908: }
909: else {
910: --tgtlinks;
911: lpp = hash(tgtstat.st_ino) + tgttab;
912: }
913:
914: for (lp1 = *lpp; lp1 != NULL; *lpp = lp1, lp1 = lp1->l_next) {
915: if (lp1 != lp)
916: continue;
917: *lpp = lp1->l_next;
918: free((char *)lp1);
919: return;
920: }
921: }
922:
923: /*
924: * The source file has a link count greater than 1.
925: * If the source file is not in the source LINK list already,
926: * add it to the list and return FALSE.
927: */
928: bool
929: linkattempt()
930: {
931: register LINK *lp;
932: register bool ret;
933: register char *cp;
934:
935: ret = TRUE;
936: if ((lp = linklocate(SOURCE)) == NULL) {
937: linkinstall(SOURCE); /* add new source LINK */
938: return FALSE;
939: }
940: cp = concat(dir2, lp->l_name);
941:
942: if (tflag) { /* test only */
943: if (tgt_dev != lp->l_tdev) {
944: errprint(fmt4, target, nolink, " to ", cp);
945: ++broken;
946: ret = FALSE;
947: } else
948: vprintf();
949: } else if (link(cp, target) == 0) { /* link succeeded */
950: vprintf();
951: } else { /* link failed */
952: errprint(fmt4, target, nolink, " to ", cp);
953: ++broken;
954: ret = FALSE;
955: }
956:
957: if (lp->l_nlink == 0)
958: linkpurge(SOURCE, lp);
959: return ret;
960: }
961:
962: /*
963: * Unlink the target.
964: * Return TRUE if the unlink is successful (or target does not exist)
965: * or FALSE if the unlink fails.
966: */
967: bool
968: tgtunlink()
969: {
970: register LINK *lp;
971:
972: if (stat(target, &tgtstat) < 0)
973: return TRUE;
974: if ((tgtstat.st_mode & S_IFMT) == S_IFDIR) {
975: errprint(fmt3, target, nounlink, "directory");
976: return FALSE;
977: } else if ((tgtstat.st_mode & S_IFMT) != (srcstat.st_mode & S_IFMT)) {
978: errprint(fmt3, source, "file type mismatch with ", target);
979: return FALSE;
980: }
981:
982: if (tflag)
983: printf(fmt2, target, "unlinked");
984: else if (unlink(target) < 0) {
985: errprint(fmt2, target, nounlink);
986: return FALSE; /* unlink failed */
987: }
988:
989: /* The unlink succeeded, decrement the target link count. */
990: if ((lp = linklocate(TARGET)) != NULL) { /* target LINK exists */
991: if (lp->l_nlink == 0)
992: linkpurge(TARGET, lp);
993: } else if (tgtstat.st_nlink > 1)
994: linkinstall(TARGET); /* add new target LINK */
995: return TRUE;
996: }
997:
998: /*
999: * Report on link botches.
1000: * This information is not known until the actual copying is finished.
1001: */
1002: report()
1003: {
1004: register LINK *lp;
1005: register LINK **lpp;
1006: static char *fmtlinks = "%s external links into hierarchy %s:\n";
1007:
1008: if (!wflag)
1009: return;
1010: if (broken)
1011: printf("%d internal link%s broken\n", broken,
1012: (broken == 1) ? "" : "s");
1013: if (srclinks) {
1014: printf(fmtlinks, "missed", dir1);
1015: for (lpp = srctab; lpp < srctab + HASHSIZE; ++lpp)
1016: for (lp = *lpp; lp != NULL; lp = lp->l_next)
1017: printf("\t%d\t%s (inode %d)\n", lp->l_nlink,
1018: concat(dir1, lp->l_name), lp->l_ino);
1019: }
1020: if (tgtlinks) {
1021: printf(fmtlinks, "broken", target);
1022: for (lpp = tgttab; lpp < tgttab + HASHSIZE; ++lpp)
1023: for (lp = *lpp; lp != NULL; lp = lp->l_next)
1024: printf("\t%d\t%s (inode %d)\n", lp->l_nlink,
1025: concat(dir2, lp->l_name), lp->l_ino);
1026: }
1027: }
1028:
1029: /*
1030: * Chown, chmod, and chdate target. Assume srcstat has status of source,
1031: * and tgtstat has status of target if it exists.
1032: */
1033: adjust()
1034: {
1035: time_t date[2];
1036:
1037: if (tflag)
1038: return;
1039: if (root)
1040: chown(target, srcstat.st_uid, srcstat.st_gid);
1041: else
1042: chown(target, uid, gid);
1043: chmod(target, srcstat.st_mode & (root ? 07777 : 06777));
1044: if (dflag) {
1045: time(&date[0]);
1046: date[1] = srcstat.st_mtime;
1047: utime(target, date);
1048: }
1049: }
1050:
1051: /*
1052: * Concatenate pieces of pathnames, a and b. If a is not "/" a '/' char
1053: * is placed between the names. Previous return is freed.
1054: */
1055: char *
1056: concat(a, b)
1057: register char *a;
1058: char *b;
1059: {
1060: static char *ret;
1061: register int a1;
1062: register char *rp;
1063:
1064: if (ret != NULL)
1065: free(ret);
1066: a1 = strlen(a);
1067: rp = ret = tmalloc(a1 + strlen(b) + 2);
1068: strcpy(rp, a);
1069: rp += a1;
1070: if (!isslash(ret))
1071: *rp++ = '/';
1072: strcpy(rp, b);
1073: return ret;
1074: }
1075:
1076: char *
1077: parent(cp)
1078: register char *cp;
1079: {
1080: static char *ret;
1081: register char *cp0;
1082:
1083: if (ret != NULL)
1084: free(ret);
1085: if ((cp0 = rindex(cp, '/')) == NULL)
1086: return ".";
1087: ret = tmalloc(cp0 - cp + 1);
1088: strncpy(ret, cp, cp0 - cp);
1089: return ret;
1090: }
1091:
1092: bool
1093: isslash(cp)
1094: register char *cp;
1095: {
1096: register int c;
1097:
1098: while ((c = *cp++) != '\0')
1099: if (c != '/')
1100: return FALSE;
1101: return TRUE;
1102: }
1103:
1104: /*
1105: * Routine exectuted when SIGINT or SIGHUP caught.
1106: * Set the interrupted flag.
1107: */
1108: onintr()
1109: {
1110: signal(SIGINT, SIG_IGN);
1111: signal(SIGHUP, SIG_IGN);
1112: ++interrupted;
1113: }
1114:
1115: /*
1116: * If sig's handler is currently SIG_DFL,
1117: * catch it with onintr and return 1.
1118: * If not, leave its handler unchanged and return 0.
1119: */
1120: int
1121: catch(sig) int sig;
1122: {
1123: int (*old)();
1124:
1125: if ((old = signal(sig, SIG_IGN)) == SIG_DFL) {
1126: signal(sig, onintr);
1127: return 1;
1128: }
1129: signal(sig, old);
1130: return 0;
1131: }
1132:
1133: vprintf()
1134: {
1135: if (vflag)
1136: printf(fmt1, target);
1137: else if (aflag) {
1138: printf(fmt1a, target);
1139: fflush(stdout);
1140: }
1141: }
1142:
1143: usage()
1144: {
1145: fprintf(stderr, "Usage: cpdir [options] dir1 dir2\n");
1146: exit(1);
1147: }
1148:
1149: nomemory()
1150: {
1151: fatal(fmt1, "out of memory");
1152: }
1153:
1154: /* end of cpdir.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.