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