DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦6595d8ed7⟧ TextFile

    Length: 23994 (0x5dba)
    Types: TextFile
    Notes: UNIX file
    Names: »restor.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »cmd/dump/restor.c« 

TextFile

/*
 * Restore.
 * restor key [args]
 * f [dump]	Use this dump, not the default.
 * v		Verbose.
 * t		Print dates of the dump.
 * x,X		Extract. X asks for reel numbers and does
 *		a rewind between reels.
 * r,R		Mass restore. R asks for reel numbers and does
 * 		a rewind between reels.
 *
 * Multiple dumps can be put on a single spool of
 * tape by dumping to the no rewind device. The restore is
 * done by positioning the tape using the `skip' command and
 * restoring using the `x' option.
 */
#include <stdio.h>
#include <dumptape.h>
#include <canon.h>
#include <filsys.h>
#include <fblk.h>
#include <discbuf.h>
#include <signal.h>

#define	NRBUF	10			/* # of restore cache buffers */

/*
 * This structure is used
 * to remember the names and i numbers
 * of the files being extracted.
 */
struct	xf
{
	char	*xf_path;		/* Path name */
	ino_t	xf_ino;			/* The associated inumber */
};

/*
 * Structure used to remember
 * things about the directories that
 * were on the tape.
 */
struct	dlist
{
	struct	dlist	*dl_dlp;	/* Link */
	ino_t	dl_ino;			/* Inumber of the directory */
	long	dl_seek;		/* Temp file seek address */
	long	dl_size;		/* Size in bytes */
};

int	key;				/* Operation */
int	vflag;				/* A verbose flag */
char	*dtn	= DTAPE;		/* Dump file name */
FILE	*dtp;				/* Its file pointer */
struct	dumpheader dh;			/* Header buffer */
int	reel	= 1;			/* Reel # */
size_t	length = 512;			/* Length of volume */
size_t	nread;				/* Bytes read from volume */
char	tfn[30]	= "/tmp/ddxxxxxx";	/* Temp file name */
FILE	*tfp;				/* Its file pointer */
struct	dlist	*dlist;			/* List of directory nodes */
struct	dlist	*droot;			/* Root (first) directory node */
char	*ddbuf;				/* Big buffer */
char	*ddend;				/* End of the big buffer */
int	ddnbuf;				/* Size of the big buffer */
union	dumpdata *ddptr;		/* Current buffer pointer */
char	*map;				/* Directory map */
int	nxf;				/* # of `x' files */
ino_t	nindisc;			/* # of inodes on the disc */
ino_t	ningrab;			/* # of inodes to grab */
struct	xf	*xfp;			/* Pointer for x names */

/*
 * Block mapping tables.
 */
#define	LNBN	((daddr_t) NBN)

char	offs[]		= {
	0,
	ND,
	ND+1,
	ND+1+1,
	ND+1+1+1
};

daddr_t	ranges[]	= {
	ND,
	ND + 1*LNBN,
	ND + 1*LNBN + 1*LNBN*LNBN,
	ND + 1*LNBN + 1*LNBN*LNBN + 1*LNBN*LNBN*LNBN
};

char	shifts[]	= {
	0,
	L2NBN,
	2*L2NBN,
	3*L2NBN
};

daddr_t	masks[]		= {
	0,
	LNBN-1,
	LNBN*LNBN-1,
	LNBN*LNBN*LNBN-1
};

/*
 * Forward references for the
 * one pass compiler.
 */
ino_t	lookup();
ino_t	numfile();
daddr_t		balloc();
char	*ctime();
int	cleanup();
struct	dlist *findnode();
union	dumpdata *readdump();
char	*calloc();
DISCBUF	*dbimap();

