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