symtab.c revision 92806
1709Swollman/*
210873Sjkh * Copyright (c) 1983, 1993
3709Swollman *	The Regents of the University of California.  All rights reserved.
437Srgrimes *
537Srgrimes * Redistribution and use in source and binary forms, with or without
637Srgrimes * modification, are permitted provided that the following conditions
737Srgrimes * are met:
837Srgrimes * 1. Redistributions of source code must retain the above copyright
937Srgrimes *    notice, this list of conditions and the following disclaimer.
108460Sjkh * 2. Redistributions in binary form must reproduce the above copyright
118460Sjkh *    notice, this list of conditions and the following disclaimer in the
128460Sjkh *    documentation and/or other materials provided with the distribution.
138460Sjkh * 3. All advertising materials mentioning features or use of this software
1437Srgrimes *    must display the following acknowledgement:
1537Srgrimes *	This product includes software developed by the University of
1637Srgrimes *	California, Berkeley and its contributors.
1737Srgrimes * 4. Neither the name of the University nor the names of its contributors
1837Srgrimes *    may be used to endorse or promote products derived from this software
1937Srgrimes *    without specific prior written permission.
2037Srgrimes *
2137Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2237Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2337Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2437Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
253843Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
263843Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
272164Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2837Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2937Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3037Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3137Srgrimes * SUCH DAMAGE.
3237Srgrimes */
3337Srgrimes
3437Srgrimes#ifndef lint
3537Srgrimes#if 0
3637Srgrimesstatic char sccsid[] = "@(#)symtab.c	8.3 (Berkeley) 4/28/95";
3737Srgrimes#endif
3837Srgrimesstatic const char rcsid[] =
3937Srgrimes  "$FreeBSD: head/sbin/restore/symtab.c 92806 2002-03-20 17:55:10Z obrien $";
4037Srgrimes#endif /* not lint */
4137Srgrimes
4237Srgrimes/*
4337Srgrimes * These routines maintain the symbol table which tracks the state
4437Srgrimes * of the file system being restored. They provide lookup by either
4537Srgrimes * name or inode number. They also provide for creation, deletion,
4637Srgrimes * and renaming of entries. Because of the dynamic nature of pathnames,
4737Srgrimes * names should not be saved, but always constructed just before they
4837Srgrimes * are needed, by calling "myname".
4937Srgrimes */
5037Srgrimes
5137Srgrimes#include <sys/param.h>
5237Srgrimes#include <sys/stat.h>
5337Srgrimes
5437Srgrimes#include <ufs/ufs/dinode.h>
5537Srgrimes
5637Srgrimes#include <errno.h>
5737Srgrimes#include <fcntl.h>
5837Srgrimes#include <stdio.h>
592164Sdg#include <stdlib.h>
602164Sdg#include <string.h>
6137Srgrimes#include <unistd.h>
6237Srgrimes
6337Srgrimes#include "restore.h"
6437Srgrimes#include "extern.h"
653036Sdg
663036Sdg/*
673036Sdg * The following variables define the inode symbol table.
688530Sdg * The primary hash table is dynamically allocated based on
698530Sdg * the number of inodes in the file system (maxino), scaled by
708530Sdg * HASHFACTOR. The variable "entry" points to the hash table;
718530Sdg * the variable "entrytblsize" indicates its size (in entries).
721692Sphk */
7337Srgrimes#define HASHFACTOR 5
748530Sdgstatic struct entry **entry;
7537Srgrimesstatic long entrytblsize;
768530Sdg
778530Sdgstatic void		 addino __P((ino_t, struct entry *));
788530Sdgstatic struct entry	*lookupparent __P((char *));
798530Sdgstatic void		 removeentry __P((struct entry *));
8037Srgrimes
81872Sache/*
82872Sache * Look up an entry by inode number
83872Sache */
84872Sachestruct entry *
854091Sachelookupino(inum)
86872Sache	ino_t inum;
877219Sjkh{
887219Sjkh	struct entry *ep;
897219Sjkh
907219Sjkh	if (inum < WINO || inum >= maxino)
917219Sjkh		return (NULL);
921675Sache	for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next)
937219Sjkh		if (ep->e_ino == inum)
947293Sjkh			return (ep);
951675Sache	return (NULL);
961675Sache}
977487Srgrimes
987460Sjkh/*
997750Srgrimes * Add an entry into the entry table
1007460Sjkh */
1017460Sjkhstatic void
1028540Srgrimesaddino(inum, np)
1037487Srgrimes	ino_t inum;
1047487Srgrimes	struct entry *np;
1057487Srgrimes{
1067487Srgrimes	struct entry **epp;
1077487Srgrimes
1087487Srgrimes	if (inum < WINO || inum >= maxino)
1097487Srgrimes		panic("addino: out of range %d\n", inum);
1107761Sache	epp = &entry[inum % entrytblsize];
1117487Srgrimes	np->e_ino = inum;
1127487Srgrimes	np->e_next = *epp;
1137487Srgrimes	*epp = np;
1147487Srgrimes	if (dflag)
1157487Srgrimes		for (np = np->e_next; np != NULL; np = np->e_next)
1167487Srgrimes			if (np->e_ino == inum)
1177487Srgrimes				badentry(np, "duplicate inum");
1187487Srgrimes}
1197487Srgrimes
1207487Srgrimes/*
1219305Sbde * Delete an entry from the entry table
1229305Sbde */
1239305Sbdevoid
1249305Sbdedeleteino(inum)
1259305Sbde	ino_t inum;
1267487Srgrimes{
1277487Srgrimes	struct entry *next;
1287487Srgrimes	struct entry **prev;
1297487Srgrimes
1307487Srgrimes	if (inum < WINO || inum >= maxino)
1317487Srgrimes		panic("deleteino: out of range %d\n", inum);
1327487Srgrimes	prev = &entry[inum % entrytblsize];
1337487Srgrimes	for (next = *prev; next != NULL; next = next->e_next) {
1347487Srgrimes		if (next->e_ino == inum) {
1357487Srgrimes			next->e_ino = 0;
1367487Srgrimes			*prev = next->e_next;
1377487Srgrimes			return;
1387487Srgrimes		}
1397487Srgrimes		prev = &next->e_next;
1407487Srgrimes	}
1417487Srgrimes	panic("deleteino: %d not found\n", inum);
1427487Srgrimes}
1437487Srgrimes
1447487Srgrimes/*
1457487Srgrimes * Look up an entry by name
1467487Srgrimes */
1477259Sjkhstruct entry *
1487487Srgrimeslookupname(name)
1497487Srgrimes	char *name;
1507708Srgrimes{
1517487Srgrimes	struct entry *ep;
1527487Srgrimes	char *np, *cp;
1537487Srgrimes	char buf[MAXPATHLEN];
1547487Srgrimes
1557487Srgrimes	cp = name;
1567708Srgrimes	for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) {
157857Sdg		for (np = buf; *cp != '/' && *cp != '\0' &&
15837Srgrimes				np < &buf[sizeof(buf)]; )
1597487Srgrimes			*np++ = *cp++;
1607487Srgrimes		if (np == &buf[sizeof(buf)])
1617487Srgrimes			break;
1627487Srgrimes		*np = '\0';
1637487Srgrimes		for ( ; ep != NULL; ep = ep->e_sibling)
1647487Srgrimes			if (strcmp(ep->e_name, buf) == 0)
1657487Srgrimes				break;
166958Sache		if (ep == NULL)
1677487Srgrimes			break;
1687487Srgrimes		if (*cp++ == '\0')
1697487Srgrimes			return (ep);
1707487Srgrimes	}
1717487Srgrimes	return (NULL);
1727487Srgrimes}
1737487Srgrimes
1741186Srgrimes/*
1751186Srgrimes * Look up the parent of a pathname
1767487Srgrimes */
1777487Srgrimesstatic struct entry *
1787487Srgrimeslookupparent(name)
1797487Srgrimes	char *name;
1807487Srgrimes{
1817219Sjkh	struct entry *ep;
1827487Srgrimes	char *tailindex;
1837487Srgrimes
1847487Srgrimes	tailindex = strrchr(name, '/');
1857487Srgrimes	if (tailindex == NULL)
1867238Sache		return (NULL);
1877487Srgrimes	*tailindex = '\0';
1887487Srgrimes	ep = lookupname(name);
1897487Srgrimes	*tailindex = '/';
1907708Srgrimes	if (ep == NULL)
1917238Sache		return (NULL);
1927487Srgrimes	if (ep->e_type != NODE)
1937708Srgrimes		panic("%s is not a directory\n", name);
1947487Srgrimes	return (ep);
1957487Srgrimes}
1967238Sache
1977487Srgrimes/*
1987487Srgrimes * Determine the current pathname of a node or leaf
1997708Srgrimes */
2009593Swollmanchar *
2019593Swollmanmyname(ep)
2029593Swollman	struct entry *ep;
2037219Sjkh{
2047219Sjkh	char *cp;
2057487Srgrimes	static char namebuf[MAXPATHLEN];
2067487Srgrimes
2077487Srgrimes	for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
2087487Srgrimes		cp -= ep->e_namlen;
2097238Sache		memmove(cp, ep->e_name, (long)ep->e_namlen);
2107238Sache		if (ep == lookupino(ROOTINO))
2117487Srgrimes			return (cp);
21210716Sjkh		*(--cp) = '/';
21310716Sjkh		ep = ep->e_parent;
21410716Sjkh	}
21510716Sjkh	panic("%s: pathname too long\n", cp);
21610716Sjkh	return(cp);
21710716Sjkh}
2187487Srgrimes
2197477Sache/*
2207477Sache * Unused symbol table entries are linked together on a free list
2217487Srgrimes * headed by the following pointer.
2227487Srgrimes */
2237487Srgrimesstatic struct entry *freelist = NULL;
2247238Sache
2257487Srgrimes/*
2267487Srgrimes * add an entry to the symbol table
2277238Sache */
2287238Sachestruct entry *
22910716Sjkhaddentry(name, inum, type)
23010716Sjkh	char *name;
23110716Sjkh	ino_t inum;
23210716Sjkh	int type;
23310716Sjkh{
23410716Sjkh	struct entry *np, *ep;
23510716Sjkh
23610716Sjkh	if (freelist != NULL) {
23710716Sjkh		np = freelist;
23810716Sjkh		freelist = np->e_next;
2397487Srgrimes		memset(np, 0, (long)sizeof(struct entry));
2407487Srgrimes	} else {
2417487Srgrimes		np = (struct entry *)calloc(1, sizeof(struct entry));
2427487Srgrimes		if (np == NULL)
2437487Srgrimes			panic("no memory to extend symbol table\n");
2447238Sache	}
2457238Sache	np->e_type = type & ~LINK;
2467487Srgrimes	ep = lookupparent(name);
2477487Srgrimes	if (ep == NULL) {
2487487Srgrimes		if (inum != ROOTINO || lookupino(ROOTINO) != NULL)
2497487Srgrimes			panic("bad name to addentry %s\n", name);
2507487Srgrimes		np->e_name = savename(name);
2517487Srgrimes		np->e_namlen = strlen(name);
2527487Srgrimes		np->e_parent = np;
2537487Srgrimes		addino(ROOTINO, np);
2547487Srgrimes		return (np);
2557487Srgrimes	}
2567487Srgrimes	np->e_name = savename(strrchr(name, '/') + 1);
2577487Srgrimes	np->e_namlen = strlen(np->e_name);
2587238Sache	np->e_parent = ep;
2597238Sache	np->e_sibling = ep->e_entries;
2607487Srgrimes	ep->e_entries = np;
2617487Srgrimes	if (type & LINK) {
2627487Srgrimes		ep = lookupino(inum);
2637487Srgrimes		if (ep == NULL)
2647487Srgrimes			panic("link to non-existent name\n");
2657487Srgrimes		np->e_ino = inum;
2667487Srgrimes		np->e_links = ep->e_links;
2677238Sache		ep->e_links = np;
2687238Sache	} else if (inum != 0) {
2697487Srgrimes		if (lookupino(inum) != NULL)
2707487Srgrimes			panic("duplicate entry\n");
2717238Sache		addino(inum, np);
2727238Sache	}
2737487Srgrimes	return (np);
2747487Srgrimes}
2757487Srgrimes
2767487Srgrimes/*
2777708Srgrimes * delete an entry from the symbol table
2787238Sache */
2797487Srgrimesvoid
2807487Srgrimesfreeentry(ep)
2817487Srgrimes	struct entry *ep;
2827487Srgrimes{
2837238Sache	struct entry *np;
2847238Sache	ino_t inum;
2857487Srgrimes
2867238Sache	if (ep->e_flags != REMOVED)
2877487Srgrimes		badentry(ep, "not marked REMOVED");
2887487Srgrimes	if (ep->e_type == NODE) {
2897487Srgrimes		if (ep->e_links != NULL)
2907487Srgrimes			badentry(ep, "freeing referenced directory");
2917487Srgrimes		if (ep->e_entries != NULL)
2927487Srgrimes			badentry(ep, "freeing non-empty directory");
2937487Srgrimes	}
2947487Srgrimes	if (ep->e_ino != 0) {
2957487Srgrimes		np = lookupino(ep->e_ino);
2967487Srgrimes		if (np == NULL)
2977296Sjkh			badentry(ep, "lookupino failed");
2987296Sjkh		if (np == ep) {
2997487Srgrimes			inum = ep->e_ino;
3007487Srgrimes			deleteino(inum);
3017487Srgrimes			if (ep->e_links != NULL)
3027487Srgrimes				addino(inum, ep->e_links);
3037487Srgrimes		} else {
3047487Srgrimes			for (; np != NULL; np = np->e_links) {
30510873Sjkh				if (np->e_links == ep) {
30610873Sjkh					np->e_links = ep->e_links;
30710873Sjkh					break;
30810873Sjkh				}
3097259Sjkh			}
31037Srgrimes			if (np == NULL)
31110873Sjkh				badentry(ep, "link not found");
31210873Sjkh		}
31310873Sjkh	}
31437Srgrimes	removeentry(ep);
31537Srgrimes	freename(ep->e_name);
316	ep->e_next = freelist;
317	freelist = ep;
318}
319
320/*
321 * Relocate an entry in the tree structure
322 */
323void
324moveentry(ep, newname)
325	struct entry *ep;
326	char *newname;
327{
328	struct entry *np;
329	char *cp;
330
331	np = lookupparent(newname);
332	if (np == NULL)
333		badentry(ep, "cannot move ROOT");
334	if (np != ep->e_parent) {
335		removeentry(ep);
336		ep->e_parent = np;
337		ep->e_sibling = np->e_entries;
338		np->e_entries = ep;
339	}
340	cp = strrchr(newname, '/') + 1;
341	freename(ep->e_name);
342	ep->e_name = savename(cp);
343	ep->e_namlen = strlen(cp);
344	if (strcmp(gentempname(ep), ep->e_name) == 0)
345		ep->e_flags |= TMPNAME;
346	else
347		ep->e_flags &= ~TMPNAME;
348}
349
350/*
351 * Remove an entry in the tree structure
352 */
353static void
354removeentry(ep)
355	struct entry *ep;
356{
357	struct entry *np;
358
359	np = ep->e_parent;
360	if (np->e_entries == ep) {
361		np->e_entries = ep->e_sibling;
362	} else {
363		for (np = np->e_entries; np != NULL; np = np->e_sibling) {
364			if (np->e_sibling == ep) {
365				np->e_sibling = ep->e_sibling;
366				break;
367			}
368		}
369		if (np == NULL)
370			badentry(ep, "cannot find entry in parent list");
371	}
372}
373
374/*
375 * Table of unused string entries, sorted by length.
376 *
377 * Entries are allocated in STRTBLINCR sized pieces so that names
378 * of similar lengths can use the same entry. The value of STRTBLINCR
379 * is chosen so that every entry has at least enough space to hold
380 * a "struct strtbl" header. Thus every entry can be linked onto an
381 * appropriate free list.
382 *
383 * NB. The macro "allocsize" below assumes that "struct strhdr"
384 *     has a size that is a power of two.
385 */
386struct strhdr {
387	struct strhdr *next;
388};
389
390#define STRTBLINCR	(sizeof(struct strhdr))
391#define allocsize(size)	(((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
392
393static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR];
394
395/*
396 * Allocate space for a name. It first looks to see if it already
397 * has an appropriate sized entry, and if not allocates a new one.
398 */
399char *
400savename(name)
401	char *name;
402{
403	struct strhdr *np;
404	long len;
405	char *cp;
406
407	if (name == NULL)
408		panic("bad name\n");
409	len = strlen(name);
410	np = strtblhdr[len / STRTBLINCR].next;
411	if (np != NULL) {
412		strtblhdr[len / STRTBLINCR].next = np->next;
413		cp = (char *)np;
414	} else {
415		cp = malloc((unsigned)allocsize(len));
416		if (cp == NULL)
417			panic("no space for string table\n");
418	}
419	(void) strcpy(cp, name);
420	return (cp);
421}
422
423/*
424 * Free space for a name. The resulting entry is linked onto the
425 * appropriate free list.
426 */
427void
428freename(name)
429	char *name;
430{
431	struct strhdr *tp, *np;
432
433	tp = &strtblhdr[strlen(name) / STRTBLINCR];
434	np = (struct strhdr *)name;
435	np->next = tp->next;
436	tp->next = np;
437}
438
439/*
440 * Useful quantities placed at the end of a dumped symbol table.
441 */
442struct symtableheader {
443	int32_t	volno;
444	int32_t	stringsize;
445	int32_t	entrytblsize;
446	time_t	dumptime;
447	time_t	dumpdate;
448	ino_t	maxino;
449	int32_t	ntrec;
450};
451
452/*
453 * dump a snapshot of the symbol table
454 */
455void
456dumpsymtable(filename, checkpt)
457	char *filename;
458	long checkpt;
459{
460	struct entry *ep, *tep;
461	ino_t i;
462	struct entry temp, *tentry;
463	long mynum = 1, stroff = 0;
464	FILE *fd;
465	struct symtableheader hdr;
466
467	vprintf(stdout, "Check pointing the restore\n");
468	if (Nflag)
469		return;
470	if ((fd = fopen(filename, "w")) == NULL) {
471		fprintf(stderr, "fopen: %s\n", strerror(errno));
472		panic("cannot create save file %s for symbol table\n",
473			filename);
474		done(1);
475	}
476	clearerr(fd);
477	/*
478	 * Assign indices to each entry
479	 * Write out the string entries
480	 */
481	for (i = WINO; i <= maxino; i++) {
482		for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
483			ep->e_index = mynum++;
484			(void) fwrite(ep->e_name, sizeof(char),
485			       (int)allocsize(ep->e_namlen), fd);
486		}
487	}
488	/*
489	 * Convert pointers to indexes, and output
490	 */
491	tep = &temp;
492	stroff = 0;
493	for (i = WINO; i <= maxino; i++) {
494		for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
495			memmove(tep, ep, (long)sizeof(struct entry));
496			tep->e_name = (char *)stroff;
497			stroff += allocsize(ep->e_namlen);
498			tep->e_parent = (struct entry *)ep->e_parent->e_index;
499			if (ep->e_links != NULL)
500				tep->e_links =
501					(struct entry *)ep->e_links->e_index;
502			if (ep->e_sibling != NULL)
503				tep->e_sibling =
504					(struct entry *)ep->e_sibling->e_index;
505			if (ep->e_entries != NULL)
506				tep->e_entries =
507					(struct entry *)ep->e_entries->e_index;
508			if (ep->e_next != NULL)
509				tep->e_next =
510					(struct entry *)ep->e_next->e_index;
511			(void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
512		}
513	}
514	/*
515	 * Convert entry pointers to indexes, and output
516	 */
517	for (i = 0; i < entrytblsize; i++) {
518		if (entry[i] == NULL)
519			tentry = NULL;
520		else
521			tentry = (struct entry *)entry[i]->e_index;
522		(void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
523	}
524	hdr.volno = checkpt;
525	hdr.maxino = maxino;
526	hdr.entrytblsize = entrytblsize;
527	hdr.stringsize = stroff;
528	hdr.dumptime = dumptime;
529	hdr.dumpdate = dumpdate;
530	hdr.ntrec = ntrec;
531	(void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
532	if (ferror(fd)) {
533		fprintf(stderr, "fwrite: %s\n", strerror(errno));
534		panic("output error to file %s writing symbol table\n",
535			filename);
536	}
537	(void) fclose(fd);
538}
539
540/*
541 * Initialize a symbol table from a file
542 */
543void
544initsymtable(filename)
545	char *filename;
546{
547	char *base;
548	long tblsize;
549	struct entry *ep;
550	struct entry *baseep, *lep;
551	struct symtableheader hdr;
552	struct stat stbuf;
553	long i;
554	int fd;
555
556	vprintf(stdout, "Initialize symbol table.\n");
557	if (filename == NULL) {
558		entrytblsize = maxino / HASHFACTOR;
559		entry = (struct entry **)
560			calloc((unsigned)entrytblsize, sizeof(struct entry *));
561		if (entry == (struct entry **)NULL)
562			panic("no memory for entry table\n");
563		ep = addentry(".", ROOTINO, NODE);
564		ep->e_flags |= NEW;
565		return;
566	}
567	if ((fd = open(filename, O_RDONLY, 0)) < 0) {
568		fprintf(stderr, "open: %s\n", strerror(errno));
569		panic("cannot open symbol table file %s\n", filename);
570	}
571	if (fstat(fd, &stbuf) < 0) {
572		fprintf(stderr, "stat: %s\n", strerror(errno));
573		panic("cannot stat symbol table file %s\n", filename);
574	}
575	tblsize = stbuf.st_size - sizeof(struct symtableheader);
576	base = calloc(sizeof(char), (unsigned)tblsize);
577	if (base == NULL)
578		panic("cannot allocate space for symbol table\n");
579	if (read(fd, base, (int)tblsize) < 0 ||
580	    read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
581		fprintf(stderr, "read: %s\n", strerror(errno));
582		panic("cannot read symbol table file %s\n", filename);
583	}
584	switch (command) {
585	case 'r':
586		/*
587		 * For normal continuation, insure that we are using
588		 * the next incremental tape
589		 */
590		if (hdr.dumpdate != dumptime) {
591			if (hdr.dumpdate < dumptime)
592				fprintf(stderr, "Incremental tape too low\n");
593			else
594				fprintf(stderr, "Incremental tape too high\n");
595			done(1);
596		}
597		break;
598	case 'R':
599		/*
600		 * For restart, insure that we are using the same tape
601		 */
602		curfile.action = SKIP;
603		dumptime = hdr.dumptime;
604		dumpdate = hdr.dumpdate;
605		if (!bflag)
606			newtapebuf(hdr.ntrec);
607		getvol(hdr.volno);
608		break;
609	default:
610		panic("initsymtable called from command %c\n", command);
611		break;
612	}
613	maxino = hdr.maxino;
614	entrytblsize = hdr.entrytblsize;
615	entry = (struct entry **)
616		(base + tblsize - (entrytblsize * sizeof(struct entry *)));
617	baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
618	lep = (struct entry *)entry;
619	for (i = 0; i < entrytblsize; i++) {
620		if (entry[i] == NULL)
621			continue;
622		entry[i] = &baseep[(long)entry[i]];
623	}
624	for (ep = &baseep[1]; ep < lep; ep++) {
625		ep->e_name = base + (long)ep->e_name;
626		ep->e_parent = &baseep[(long)ep->e_parent];
627		if (ep->e_sibling != NULL)
628			ep->e_sibling = &baseep[(long)ep->e_sibling];
629		if (ep->e_links != NULL)
630			ep->e_links = &baseep[(long)ep->e_links];
631		if (ep->e_entries != NULL)
632			ep->e_entries = &baseep[(long)ep->e_entries];
633		if (ep->e_next != NULL)
634			ep->e_next = &baseep[(long)ep->e_next];
635	}
636}
637