main(argc, argv)
char *argv[];
{
	register char *p;
	register c, i;
	register struct xf *rxfp;
	char *name, *path;
	ino_t ino;

	if (argc < 2)
		usage();
	p = argv[1];
	i = 1;
	while ((c = *p++) != '\0') {
		switch (c) {

		case 'f':
			if (++i >= argc)
				usage();
			dtn = argv[i];
			break;

		case 'r':
		case 'R':
		case 'x':
		case 'X':
		case 't':
			if (key != 0)
				usage();
			key = c;
			break;

		case 'v':
			vflag = 1;
			break;

		case '-':
		{
			/* Cf. nextvol() */
			extern long RESTMIN, RESTMAX;

			if (++i >= argc)
				usage();
			RESTMIN = atoi(argv[i]);
			if (++i >= argc)
				usage();
			RESTMAX = atoi(argv[i]);
			break;
		}

		default:
			usage();
		}
	}
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, cleanup);
	switch (key) {

	case 'r':
	case 'R':
		if (++i >= argc)
			usage();
		if ((dbfp = fopen(argv[i], "r+w")) == NULL)
			message(1, "%s: cannot open filesystem", argv[i]);
		dbclaim(NRBUF);
		if (key == 'r') {
			opendump();
			nextvol(1);
		} else {
			for (;;) {
				reel = getreel();
				opendump();
				if (readhead(0) != 0)
					break;
				fclose(dtp);
			}
		}
		restore();
		dbflush(1);
		break;

	case 'x':
	case 'X':
		opendump();
		nextvol(1);
		mktemp(tfn);
		if ((tfp = fopen(tfn, "w")) == NULL
		||  (tfp = freopen(tfn, "r+w", tfp)) == NULL)
			message(1, "cannot create temporary file");
		readdirs();
		xfp = (struct xf *)malloc((argc-i)*sizeof(struct xf));
		if (xfp == NULL)
			message(1, "too many restore names");
		rxfp = xfp;
		while (++i < argc) {
			name = argv[i];
			if ((ino = numfile(name)) != 0) {
				if (ino<ROOTIN || ino>dh.dh_nino) {
					message(0, "%s: bad inumber", name);
					continue;
				}
				path = "(by ino)";
			} else  if ((ino = lookup(name)) != 0)
				path = name;
			else {
				message(0, "%s: not found", name);
				continue;
			}
			if (getmap(ino) == 0) {
				message(0, "%s: not dumped", name);
				continue;
			}
			rxfp->xf_path = path;
			rxfp->xf_ino = ino;
			rxfp++;
			++nxf;
		}
		if (nxf == 0)
			break;
		for (i=0; i<nxf; ++i)
			printf("%u\t%s\n", xfp[i].xf_ino, xfp[i].xf_path);
		if (key == 'x')
			readfile(0);
		else {
			for (;;) {
				fclose(dtp);
				reel = getreel();
				opendump();
				readfile(1);
				for (i=0; i<nxf && xfp[i].xf_ino==0; ++i)
					;
				if (i == nxf)
					break;
			}
		}
		fclose(dtp);
		for (i=0; i<nxf; ++i)
			if (xfp[i].xf_ino != 0)
				message(0, "%s: not restored", xfp[i].xf_path);
		break;

	case 't':
		opendump();
		readhead(1);
		fprintf(stderr, "Dump since %s", ctime(&dh.dh_ddate));
		fprintf(stderr, "Dumped  on %s", ctime(&dh.dh_bdate));
		break;

	default:
		usage();
	}
	delexit(0);
}

/*
 * If the supplied character string
 * is all number convert it to binary and
 * return it. Otherwise return 0. A
 * file name that is all numeric is taken
 * to be an inumber.
 */
ino_t
numfile(s)
register char *s;
{
	register ino_t ino;
	register c;

	ino = 0;
	while ((c = *s++)>='0' && c<='9')
		ino = 10*ino + c - '0';
	if (c != '\0')
		return (0);
	return (ino);
}

/*
 * Open the dump tape.
 * Die if the tape cannot be opened
 * for any reason.
 */
opendump()
{
	if ((dtp = fopen(dtn, "r")) == NULL)
		message(1, "%s: cannot open dump file", dtn);
}

/*
 * Read reel number.
 */
getreel()
{
	register c, flag, reel;

	for (;;) {
		fprintf(stderr, "restor: desired volume? ");
		reel = 0;
		flag = 0;
		while ((c = getchar())>='0' && c<='9') {
			flag = 1;
			reel = 10*reel + c - '0';
		}
		if (c == EOF)
			delexit(1);
		if (c=='\n' && flag!=0)
			return (reel);
		message(0, "bad reel number");
		while (c!=EOF && c!='\n')
			c = getchar();
		if (c == EOF)
			delexit(1);
	}
}

