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