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