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