xref: /illumos-gate/usr/src/cmd/backup/restore/symtab.c (revision c0882bf4)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
8 /*	  All Rights Reserved	*/
9 
10 /*
11  * Copyright (c) 1996,1998,2001 by Sun Microsystems, Inc.
12  * All rights reserved.
13  */
14 
15 /*
16  * These routines maintain the symbol table which tracks the state
17  * of the file system being restored. They provide lookup by either
18  * name or inode number. They also provide for creation, deletion,
19  * and renaming of entries. Because of the dynamic nature of pathnames,
20  * names should not be saved, but always constructed just before they
21  * are needed, by calling "myname".
22  */
23 
24 #include "restore.h"
25 #include <limits.h>
26 
27 /*
28  * The following variables define the inode symbol table.
29  * The primary hash table is dynamically allocated based on
30  * the number of inodes in the file system (maxino), scaled by
31  * HASHFACTOR. The variable "entry" points to the hash table;
32  * the variable "entrytblsize" indicates its size (in entries).
33  */
34 #define	HASHFACTOR 5
35 static struct entry **entry;
36 static uint_t entrytblsize;
37 
38 #ifdef __STDC__
39 static void addino(ino_t, struct entry *);
40 static struct entry *lookupparent(char *);
41 static void removeentry(struct entry *);
42 #else
43 static void addino();
44 static struct entry *lookupparent();
45 static void removeentry();
46 #endif
47 
48 /*
49  * Look up an entry by inode number
50  */
51 struct entry *
52 lookupino(inum)
53 	ino_t inum;
54 {
55 	struct entry *ep;
56 
57 	if (inum < ROOTINO || inum >= maxino)
58 		return (NIL);
59 	for (ep = entry[inum % entrytblsize]; ep != NIL; ep = ep->e_next)
60 		if (ep->e_ino == inum)
61 			return (ep);
62 	return (NIL);
63 }
64 
65 /*
66  * We now ignore inodes that are out of range.  This
67  * allows us to attempt to proceed in the face of
68  * a corrupted archive, albeit with future complaints
69  * about failed inode lookups.  We only complain once
70  * about range problems, to avoid irritating the user
71  * without providing any useful information.  Failed
72  * lookups have the bogus name, which is useful, so
73  * they always happen.
74  */
75 static int complained_about_range = 0;
76 
77 /*
78  * Add an entry into the entry table
79  */
80 static void
81 addino(inum, np)
82 	ino_t inum;
83 	struct entry *np;
84 {
85 	struct entry **epp;
86 
87 	if (inum < ROOTINO || inum >= maxino) {
88 		if (!complained_about_range) {
89 			panic(gettext("%s: out of range %d\n"),
90 			    "addino", inum);
91 			complained_about_range = 1;
92 		}
93 		return;
94 	}
95 	epp = &entry[inum % entrytblsize];
96 	np->e_ino = inum;
97 	np->e_next = *epp;
98 	*epp = np;
99 	if (dflag)
100 		for (np = np->e_next; np != NIL; np = np->e_next)
101 			if (np->e_ino == inum)
102 				badentry(np, gettext("duplicate inum"));
103 }
104 
105 /*
106  * Delete an entry from the entry table.  We assume our caller
107  * arranges for the necessary memory reclamation, if needed.
108  */
109 void
110 deleteino(inum)
111 	ino_t inum;
112 {
113 	struct entry *next;
114 	struct entry **prev;
115 
116 	if (inum < ROOTINO || inum >= maxino) {
117 		if (!complained_about_range) {
118 			panic(gettext("%s: out of range %d\n"),
119 			    "deleteino", inum);
120 			complained_about_range = 1;
121 		}
122 		return;
123 	}
124 
125 	prev = &entry[inum % entrytblsize];
126 	for (next = *prev; next != NIL; next = next->e_next) {
127 		if (next->e_ino == inum) {
128 			next->e_ino = 0;
129 			*prev = next->e_next;
130 			return;
131 		}
132 		prev = &next->e_next;
133 	}
134 }
135 
136 /*
137  * Look up an entry by name.
138  *	NOTE: this function handles "complex" pathnames (as returned
139  *	by myname()) for extended file attributes.  The name string
140  *	provided to this function should be terminated with *two*
141  *	NULL characters.
142  */
143 struct entry *
144 lookupname(name)
145 	char *name;
146 {
147 	struct entry *ep;
148 	char *np, *cp;
149 	char buf[MAXPATHLEN];
150 
151 	if (strlen(name) > (sizeof (buf) - 1)) {
152 		(void) fprintf(stderr, gettext("%s: ignoring too-long name\n"),
153 		    "lookupname");
154 		return (NIL);
155 	}
156 
157 	cp = name;
158 	for (ep = lookupino(ROOTINO); ep != NIL; ep = ep->e_entries) {
159 		np = buf;
160 		while (*cp != '/' && *cp != '\0')
161 			*np++ = *cp++;
162 		*np = '\0';
163 		for (; ep != NIL; ep = ep->e_sibling)
164 			if (strcmp(ep->e_name, buf) == 0)
165 				break;
166 		if (*cp++ == '\0') {
167 			if (*cp != '\0') {
168 				ep = ep->e_xattrs;
169 				/*
170 				 * skip over the "./" prefix on all
171 				 * extended attribute paths
172 				 */
173 				cp += 2;
174 			}
175 			if (*cp == '\0')
176 				return (ep);
177 		}
178 		if (ep == NIL)
179 			break;
180 	}
181 	return (NIL);
182 }
183 
184 /*
185  * Look up the parent of a pathname.  This routine accepts complex
186  * names so the provided name argument must terminate with two NULLs.
187  */
188 static struct entry *
189 lookupparent(name)
190 	char *name;
191 {
192 	struct entry *ep;
193 	char *tailindex, savechar, *lastpart;
194 	int xattrparent = 0;
195 
196 	/* find the last component of the complex name */
197 	lastpart = name;
198 	LASTPART(lastpart);
199 	tailindex = strrchr(lastpart, '/');
200 	if (tailindex == 0) {
201 		if (lastpart == name)
202 			return (NIL);
203 		/*
204 		 * tailindex normaly points to the '/' character
205 		 * dividing the path, but in the case of an extended
206 		 * attribute transition it will point to the NULL
207 		 * separator in front of the attribute path.
208 		 */
209 		tailindex = lastpart - 1;
210 		xattrparent = 1;
211 	} else {
212 		*tailindex = '\0';
213 	}
214 	savechar = *(tailindex+1);
215 	*(tailindex+1) = '\0';
216 	ep = lookupname(name);
217 	if (ep != NIL && !xattrparent && ep->e_type != NODE)
218 		panic(gettext("%s is not a directory\n"), name);
219 	if (!xattrparent) *tailindex = '/';
220 	*(tailindex+1) = savechar;
221 	return (ep);
222 }
223 
224 /*
225  * Determine the current pathname of a node or leaf.
226  * The returned pathname will be multiple strings with NULL separators:
227  *
228  *	./<path>/entry\0<path>/attrentry\0<path>/...\0\0
229  *	^	        ^		  ^	    ^
230  *   return pntr    entry attr	    recursive attr  terminator
231  *
232  * Guaranteed to return a name that fits within MAXCOMPLEXLEN and is
233  * terminated with two NULLs.
234  */
235 char *
236 myname(ep)
237 	struct entry *ep;
238 {
239 	char *cp;
240 	struct entry *root = lookupino(ROOTINO);
241 	static char namebuf[MAXCOMPLEXLEN];
242 
243 	cp = &namebuf[MAXCOMPLEXLEN - 3];
244 	*(cp + 1) = '\0';
245 	*(cp + 2) = '\0';
246 	while (cp > &namebuf[ep->e_namlen]) {
247 		cp -= ep->e_namlen;
248 		bcopy(ep->e_name, cp, (size_t)ep->e_namlen);
249 		if (ep == root)
250 			return (cp);
251 		if (ep->e_flags & XATTRROOT)
252 			*(--cp) = '\0';
253 		else
254 			*(--cp) = '/';
255 		ep = ep->e_parent;
256 	}
257 	panic(gettext("%s%s: pathname too long\n"), "...", cp);
258 	return (cp);
259 }
260 
261 /*
262  * Unused symbol table entries are linked together on a freelist
263  * headed by the following pointer.
264  */
265 static struct entry *freelist = NIL;
266 
267 /*
268  * add an entry to the symbol table
269  */
270 struct entry *
271 addentry(name, inum, type)
272 	char *name;
273 	ino_t inum;
274 	int type;
275 {
276 	struct entry *np, *ep;
277 	char *cp;
278 
279 	if (freelist != NIL) {
280 		np = freelist;
281 		freelist = np->e_next;
282 		(void) bzero((char *)np, (size_t)sizeof (*np));
283 	} else {
284 		np = (struct entry *)calloc(1, sizeof (*np));
285 		if (np == NIL) {
286 			(void) fprintf(stderr,
287 			    gettext("no memory to extend symbol table\n"));
288 			done(1);
289 		}
290 	}
291 	np->e_type = type & ~(LINK|ROOT);
292 	if (inattrspace)
293 		np->e_flags |= XATTR;
294 	ep = lookupparent(name);
295 	if (ep == NIL) {
296 		if (inum != ROOTINO || lookupino(ROOTINO) != NIL) {
297 			(void) fprintf(stderr, gettext(
298 			    "%s: bad name %s\n"), "addentry", name);
299 			assert(0);
300 			done(1);
301 		}
302 		np->e_name = savename(name);
303 		/* LINTED: savename guarantees that strlen fits in e_namlen */
304 		np->e_namlen = strlen(name);
305 		np->e_parent = np;
306 		addino(ROOTINO, np);
307 		return (np);
308 	}
309 
310 	if (np->e_flags & XATTR) {
311 		/*
312 		 * skip to the last part of the complex string: it
313 		 * containes the extended attribute file name.
314 		 */
315 		LASTPART(name);
316 	}
317 	cp = strrchr(name, '/');
318 	if (cp == NULL)
319 		cp = name;
320 	else
321 		cp++;
322 
323 	np->e_name = savename(cp);
324 	/* LINTED: savename guarantees that strlen will fit */
325 	np->e_namlen = strlen(np->e_name);
326 	np->e_parent = ep;
327 	/*
328 	 * Extended attribute root directories must be linked to their
329 	 * "parents" via the e_xattrs field.  Other entries are simply
330 	 * added to their parent directories e_entries list.
331 	 */
332 	if ((type & ROOT) && (np->e_flags & XATTR)) {
333 		/* link this extended attribute root dir to its "parent" */
334 		ep->e_xattrs = np;
335 	} else {
336 		/* add this entry to the entry list of the parent dir */
337 		np->e_sibling = ep->e_entries;
338 		ep->e_entries = np;
339 	}
340 	if (type & LINK) {
341 		ep = lookupino(inum);
342 		if (ep == NIL) {
343 			/* XXX just bail on this one and continue? */
344 			(void) fprintf(stderr,
345 			    gettext("link to non-existent name\n"));
346 			done(1);
347 		}
348 		np->e_ino = inum;
349 		np->e_links = ep->e_links;
350 		ep->e_links = np;
351 	} else if (inum != 0) {
352 		ep = lookupino(inum);
353 		if (ep != NIL)
354 			panic(gettext("duplicate entry\n"));
355 		else
356 			addino(inum, np);
357 	}
358 	return (np);
359 }
360 
361 /*
362  * delete an entry from the symbol table
363  */
364 void
365 freeentry(ep)
366 	struct entry *ep;
367 {
368 	struct entry *np;
369 	ino_t inum;
370 
371 	if ((ep->e_flags & REMOVED) == 0)
372 		badentry(ep, gettext("not marked REMOVED"));
373 	if (ep->e_type == NODE) {
374 		if (ep->e_links != NIL)
375 			badentry(ep, gettext("freeing referenced directory"));
376 		if (ep->e_entries != NIL)
377 			badentry(ep, gettext("freeing non-empty directory"));
378 	}
379 	if (ep->e_ino != 0) {
380 		np = lookupino(ep->e_ino);
381 		if (np == NIL)
382 			badentry(ep, gettext("lookupino failed"));
383 		if (np == ep) {
384 			inum = ep->e_ino;
385 			deleteino(inum);
386 			if (ep->e_links != NIL)
387 				addino(inum, ep->e_links);
388 		} else {
389 			for (; np != NIL; np = np->e_links) {
390 				if (np->e_links == ep) {
391 					np->e_links = ep->e_links;
392 					break;
393 				}
394 			}
395 			if (np == NIL)
396 				badentry(ep, gettext("link not found"));
397 		}
398 	}
399 	removeentry(ep);
400 	freename(ep->e_name);
401 	ep->e_next = freelist;
402 	freelist = ep;
403 }
404 
405 /*
406  * Relocate an entry in the tree structure
407  */
408 void
409 moveentry(ep, newname)
410 	struct entry *ep;
411 	char *newname;
412 {
413 	struct entry *np;
414 	char *cp;
415 
416 	np = lookupparent(newname);
417 	if (np == NIL)
418 		badentry(ep, gettext("cannot move ROOT"));
419 	if (np != ep->e_parent) {
420 		removeentry(ep);
421 		ep->e_parent = np;
422 		ep->e_sibling = np->e_entries;
423 		np->e_entries = ep;
424 	}
425 	/* find the last component of the complex name */
426 	LASTPART(newname);
427 	cp = strrchr(newname, '/') + 1;
428 	if (cp == (char *)1)
429 		cp = newname;
430 	freename(ep->e_name);
431 	ep->e_name = savename(cp);
432 	/* LINTED: savename guarantees that strlen will fit */
433 	ep->e_namlen = strlen(cp);
434 	if (strcmp(gentempname(ep), ep->e_name) == 0) {
435 		/* LINTED: result fits in a short */
436 		ep->e_flags |= TMPNAME;
437 	} else {
438 		/* LINTED: result fits in a short */
439 		ep->e_flags &= ~TMPNAME;
440 	}
441 }
442 
443 /*
444  * Remove an entry in the tree structure
445  */
446 static void
447 removeentry(ep)
448 	struct entry *ep;
449 {
450 	struct entry *np;
451 
452 	np = ep->e_parent;
453 	if (ep->e_flags & XATTRROOT) {
454 		if (np->e_xattrs == ep)
455 			np->e_xattrs = NIL;
456 		else
457 			badentry(ep, gettext(
458 				"parent does not reference this xattr tree"));
459 	} else if (np->e_entries == ep) {
460 		np->e_entries = ep->e_sibling;
461 	} else {
462 		for (np = np->e_entries; np != NIL; np = np->e_sibling) {
463 			if (np->e_sibling == ep) {
464 				np->e_sibling = ep->e_sibling;
465 				break;
466 			}
467 		}
468 		if (np == NIL)
469 			badentry(ep, gettext(
470 				"cannot find entry in parent list"));
471 	}
472 }
473 
474 /*
475  * Table of unused string entries, sorted by length.
476  *
477  * Entries are allocated in STRTBLINCR sized pieces so that names
478  * of similar lengths can use the same entry. The value of STRTBLINCR
479  * is chosen so that every entry has at least enough space to hold
480  * a "struct strtbl" header. Thus every entry can be linked onto an
481  * apprpriate free list.
482  *
483  * NB. The macro "allocsize" below assumes that "struct strhdr"
484  *	has a size that is a power of two. Also, an extra byte is
485  *	allocated for the string to provide space for the two NULL
486  *	string terminator required for extended attribute paths.
487  */
488 struct strhdr {
489 	struct strhdr *next;
490 };
491 
492 #define	STRTBLINCR	((size_t)sizeof (struct strhdr))
493 #define	allocsize(size)	(((size) + 2 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
494 
495 static struct strhdr strtblhdr[allocsize(MAXCOMPLEXLEN) / STRTBLINCR];
496 
497 /*
498  * Allocate space for a name. It first looks to see if it already
499  * has an appropriate sized entry, and if not allocates a new one.
500  */
501 char *
502 savename(name)
503 	char *name;
504 {
505 	struct strhdr *np;
506 	size_t len, as;
507 	char *cp;
508 
509 	if (name == NULL) {
510 		(void) fprintf(stderr, gettext("bad name\n"));
511 		done(1);
512 	}
513 	len = strlen(name);
514 	if (len > MAXPATHLEN) {
515 		(void) fprintf(stderr, gettext("name too long\n"));
516 		done(1);
517 	}
518 	as = allocsize(len);
519 	np = strtblhdr[as / STRTBLINCR].next;
520 	if (np != NULL) {
521 		strtblhdr[as / STRTBLINCR].next = np->next;
522 		cp = (char *)np;
523 	} else {
524 		/* Note that allocsize() adds 2 for the trailing \0s */
525 		cp = malloc(as);
526 		if (cp == NULL) {
527 			(void) fprintf(stderr,
528 			    gettext("no space for string table\n"));
529 			done(1);
530 		}
531 	}
532 	(void) strcpy(cp, name);
533 	/* add an extra null for complex (attribute) name support */
534 	cp[len+1] = '\0';
535 	return (cp);
536 }
537 
538 /*
539  * Free space for a name. The resulting entry is linked onto the
540  * appropriate free list.
541  */
542 void
543 freename(name)
544 	char *name;
545 {
546 	struct strhdr *tp, *np;
547 
548 	/* NULL case should never happen, but might as well be careful */
549 	if (name != NULL) {
550 		tp = &strtblhdr[allocsize(strlen(name)) / STRTBLINCR];
551 		/*LINTED [name points to at least sizeof (struct strhdr)]*/
552 		np = (struct strhdr *)name;
553 		np->next = tp->next;
554 		tp->next = np;
555 	}
556 }
557 
558 /*
559  * Useful quantities placed at the end of a dumped symbol table.
560  */
561 struct symtableheader {
562 	int	volno;
563 	uint_t	stringsize;
564 	uint_t	entrytblsize;
565 	time_t	dumptime;
566 	time_t	dumpdate;
567 	ino_t	maxino;
568 	uint_t	ntrec;
569 };
570 
571 /*
572  * dump a snapshot of the symbol table
573  */
574 void
575 dumpsymtable(filename, checkpt)
576 	char *filename;
577 	int checkpt;
578 {
579 	struct entry *ep, *tep;
580 	ino_t i;
581 	struct entry temp, *tentry;
582 	int mynum = 1;
583 	uint_t stroff;
584 	FILE *fp;
585 	struct symtableheader hdr;
586 
587 	vprintf(stdout, gettext("Check pointing the restore\n"));
588 	if ((fp = safe_fopen(filename, "w", 0600)) == (FILE *)NULL) {
589 		perror("fopen");
590 		(void) fprintf(stderr,
591 		    gettext("cannot create save file %s for symbol table\n"),
592 		    filename);
593 		done(1);
594 	}
595 	clearerr(fp);
596 	/*
597 	 * Assign an index to each entry
598 	 * Write out the string entries
599 	 */
600 	for (i = ROOTINO; i < maxino; i++) {
601 		for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
602 			ep->e_index = mynum++;
603 			(void) fwrite(ep->e_name, sizeof (ep->e_name[0]),
604 			    (size_t)allocsize(ep->e_namlen), fp);
605 		}
606 	}
607 	/*
608 	 * Convert e_name pointers to offsets, other pointers
609 	 * to indices, and output
610 	 */
611 	tep = &temp;
612 	stroff = 0;
613 	for (i = ROOTINO; !ferror(fp) && i < maxino; i++) {
614 		for (ep = lookupino(i);
615 		    !ferror(fp) && ep != NIL;
616 		    ep = ep->e_links) {
617 			bcopy((char *)ep, (char *)tep, sizeof (*tep));
618 			/* LINTED: type pun ok */
619 			tep->e_name = (char *)stroff;
620 			stroff += allocsize(ep->e_namlen);
621 			tep->e_parent = (struct entry *)ep->e_parent->e_index;
622 			if (ep->e_links != NIL)
623 				tep->e_links =
624 					(struct entry *)ep->e_links->e_index;
625 			if (ep->e_sibling != NIL)
626 				tep->e_sibling =
627 					(struct entry *)ep->e_sibling->e_index;
628 			if (ep->e_entries != NIL)
629 				tep->e_entries =
630 					(struct entry *)ep->e_entries->e_index;
631 			if (ep->e_xattrs != NIL)
632 				tep->e_xattrs =
633 					(struct entry *)ep->e_xattrs->e_index;
634 			if (ep->e_next != NIL)
635 				tep->e_next =
636 					(struct entry *)ep->e_next->e_index;
637 			(void) fwrite((char *)tep, sizeof (*tep), 1, fp);
638 		}
639 	}
640 	/*
641 	 * Convert entry pointers to indices, and output
642 	 */
643 	for (i = 0; !ferror(fp) && i < (ino_t)entrytblsize; i++) {
644 		if (entry[i] == NIL)
645 			tentry = NIL;
646 		else
647 			tentry = (struct entry *)entry[i]->e_index;
648 		(void) fwrite((char *)&tentry, sizeof (tentry), 1, fp);
649 	}
650 
651 	if (!ferror(fp)) {
652 		/* Ought to have a checksum or magic number */
653 		hdr.volno = checkpt;
654 		hdr.maxino = maxino;
655 		hdr.entrytblsize = entrytblsize;
656 		hdr.stringsize = stroff;
657 		hdr.dumptime = dumptime;
658 		hdr.dumpdate = dumpdate;
659 		hdr.ntrec = ntrec;
660 		(void) fwrite((char *)&hdr, sizeof (hdr), 1, fp);
661 	}
662 
663 	if (ferror(fp)) {
664 		perror("fwrite");
665 		panic(gettext("output error to file %s writing symbol table\n"),
666 		    filename);
667 	}
668 	(void) fclose(fp);
669 }
670 
671 /*
672  * Initialize a symbol table from a file
673  */
674 void
675 initsymtable(filename)
676 	char *filename;
677 {
678 	char *base;
679 	off64_t tblsize;
680 	struct entry *ep;
681 	struct entry *baseep, *lep;
682 	struct symtableheader hdr;
683 	struct stat64 stbuf;
684 	uint_t i;
685 	int fd;
686 
687 	vprintf(stdout, gettext("Initialize symbol table.\n"));
688 	if (filename == NULL) {
689 		if ((maxino / HASHFACTOR) > UINT_MAX) {
690 			(void) fprintf(stderr,
691 			    gettext("file system too large\n"));
692 			done(1);
693 		}
694 		/* LINTED: result fits in entrytblsize */
695 		entrytblsize = maxino / HASHFACTOR;
696 		entry = (struct entry **)
697 			/* LINTED entrytblsize fits in a size_t */
698 			calloc((size_t)entrytblsize, sizeof (*entry));
699 		if (entry == (struct entry **)NULL) {
700 			(void) fprintf(stderr,
701 			    gettext("no memory for entry table\n"));
702 			done(1);
703 		}
704 		ep = addentry(".", ROOTINO, NODE);
705 		/* LINTED: result fits in a short */
706 		ep->e_flags |= NEW;
707 		return;
708 	}
709 	if ((fd = open(filename, O_RDONLY|O_LARGEFILE)) < 0) {
710 		perror("open");
711 		(void) fprintf(stderr,
712 		    gettext("cannot open symbol table file %s\n"), filename);
713 		done(1);
714 	}
715 	if (fstat64(fd, &stbuf) < 0) {
716 		perror("stat");
717 		(void) fprintf(stderr,
718 		    gettext("cannot stat symbol table file %s\n"), filename);
719 		(void) close(fd);
720 		done(1);
721 	}
722 	/*
723 	 * The symbol table file is too small so say we can't read it.
724 	 */
725 	if (stbuf.st_size < sizeof (hdr)) {
726 		(void) fprintf(stderr,
727 		    gettext("cannot read symbol table file %s\n"), filename);
728 		(void) close(fd);
729 		done(1);
730 	}
731 	tblsize = stbuf.st_size - sizeof (hdr);
732 	if (tblsize > ULONG_MAX) {
733 		(void) fprintf(stderr,
734 		    gettext("symbol table file too large\n"));
735 		(void) close(fd);
736 		done(1);
737 	}
738 	/* LINTED tblsize fits in a size_t */
739 	base = calloc((size_t)sizeof (char), (size_t)tblsize);
740 	if (base == NULL) {
741 		(void) fprintf(stderr,
742 		    gettext("cannot allocate space for symbol table\n"));
743 		(void) close(fd);
744 		done(1);
745 	}
746 	/* LINTED tblsize fits in a size_t */
747 	if (read(fd, base, (size_t)tblsize) < 0 ||
748 	    read(fd, (char *)&hdr, sizeof (hdr)) < 0) {
749 		perror("read");
750 		(void) fprintf(stderr,
751 		    gettext("cannot read symbol table file %s\n"), filename);
752 		(void) close(fd);
753 		done(1);
754 	}
755 	(void) close(fd);
756 	switch (command) {
757 	case 'r':
758 	case 'M':
759 		/*
760 		 * For normal continuation, insure that we are using
761 		 * the next incremental tape
762 		 */
763 		if (hdr.dumpdate != dumptime) {
764 			if (hdr.dumpdate < dumptime)
765 				(void) fprintf(stderr, gettext(
766 					"Incremental volume too low\n"));
767 			else
768 				(void) fprintf(stderr, gettext(
769 					"Incremental volume too high\n"));
770 			done(1);
771 		}
772 		break;
773 	case 'R':
774 		/*
775 		 * For restart, insure that we are using the same tape
776 		 */
777 		curfile.action = SKIP;
778 		dumptime = hdr.dumptime;
779 		dumpdate = hdr.dumpdate;
780 		if (!bflag)
781 			newtapebuf(hdr.ntrec);
782 		getvol(hdr.volno);
783 		break;
784 	default:
785 		(void) fprintf(stderr,
786 		    gettext("initsymtable called from command %c\n"),
787 		    (uchar_t)command);
788 		done(1);
789 		/*NOTREACHED*/
790 	}
791 	maxino = hdr.maxino;
792 	entrytblsize = hdr.entrytblsize;
793 	/*LINTED [pointer cast alignment]*/
794 	entry = (struct entry **)
795 	    (base + tblsize - (entrytblsize * sizeof (*entry)));
796 	if (((ulong_t)entry % 4) != 0) {
797 		(void) fprintf(stderr,
798 		    gettext("Symbol table file corrupted\n"));
799 		done(1);
800 	}
801 	/*LINTED [rvalue % 4 == 0] */
802 	baseep = (struct entry *)
803 	    (base + hdr.stringsize - sizeof (*baseep));
804 	if (((ulong_t)baseep % 4) != 0) {
805 		(void) fprintf(stderr,
806 		    gettext("Symbol table file corrupted\n"));
807 		done(1);
808 	}
809 	lep = (struct entry *)entry;
810 	for (i = 0; i < entrytblsize; i++) {
811 		if (entry[i] == NIL)
812 			continue;
813 		entry[i] = &baseep[(long)entry[i]];
814 	}
815 	for (ep = &baseep[1]; ep < lep; ep++) {
816 		ep->e_name = base + (long)ep->e_name;
817 		ep->e_parent = &baseep[(long)ep->e_parent];
818 		if (ep->e_sibling != NIL)
819 			ep->e_sibling = &baseep[(long)ep->e_sibling];
820 		if (ep->e_links != NIL)
821 			ep->e_links = &baseep[(long)ep->e_links];
822 		if (ep->e_entries != NIL)
823 			ep->e_entries = &baseep[(long)ep->e_entries];
824 		if (ep->e_xattrs != NIL)
825 			ep->e_xattrs = &baseep[(long)ep->e_xattrs];
826 		if (ep->e_next != NIL)
827 			ep->e_next = &baseep[(long)ep->e_next];
828 	}
829 }
830