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
35static struct entry **entry;
36static uint_t entrytblsize;
37
38#ifdef __STDC__
39static void addino(ino_t, struct entry *);
40static struct entry *lookupparent(char *);
41static void removeentry(struct entry *);
42#else
43static void addino();
44static struct entry *lookupparent();
45static void removeentry();
46#endif
47
48/*
49 * Look up an entry by inode number
50 */
51struct entry *
52lookupino(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 */
75static int complained_about_range = 0;
76
77/*
78 * Add an entry into the entry table
79 */
80static void
81addino(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 */
109void
110deleteino(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 */
143struct entry *
144lookupname(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 */
188static struct entry *
189lookupparent(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 */
235char *
236myname(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 */
265static struct entry *freelist = NIL;
266
267/*
268 * add an entry to the symbol table
269 */
270struct entry *
271addentry(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 */
364void
365freeentry(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 */
408void
409moveentry(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 */
446static void
447removeentry(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 */
488struct 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
495static 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 */
501char *
502savename(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 */
542void
543freename(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 */
561struct 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 */
574void
575dumpsymtable(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 */
674void
675initsymtable(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