/*
 * Do the hard work of a
 * restore.
 * (NOTE: I think that the flag is now a fossil).
 */
readfile(flag)
{
	register union dumpdata *ddp;
	register ino_t ino;
	register FILE *rfp;
	register i;
	int bwerror, outsync;
	char rfn[20];

	if (flag) {
		while ((ddp = readdump()) != NULL)
			if (ddp->dd_type != DD_DATA)
				break;
#if 0
		if (readhead(0) == 0)
			return;
		while ((ddp = readdump()) != NULL) {
			if (ddp->dd_type != DD_MAP)
				break;
			canino(ddp->dd_ino);
			canint(ddp->dd_nmap);
			setmap(ddp);
		}
#endif
	} else
		ddp = readdump();
	if (ddp==NULL || anyfiles()==0)
		return;
	outsync = 0;
	do {
		switch (ddp->dd_type) {

		case DD_EOT:
			return;

		case DD_INO:
			canino(ddp->dd_ino);
			ino = ddp->dd_ino;
			rfp = NULL;
			bwerror = 0;
			if (outsync == 1)
				message(0, "skipped 1 item");
			else if (outsync != 0)
				message(0, "skipped %d items", outsync);
			outsync = 0;
			for (i=0; i<nxf && xfp[i].xf_ino!=ino; ++i)
				;
			if (i != nxf) {
				sprintf(rfn, "%u", ino);
				if ((rfp = fopen(rfn, "w")) == NULL)
					message(0, "%s: cannot create", rfn);
				else
					xfp[i].xf_ino = 0;
			}
			while ((ddp = readdump()) != NULL) {
				if (ddp->dd_type != DD_DATA)
					break;
				canino(ddp->dd_ino);
				if (ddp->dd_ino != ino) {
					if (outsync == 0)
						message(0, "data sync");
					++outsync;
					continue;
				}
				if (rfp != NULL) {
					candaddr(ddp->dd_block);
					canint(ddp->dd_size);
					if (bwrite(rfp, ddp) == 0)
						bwerror = 1;
				}
			}
			if (bwerror)
				message("%s: write error", rfn);
			if (rfp != NULL)
				fclose(rfp);
			if (anyfiles() == 0)
				return;
			break;

		default:
			if (outsync == 0)
				message(0, "inode sync");
			++outsync;
		}
	} while (ddp != NULL);
}

/*
 * Do a restore.
 * The tape is open and the header
 * has been checked.
 */
