xref: /illumos-gate/usr/src/cmd/backup/restore/restore.c (revision 7f7322febbcfe774b7270abc3b191c094bfcc517)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved	*/
8 
9 /*
10  * Copyright (c) 1983 Regents of the University of California.
11  * All rights reserved.  The Berkeley software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #include "restore.h"
18 /* undef MAXNAMLEN to prevent compiler warnings about redef in dirent.h */
19 #undef MAXNAMLEN
20 #include <dirent.h>
21 
22 #ifdef __STDC__
23 static char *keyval(int);
24 static void removexattrs(struct entry *);
25 static void movexattrs(char *, char *);
26 #else
27 static char *keyval();
28 static void removexattrs();
29 static void movexattrs();
30 #endif
31 
32 /*
33  * This implements the 't' option.
34  * List entries on the tape.
35  */
36 long
37 listfile(name, ino, type)
38 	char *name;
39 	ino_t ino;
40 	int type;
41 {
42 	long descend = hflag ? GOOD : FAIL;
43 
44 	if (BIT(ino, dumpmap) == 0) {
45 		return (descend);
46 	}
47 	vprintf(stdout, "%s", type == LEAF ? gettext("leaf") : gettext("dir "));
48 	(void) fprintf(stdout, "%10lu\t%s\n", ino, name);
49 	return (descend);
50 }
51 
52 /*
53  * This implements the 'x' option.
54  * Request that new entries be extracted.
55  */
56 long
57 addfile(name, ino, type)
58 	char *name;
59 	ino_t ino;
60 	int type;
61 {
62 	struct entry *ep;
63 	long descend = hflag ? GOOD : FAIL;
64 	char buf[100];
65 
66 	/* Don't know if ino_t is long or long long, so be safe w/ *printf() */
67 
68 	if (BIT(ino, dumpmap) == 0) {
69 		if (mflag) {
70 			dprintf(stdout, gettext(
71 			    "%s: not on the volume\n"), name);
72 		} else {
73 			dprintf(stdout, gettext(
74 			    "inode %llu: not on the volume\n"),
75 			    (u_longlong_t)ino);
76 		}
77 		return (descend);
78 	}
79 	if (!mflag) {
80 		(void) snprintf(buf, sizeof (buf), "./%llu", (u_longlong_t)ino);
81 		buf[sizeof (buf) - 1] = '\0';
82 		name = buf;
83 		if (type == NODE) {
84 			(void) genliteraldir(name, ino);
85 			return (descend);
86 		}
87 	}
88 	ep = lookupino(ino);
89 	if (ep != NIL) {
90 		if (strcmp(name, myname(ep)) == 0) {
91 			/* LINTED: result fits into a short */
92 			ep->e_flags |= NEW;
93 			return (descend);
94 		}
95 		type |= LINK;
96 	}
97 	ep = addentry(name, ino, type);
98 	if (type == NODE)
99 		newnode(ep);
100 	/* LINTED: result fits into a short */
101 	ep->e_flags |= NEW;
102 	return (descend);
103 }
104 
105 /*
106  * This is used by the 'i' option to undo previous requests made by addfile.
107  * Delete entries from the request queue.
108  */
109 /* ARGSUSED */
110 long
111 deletefile(name, ino, type)
112 	char *name;
113 	ino_t ino;
114 	int type;
115 {
116 	long descend = hflag ? GOOD : FAIL;
117 	struct entry *ep;
118 
119 	if (BIT(ino, dumpmap) == 0) {
120 		return (descend);
121 	}
122 	ep = lookupino(ino);
123 	if (ep != NIL) {
124 		/* LINTED: result fits into a short */
125 		ep->e_flags &= ~NEW;
126 	}
127 	return (descend);
128 }
129 
130 /*
131  * The following four routines implement the incremental
132  * restore algorithm. The first removes old entries, the second
133  * does renames and calculates the extraction list, the third
134  * cleans up link names missed by the first two, and the final
135  * one deletes old directories.
136  *
137  * Directories cannot be immediately deleted, as they may have
138  * other files in them which need to be moved out first. As
139  * directories to be deleted are found, they are put on the
140  * following deletion list. After all deletions and renames
141  * are done, this list is actually deleted.
142  */
143 static struct entry *removelist;
144 
145 /*
146  *	Remove unneeded leaves from the old tree.
147  *	Remove directories from the lookup chains.
148  */
149 void
150 #ifdef __STDC__
151 removeoldleaves(void)
152 #else
153 removeoldleaves()
154 #endif
155 {
156 	struct entry *ep;
157 	ino_t i;
158 
159 	vprintf(stdout, gettext("Mark entries to be removed.\n"));
160 	for (i = ROOTINO + 1; i < maxino; i++) {
161 		if (BIT(i, clrimap))
162 			continue;
163 		ep = lookupino(i);
164 		if (ep == NIL)
165 			continue;
166 		while (ep != NIL) {
167 			dprintf(stdout, gettext("%s: REMOVE\n"), myname(ep));
168 			removexattrs(ep->e_xattrs);
169 			if (ep->e_type == LEAF) {
170 				removeleaf(ep);
171 				freeentry(ep);
172 			} else {
173 				mktempname(ep);
174 				deleteino(ep->e_ino);
175 				/*
176 				 * once the inode is deleted from the symbol
177 				 * table, the e_next field is reusable
178 				 */
179 				ep->e_next = removelist;
180 				removelist = ep;
181 			}
182 			ep = ep->e_links;
183 		}
184 	}
185 }
186 
187 /*
188  *	For each directory entry on the incremental tape, determine which
189  *	category it falls into as follows:
190  *	KEEP - entries that are to be left alone.
191  *	NEW - new entries to be added.
192  *	EXTRACT - files that must be updated with new contents.
193  *	LINK - new links to be added.
194  *	Renames are done at the same time.
195  */
196 long
197 nodeupdates(name, ino, type)
198 	char *name;
199 	ino_t ino;
200 	int type;
201 {
202 	struct entry *ep, *np, *ip;
203 	long descend = GOOD;
204 	int lookuptype = 0;
205 	int key = 0;
206 		/* key values */
207 #define	ONTAPE	0x1	/* inode is on the tape */
208 #define	INOFND	0x2	/* inode already exists */
209 #define	NAMEFND	0x4	/* name already exists */
210 #define	MODECHG	0x8	/* mode of inode changed */
211 
212 	/*
213 	 * This routine is called once for each element in the
214 	 * directory hierarchy, with a full path name.
215 	 * The "type" value is incorrectly specified as LEAF for
216 	 * directories that are not on the dump tape.
217 	 *
218 	 * Check to see if the file is on the tape.
219 	 */
220 	if (BIT(ino, dumpmap))
221 		key |= ONTAPE;
222 	/*
223 	 * Check to see if the name exists, and if the name is a link.
224 	 */
225 	np = lookupname(name);
226 	if (np != NIL) {
227 		key |= NAMEFND;
228 		ip = lookupino(np->e_ino);
229 		if (ip == NULL) {
230 			(void) fprintf(stderr,
231 			    gettext("corrupted symbol table\n"));
232 			done(1);
233 		}
234 		if (ip != np)
235 			lookuptype = LINK;
236 	}
237 	/*
238 	 * Check to see if the inode exists, and if one of its links
239 	 * corresponds to the name (if one was found).
240 	 */
241 	ip = lookupino(ino);
242 	if (ip != NIL) {
243 		key |= INOFND;
244 		for (ep = ip->e_links; ep != NIL; ep = ep->e_links) {
245 			if (ep == np) {
246 				/*
247 				 * Need to set the NEW flag on the hard link
248 				 * so it gets created because we extract the
249 				 * "parent".  If the NAMEFND key is set, remove
250 				 * the leaf.
251 				 */
252 				if (ip->e_flags & EXTRACT) {
253 					if (key & NAMEFND) {
254 						removeleaf(np);
255 						freeentry(np);
256 						np = NIL;
257 						key &= ~NAMEFND;
258 					}
259 					ep->e_flags |= NEW;
260 				} else {
261 					ip = ep;
262 				}
263 				break;
264 			}
265 		}
266 	}
267 	/*
268 	 * If both a name and an inode are found, but they do not
269 	 * correspond to the same file, then both the inode that has
270 	 * been found and the inode corresponding to the name that
271 	 * has been found need to be renamed. The current pathname
272 	 * is the new name for the inode that has been found. Since
273 	 * all files to be deleted have already been removed, the
274 	 * named file is either a now-unneeded link, or it must live
275 	 * under a new name in this dump level. If it is a link, it
276 	 * can be removed. If it is not a link, it is given a
277 	 * temporary name in anticipation that it will be renamed
278 	 * when it is later found by inode number.
279 	 */
280 	if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
281 		if (lookuptype == LINK) {
282 			removeleaf(np);
283 			freeentry(np);
284 		} else {
285 			dprintf(stdout,
286 			    gettext("name/inode conflict, mktempname %s\n"),
287 				myname(np));
288 			mktempname(np);
289 		}
290 		np = NIL;
291 		key &= ~NAMEFND;
292 	}
293 	if ((key & ONTAPE) &&
294 	    (((key & INOFND) && ip->e_type != type) ||
295 	    ((key & NAMEFND) && np->e_type != type)))
296 		key |= MODECHG;
297 
298 	/*
299 	 * Decide on the disposition of the file based on its flags.
300 	 * Note that we have already handled the case in which
301 	 * a name and inode are found that correspond to different files.
302 	 * Thus if both NAMEFND and INOFND are set then ip == np.
303 	 */
304 	switch (key) {
305 
306 	/*
307 	 * A previously existing file has been found.
308 	 * Mark it as KEEP so that other links to the inode can be
309 	 * detected, and so that it will not be reclaimed by the search
310 	 * for unreferenced names.
311 	 */
312 	case INOFND|NAMEFND:
313 		/* LINTED: result fits into a short */
314 		ip->e_flags |= KEEP;
315 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
316 		    flagvalues(ip));
317 		break;
318 
319 	/*
320 	 * A file on the tape has a name which is the same as a name
321 	 * corresponding to a different file in the previous dump.
322 	 * Since all files to be deleted have already been removed,
323 	 * this file is either a now-unneeded link, or it must live
324 	 * under a new name in this dump level. If it is a link, it
325 	 * can simply be removed. If it is not a link, it is given a
326 	 * temporary name in anticipation that it will be renamed
327 	 * when it is later found by inode number (see INOFND case
328 	 * below). The entry is then treated as a new file.
329 	 */
330 	case ONTAPE|NAMEFND:
331 	case ONTAPE|NAMEFND|MODECHG:
332 		if (lookuptype == LINK) {
333 			removeleaf(np);
334 			freeentry(np);
335 		} else {
336 			mktempname(np);
337 		}
338 		/*FALLTHROUGH*/
339 
340 	/*
341 	 * A previously non-existent file.
342 	 * Add it to the file system, and request its extraction.
343 	 * If it is a directory, create it immediately.
344 	 * (Since the name is unused there can be no conflict)
345 	 */
346 	case ONTAPE:
347 		ep = addentry(name, ino, type);
348 		if (type == NODE)
349 			newnode(ep);
350 		/* LINTED: result fits into a short */
351 		ep->e_flags |= NEW|KEEP;
352 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
353 			flagvalues(ep));
354 		break;
355 
356 	/*
357 	 * A file with the same inode number, but a different
358 	 * name has been found. If the other name has not already
359 	 * been found (indicated by the KEEP flag, see above) then
360 	 * this must be a new name for the file, and it is renamed.
361 	 * If the other name has been found then this must be a
362 	 * link to the file. Hard links to directories are not
363 	 * permitted, and are either deleted or converted to
364 	 * symbolic links. Finally, if the file is on the tape,
365 	 * a request is made to extract it.
366 	 */
367 	case ONTAPE|INOFND:
368 		if (type == LEAF && (ip->e_flags & KEEP) == 0) {
369 			/* LINTED: result fits into a short */
370 			ip->e_flags |= EXTRACT;
371 		}
372 		/*FALLTHROUGH*/
373 	case INOFND:
374 		if ((ip->e_flags & KEEP) == 0) {
375 			renameit(myname(ip), name);
376 			moveentry(ip, name);
377 			/* LINTED: result fits into a short */
378 			ip->e_flags |= KEEP;
379 			dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
380 			    flagvalues(ip));
381 			break;
382 		}
383 		if (ip->e_type == NODE) {
384 			descend = FAIL;
385 			(void) fprintf(stderr, gettext(
386 			    "deleted hard link %s to directory %s\n"),
387 			    name, myname(ip));
388 			break;
389 		}
390 		ep = addentry(name, ino, type|LINK);
391 		/* LINTED: result fits into a short */
392 		ep->e_flags |= NEW;
393 		dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
394 		    flagvalues(ep));
395 		break;
396 
397 	/*
398 	 * A previously known file which is to be updated.
399 	 */
400 	case ONTAPE|INOFND|NAMEFND:
401 		if (type == LEAF && lookuptype != LINK) {
402 			/* LINTED: result fits into a short */
403 			np->e_flags |= EXTRACT;
404 		}
405 		/* LINTED: result fits into a short */
406 		np->e_flags |= KEEP;
407 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
408 			flagvalues(np));
409 		break;
410 
411 	/*
412 	 * An inode is being reused in a completely different way.
413 	 * Normally an extract can simply do an "unlink" followed
414 	 * by a "creat". Here we must do effectively the same
415 	 * thing. The complications arise because we cannot really
416 	 * delete a directory since it may still contain files
417 	 * that we need to rename, so we delete it from the symbol
418 	 * table, and put it on the list to be deleted eventually.
419 	 * Conversely if a directory is to be created, it must be
420 	 * done immediately, rather than waiting until the
421 	 * extraction phase.
422 	 */
423 	case ONTAPE|INOFND|MODECHG:
424 	case ONTAPE|INOFND|NAMEFND|MODECHG:
425 		if (ip->e_flags & KEEP) {
426 			badentry(ip, gettext("cannot KEEP and change modes"));
427 			break;
428 		}
429 		if (ip->e_type == LEAF) {
430 			/* changing from leaf to node */
431 			removeleaf(ip);
432 			freeentry(ip);
433 			ip = addentry(name, ino, type);
434 			newnode(ip);
435 		} else {
436 			/* changing from node to leaf */
437 			if ((ip->e_flags & TMPNAME) == 0)
438 				mktempname(ip);
439 			deleteino(ip->e_ino);
440 			ip->e_next = removelist;
441 			removelist = ip;
442 			ip = addentry(name, ino, type);
443 		}
444 		/* LINTED: result fits into a short */
445 		ip->e_flags |= NEW|KEEP;
446 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
447 			flagvalues(ip));
448 		break;
449 
450 	/*
451 	 * A hard link to a directory that has been removed.
452 	 * Ignore it.
453 	 */
454 	case NAMEFND:
455 		dprintf(stdout, gettext("[%s] %s: Extraneous name\n"),
456 			keyval(key),
457 			name);
458 		descend = FAIL;
459 		break;
460 
461 	/*
462 	 * If we find a directory entry for a file that is not on
463 	 * the tape, then we must have found a file that was created
464 	 * while the dump was in progress. Since we have no contents
465 	 * for it, we discard the name knowing that it will be on the
466 	 * next incremental tape.
467 	 */
468 	case 0:
469 		(void) fprintf(stderr,
470 		    gettext("%s: (inode %lu) not found on volume\n"),
471 		    name, ino);
472 		break;
473 
474 	/*
475 	 * If any of these arise, something is grievously wrong with
476 	 * the current state of the symbol table.
477 	 */
478 	case INOFND|NAMEFND|MODECHG:
479 	case NAMEFND|MODECHG:
480 	case INOFND|MODECHG:
481 		(void) fprintf(stderr, "[%s] %s: %s\n",
482 		    keyval(key), name, gettext("inconsistent state"));
483 		done(1);
484 		/*NOTREACHED*/
485 
486 	/*
487 	 * These states "cannot" arise for any state of the symbol table.
488 	 */
489 	case ONTAPE|MODECHG:
490 	case MODECHG:
491 	default:
492 		(void) fprintf(stderr, "[%s] %s: %s\n",
493 		    keyval(key), name, gettext("impossible state"));
494 		done(1);
495 		/*NOTREACHED*/
496 	}
497 	return (descend);
498 }
499 
500 /*
501  * Calculate the active flags in a key.
502  */
503 static char *
504 keyval(key)
505 	int key;
506 {
507 	static char keybuf[32];
508 
509 	/* Note longest case is everything except |NIL */
510 
511 	(void) strcpy(keybuf, "|NIL");
512 	keybuf[0] = '\0';
513 	if (key & ONTAPE)
514 		(void) strcat(keybuf, "|ONTAPE");
515 	if (key & INOFND)
516 		(void) strcat(keybuf, "|INOFND");
517 	if (key & NAMEFND)
518 		(void) strcat(keybuf, "|NAMEFND");
519 	if (key & MODECHG)
520 		(void) strcat(keybuf, "|MODECHG");
521 	return (&keybuf[1]);
522 }
523 
524 /*
525  * Find unreferenced link names.
526  */
527 void
528 #ifdef __STDC__
529 findunreflinks(void)
530 #else
531 findunreflinks()
532 #endif
533 {
534 	struct entry *ep, *np;
535 	ino_t i;
536 
537 	vprintf(stdout, gettext("Find unreferenced names.\n"));
538 	for (i = ROOTINO; i < maxino; i++) {
539 		ep = lookupino(i);
540 		if (ep == NIL || ep->e_type == LEAF || BIT(i, dumpmap) == 0)
541 			continue;
542 		for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
543 			if (np->e_flags == 0) {
544 				dprintf(stdout, gettext(
545 				    "%s: remove unreferenced name\n"),
546 				    myname(np));
547 				removeleaf(np);
548 				freeentry(np);
549 			}
550 		}
551 	}
552 	/*
553 	 * Any leaves remaining in removed directories are unreferenced.
554 	 */
555 	for (ep = removelist; ep != NIL; ep = ep->e_next) {
556 		for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
557 			if (np->e_type == LEAF) {
558 				if (np->e_flags != 0)
559 					badentry(np, gettext(
560 						"unreferenced with flags"));
561 				dprintf(stdout, gettext(
562 				    "%s: remove unreferenced name\n"),
563 				    myname(np));
564 				removeleaf(np);
565 				freeentry(np);
566 			}
567 		}
568 	}
569 }
570 
571 /*
572  * Remove old nodes (directories).
573  * Note that this routine runs in O(N*D) where:
574  *	N is the number of directory entries to be removed.
575  *	D is the maximum depth of the tree.
576  * If N == D this can be quite slow. If the list were
577  * topologically sorted, the deletion could be done in
578  * time O(N).
579  */
580 void
581 #ifdef __STDC__
582 removeoldnodes(void)
583 #else
584 removeoldnodes()
585 #endif
586 {
587 	struct entry *ep, **prev;
588 	long change;
589 
590 	vprintf(stdout, gettext("Remove old nodes (directories).\n"));
591 	do	{
592 		change = 0;
593 		prev = &removelist;
594 		for (ep = removelist; ep != NIL; ep = *prev) {
595 			if (ep->e_entries != NIL) {
596 				prev = &ep->e_next;
597 				continue;
598 			}
599 			*prev = ep->e_next;
600 			removenode(ep);
601 			freeentry(ep);
602 			change++;
603 		}
604 	} while (change);
605 	for (ep = removelist; ep != NIL; ep = ep->e_next)
606 		badentry(ep, gettext("cannot remove, non-empty"));
607 }
608 
609 /*
610  * This is the routine used to extract files for the 'r' command.
611  * Extract new leaves.
612  */
613 void
614 createleaves(symtabfile)
615 	char *symtabfile;
616 {
617 	struct entry *ep;
618 	char name[MAXCOMPLEXLEN];
619 	ino_t first;
620 	int curvol;
621 
622 	if (command == 'R') {
623 		vprintf(stdout, gettext("Continue extraction of new leaves\n"));
624 	} else {
625 		vprintf(stdout, gettext("Extract new leaves.\n"));
626 		dumpsymtable(symtabfile, volno);
627 	}
628 	first = lowerbnd(ROOTINO);
629 	curvol = volno;
630 	while (curfile.ino < maxino) {
631 		first = lowerbnd(first);
632 		/*
633 		 * If the next available file is not the one which we
634 		 * expect then we have missed one or more files. Since
635 		 * we do not request files that were not on the tape,
636 		 * the lost files must have been due to a tape read error,
637 		 * or a file that was removed while the dump was in progress.
638 		 *
639 		 * The loop will terminate with first == maxino, if not
640 		 * sooner.  Due to the e_flags manipulation, lowerbnd()
641 		 * will never return its argument.
642 		 */
643 		while (first < curfile.ino) {
644 			ep = lookupino(first);
645 			if (ep == NIL) {
646 				(void) fprintf(stderr,
647 				    gettext("%d: bad first\n"), first);
648 				done(1);
649 			}
650 			(void) fprintf(stderr,
651 			    gettext("%s: not found on volume\n"),
652 			    myname(ep));
653 			/* LINTED: result fits into a short */
654 			ep->e_flags &= ~(NEW|EXTRACT);
655 			first = lowerbnd(first);
656 		}
657 		/*
658 		 * If we find files on the tape that have no corresponding
659 		 * directory entries, then we must have found a file that
660 		 * was created while the dump was in progress. Since we have
661 		 * no name for it, we discard it knowing that it will be
662 		 * on the next incremental tape.
663 		 */
664 		if (first != curfile.ino) {
665 			(void) fprintf(stderr,
666 			    gettext("expected next file %d, got %d\n"),
667 				first, curfile.ino);
668 			skipfile();
669 			goto next;
670 		}
671 		ep = lookupino(curfile.ino);
672 		if (ep == NIL) {
673 			(void) fprintf(stderr,
674 			    gettext("unknown file on volume\n"));
675 			done(1);
676 		}
677 		if ((ep->e_flags & (NEW|EXTRACT)) == 0)
678 			badentry(ep, gettext("unexpected file on volume"));
679 		/*
680 		 * If the file is to be extracted, then the old file must
681 		 * be removed since its type may change from one leaf type
682 		 * to another (eg "file" to "character special"). But we
683 		 * also need to preserve any existing extended attributes;
684 		 * so first rename the file, then move its attributes, then
685 		 * remove it.
686 		 */
687 		if ((ep->e_flags & EXTRACT) != 0) {
688 			char *sname = savename(ep->e_name);
689 			complexcpy(name, myname(ep), MAXCOMPLEXLEN);
690 			mktempname(ep);
691 			(void) extractfile(name);
692 			movexattrs(myname(ep), name);
693 			removeleaf(ep);
694 			freename(ep->e_name);
695 			ep->e_name = sname;
696 			ep->e_namlen = strlen(ep->e_name);
697 			/* LINTED: result fits into a short */
698 			ep->e_flags &= ~REMOVED;
699 		} else {
700 			(void) extractfile(myname(ep));
701 		}
702 		/* LINTED: result fits into a short */
703 		ep->e_flags &= ~(NEW|EXTRACT);
704 		/*
705 		 * We checkpoint the restore after every tape reel, so
706 		 * as to simplify the amount of work required by the
707 		 * 'R' command.
708 		 */
709 	next:
710 		if (curvol != volno) {
711 			dumpsymtable(symtabfile, volno);
712 			skipmaps();
713 			curvol = volno;
714 		}
715 	}
716 }
717 
718 /*
719  * This is the routine used to extract files for the 'x' and 'i' commands.
720  * Efficiently extract a subset of the files on a tape.
721  */
722 void
723 #ifdef __STDC__
724 createfiles(void)
725 #else
726 createfiles()
727 #endif
728 {
729 	ino_t first, next, last;
730 	struct entry *ep;
731 	int curvol, nextvol;
732 
733 	vprintf(stdout, gettext("Extract requested files\n"));
734 	first = lowerbnd(ROOTINO);
735 	last = upperbnd(maxino - 1);
736 	nextvol = volnumber(first);
737 	if (nextvol == 0) {
738 		curfile.action = SKIP;
739 		getvol(1);
740 		skipmaps();
741 		skipdirs();
742 	}
743 	for (;;) {
744 		first = lowerbnd(first);
745 		last = upperbnd(last);
746 		/*
747 		 * Check to see if any files remain to be extracted
748 		 */
749 		if (first > last)
750 			return;
751 		/*
752 		 * If a map of inode numbers to tape volumes is
753 		 * available, then select the next volume to be read.
754 		 */
755 		if (nextvol > 0) {
756 			nextvol = volnumber(first);
757 			if (nextvol != volno) {
758 				curfile.action = UNKNOWN;
759 				getvol(nextvol);
760 				skipmaps();
761 			}
762 		}
763 		/*
764 		 * Reject any volumes with inodes greater than
765 		 * the last one needed. This will only be true
766 		 * if the above code has not selected a volume.
767 		 */
768 		while (curfile.ino > last) {
769 			curfile.action = SKIP;
770 			getvol(0);
771 			skipmaps();
772 			skipdirs();
773 		}
774 		/*
775 		 * Decide on the next inode needed.
776 		 * Skip across the inodes until it is found
777 		 * or an out of order volume change is encountered
778 		 */
779 		next = lowerbnd(curfile.ino);
780 		do	{
781 			curvol = volno;
782 			while (next > curfile.ino && volno == curvol)
783 				skipfile();
784 			skipmaps();
785 			skipdirs();
786 		} while (volno == curvol + 1);
787 		/*
788 		 * If volume change out of order occurred the
789 		 * current state must be recalculated
790 		 */
791 		if (volno != curvol)
792 			continue;
793 		/*
794 		 * If the current inode is greater than the one we were
795 		 * looking for then we missed the one we were looking for.
796 		 * Since we only attempt to extract files listed in the
797 		 * dump map, the lost files must have been due to a tape
798 		 * read error, or a file that was removed while the dump
799 		 * was in progress. Thus we report all requested files
800 		 * between the one we were looking for, and the one we
801 		 * found as missing, and delete their request flags.
802 		 */
803 		while (next < curfile.ino) {
804 			ep = lookupino(next);
805 			if (ep == NIL) {
806 				(void) fprintf(stderr,
807 				    gettext("corrupted symbol table\n"));
808 				done(1);
809 			}
810 			(void) fprintf(stderr,
811 			    gettext("%s: not found on volume\n"),
812 			    myname(ep));
813 			/* LINTED: result fits into a short */
814 			ep->e_flags &= ~NEW;
815 			next = lowerbnd(next);
816 		}
817 		/*
818 		 * The current inode is the one that we are looking for,
819 		 * so extract it per its requested name.
820 		 */
821 		if (next == curfile.ino && next <= last) {
822 			ep = lookupino(next);
823 			if (ep == NIL) {
824 				(void) fprintf(stderr,
825 				    gettext("corrupted symbol table\n"));
826 				done(1);
827 			}
828 			(void) extractfile(myname(ep));
829 			/* LINTED: result fits into a short */
830 			ep->e_flags &= ~NEW;
831 			if (volno != curvol)
832 				skipmaps();
833 		}
834 	}
835 }
836 
837 /*
838  * Add links.
839  */
840 void
841 #ifdef __STDC__
842 createlinks(void)
843 #else
844 createlinks()
845 #endif
846 {
847 	struct entry *np, *ep;
848 	ino_t i;
849 	int dfd;
850 	char *to, *from;
851 	int saverr;
852 
853 	vprintf(stdout, gettext("Add links\n"));
854 	for (i = ROOTINO; i < maxino; i++) {
855 		ep = lookupino(i);
856 		if (ep == NIL)
857 			continue;
858 		to = savename(myname(ep));
859 		for (np = ep->e_links; np != NIL; np = np->e_links) {
860 			if ((np->e_flags & NEW) == 0)
861 				continue;
862 			resolve(myname(np), &dfd, &from);
863 			if (dfd != AT_FDCWD) {
864 				if (fchdir(dfd) < 0) {
865 					saverr = errno;
866 					(void) fprintf(stderr,
867 					gettext("%s->%s: link failed: %s\n"),
868 						from, to, strerror(saverr));
869 					(void) close(dfd);
870 					continue;
871 				}
872 			}
873 			if (ep->e_type == NODE) {
874 				(void) lf_linkit(to, from, SYMLINK);
875 			} else {
876 				(void) lf_linkit(to, from, HARDLINK);
877 			}
878 			/* LINTED: result fits into a short */
879 			np->e_flags &= ~NEW;
880 			if (dfd != AT_FDCWD) {
881 				fchdir(savepwd);
882 				(void) close(dfd);
883 			}
884 		}
885 		freename(to);
886 	}
887 }
888 
889 /*
890  * Check the symbol table.
891  * We do this to insure that all the requested work was done, and
892  * that no temporary names remain.
893  */
894 void
895 #ifdef __STDC__
896 checkrestore(void)
897 #else
898 checkrestore()
899 #endif
900 {
901 	struct entry *ep;
902 	ino_t i;
903 
904 	vprintf(stdout, gettext("Check the symbol table.\n"));
905 	for (i = ROOTINO; i < maxino; i++) {
906 		for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
907 			/* LINTED: result fits into a short */
908 			ep->e_flags &= ~KEEP;
909 			if (ep->e_type == NODE) {
910 				/* LINTED: result fits into a short */
911 				ep->e_flags &= ~(NEW|EXISTED);
912 			}
913 			if ((ep->e_flags & ~(XATTR|XATTRROOT)) != 0)
914 				badentry(ep, gettext("incomplete operations"));
915 		}
916 	}
917 }
918 
919 /*
920  * Compare with the directory structure on the tape
921  * A paranoid check that things are as they should be.
922  */
923 long
924 verifyfile(name, ino, type)
925 	char *name;
926 	ino_t ino;
927 	int type;
928 {
929 	struct entry *np, *ep;
930 	long descend = GOOD;
931 
932 	ep = lookupname(name);
933 	if (ep == NIL) {
934 		(void) fprintf(stderr,
935 		    gettext("Warning: missing name %s\n"), name);
936 		return (FAIL);
937 	}
938 	np = lookupino(ino);
939 	if (np != ep)
940 		descend = FAIL;
941 	for (; np != NIL; np = np->e_links)
942 		if (np == ep)
943 			break;
944 	if (np == NIL) {
945 		(void) fprintf(stderr, gettext("missing inumber %d\n"), ino);
946 		done(1);
947 	}
948 	if (ep->e_type == LEAF && type != LEAF)
949 		badentry(ep, gettext("type should be LEAF"));
950 	return (descend);
951 }
952 
953 /*
954  * This routine does not actually remove any attribute files, it
955  * just removes entries from the symbol table.  The attribute files
956  * themselves are assumed to be removed automatically when the
957  * parent file is removed.
958  */
959 static void
960 removexattrs(ep)
961 	struct entry *ep;
962 {
963 	struct entry *np = ep;
964 
965 	if (ep == NIL)
966 		return;
967 	for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
968 		if (np->e_type == NODE) {
969 			removexattrs(np);
970 		} else {
971 			np->e_flags |= REMOVED;
972 			freeentry(np);
973 		}
974 	}
975 	ep->e_flags |= REMOVED;
976 	freeentry(ep);
977 }
978 
979 /*
980  * Move all the extended attributes associated with orig to
981  * the file named by the second argument (targ).
982  */
983 static void
984 movexattrs(orig, targ)
985 	char *orig;
986 	char *targ;
987 {
988 	char *to, *from;
989 	int fromfd, fromdir, tofd, todir, tfd;
990 	DIR *dirp = NULL;
991 	struct dirent *dp = NULL;
992 
993 	fromfd = tofd = fromdir = todir = tfd = -1;
994 
995 	resolve(orig, &tfd, &from);
996 	if (tfd == AT_FDCWD && pathconf(orig, _PC_XATTR_EXISTS) != 1) {
997 		/* no attributes to move */
998 		return;
999 	}
1000 	if ((fromfd = openat64(tfd, from, O_RDONLY|O_NONBLOCK)) == -1) {
1001 		fprintf(stderr, gettext("%s: cannot move attributes: "), from);
1002 		perror("");
1003 		if (tfd != AT_FDCWD) (void) close(tfd);
1004 		goto out;
1005 	}
1006 
1007 	if (fpathconf(fromfd, _PC_XATTR_EXISTS) != 1) {
1008 		/* no attributes to move */
1009 		if (tfd != AT_FDCWD) (void) close(tfd);
1010 		goto out;
1011 	}
1012 	if ((fromdir = openat64(fromfd, ".",
1013 				O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) {
1014 		fprintf(stderr, gettext("%s: cannot access attributes: "),
1015 			from);
1016 		perror("");
1017 		if (tfd != AT_FDCWD) (void) close(tfd);
1018 		goto out;
1019 	}
1020 	if (tfd != AT_FDCWD) (void) close(tfd);
1021 
1022 	resolve(targ, &tfd, &to);
1023 	if ((tofd = openat64(tfd, to, O_RDONLY|O_NONBLOCK)) == -1 ||
1024 	    (todir = openat64(tofd, ".", O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) {
1025 		fprintf(stderr, gettext("%s: cannot create attributes: "), to);
1026 		perror("");
1027 		goto out;
1028 	}
1029 	if (tfd != AT_FDCWD) (void) close(tfd);
1030 	(void) close(tofd);
1031 
1032 	if ((tfd = dup(fromdir)) == -1 ||
1033 	    (dirp = fdopendir(tfd)) == NULL) {
1034 		fprintf(stderr,
1035 	gettext("%s: cannot allocate DIR structure to attribute directory: "),
1036 			from);
1037 		perror("");
1038 		if (tfd != -1) (void) close(tfd);
1039 		goto out;
1040 	}
1041 
1042 	while ((dp = readdir(dirp)) != NULL) {
1043 		if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
1044 			(dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
1045 			dp->d_name[2] == '\0'))
1046 			continue;
1047 		if ((renameat(fromdir, dp->d_name, todir, dp->d_name)) == -1) {
1048 			fprintf(stderr,
1049 				gettext("%s: cannot move attribute %s: "),
1050 				from, dp->d_name);
1051 			goto out;
1052 		}
1053 	}
1054 out:
1055 	if (fromfd != -1)
1056 		(void) close(fromfd);
1057 	if (tofd != -1)
1058 		(void) close(tofd);
1059 	if (dirp != NULL)
1060 		(void) closedir(dirp);
1061 	if (fromdir != -1)
1062 		(void) close(fromdir);
1063 	if (todir != -1)
1064 		(void) close(todir);
1065 }
1066