xref: /illumos-gate/usr/src/cmd/backup/restore/restore.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2006 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 || key == (ONTAPE|NAMEFND)) {
333 			removeleaf(np);
334 			freeentry(np);
335 		} else {
336 			/*
337 			 * Create a temporary node only if MODECHG.
338 			 */
339 			mktempname(np);
340 		}
341 		/*FALLTHROUGH*/
342 
343 	/*
344 	 * A previously non-existent file.
345 	 * Add it to the file system, and request its extraction.
346 	 * If it is a directory, create it immediately.
347 	 * (Since the name is unused there can be no conflict)
348 	 */
349 	case ONTAPE:
350 		ep = addentry(name, ino, type);
351 		if (type == NODE)
352 			newnode(ep);
353 		/* LINTED: result fits into a short */
354 		ep->e_flags |= NEW|KEEP;
355 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
356 			flagvalues(ep));
357 		break;
358 
359 	/*
360 	 * A file with the same inode number, but a different
361 	 * name has been found. If the other name has not already
362 	 * been found (indicated by the KEEP flag, see above) then
363 	 * this must be a new name for the file, and it is renamed.
364 	 * If the other name has been found then this must be a
365 	 * link to the file. Hard links to directories are not
366 	 * permitted, and are either deleted or converted to
367 	 * symbolic links. Finally, if the file is on the tape,
368 	 * a request is made to extract it.
369 	 */
370 	case ONTAPE|INOFND:
371 		if (type == LEAF && (ip->e_flags & KEEP) == 0) {
372 			/* LINTED: result fits into a short */
373 			ip->e_flags |= EXTRACT;
374 		}
375 		/*FALLTHROUGH*/
376 	case INOFND:
377 		if ((ip->e_flags & KEEP) == 0) {
378 			renameit(myname(ip), name);
379 			moveentry(ip, name);
380 			/* LINTED: result fits into a short */
381 			ip->e_flags |= KEEP;
382 			dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
383 			    flagvalues(ip));
384 			break;
385 		}
386 		if (ip->e_type == NODE) {
387 			descend = FAIL;
388 			(void) fprintf(stderr, gettext(
389 			    "deleted hard link %s to directory %s\n"),
390 			    name, myname(ip));
391 			break;
392 		}
393 		ep = addentry(name, ino, type|LINK);
394 		/* LINTED: result fits into a short */
395 		ep->e_flags |= NEW;
396 		dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
397 		    flagvalues(ep));
398 		break;
399 
400 	/*
401 	 * A previously known file which is to be updated.
402 	 */
403 	case ONTAPE|INOFND|NAMEFND:
404 		/*
405 		 * Extract leaf nodes.
406 		 */
407 		if (type == LEAF) {
408 			/* LINTED: result fits into a short */
409 			np->e_flags |= EXTRACT;
410 		}
411 		/* LINTED: result fits into a short */
412 		np->e_flags |= KEEP;
413 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
414 			flagvalues(np));
415 		break;
416 
417 	/*
418 	 * An inode is being reused in a completely different way.
419 	 * Normally an extract can simply do an "unlink" followed
420 	 * by a "creat". Here we must do effectively the same
421 	 * thing. The complications arise because we cannot really
422 	 * delete a directory since it may still contain files
423 	 * that we need to rename, so we delete it from the symbol
424 	 * table, and put it on the list to be deleted eventually.
425 	 * Conversely if a directory is to be created, it must be
426 	 * done immediately, rather than waiting until the
427 	 * extraction phase.
428 	 */
429 	case ONTAPE|INOFND|MODECHG:
430 	case ONTAPE|INOFND|NAMEFND|MODECHG:
431 		if (ip->e_flags & KEEP) {
432 			badentry(ip, gettext("cannot KEEP and change modes"));
433 			break;
434 		}
435 		if (ip->e_type == LEAF) {
436 			/* changing from leaf to node */
437 			removeleaf(ip);
438 			freeentry(ip);
439 			ip = addentry(name, ino, type);
440 			newnode(ip);
441 		} else {
442 			/* changing from node to leaf */
443 			if ((ip->e_flags & TMPNAME) == 0)
444 				mktempname(ip);
445 			deleteino(ip->e_ino);
446 			ip->e_next = removelist;
447 			removelist = ip;
448 			ip = addentry(name, ino, type);
449 		}
450 		/* LINTED: result fits into a short */
451 		ip->e_flags |= NEW|KEEP;
452 		dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
453 			flagvalues(ip));
454 		break;
455 
456 	/*
457 	 * A hard link to a directory that has been removed.
458 	 * Ignore it.
459 	 */
460 	case NAMEFND:
461 		dprintf(stdout, gettext("[%s] %s: Extraneous name\n"),
462 			keyval(key),
463 			name);
464 		descend = FAIL;
465 		break;
466 
467 	/*
468 	 * If we find a directory entry for a file that is not on
469 	 * the tape, then we must have found a file that was created
470 	 * while the dump was in progress. Since we have no contents
471 	 * for it, we discard the name knowing that it will be on the
472 	 * next incremental tape.
473 	 */
474 	case 0:
475 		(void) fprintf(stderr,
476 		    gettext("%s: (inode %lu) not found on volume\n"),
477 		    name, ino);
478 		break;
479 
480 	/*
481 	 * If any of these arise, something is grievously wrong with
482 	 * the current state of the symbol table.
483 	 */
484 	case INOFND|NAMEFND|MODECHG:
485 	case NAMEFND|MODECHG:
486 	case INOFND|MODECHG:
487 		(void) fprintf(stderr, "[%s] %s: %s\n",
488 		    keyval(key), name, gettext("inconsistent state"));
489 		done(1);
490 		/*NOTREACHED*/
491 
492 	/*
493 	 * These states "cannot" arise for any state of the symbol table.
494 	 */
495 	case ONTAPE|MODECHG:
496 	case MODECHG:
497 	default:
498 		(void) fprintf(stderr, "[%s] %s: %s\n",
499 		    keyval(key), name, gettext("impossible state"));
500 		done(1);
501 		/*NOTREACHED*/
502 	}
503 	return (descend);
504 }
505 
506 /*
507  * Calculate the active flags in a key.
508  */
509 static char *
510 keyval(key)
511 	int key;
512 {
513 	static char keybuf[32];
514 
515 	/* Note longest case is everything except |NIL */
516 
517 	(void) strcpy(keybuf, "|NIL");
518 	keybuf[0] = '\0';
519 	if (key & ONTAPE)
520 		(void) strcat(keybuf, "|ONTAPE");
521 	if (key & INOFND)
522 		(void) strcat(keybuf, "|INOFND");
523 	if (key & NAMEFND)
524 		(void) strcat(keybuf, "|NAMEFND");
525 	if (key & MODECHG)
526 		(void) strcat(keybuf, "|MODECHG");
527 	return (&keybuf[1]);
528 }
529 
530 /*
531  * Find unreferenced link names.
532  */
533 void
534 #ifdef __STDC__
535 findunreflinks(void)
536 #else
537 findunreflinks()
538 #endif
539 {
540 	struct entry *ep, *np;
541 	ino_t i;
542 
543 	vprintf(stdout, gettext("Find unreferenced names.\n"));
544 	for (i = ROOTINO; i < maxino; i++) {
545 		ep = lookupino(i);
546 		if (ep == NIL || ep->e_type == LEAF || BIT(i, dumpmap) == 0)
547 			continue;
548 		for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
549 			if (np->e_flags == 0) {
550 				dprintf(stdout, gettext(
551 				    "%s: remove unreferenced name\n"),
552 				    myname(np));
553 				removeleaf(np);
554 				freeentry(np);
555 			}
556 		}
557 	}
558 	/*
559 	 * Any leaves remaining in removed directories are unreferenced.
560 	 */
561 	for (ep = removelist; ep != NIL; ep = ep->e_next) {
562 		for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
563 			if (np->e_type == LEAF) {
564 				if (np->e_flags != 0)
565 					badentry(np, gettext(
566 						"unreferenced with flags"));
567 				dprintf(stdout, gettext(
568 				    "%s: remove unreferenced name\n"),
569 				    myname(np));
570 				removeleaf(np);
571 				freeentry(np);
572 			}
573 		}
574 	}
575 }
576 
577 /*
578  * Remove old nodes (directories).
579  * Note that this routine runs in O(N*D) where:
580  *	N is the number of directory entries to be removed.
581  *	D is the maximum depth of the tree.
582  * If N == D this can be quite slow. If the list were
583  * topologically sorted, the deletion could be done in
584  * time O(N).
585  */
586 void
587 #ifdef __STDC__
588 removeoldnodes(void)
589 #else
590 removeoldnodes()
591 #endif
592 {
593 	struct entry *ep, **prev;
594 	long change;
595 
596 	vprintf(stdout, gettext("Remove old nodes (directories).\n"));
597 	do	{
598 		change = 0;
599 		prev = &removelist;
600 		for (ep = removelist; ep != NIL; ep = *prev) {
601 			if (ep->e_entries != NIL) {
602 				prev = &ep->e_next;
603 				continue;
604 			}
605 			*prev = ep->e_next;
606 			removenode(ep);
607 			freeentry(ep);
608 			change++;
609 		}
610 	} while (change);
611 	for (ep = removelist; ep != NIL; ep = ep->e_next)
612 		badentry(ep, gettext("cannot remove, non-empty"));
613 }
614 
615 /*
616  * This is the routine used to extract files for the 'r' command.
617  * Extract new leaves.
618  */
619 void
620 createleaves(symtabfile)
621 	char *symtabfile;
622 {
623 	struct entry *ep;
624 	char name[MAXCOMPLEXLEN];
625 	ino_t first;
626 	int curvol;
627 
628 	if (command == 'R') {
629 		vprintf(stdout, gettext("Continue extraction of new leaves\n"));
630 	} else {
631 		vprintf(stdout, gettext("Extract new leaves.\n"));
632 		dumpsymtable(symtabfile, volno);
633 	}
634 	first = lowerbnd(ROOTINO);
635 	curvol = volno;
636 	while (curfile.ino < maxino) {
637 		first = lowerbnd(first);
638 		/*
639 		 * If the next available file is not the one which we
640 		 * expect then we have missed one or more files. Since
641 		 * we do not request files that were not on the tape,
642 		 * the lost files must have been due to a tape read error,
643 		 * or a file that was removed while the dump was in progress.
644 		 *
645 		 * The loop will terminate with first == maxino, if not
646 		 * sooner.  Due to the e_flags manipulation, lowerbnd()
647 		 * will never return its argument.
648 		 */
649 		while (first < curfile.ino) {
650 			ep = lookupino(first);
651 			if (ep == NIL) {
652 				(void) fprintf(stderr,
653 				    gettext("%d: bad first\n"), first);
654 				done(1);
655 			}
656 			(void) fprintf(stderr,
657 			    gettext("%s: not found on volume\n"),
658 			    myname(ep));
659 			/* LINTED: result fits into a short */
660 			ep->e_flags &= ~(NEW|EXTRACT);
661 			first = lowerbnd(first);
662 		}
663 		/*
664 		 * If we find files on the tape that have no corresponding
665 		 * directory entries, then we must have found a file that
666 		 * was created while the dump was in progress. Since we have
667 		 * no name for it, we discard it knowing that it will be
668 		 * on the next incremental tape.
669 		 */
670 		if (first != curfile.ino) {
671 			(void) fprintf(stderr,
672 			    gettext("expected next file %d, got %d\n"),
673 				first, curfile.ino);
674 			skipfile();
675 			goto next;
676 		}
677 		ep = lookupino(curfile.ino);
678 		if (ep == NIL) {
679 			(void) fprintf(stderr,
680 			    gettext("unknown file on volume\n"));
681 			done(1);
682 		}
683 		if ((ep->e_flags & (NEW|EXTRACT)) == 0)
684 			badentry(ep, gettext("unexpected file on volume"));
685 		/*
686 		 * If the file is to be extracted, then the old file must
687 		 * be removed since its type may change from one leaf type
688 		 * to another (eg "file" to "character special"). But we
689 		 * also need to preserve any existing extended attributes;
690 		 * so first rename the file, then move its attributes, then
691 		 * remove it.
692 		 */
693 		if ((ep->e_flags & EXTRACT) != 0) {
694 			char *sname = savename(ep->e_name);
695 			complexcpy(name, myname(ep), MAXCOMPLEXLEN);
696 			mktempname(ep);
697 			(void) extractfile(name);
698 			movexattrs(myname(ep), name);
699 			removeleaf(ep);
700 			freename(ep->e_name);
701 			ep->e_name = sname;
702 			ep->e_namlen = strlen(ep->e_name);
703 			/* LINTED: result fits into a short */
704 			ep->e_flags &= ~REMOVED;
705 		} else {
706 			(void) extractfile(myname(ep));
707 		}
708 		/* LINTED: result fits into a short */
709 		ep->e_flags &= ~(NEW|EXTRACT);
710 		/*
711 		 * We checkpoint the restore after every tape reel, so
712 		 * as to simplify the amount of work required by the
713 		 * 'R' command.
714 		 */
715 	next:
716 		if (curvol != volno) {
717 			dumpsymtable(symtabfile, volno);
718 			skipmaps();
719 			curvol = volno;
720 		}
721 	}
722 }
723 
724 /*
725  * This is the routine used to extract files for the 'x' and 'i' commands.
726  * Efficiently extract a subset of the files on a tape.
727  */
728 void
729 #ifdef __STDC__
730 createfiles(void)
731 #else
732 createfiles()
733 #endif
734 {
735 	ino_t first, next, last;
736 	struct entry *ep;
737 	int curvol, nextvol;
738 
739 	vprintf(stdout, gettext("Extract requested files\n"));
740 	first = lowerbnd(ROOTINO);
741 	last = upperbnd(maxino - 1);
742 	nextvol = volnumber(first);
743 	if (nextvol == 0) {
744 		curfile.action = SKIP;
745 		getvol(1);
746 		skipmaps();
747 		skipdirs();
748 	}
749 	for (;;) {
750 		first = lowerbnd(first);
751 		last = upperbnd(last);
752 		/*
753 		 * Check to see if any files remain to be extracted
754 		 */
755 		if (first > last)
756 			return;
757 		/*
758 		 * If a map of inode numbers to tape volumes is
759 		 * available, then select the next volume to be read.
760 		 */
761 		if (nextvol > 0) {
762 			nextvol = volnumber(first);
763 			if (nextvol != volno) {
764 				curfile.action = UNKNOWN;
765 				getvol(nextvol);
766 				skipmaps();
767 			}
768 		}
769 		/*
770 		 * Reject any volumes with inodes greater than
771 		 * the last one needed. This will only be true
772 		 * if the above code has not selected a volume.
773 		 */
774 		while (curfile.ino > last) {
775 			curfile.action = SKIP;
776 			getvol(0);
777 			skipmaps();
778 			skipdirs();
779 		}
780 		/*
781 		 * Decide on the next inode needed.
782 		 * Skip across the inodes until it is found
783 		 * or an out of order volume change is encountered
784 		 */
785 		next = lowerbnd(curfile.ino);
786 		do	{
787 			curvol = volno;
788 			while (next > curfile.ino && volno == curvol)
789 				skipfile();
790 			skipmaps();
791 			skipdirs();
792 		} while (volno == curvol + 1);
793 		/*
794 		 * If volume change out of order occurred the
795 		 * current state must be recalculated
796 		 */
797 		if (volno != curvol)
798 			continue;
799 		/*
800 		 * If the current inode is greater than the one we were
801 		 * looking for then we missed the one we were looking for.
802 		 * Since we only attempt to extract files listed in the
803 		 * dump map, the lost files must have been due to a tape
804 		 * read error, or a file that was removed while the dump
805 		 * was in progress. Thus we report all requested files
806 		 * between the one we were looking for, and the one we
807 		 * found as missing, and delete their request flags.
808 		 */
809 		while (next < curfile.ino) {
810 			ep = lookupino(next);
811 			if (ep == NIL) {
812 				(void) fprintf(stderr,
813 				    gettext("corrupted symbol table\n"));
814 				done(1);
815 			}
816 			(void) fprintf(stderr,
817 			    gettext("%s: not found on volume\n"),
818 			    myname(ep));
819 			/* LINTED: result fits into a short */
820 			ep->e_flags &= ~NEW;
821 			next = lowerbnd(next);
822 		}
823 		/*
824 		 * The current inode is the one that we are looking for,
825 		 * so extract it per its requested name.
826 		 */
827 		if (next == curfile.ino && next <= last) {
828 			ep = lookupino(next);
829 			if (ep == NIL) {
830 				(void) fprintf(stderr,
831 				    gettext("corrupted symbol table\n"));
832 				done(1);
833 			}
834 			(void) extractfile(myname(ep));
835 			/* LINTED: result fits into a short */
836 			ep->e_flags &= ~NEW;
837 			if (volno != curvol)
838 				skipmaps();
839 		}
840 	}
841 }
842 
843 /*
844  * Add links.
845  */
846 void
847 #ifdef __STDC__
848 createlinks(void)
849 #else
850 createlinks()
851 #endif
852 {
853 	struct entry *np, *ep;
854 	ino_t i;
855 	int dfd;
856 	char *to, *from;
857 	int saverr;
858 
859 	vprintf(stdout, gettext("Add links\n"));
860 	for (i = ROOTINO; i < maxino; i++) {
861 		ep = lookupino(i);
862 		if (ep == NIL)
863 			continue;
864 		to = savename(myname(ep));
865 		for (np = ep->e_links; np != NIL; np = np->e_links) {
866 			if ((np->e_flags & NEW) == 0)
867 				continue;
868 			resolve(myname(np), &dfd, &from);
869 			if (dfd != AT_FDCWD) {
870 				if (fchdir(dfd) < 0) {
871 					saverr = errno;
872 					(void) fprintf(stderr,
873 					gettext("%s->%s: link failed: %s\n"),
874 						from, to, strerror(saverr));
875 					(void) close(dfd);
876 					continue;
877 				}
878 			}
879 			if (ep->e_type == NODE) {
880 				(void) lf_linkit(to, from, SYMLINK);
881 			} else {
882 				(void) lf_linkit(to, from, HARDLINK);
883 			}
884 			/* LINTED: result fits into a short */
885 			np->e_flags &= ~NEW;
886 			if (dfd != AT_FDCWD) {
887 				fchdir(savepwd);
888 				(void) close(dfd);
889 			}
890 		}
891 		freename(to);
892 	}
893 }
894 
895 /*
896  * Check the symbol table.
897  * We do this to insure that all the requested work was done, and
898  * that no temporary names remain.
899  */
900 void
901 #ifdef __STDC__
902 checkrestore(void)
903 #else
904 checkrestore()
905 #endif
906 {
907 	struct entry *ep;
908 	ino_t i;
909 
910 	vprintf(stdout, gettext("Check the symbol table.\n"));
911 	for (i = ROOTINO; i < maxino; i++) {
912 		for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
913 			/* LINTED: result fits into a short */
914 			ep->e_flags &= ~KEEP;
915 			if (ep->e_type == NODE) {
916 				/* LINTED: result fits into a short */
917 				ep->e_flags &= ~(NEW|EXISTED);
918 			}
919 			if ((ep->e_flags & ~(XATTR|XATTRROOT)) != 0)
920 				badentry(ep, gettext("incomplete operations"));
921 		}
922 	}
923 }
924 
925 /*
926  * Compare with the directory structure on the tape
927  * A paranoid check that things are as they should be.
928  */
929 long
930 verifyfile(name, ino, type)
931 	char *name;
932 	ino_t ino;
933 	int type;
934 {
935 	struct entry *np, *ep;
936 	long descend = GOOD;
937 
938 	ep = lookupname(name);
939 	if (ep == NIL) {
940 		(void) fprintf(stderr,
941 		    gettext("Warning: missing name %s\n"), name);
942 		return (FAIL);
943 	}
944 	np = lookupino(ino);
945 	if (np != ep)
946 		descend = FAIL;
947 	for (; np != NIL; np = np->e_links)
948 		if (np == ep)
949 			break;
950 	if (np == NIL) {
951 		(void) fprintf(stderr, gettext("missing inumber %d\n"), ino);
952 		done(1);
953 	}
954 	if (ep->e_type == LEAF && type != LEAF)
955 		badentry(ep, gettext("type should be LEAF"));
956 	return (descend);
957 }
958 
959 /*
960  * This routine does not actually remove any attribute files, it
961  * just removes entries from the symbol table.  The attribute files
962  * themselves are assumed to be removed automatically when the
963  * parent file is removed.
964  */
965 static void
966 removexattrs(ep)
967 	struct entry *ep;
968 {
969 	struct entry *np = ep;
970 
971 	if (ep == NIL)
972 		return;
973 	for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
974 		if (np->e_type == NODE) {
975 			removexattrs(np);
976 		} else {
977 			np->e_flags |= REMOVED;
978 			freeentry(np);
979 		}
980 	}
981 	ep->e_flags |= REMOVED;
982 	freeentry(ep);
983 }
984 
985 /*
986  * Move all the extended attributes associated with orig to
987  * the file named by the second argument (targ).
988  */
989 static void
990 movexattrs(orig, targ)
991 	char *orig;
992 	char *targ;
993 {
994 	char *to, *from;
995 	int fromfd, fromdir, tofd, todir, tfd;
996 	DIR *dirp = NULL;
997 	struct dirent *dp = NULL;
998 
999 	fromfd = tofd = fromdir = todir = tfd = -1;
1000 
1001 	resolve(orig, &tfd, &from);
1002 	if (tfd == AT_FDCWD && pathconf(orig, _PC_XATTR_EXISTS) != 1) {
1003 		/* no attributes to move */
1004 		return;
1005 	}
1006 	if ((fromfd = openat64(tfd, from, O_RDONLY|O_NONBLOCK)) == -1) {
1007 		fprintf(stderr, gettext("%s: cannot move attributes: "), from);
1008 		perror("");
1009 		if (tfd != AT_FDCWD) (void) close(tfd);
1010 		goto out;
1011 	}
1012 
1013 	if (fpathconf(fromfd, _PC_XATTR_EXISTS) != 1) {
1014 		/* no attributes to move */
1015 		if (tfd != AT_FDCWD) (void) close(tfd);
1016 		goto out;
1017 	}
1018 	if ((fromdir = openat64(fromfd, ".",
1019 				O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) {
1020 		fprintf(stderr, gettext("%s: cannot access attributes: "),
1021 			from);
1022 		perror("");
1023 		if (tfd != AT_FDCWD) (void) close(tfd);
1024 		goto out;
1025 	}
1026 	if (tfd != AT_FDCWD) (void) close(tfd);
1027 
1028 	resolve(targ, &tfd, &to);
1029 	if ((tofd = openat64(tfd, to, O_RDONLY|O_NONBLOCK)) == -1 ||
1030 	    (todir = openat64(tofd, ".", O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) {
1031 		fprintf(stderr, gettext("%s: cannot create attributes: "), to);
1032 		perror("");
1033 		goto out;
1034 	}
1035 	if (tfd != AT_FDCWD) (void) close(tfd);
1036 	(void) close(tofd);
1037 
1038 	if ((tfd = dup(fromdir)) == -1 ||
1039 	    (dirp = fdopendir(tfd)) == NULL) {
1040 		fprintf(stderr,
1041 	gettext("%s: cannot allocate DIR structure to attribute directory: "),
1042 			from);
1043 		perror("");
1044 		if (tfd != -1) (void) close(tfd);
1045 		goto out;
1046 	}
1047 
1048 	while ((dp = readdir(dirp)) != NULL) {
1049 		if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
1050 			(dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
1051 			dp->d_name[2] == '\0'))
1052 			continue;
1053 		if ((renameat(fromdir, dp->d_name, todir, dp->d_name)) == -1) {
1054 			fprintf(stderr,
1055 				gettext("%s: cannot move attribute %s: "),
1056 				from, dp->d_name);
1057 			goto out;
1058 		}
1059 	}
1060 out:
1061 	if (fromfd != -1)
1062 		(void) close(fromfd);
1063 	if (tofd != -1)
1064 		(void) close(tofd);
1065 	if (dirp != NULL)
1066 		(void) closedir(dirp);
1067 	if (fromdir != -1)
1068 		(void) close(fromdir);
1069 	if (todir != -1)
1070 		(void) close(todir);
1071 }
1072