restore()
{
	register union dumpdata *ddp;
	register DISCBUF *dbp;
	struct filsys *fsp;
	register ino_t ino;
	struct dinode *dip;
	struct dinode dinode;
	int me, outsync;
	int ifmt, ndeleted;

	/*
	 * If the target file system is not
	 * large enough, complain about it and pretend
	 * that all of the inodes that are beyond the
	 * end are not there. This may, of course, leave
	 * the file system addled.
	 */
	dbp = dbread((long) SUPERI);
	fsp = (struct filsys *) dbp->db_data;
	nindisc = fsp->s_isize;
	canino(nindisc);
	nindisc = INOPB * (nindisc-INODEI);
	ningrab = dh.dh_nino;
	if (ningrab > nindisc) {
		message(0, "I-list too small, some I-nodes may be deleted");
		ningrab = nindisc;
	}
	/*
	 * Read in the map.
	 */
	while ((ddp=readdump()) != NULL) {
		if (ddp->dd_type != DD_MAP)
			break;
		canino(ddp->dd_ino);
		canint(ddp->dd_nmap);
		setmap(ddp);
	}
	/*
	 * Do required inode clears.
	 * Never clear the badblock inode.
	 * Clear only as far as we are restoring;
	 * that is, ningrab inodes.
	 */
	for (ino=ROOTIN; ino<=ningrab; ++ino) {
		me = getmap(ino);
		if ((me&DD_BUSY)==0 || (me&DD_HERE)==DD_HERE) {
			if (vflag)
				message(0, "clri I#%u", ino);
			clri(ino);
		}
	}
	/*
	 * Read through the tape.
	 * Look for files that we are going to
	 * restore and do so.
	 */
	outsync  = 0;
	ndeleted = 0;
	while (ddp!=NULL && ddp->dd_type!=DD_EOT) {
		if (ddp->dd_type != DD_INO) {
			if (outsync++ == 0)
				message(0, "inode sync");
			ddp = readdump();
			continue;
		}
		/*
		 * Restore the file.
		 * Block by block.
		 * Don't move anything.
		 */
		canino(ddp->dd_ino);
		if ((ino=ddp->dd_ino) > ningrab) {
			if (vflag != 0)
				message(0, "deleted I#%u", (unsigned)ino);
			++ndeleted;
			printskip(outsync);
			outsync = 0;
			while ((ddp=readdump()) != NULL) {
				if (ddp->dd_type != DD_DATA)
					break;
				canino(ddp->dd_ino);
				if (ddp->dd_ino!=ino && outsync++==0)
					message(0, "skip sync");
			}
			continue;
		}
		if (vflag != 0)
			message(0, "restoring I#%u", (unsigned)ino);
		copyb(&dinode, &ddp->dd_dinode, sizeof(dinode));
		canshort(ddp->dd_dinode.di_mode);
		ifmt = ddp->dd_dinode.di_mode&IFMT;
		if (ifmt==IFREG || ifmt==IFDIR)
			zerob(dinode.di_addr, sizeof(dinode.di_addr));
		printskip(outsync);
		outsync = 0;
		while ((ddp=readdump()) != NULL) {
			if (ddp->dd_type != DD_DATA)
				break;
			canino(ddp->dd_ino);
			if (ddp->dd_ino != ino) {
				if (outsync++ == 0)
					message(0, "data sync");
				continue;
			}
			candaddr(ddp->dd_block);
			canint(ddp->dd_size);
			dbp = dbimap(&dinode, ddp->dd_block);
			copyb(dbp->db_data,ddp->dd_data,ddp->dd_size);
			dbfree(dbp, DB_DIRT);
		}
		dbp = dbread((long) (--ino/INOPB + INODEI));
		dip = (struct dinode *)(dbp->db_data) + ino%INOPB;
		if (dip->di_mode != 0) {
			message(0, "I#%u busy", (unsigned)ino+1);
			clri(ino+1);
		}
		copyb((char *)dip, (char *)&dinode, sizeof(dinode));
		dbfree(dbp, DB_DIRT);
	}
	if (ndeleted != 0)
		message(0, "%d I-nodes deleted", ndeleted);
	fixilist();
}

/*
 * Print out a message that
 * tells the number of data items that were
 * skipped on the tape. Special stuff for
 * handling 0 and 1 items.
 */
printskip(n)
{
	if (n == 1)
		message(0, "skipped 1 item");
	else if (n != 0)
		message(0, "skipped %d items", n);
}

/*
 * Fix the ifree list in
 * the super block. It is safer to
 * always reconstruct it.
 */
fixilist()
{
	DISCBUF *ibp, *sbp;
	register struct filsys *fsp;
	register struct dinode *dip;
	register ino_t  ino;
	short ninode, tinode, minode;
	ino_t tmpino;

	if (vflag)
		message(0, "building I-free list");
	sbp = dbread((long) SUPERI);
	fsp = (struct filsys *)(sbp->db_data);
	tinode = 0;
	ninode = 0;
	minode = fsp->s_isize;
	canshort(minode);
	minode = INOPB * (minode-INODEI);
	ibp = NULL;
	for (ino=1; ino<=minode; ++ino) {
		if ((ino-1)%INOPB == 0) {
			if (ibp != NULL)
				dbfree(ibp, 0);
			ibp = dbread((long) ((ino-1)/INOPB + INODEI));
			dip = (struct dinode *)(ibp->db_data);
		}
		if (dip->di_mode == 0) {
			++tinode;
			if (ninode < NICINOD) {
				tmpino = ino;
				canino(tmpino);
				fsp->s_inode[ninode++] = tmpino;
			}
		}
		++dip;
	}
	dbfree(ibp, 0);
	canshort(tinode);
	fsp->s_tinode = tinode;
	canshort(ninode);
	fsp->s_ninode = ninode;
	strncpy(fsp->s_fname, dh.dh_fname, sizeof(dh.dh_fname));
	strncpy(fsp->s_fpack, dh.dh_fpack, sizeof(dh.dh_fpack));
	dbfree(sbp, DB_DIRT);
}

