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