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