/*
 * Clear an inode.
 * Free all of its blocks and
 * zero the on disc inode. Don't worry about
 * the super block as it is always rebuilt
 * at the end.
 */
clri(ino)
ino_t ino;
{
	register DISCBUF *dbp;
	register struct dinode *dip;
	register i;
	short dimode;
	daddr_t addr[NADDR];

	dbp = dbread((long) ((ino-1)/INOPB + INODEI));
	dip = (struct dinode *)(dbp->db_data) + (ino-1)%INOPB;
	dimode = dip->di_mode;
	canshort(dimode);
	if ((dimode&IFMT)==IFDIR || (dimode&IFMT)==IFREG) {
		l3tol(addr, dip->di_addr, NADDR);
		for (i=0; i<NADDR-3; ++i)
			bfree(addr[i], 0);
		bfree(addr[NADDR-3], 1);
		bfree(addr[NADDR-2], 2);
		bfree(addr[NADDR-1], 3);
	}
	zerob((char *) dip, sizeof(struct dinode));
	dbfree(dbp, DB_DIRT);
}

/*
 * Free a block.
 * The first argument is the block
 * number. 0 here means no block is allocated
 * and the call is a nop. The second argument
 * is the number of levels of indirect blocks
 * to read through.
 */
bfree(bn, nil)
daddr_t bn;
{
	register DISCBUF *dbp, *dbp1;
	struct filsys *fsp;
	struct fblk *fbp;
	int dbp1flag;
	int i;
	daddr_t ibn;

	if (bn == 0)
		return;
	dbp1 = NULL;
	if (nil != 0) {
		dbp1flag = 0;
		dbp1 = dbread((long) bn);
		for (i=0; i<NBN; ++i) {
			ibn = ((daddr_t *) dbp1->db_data)[i];
			if (ibn != 0) {
				candaddr(ibn);	/* Added by Mike */
				bfree(ibn, nil-1);
			}
		}
	}
	dbp = dbread((long) SUPERI);
	fsp = (struct filsys *) dbp->db_data;
	canshort(fsp->s_nfree);
	if (fsp->s_nfree == NICFREE) {
		if (dbp1 == NULL)
			dbp1 = dbread((long) bn);
		dbp1flag = DB_DIRT;
		fbp = (struct fblk *) dbp1->db_data;
		fbp->df_nfree = fsp->s_nfree;
		canshort(fbp->df_nfree);
		copyb(fbp->df_free, fsp->s_free, sizeof(fsp->s_free));
		fsp->s_nfree = 0;
	}
	candaddr(bn);
	fsp->s_free[fsp->s_nfree++] = bn;
	canshort(fsp->s_nfree);
	candaddr(fsp->s_tfree);
	++fsp->s_tfree;
	candaddr(fsp->s_tfree);
	if (dbp1 != NULL)
		dbfree(dbp1, dbp1flag);
	dbfree(dbp, DB_DIRT);
}

/*
 * Allocate a block.
 * Return 0 if there are no blocks
 * remaining.
 */
