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
listfile(name,ino,type)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
addfile(name,ino,type)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
deletefile(name,ino,type)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
removeoldleaves()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
nodeupdates(name,ino,type)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 *
keyval(key)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
findunreflinks()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
removeoldnodes()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
createleaves(symtabfile)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
createfiles()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
createlinks()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
checkrestore()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
verifyfile(name,ino,type)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