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