daddr_t
balloc()
{
	register struct filsys *fsp;
	register DISCBUF *dbp;
	register DISCBUF *dbp1;
	register struct fblk *fbp;
	short nfree;
	daddr_t tfree, bn;

	dbp = dbread((long) SUPERI);
	fsp = (struct filsys *) dbp->db_data;
	if ((tfree = fsp->s_tfree) == 0)
		message(1, "out of space");
	candaddr(tfree);
	nfree = fsp->s_nfree;
	canshort(nfree);
	if ((bn = fsp->s_free[--nfree]) == 0)
		message(1, "out of space and tfree lied");
	candaddr(bn);
	if (nfree == 0) {
		dbp1 = dbread((long) bn);
		fbp = (struct fblk *) dbp1->db_data;
		nfree = fbp->df_nfree;
		canshort(nfree);
		copyb(fsp->s_free, fbp->df_free, sizeof(fsp->s_free));
		dbfree(dbp1, 0);
	}
	--tfree;
	canshort(nfree);
	fsp->s_nfree = nfree;
	candaddr(tfree);
	fsp->s_tfree = tfree;
	dbfree(dbp, DB_DIRT);
	return (bn);
}

/*
 * Quickly zero out a block of
 * memory. Used to clear out disc inodes
 * and other similar things.
 */
zerob(ap, an)
char *ap;
{
	register char *p;
	register n;

	if ((n = an) != 0) {
		p = ap;
		do {
			*p++ = 0;
		} while (--n);
	}
}

/*
 * Quickly move a block of
 * memory from one place to another
 * place.
 */
copyb(atp, afp, an)
char *atp;
char *afp;
{
	register char *tp, *fp;
	register n;

	if ((n = an) != 0) {
		tp = atp;
		fp = afp;
		do {
			*tp++ = *fp++;
		} while (--n);
	}
}

/*
 * Yet another version of
 * the inode mapping code. This version
 * allocates blocks if they are not present
 * in the file. It makes good use of the
 * buffer cache.
 * A pointer to a DISCBUF holding the
 * block is returned. Usually this will be
 * a buffer created by `dbzero'.
 */
DISCBUF *
dbimap(dip, lb)
struct dinode *dip;
daddr_t lb;
{
	register DISCBUF *dbp;
	register il, newblock;
	daddr_t addr[NADDR];
	daddr_t pb, bpos, *bkp;

	l3tol(addr, dip->di_addr, NADDR);
	for (il=0; il<4; ++il) {
		if (lb < ranges[il]) {
			if (il > 0)
				lb -= ranges[il-1];
			bpos = lb >> shifts[il];
			lb &= masks[il];
			bkp = &addr[(int)bpos + offs[il]];
			newblock = 0;
			if ((pb = *bkp) == 0) {
				newblock = 1;
				*bkp = pb = balloc();
				ltol3(dip->di_addr, addr, NADDR);
			}
			if (pb != 0) {
				while (il-- > 0) {
					if (newblock)
						dbp = dbzero(pb); else
						dbp = dbread(pb);
					bpos = lb >> shifts[il];
					lb &= masks[il];
					bkp = (long *)dbp->db_data + bpos;
					pb = *bkp;
					candaddr(pb);
					if (pb == 0) {
						newblock = 1;
						pb = balloc();
						*bkp = pb;
						candaddr(*bkp);
						dbfree(dbp, DB_DIRT);
					} else {
						newblock = 0;
						dbfree(dbp, 0);
					}
					if (pb == 0)
						break;
				}
			}
			if (pb != 0) {
				if (newblock)
					dbp = dbzero(pb); else
					dbp = dbread(pb);
				return (dbp);
			}
			return (NULL);
		}
	}
	message(0, "file too large to map");
	return (NULL);
}

/*
 * Write a block.
 */
bwrite(fp, ddp)
register FILE *fp;
register union dumpdata *ddp;
{
	lseek(fileno(fp), BUFSIZ*ddp->dd_block, 0);
	if (write(fileno(fp), ddp->dd_data, ddp->dd_size) != ddp->dd_size)
		return (0);
	return (1);
}

/*
 * Check if any files in the
 * `x' file list remain on this
 * tape (as indicated by the
 * map).
 */
anyfiles()
{
	register ino_t ino;
	register i;

	for (i=0; i<nxf; ++i) {
		if ((ino = xfp[i].xf_ino)!=0 && getmap(ino)!=0)
			return (1);
	}
	return (0);
}

/*
 * Read and validate tape header.
 * The `quit' flag is true if errors
 * are fatal.
 * Only allocate the map first time.
 */
