|
|
1.1 root 1: /*
2: * Copyright (c) 1983 The Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted
6: * provided that: (1) source distributions retain this entire copyright
7: * notice and comment, and (2) distributions including binaries display
8: * the following acknowledgement: ``This product includes software
9: * developed by the University of California, Berkeley and its contributors''
10: * in the documentation or other materials provided with the distribution
11: * and in all advertising materials mentioning features or use of this
12: * software. Neither the name of the University nor the names of its
13: * contributors may be used to endorse or promote products derived
14: * from this software without specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: static char sccsid[] = "@(#)restore.c 5.7 (Berkeley) 6/1/90";
22: #endif /* not lint */
23:
24: #include "restore.h"
25:
26: /*
27: * This implements the 't' option.
28: * List entries on the tape.
29: */
30: long
31: listfile(name, ino, type)
32: char *name;
33: ino_t ino;
34: int type;
35: {
36: long descend = hflag ? GOOD : FAIL;
37:
38: if (BIT(ino, dumpmap) == 0) {
39: return (descend);
40: }
41: vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
42: fprintf(stdout, "%10d\t%s\n", ino, name);
43: return (descend);
44: }
45:
46: /*
47: * This implements the 'x' option.
48: * Request that new entries be extracted.
49: */
50: long
51: addfile(name, ino, type)
52: char *name;
53: ino_t ino;
54: int type;
55: {
56: register struct entry *ep;
57: long descend = hflag ? GOOD : FAIL;
58: char buf[100];
59:
60: if (BIT(ino, dumpmap) == 0) {
61: dprintf(stdout, "%s: not on the tape\n", name);
62: return (descend);
63: }
64: if (!mflag) {
65: (void) sprintf(buf, "./%u", ino);
66: name = buf;
67: if (type == NODE) {
68: (void) genliteraldir(name, ino);
69: return (descend);
70: }
71: }
72: ep = lookupino(ino);
73: if (ep != NIL) {
74: if (strcmp(name, myname(ep)) == 0) {
75: ep->e_flags |= NEW;
76: return (descend);
77: }
78: type |= LINK;
79: }
80: ep = addentry(name, ino, type);
81: if (type == NODE)
82: newnode(ep);
83: ep->e_flags |= NEW;
84: return (descend);
85: }
86:
87: /*
88: * This is used by the 'i' option to undo previous requests made by addfile.
89: * Delete entries from the request queue.
90: */
91: /* ARGSUSED */
92: long
93: deletefile(name, ino, type)
94: char *name;
95: ino_t ino;
96: int type;
97: {
98: long descend = hflag ? GOOD : FAIL;
99: struct entry *ep;
100:
101: if (BIT(ino, dumpmap) == 0) {
102: return (descend);
103: }
104: ep = lookupino(ino);
105: if (ep != NIL)
106: ep->e_flags &= ~NEW;
107: return (descend);
108: }
109:
110: /*
111: * The following four routines implement the incremental
112: * restore algorithm. The first removes old entries, the second
113: * does renames and calculates the extraction list, the third
114: * cleans up link names missed by the first two, and the final
115: * one deletes old directories.
116: *
117: * Directories cannot be immediately deleted, as they may have
118: * other files in them which need to be moved out first. As
119: * directories to be deleted are found, they are put on the
120: * following deletion list. After all deletions and renames
121: * are done, this list is actually deleted.
122: */
123: static struct entry *removelist;
124:
125: /*
126: * Remove unneeded leaves from the old tree.
127: * Remove directories from the lookup chains.
128: */
129: removeoldleaves()
130: {
131: register struct entry *ep;
132: register ino_t i;
133:
134: vprintf(stdout, "Mark entries to be removed.\n");
135: for (i = ROOTINO + 1; i < maxino; i++) {
136: ep = lookupino(i);
137: if (ep == NIL)
138: continue;
139: if (BIT(i, clrimap))
140: continue;
141: for ( ; ep != NIL; ep = ep->e_links) {
142: dprintf(stdout, "%s: REMOVE\n", myname(ep));
143: if (ep->e_type == LEAF) {
144: removeleaf(ep);
145: freeentry(ep);
146: } else {
147: mktempname(ep);
148: deleteino(ep->e_ino);
149: ep->e_next = removelist;
150: removelist = ep;
151: }
152: }
153: }
154: }
155:
156: /*
157: * For each directory entry on the incremental tape, determine which
158: * category it falls into as follows:
159: * KEEP - entries that are to be left alone.
160: * NEW - new entries to be added.
161: * EXTRACT - files that must be updated with new contents.
162: * LINK - new links to be added.
163: * Renames are done at the same time.
164: */
165: long
166: nodeupdates(name, ino, type)
167: char *name;
168: ino_t ino;
169: int type;
170: {
171: register struct entry *ep, *np, *ip;
172: long descend = GOOD;
173: int lookuptype = 0;
174: int key = 0;
175: /* key values */
176: # define ONTAPE 0x1 /* inode is on the tape */
177: # define INOFND 0x2 /* inode already exists */
178: # define NAMEFND 0x4 /* name already exists */
179: # define MODECHG 0x8 /* mode of inode changed */
180: extern char *keyval();
181:
182: /*
183: * This routine is called once for each element in the
184: * directory hierarchy, with a full path name.
185: * The "type" value is incorrectly specified as LEAF for
186: * directories that are not on the dump tape.
187: *
188: * Check to see if the file is on the tape.
189: */
190: if (BIT(ino, dumpmap))
191: key |= ONTAPE;
192: /*
193: * Check to see if the name exists, and if the name is a link.
194: */
195: np = lookupname(name);
196: if (np != NIL) {
197: key |= NAMEFND;
198: ip = lookupino(np->e_ino);
199: if (ip == NULL)
200: panic("corrupted symbol table\n");
201: if (ip != np)
202: lookuptype = LINK;
203: }
204: /*
205: * Check to see if the inode exists, and if one of its links
206: * corresponds to the name (if one was found).
207: */
208: ip = lookupino(ino);
209: if (ip != NIL) {
210: key |= INOFND;
211: for (ep = ip->e_links; ep != NIL; ep = ep->e_links) {
212: if (ep == np) {
213: ip = ep;
214: break;
215: }
216: }
217: }
218: /*
219: * If both a name and an inode are found, but they do not
220: * correspond to the same file, then both the inode that has
221: * been found and the inode corresponding to the name that
222: * has been found need to be renamed. The current pathname
223: * is the new name for the inode that has been found. Since
224: * all files to be deleted have already been removed, the
225: * named file is either a now unneeded link, or it must live
226: * under a new name in this dump level. If it is a link, it
227: * can be removed. If it is not a link, it is given a
228: * temporary name in anticipation that it will be renamed
229: * when it is later found by inode number.
230: */
231: if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
232: if (lookuptype == LINK) {
233: removeleaf(np);
234: freeentry(np);
235: } else {
236: dprintf(stdout, "name/inode conflict, mktempname %s\n",
237: myname(np));
238: mktempname(np);
239: }
240: np = NIL;
241: key &= ~NAMEFND;
242: }
243: if ((key & ONTAPE) &&
244: (((key & INOFND) && ip->e_type != type) ||
245: ((key & NAMEFND) && np->e_type != type)))
246: key |= MODECHG;
247:
248: /*
249: * Decide on the disposition of the file based on its flags.
250: * Note that we have already handled the case in which
251: * a name and inode are found that correspond to different files.
252: * Thus if both NAMEFND and INOFND are set then ip == np.
253: */
254: switch (key) {
255:
256: /*
257: * A previously existing file has been found.
258: * Mark it as KEEP so that other links to the inode can be
259: * detected, and so that it will not be reclaimed by the search
260: * for unreferenced names.
261: */
262: case INOFND|NAMEFND:
263: ip->e_flags |= KEEP;
264: dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
265: flagvalues(ip));
266: break;
267:
268: /*
269: * A file on the tape has a name which is the same as a name
270: * corresponding to a different file in the previous dump.
271: * Since all files to be deleted have already been removed,
272: * this file is either a now unneeded link, or it must live
273: * under a new name in this dump level. If it is a link, it
274: * can simply be removed. If it is not a link, it is given a
275: * temporary name in anticipation that it will be renamed
276: * when it is later found by inode number (see INOFND case
277: * below). The entry is then treated as a new file.
278: */
279: case ONTAPE|NAMEFND:
280: case ONTAPE|NAMEFND|MODECHG:
281: if (lookuptype == LINK) {
282: removeleaf(np);
283: freeentry(np);
284: } else {
285: mktempname(np);
286: }
287: /* fall through */
288:
289: /*
290: * A previously non-existent file.
291: * Add it to the file system, and request its extraction.
292: * If it is a directory, create it immediately.
293: * (Since the name is unused there can be no conflict)
294: */
295: case ONTAPE:
296: ep = addentry(name, ino, type);
297: if (type == NODE)
298: newnode(ep);
299: ep->e_flags |= NEW|KEEP;
300: dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
301: flagvalues(ep));
302: break;
303:
304: /*
305: * A file with the same inode number, but a different
306: * name has been found. If the other name has not already
307: * been found (indicated by the KEEP flag, see above) then
308: * this must be a new name for the file, and it is renamed.
309: * If the other name has been found then this must be a
310: * link to the file. Hard links to directories are not
311: * permitted, and are either deleted or converted to
312: * symbolic links. Finally, if the file is on the tape,
313: * a request is made to extract it.
314: */
315: case ONTAPE|INOFND:
316: if (type == LEAF && (ip->e_flags & KEEP) == 0)
317: ip->e_flags |= EXTRACT;
318: /* fall through */
319: case INOFND:
320: if ((ip->e_flags & KEEP) == 0) {
321: renameit(myname(ip), name);
322: moveentry(ip, name);
323: ip->e_flags |= KEEP;
324: dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
325: flagvalues(ip));
326: break;
327: }
328: if (ip->e_type == NODE) {
329: descend = FAIL;
330: fprintf(stderr,
331: "deleted hard link %s to directory %s\n",
332: name, myname(ip));
333: break;
334: }
335: ep = addentry(name, ino, type|LINK);
336: ep->e_flags |= NEW;
337: dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
338: flagvalues(ep));
339: break;
340:
341: /*
342: * A previously known file which is to be updated.
343: */
344: case ONTAPE|INOFND|NAMEFND:
345: if (type == LEAF && lookuptype != LINK)
346: np->e_flags |= EXTRACT;
347: np->e_flags |= KEEP;
348: dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
349: flagvalues(np));
350: break;
351:
352: /*
353: * An inode is being reused in a completely different way.
354: * Normally an extract can simply do an "unlink" followed
355: * by a "creat". Here we must do effectively the same
356: * thing. The complications arise because we cannot really
357: * delete a directory since it may still contain files
358: * that we need to rename, so we delete it from the symbol
359: * table, and put it on the list to be deleted eventually.
360: * Conversely if a directory is to be created, it must be
361: * done immediately, rather than waiting until the
362: * extraction phase.
363: */
364: case ONTAPE|INOFND|MODECHG:
365: case ONTAPE|INOFND|NAMEFND|MODECHG:
366: if (ip->e_flags & KEEP) {
367: badentry(ip, "cannot KEEP and change modes");
368: break;
369: }
370: if (ip->e_type == LEAF) {
371: /* changing from leaf to node */
372: removeleaf(ip);
373: freeentry(ip);
374: ip = addentry(name, ino, type);
375: newnode(ip);
376: } else {
377: /* changing from node to leaf */
378: if ((ip->e_flags & TMPNAME) == 0)
379: mktempname(ip);
380: deleteino(ip->e_ino);
381: ip->e_next = removelist;
382: removelist = ip;
383: ip = addentry(name, ino, type);
384: }
385: ip->e_flags |= NEW|KEEP;
386: dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
387: flagvalues(ip));
388: break;
389:
390: /*
391: * A hard link to a diirectory that has been removed.
392: * Ignore it.
393: */
394: case NAMEFND:
395: dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
396: name);
397: descend = FAIL;
398: break;
399:
400: /*
401: * If we find a directory entry for a file that is not on
402: * the tape, then we must have found a file that was created
403: * while the dump was in progress. Since we have no contents
404: * for it, we discard the name knowing that it will be on the
405: * next incremental tape.
406: */
407: case NIL:
408: fprintf(stderr, "%s: (inode %d) not found on tape\n",
409: name, ino);
410: break;
411:
412: /*
413: * If any of these arise, something is grievously wrong with
414: * the current state of the symbol table.
415: */
416: case INOFND|NAMEFND|MODECHG:
417: case NAMEFND|MODECHG:
418: case INOFND|MODECHG:
419: fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
420: name);
421: break;
422:
423: /*
424: * These states "cannot" arise for any state of the symbol table.
425: */
426: case ONTAPE|MODECHG:
427: case MODECHG:
428: default:
429: panic("[%s] %s: impossible state\n", keyval(key), name);
430: break;
431: }
432: return (descend);
433: }
434:
435: /*
436: * Calculate the active flags in a key.
437: */
438: char *
439: keyval(key)
440: int key;
441: {
442: static char keybuf[32];
443:
444: (void) strcpy(keybuf, "|NIL");
445: keybuf[0] = '\0';
446: if (key & ONTAPE)
447: (void) strcat(keybuf, "|ONTAPE");
448: if (key & INOFND)
449: (void) strcat(keybuf, "|INOFND");
450: if (key & NAMEFND)
451: (void) strcat(keybuf, "|NAMEFND");
452: if (key & MODECHG)
453: (void) strcat(keybuf, "|MODECHG");
454: return (&keybuf[1]);
455: }
456:
457: /*
458: * Find unreferenced link names.
459: */
460: findunreflinks()
461: {
462: register struct entry *ep, *np;
463: register ino_t i;
464:
465: vprintf(stdout, "Find unreferenced names.\n");
466: for (i = ROOTINO; i < maxino; i++) {
467: ep = lookupino(i);
468: if (ep == NIL || ep->e_type == LEAF || BIT(i, dumpmap) == 0)
469: continue;
470: for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
471: if (np->e_flags == 0) {
472: dprintf(stdout,
473: "%s: remove unreferenced name\n",
474: myname(np));
475: removeleaf(np);
476: freeentry(np);
477: }
478: }
479: }
480: /*
481: * Any leaves remaining in removed directories is unreferenced.
482: */
483: for (ep = removelist; ep != NIL; ep = ep->e_next) {
484: for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
485: if (np->e_type == LEAF) {
486: if (np->e_flags != 0)
487: badentry(np, "unreferenced with flags");
488: dprintf(stdout,
489: "%s: remove unreferenced name\n",
490: myname(np));
491: removeleaf(np);
492: freeentry(np);
493: }
494: }
495: }
496: }
497:
498: /*
499: * Remove old nodes (directories).
500: * Note that this routine runs in O(N*D) where:
501: * N is the number of directory entries to be removed.
502: * D is the maximum depth of the tree.
503: * If N == D this can be quite slow. If the list were
504: * topologically sorted, the deletion could be done in
505: * time O(N).
506: */
507: removeoldnodes()
508: {
509: register struct entry *ep, **prev;
510: long change;
511:
512: vprintf(stdout, "Remove old nodes (directories).\n");
513: do {
514: change = 0;
515: prev = &removelist;
516: for (ep = removelist; ep != NIL; ep = *prev) {
517: if (ep->e_entries != NIL) {
518: prev = &ep->e_next;
519: continue;
520: }
521: *prev = ep->e_next;
522: removenode(ep);
523: freeentry(ep);
524: change++;
525: }
526: } while (change);
527: for (ep = removelist; ep != NIL; ep = ep->e_next)
528: badentry(ep, "cannot remove, non-empty");
529: }
530:
531: /*
532: * This is the routine used to extract files for the 'r' command.
533: * Extract new leaves.
534: */
535: createleaves(symtabfile)
536: char *symtabfile;
537: {
538: register struct entry *ep;
539: ino_t first;
540: long curvol;
541:
542: if (command == 'R') {
543: vprintf(stdout, "Continue extraction of new leaves\n");
544: } else {
545: vprintf(stdout, "Extract new leaves.\n");
546: dumpsymtable(symtabfile, volno);
547: }
548: first = lowerbnd(ROOTINO);
549: curvol = volno;
550: while (curfile.ino < maxino) {
551: first = lowerbnd(first);
552: /*
553: * If the next available file is not the one which we
554: * expect then we have missed one or more files. Since
555: * we do not request files that were not on the tape,
556: * the lost files must have been due to a tape read error,
557: * or a file that was removed while the dump was in progress.
558: */
559: while (first < curfile.ino) {
560: ep = lookupino(first);
561: if (ep == NIL)
562: panic("%d: bad first\n", first);
563: fprintf(stderr, "%s: not found on tape\n", myname(ep));
564: ep->e_flags &= ~(NEW|EXTRACT);
565: first = lowerbnd(first);
566: }
567: /*
568: * If we find files on the tape that have no corresponding
569: * directory entries, then we must have found a file that
570: * was created while the dump was in progress. Since we have
571: * no name for it, we discard it knowing that it will be
572: * on the next incremental tape.
573: */
574: if (first != curfile.ino) {
575: fprintf(stderr, "expected next file %d, got %d\n",
576: first, curfile.ino);
577: skipfile();
578: goto next;
579: }
580: ep = lookupino(curfile.ino);
581: if (ep == NIL)
582: panic("unknown file on tape\n");
583: if ((ep->e_flags & (NEW|EXTRACT)) == 0)
584: badentry(ep, "unexpected file on tape");
585: /*
586: * If the file is to be extracted, then the old file must
587: * be removed since its type may change from one leaf type
588: * to another (eg "file" to "character special").
589: */
590: if ((ep->e_flags & EXTRACT) != 0) {
591: removeleaf(ep);
592: ep->e_flags &= ~REMOVED;
593: }
594: (void) extractfile(myname(ep));
595: ep->e_flags &= ~(NEW|EXTRACT);
596: /*
597: * We checkpoint the restore after every tape reel, so
598: * as to simplify the amount of work re quired by the
599: * 'R' command.
600: */
601: next:
602: if (curvol != volno) {
603: dumpsymtable(symtabfile, volno);
604: skipmaps();
605: curvol = volno;
606: }
607: }
608: }
609:
610: /*
611: * This is the routine used to extract files for the 'x' and 'i' commands.
612: * Efficiently extract a subset of the files on a tape.
613: */
614: createfiles()
615: {
616: register ino_t first, next, last;
617: register struct entry *ep;
618: long curvol;
619:
620: vprintf(stdout, "Extract requested files\n");
621: curfile.action = SKIP;
622: getvol((long)1);
623: skipmaps();
624: skipdirs();
625: first = lowerbnd(ROOTINO);
626: last = upperbnd(maxino - 1);
627: for (;;) {
628: first = lowerbnd(first);
629: last = upperbnd(last);
630: /*
631: * Check to see if any files remain to be extracted
632: */
633: if (first > last)
634: return;
635: /*
636: * Reject any volumes with inodes greater
637: * than the last one needed
638: */
639: while (curfile.ino > last) {
640: curfile.action = SKIP;
641: getvol((long)0);
642: skipmaps();
643: skipdirs();
644: }
645: /*
646: * Decide on the next inode needed.
647: * Skip across the inodes until it is found
648: * or an out of order volume change is encountered
649: */
650: next = lowerbnd(curfile.ino);
651: do {
652: curvol = volno;
653: while (next > curfile.ino && volno == curvol)
654: skipfile();
655: skipmaps();
656: skipdirs();
657: } while (volno == curvol + 1);
658: /*
659: * If volume change out of order occurred the
660: * current state must be recalculated
661: */
662: if (volno != curvol)
663: continue;
664: /*
665: * If the current inode is greater than the one we were
666: * looking for then we missed the one we were looking for.
667: * Since we only attempt to extract files listed in the
668: * dump map, the lost files must have been due to a tape
669: * read error, or a file that was removed while the dump
670: * was in progress. Thus we report all requested files
671: * between the one we were looking for, and the one we
672: * found as missing, and delete their request flags.
673: */
674: while (next < curfile.ino) {
675: ep = lookupino(next);
676: if (ep == NIL)
677: panic("corrupted symbol table\n");
678: fprintf(stderr, "%s: not found on tape\n", myname(ep));
679: ep->e_flags &= ~NEW;
680: next = lowerbnd(next);
681: }
682: /*
683: * The current inode is the one that we are looking for,
684: * so extract it per its requested name.
685: */
686: if (next == curfile.ino && next <= last) {
687: ep = lookupino(next);
688: if (ep == NIL)
689: panic("corrupted symbol table\n");
690: (void) extractfile(myname(ep));
691: ep->e_flags &= ~NEW;
692: if (volno != curvol)
693: skipmaps();
694: }
695: }
696: }
697:
698: /*
699: * Add links.
700: */
701: createlinks()
702: {
703: register struct entry *np, *ep;
704: register ino_t i;
705: char name[BUFSIZ];
706:
707: vprintf(stdout, "Add links\n");
708: for (i = ROOTINO; i < maxino; i++) {
709: ep = lookupino(i);
710: if (ep == NIL)
711: continue;
712: for (np = ep->e_links; np != NIL; np = np->e_links) {
713: if ((np->e_flags & NEW) == 0)
714: continue;
715: (void) strcpy(name, myname(ep));
716: if (ep->e_type == NODE) {
717: (void) linkit(name, myname(np), SYMLINK);
718: } else {
719: (void) linkit(name, myname(np), HARDLINK);
720: }
721: np->e_flags &= ~NEW;
722: }
723: }
724: }
725:
726: /*
727: * Check the symbol table.
728: * We do this to insure that all the requested work was done, and
729: * that no temporary names remain.
730: */
731: checkrestore()
732: {
733: register struct entry *ep;
734: register ino_t i;
735:
736: vprintf(stdout, "Check the symbol table.\n");
737: for (i = ROOTINO; i < maxino; i++) {
738: for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
739: ep->e_flags &= ~KEEP;
740: if (ep->e_type == NODE)
741: ep->e_flags &= ~(NEW|EXISTED);
742: if (ep->e_flags != NULL)
743: badentry(ep, "incomplete operations");
744: }
745: }
746: }
747:
748: /*
749: * Compare with the directory structure on the tape
750: * A paranoid check that things are as they should be.
751: */
752: long
753: verifyfile(name, ino, type)
754: char *name;
755: ino_t ino;
756: int type;
757: {
758: struct entry *np, *ep;
759: long descend = GOOD;
760:
761: ep = lookupname(name);
762: if (ep == NIL) {
763: fprintf(stderr, "Warning: missing name %s\n", name);
764: return (FAIL);
765: }
766: np = lookupino(ino);
767: if (np != ep)
768: descend = FAIL;
769: for ( ; np != NIL; np = np->e_links)
770: if (np == ep)
771: break;
772: if (np == NIL)
773: panic("missing inumber %d\n", ino);
774: if (ep->e_type == LEAF && type != LEAF)
775: badentry(ep, "type should be LEAF");
776: return (descend);
777: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.