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