readhead(quit)
{
	register char *p;
	register checksum;

	if (read(fileno(dtp), &dh, sizeof dh) != sizeof dh) {
		message(quit, "header read error");
		return (0);
	}
	nread = sizeof dh;
	canint(dh.dh_magic);
	canino(dh.dh_nino);
	cantime(dh.dh_bdate);
	cantime(dh.dh_ddate);
	canint(dh.dh_level);
	canint(dh.dh_reel);
	canint(dh.dh_blocking);
	cansize(dh.dh_nbyte);
	canint(dh.dh_checksum);
	if (dh.dh_magic != DH_MAG) {
		message(quit, "not a dump");
		return (0);
	}
	p = (char *) &dh;
	checksum = 0;
	while (p < (char *) &dh.dh_checksum)
		checksum += (*p++) & 0377;
	if (checksum != dh.dh_checksum) {
		message(quit, "checksum error");
		return (0);
	}
	if (dh.dh_reel != reel) {
		message(quit, "wrong reel (is %d, not %d)", dh.dh_reel, reel);
		return (0);
	}
	++reel;
	length = dh.dh_nbyte;
	if (map == NULL) {
		if ((map = calloc(sizeof(char), dh.dh_nino)) == NULL)
			message(1, "out of memory (map)");
	}
	if (ddbuf != NULL)
		free(ddbuf);
	ddnbuf = dh.dh_blocking * sizeof(union dumpdata);
	if ((ddbuf = malloc(ddnbuf)) == NULL)
		message(1, "out of memory (big buffer)");
	ddend = &ddbuf[ddnbuf];
	ddptr = (union dumpdata *) ddend;
	return (1);
}

/*
 * Read in directories and set up
 * the map. The last tape record is ungotten
 * so that the extract code can be made a
 * little simpler.
 */
readdirs()
{
	register union dumpdata *ddp;
	register struct dlist *dlp;
	unsigned short mode;

	while ((ddp = readdump()) != NULL) {
		switch (ddp->dd_type) {

		case DD_EOT:
			--ddptr;
			return;

		case DD_MAP:
			canino(ddp->dd_ino);
			canint(ddp->dd_nmap);
			setmap(ddp);
			break;

		case DD_INO:
			mode = ddp->dd_dinode.di_mode;
			canshort(mode);
			if ((mode&IFMT) != IFDIR) {
				--ddptr;
				return;
			}
			canino(ddp->dd_ino);
			cansize(ddp->dd_dinode.di_size);
			dlp = (struct dlist *) malloc(sizeof(struct dlist));
			if (dlp == NULL)
				message(1, "out of memory (dlist)");
			dlp->dl_dlp = dlist;
			dlist = dlp;
			if (droot == NULL)
				droot = dlp;
			dlp->dl_ino = ddp->dd_ino;
			dlp->dl_seek = ftell(tfp);
			dlp->dl_size = ddp->dd_dinode.di_size;
			break;

		case DD_DATA:
			canino(ddp->dd_ino);
			candaddr(ddp->dd_block);
			canint(ddp->dd_size);
			if (dlist==NULL || dlist->dl_ino!=ddp->dd_ino)
				message(1, "directory out of sync");
			fseek(tfp, dlp->dl_seek+(BUFSIZ*ddp->dd_block), 0);
			fwrite(ddp->dd_data, sizeof(char), ddp->dd_size, tfp);
			if (ferror(tfp))
				message(1, "directory write error");
			break;

		default:
			message(1, "bad type %d", ddp->dd_type);
		}
	}
}

/*
 * Fill in map.
 */
setmap(ddp)
union dumpdata *ddp;
{
	register char *p1, *p2;
	register nb;

	if ((nb = ddp->dd_nmap) != 0) {
		p1 = &map[ddp->dd_ino-1];
		p2 = ddp->dd_map;
		do {
			*p1++ = *p2++;
		} while (--nb);
	}
}

/*
 * Get map item.
 */
getmap(ino)
ino_t ino;
{
	return (map[ino-1]);
}

/*
 * Read dump file.
 * Canonize the type and look after
 * multi-volume (reel) dumps.
 */
