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
37static struct entry **entry;
38static uint_t entrytblsize;
39
40#ifdef __STDC__
41static void addino(ino_t, struct entry *);
42static struct entry *lookupparent(char *);
43static void removeentry(struct entry *);
44#else
45static void addino();
46static struct entry *lookupparent();
47static void removeentry();
48#endif
49
50/*
51 * Look up an entry by inode number
52 */
53struct entry *
54lookupino(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 */
77static int complained_about_range = 0;
78
79/*
80 * Add an entry into the entry table
81 */
82static void
83addino(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 */
111void
112deleteino(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 */
145struct entry *
146lookupname(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 */
190static struct entry *
191lookupparent(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 */
237char *
238myname(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 */
267static struct entry *freelist = NIL;
268
269/*
270 * add an entry to the symbol table
271 */
272struct entry *
273addentry(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 */
366void
367freeentry(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 */
410void
411moveentry(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 */
448static void
449removeentry(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 */
490struct 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
497static 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 */
503char *
504savename(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 */
544void
545freename(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 */
563struct 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 */
576void
577dumpsymtable(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 */
676void
677initsymtable(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