union	dumpdata *
readdump()
{
	register nb;

	while ((char *) ddptr == ddend) {
		if (length != 0 && (nread+ddnbuf) > length) {
			nextvol(0);
			continue;
		}
		if ((nb = read(fileno(dtp), ddbuf, ddnbuf)) != ddnbuf) {
			if (nb != 0)
				message(1, "dump read error");
			nextvol(0);
			continue;
		}
		ddptr = (union dumpdata *) ddbuf;
		nread += nb;
		break;
	}
	canint(ddptr->dd_type);
	return (ddptr++);
}

/*
 * Read the next volume (reel or diskette)
 * from the dump.
 * The flag is passed onto readhead for quiting.
 *
 * RESTMIN, RESTMAX, and restime are used to bound the time taken
 * to restor a volume in hopes of preventing unbounded copies of
 * Coherent dump distributions.  The values are passed with an
 * undocumented '-' option which specifies RESTMIN and RESTMAX.
 */
long RESTMIN, RESTMAX, restime, time();

nextvol(flag)
int flag;
{
	register int c;
	register char *vtype = "reel";

	restime += time(NULL);
	/* Spurious error to detect copies of dump volumes
	 * made with ms-dos formatter/copier.  Activated by
	 * '-' key modifier in command list.
	 * Not documented in manual.
	 */
	if (RESTMIN && reel > 1 && (restime < RESTMIN || restime > RESTMAX))
		message(1, "volume sync: %D", restime);
	for (;;) {
		fclose(dtp);
		if (length != 0)
			vtype = "volume";
		fprintf(stderr, "restor: mount %s %d, type return key...",
			vtype, reel);
		while ((c = getchar())!=EOF && c!='\n')
			;
		if (c == EOF)
			delexit(1);
		restime = -time(NULL);
		opendump();
		if (readhead(flag) != 0)
			break;
	}
}

/*
 * Lookup a file, by name.
 * Return the inumber.
 * This routine only looks in
 * directories. The file may
 * not actually be on the tape.
 */
ino_t
lookup(cp)
register char *cp;
{
	char db[DIRSIZ];
	struct direct dirbuf;
	ino_t ino;
	long seek;
	struct dlist *dlp;

	ino = ROOTIN;
	for (;;) {
		{
			register char *dp;
			register c;

			while ((c = *cp++) == '/')
				;
			if (c == '\0')
				return (ino);
			dp = db;
			for (;;) {
				if (dp < &db[DIRSIZ])
					*dp++ = c;
				if ((c = *cp)=='\0' || c=='/')
					break;
				++cp;
			}
			while (dp < &db[DIRSIZ])
				*dp++ = 0;
		}
		if ((dlp = findnode(ino)) == NULL)
			return (0);
		seek = dlp->dl_seek;
		for (;;) {
			if (seek-dlp->dl_seek >= dlp->dl_size)
				return (0);
			fseek(tfp, seek, 0);
			fread(&dirbuf, sizeof(dirbuf), 1, tfp);
			if (ferror(tfp))
				message(1, "temporary file read error");
			canino(dirbuf.d_ino);
			ino = dirbuf.d_ino;
			if (ino!=0 && strncmp(db, dirbuf.d_name, DIRSIZ)==0)
				break;
			seek += sizeof(struct direct);
		}
	}
}

/*
 * Look for a directory inode
 * in the dlist.
 */
struct	dlist *
findnode(ino)
register ino_t ino;
{
	register struct dlist *dlp;

	dlp = dlist;
	while (dlp != NULL) {
		if (dlp->dl_ino == ino)
			break;
		dlp = dlp->dl_dlp;
	}
	return (dlp);
}

/*
 * Usage message.
 */
usage()
{
	fprintf(stderr, "Usage: restor key [args]\n");
	delexit(1);
}

/*
 * Message output.
 */
message(quit, a)
{
	fprintf(stderr, "restor: %r\n", &a);
	if (quit)
		delexit(1);
}

/*
 * Cleanup function.
 * Called from the interrupt signal.
 */
cleanup()
{
	delexit(1);
}

/*
 * Exit.
 * Delete the temp file, if there.
 */
delexit(s)
{
	if (tfp != NULL)
		unlink(tfn);
	exit(s);
}