4.1cBSD/usr/src/etc/restor/restor.c

Compare this file to the similar file:
Show the results in this format:

/* Copyright (c) 1982 Regents of the University of California */

#ifndef lint
char version[] = "@(#)restor.c 2.17 1/8/83";
#endif

/*	Modified to include h option (recursively extract all files within
 *	a subtree) and m option (recreate the heirarchical structure of
 *	that subtree and move extracted files to their proper homes).
 *	8/29/80		by Mike Litzkow
 *
 *	Modified to work on the new file system
 *	1/19/82		by Kirk McKusick
 *
 *	Includes the s (skip files) option for use with multiple dumps on
 *	a single tape.
 */

#define MAXINO	3000
#define BITS	8
#define NCACHE	3
#define SIZEINC 10

#include <stdio.h>
#include <signal.h>
#include <fstab.h>
#include <sys/param.h>
#include <sys/inode.h>
#include <sys/fs.h>
#include <dir.h>
#include <stat.h>
#include <dumprestor.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>

#define ODIRSIZ 14
struct odirect {
	u_short	d_ino;
	char	d_name[ODIRSIZ];
};

#define	MWORD(m,i) (m[(unsigned)(i-1)/NBBY])
#define	MBIT(i)	(1<<((unsigned)(i-1)%NBBY))
#define	BIS(i,w)	(MWORD(w,i) |=  MBIT(i))
#define	BIC(i,w)	(MWORD(w,i) &= ~MBIT(i))
#define	BIT(i,w)	(MWORD(w,i) & MBIT(i))

ino_t	ino;

int	eflag = 0, hflag = 1, mflag = 1, tflag = 0, vflag = 0, yflag = 0;
int	cvtflag = 0;
#define vprintf		if (vflag) fprintf

long	fssize;
char	tapename[] = "/dev/rmt8";
char	*magtape = tapename;
int	mt;
int	dumpnum = 1;
int	volno = 1;
int	curblk = 0;
int	bct = NTREC+1;
char	tbf[NTREC*TP_BSIZE];

daddr_t	seekpt;
FILE	*df;
DIR	*dirp;
int	ofile;
char	dirfile[] = "/tmp/rstaXXXXXX";
char	lnkbuf[MAXPATHLEN + 1];
int	pathlen;

#define INOHASH(val) (val % MAXINO)
struct inotab {
	struct inotab *t_next;
	ino_t	t_ino;
	daddr_t	t_seekpt;
	time_t t_timep[2];
} *inotab[MAXINO];
int maxino = 0;

#define XISDIR	1
#define XTRACTD	2
#define XINUSE	4
#define XLINKED	8
struct xtrlist {
	struct xtrlist	*x_next;
	struct xtrlist	*x_linkedto;
	ino_t		x_ino;
	char		x_flags;
	char 		x_name[1];
	/* actually longer */
} *xtrlist[MAXINO];
int xtrcnt = 0;
struct xtrlist *entry;
struct xtrlist *unknown;
struct xtrlist *allocxtr();

char	*dumpmap;
char	*clrimap;

char	clearedbuf[MAXBSIZE];

extern char *ctime();
extern int seek();
ino_t search();
int dirwrite();
#ifdef RRESTOR
char *host;
#endif

main(argc, argv)
	int argc;
	char *argv[];
{
	register char *cp;
	char command;
	int (*signal())();
	int done();

	if (signal(SIGINT, done) == SIG_IGN)
		signal(SIGINT, SIG_IGN);
	if (signal(SIGTERM, done) == SIG_IGN)
		signal(SIGTERM, SIG_IGN);
	mktemp(dirfile);
	if (argc < 2) {
usage:
		fprintf(stderr, "Usage: restor xfhmsvy file file... or restor tf\n");
		done(1);
	}
	argv++;
	argc -= 2;
	command = '\0';
	for (cp = *argv++; *cp; cp++) {
		switch (*cp) {
		case '-':
			break;
		case 'c':
			cvtflag++;
			break;
		case 'f':
			magtape = *argv++;
#ifdef RRESTOR
		{ char *index();
		  host = magtape;
		  magtape = index(host, ':');
		  if (magtape == 0) {
nohost:
			msg("need keyletter ``f'' and device ``host:tape''");
			done(1);
		  }
		  *magtape++ = 0;
		  if (rmthost(host) == 0)
			done(1);
		}
#endif
			argc--;
			break;
		/* s dumpnum (skip to) for multifile dump tapes */
		case 's':
			dumpnum = atoi(*argv++);
			if (dumpnum <= 0) {
				fprintf(stderr, "Dump number must be a positive integer\n");
				done(1);
			}
			argc--;
			break;
		case 'h':
			hflag = 0;
			break;
		case 'm':
			mflag = 0;
			break;
		case 'x':
			if (command != '\0') {
				fprintf(stderr, "x and t are exclusive\n");
				goto usage;
			}
			command = 'x';
			break;
		case 'v':
			vflag++;
			break;
		case 'y':
			yflag++;
			break;
		case 't':
			if (command != '\0') {
				fprintf(stderr, "x and t are exclusive\n");
				goto usage;
			}
			command = 't';
			tflag++;		
			vflag++;
			break;
		default:
			fprintf(stderr, "Bad key character %c\n", *cp);
			goto usage;
		}
	}
	if (command == '\0') {
		fprintf(stderr, "must specify x or t\n");
		goto usage;
	}
#ifdef RRESTOR
	if (host == 0)
		goto nohost;
#endif
	setuid(getuid());	/* no longer need or want root privileges */
	doit(command, argc, argv);
	done(0);
}

doit(command, argc, argv)
	char	command;
	int	argc;
	char	*argv[]; 
{
	struct mtop tcom;
	char	*ststore();
	register struct inotab *itp;
	register struct xtrlist *xp;
	struct xtrlist **xpp;
	ino_t	d;
	int	xtrfile(), xtrskip(), xtrcvtdir(), xtrcvtskip(),
		xtrlnkfile(), xtrlnkskip(), null();
	int	mode, uid, gid, i;
	struct	stat stbuf;
	time_t	timep[2];
	char	name[BUFSIZ + 1];

	if (argc == 0) {
		argc++;
		*--argv = ".";
	}
#ifdef RRESTOR
	if ((mt = rmtopen(magtape, 0)) < 0) {
#else
	if ((mt = open(magtape, 0)) < 0) {
#endif
		fprintf(stderr, "%s: cannot open tape\n", magtape);
		done(1);
	}
	if (dumpnum != 1) {
		tcom.mt_op = MTFSF;
		tcom.mt_count = dumpnum -1;
#ifdef RRESTOR
		rmtioctl(MTFSF,dumpnum - 1);
#else
		if (ioctl(mt,MTIOCTOP,&tcom) < 0)
			perror("ioctl MTFSF");
#endif
	}
	bzero(clearedbuf, (long)MAXBSIZE);
	if (readhdr(&spcl) == 0) {
		bct--; /* push back this block */
		cvtflag++;
		if (readhdr(&spcl) == 0) {
			fprintf(stderr, "Tape is not a dump tape\n");
			done(1);
		}
		fprintf(stderr, "Converting to new file system format.\n");
	}
	if (vflag) {
		fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
		fprintf(stdout, "Dumped from: %s", ctime(&spcl.c_ddate));
	}
	df = fopen(dirfile, "w");
	if (df == 0) {
		fprintf(stderr,
		    "restor: %s - cannot create directory temporary\n",
		    dirfile);
		done(1);
	}
	if (stat(".", &stbuf) < 0) {
		fprintf(stderr, "cannot stat .\n");
		done(1);
	}
	fssize = stbuf.st_blksize;
	if (fssize <= 0 || ((fssize - 1) & fssize) != 0) {
		fprintf(stderr, "bad block size %d\n", fssize);
		done(1);
	}
	if (checkvol(&spcl, 1) == 0) {
		fprintf(stderr, "Tape is not volume 1 of the dump\n");
	}
	clrimap = 0;
	dumpmap = 0;
	unknown = allocxtr(0, "name unknown - not extracted", 0);
	pass1();  /* This sets the various maps on the way by */
	while (argc--) {
		if ((d = psearch(*argv)) == 0 || BIT(d,dumpmap) == 0) {
			fprintf(stderr, "%s: not on tape\n", *argv++);
			continue;
		}
		if (mflag && !tflag)
			checkdir(*argv);
		if (hflag)
			getleaves(d, *argv++);
		else
			(void)allocxtr(d, *argv++, XINUSE);
	}
	if (dumpnum > 1) {
		/*
		 * if this is a multi-dump tape we always start with 
		 * volume 1, so as to avoid accidentally restoring
		 * from a different dump!
		 */
		resetmt();
		dumpnum = 1;
		volno = 1;
		readhdr(&spcl);
		goto rbits;
	}
newvol:
#ifdef RRESTOR
	rmtclose();
#else
	close(mt);
#endif
	if (tflag)
		return;
getvol:
	fprintf(stderr, "Mount desired tape volume; Specify volume #: ");
	if (gets(tbf) == NULL)
		return;
	volno = atoi(tbf);
	if (volno <= 0) {
		fprintf(stderr, "Volume numbers are positive numerics\n");
		goto getvol;
	}
#ifdef RRESTOR
	if ((mt = rmtopen(magtape, 0)) == -1) {
#else
	if ((mt = open(magtape, 0)) == -1) {
#endif
		fprintf(stderr, "Cannot open tape!\n");
		goto getvol;
	}
	if (dumpnum > 1)
		resetmt();
	if (readhdr(&spcl) == 0) {
		fprintf(stderr, "tape is not dump tape\n");
		goto newvol;
	}
	if (checkvol(&spcl, volno) == 0) {
		fprintf(stderr, "Wrong volume (%d)\n", spcl.c_volume);
		goto newvol;
	}
rbits:
	while (gethead(&spcl) == 0)
		;
	if (checktype(&spcl, TS_INODE) == 1) {
		fprintf(stderr, "Can't find inode mask!\n");
		goto newvol;
	}
	if (checktype(&spcl, TS_BITS) == 0)
		goto rbits;
	readbits(&dumpmap);
	while (xtrcnt > 0) {
again:
		if (ishead(&spcl) == 0) {
			i = 0;
			while (gethead(&spcl) == 0)
				i++;
			fprintf(stderr, "resync restor, skipped %d blocks\n",
			    i);
		}
		if (checktype(&spcl, TS_END) == 1) {
			fprintf(stderr, "end of tape\n");
			break;
		}
		if (checktype(&spcl, TS_INODE) == 0) {
			gethead(&spcl);
			goto again;
		}
		d = spcl.c_inumber;
		entry = unknown;
		entry->x_ino = d;
		for (xp = xtrlist[INOHASH(d)]; xp; xp = xp->x_next) {
			if (d != xp->x_ino || (xp->x_flags & XLINKED))
				continue;
			entry = xp;
			timep[0] = spcl.c_dinode.di_atime;
			timep[1] = spcl.c_dinode.di_mtime;
			mode = spcl.c_dinode.di_mode;
			if (mflag)
				strcpy(name, xp->x_name);
			else
				sprintf(name, "%u", xp->x_ino);
			switch (mode & IFMT) {
			default:
				fprintf(stderr, "%s: unknown file mode 0%o\n",
				    name, mode);
				xp->x_flags |= XTRACTD;
				xtrcnt--;
				goto skipfile;
			case IFCHR:
			case IFBLK:
				vprintf(stdout, "extract special file %s\n", name);
				if (mknod(name, mode, spcl.c_dinode.di_rdev)) {
					fprintf(stderr, "%s: cannot create special file\n", name);
					xp->x_flags |= XTRACTD;
					xtrcnt--;
					goto skipfile;
				}
				getfile(null, null, spcl.c_dinode.di_size);
				break;
			case IFDIR:
				if (mflag) {
					for (itp = inotab[INOHASH(d)];
					     itp; itp = itp->t_next) {
						if (itp->t_ino != d)
							continue;
						itp->t_timep[0] = timep[0];
						itp->t_timep[1] = timep[1];
						break;
					}
					if (itp == 0) 
						fprintf(stderr, "missing directory entry\n");
					vprintf(stdout, "extract directory %s\n", name);
					strncat(name, "/.", BUFSIZ);
					checkdir(name);
					chown(xp->x_name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
					getfile(null, null, spcl.c_dinode.di_size);
					break;
				}
				vprintf(stdout, "extract file %s\n", name);
				if ((ofile = creat(name, 0666)) < 0) {
					fprintf(stderr, "%s: cannot create file\n", name);
					xp->x_flags |= XTRACTD;
					xtrcnt--;
					goto skipfile;
				}
				chown(name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
				if (cvtflag) {
					getfile(xtrcvtdir, xtrcvtskip,
					    spcl.c_dinode.di_size);
					flushent(xtrfile);
				} else
					getfile(xtrfile, xtrskip,
					    spcl.c_dinode.di_size);
				close(ofile);
				break;
			case IFLNK:
				/*
				 * Some early dump tapes have symbolic links
				 * present without the associated data blocks.
				 * This hack avoids trashing a file system with
				 * inodes with missing data blocks.
				 */
				if (spcl.c_count == 0) {
					vprintf(stdout, "%s: 0 length symbolic link (ignored)\n", name);
					xp->x_flags |= XTRACTD;
					xtrcnt--;
					goto skipfile;
				}
				vprintf(stdout, "extract symbolic link %s\n", name);
				uid = spcl.c_dinode.di_uid;
				gid = spcl.c_dinode.di_gid;
				lnkbuf[0] = '\0';
				pathlen = 0;
				getfile(xtrlnkfile, xtrlnkskip, spcl.c_dinode.di_size);
				if (symlink(lnkbuf, name) < 0) {
					fprintf(stderr, "%s: cannot create symbolic link\n", name);
					xp->x_flags |= XTRACTD;
					xtrcnt--;
					goto finished;
				}
				chown(name, uid, gid);
				break;
			case IFREG:
				vprintf(stdout, "extract file %s\n", name);
				if ((ofile = creat(name, 0666)) < 0) {
					fprintf(stderr, "%s: cannot create file\n", name);
					xp->x_flags |= XTRACTD;
					xtrcnt--;
					goto skipfile;
				}
				chown(name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
				getfile(xtrfile, xtrskip, spcl.c_dinode.di_size);
				close(ofile);
				break;
			}
			chmod(name, mode);
			utime(name, timep);
			xp->x_flags |= XTRACTD;
			xtrcnt--;
			goto finished;
		}
skipfile:
		getfile(null, null, spcl.c_dinode.di_size);
finished:
		;
	}
	if (xtrcnt == 0 && !mflag)
		return;
	for (xpp = xtrlist; xpp < &xtrlist[MAXINO]; xpp++) {
		for (xp = *xpp; xp; xp = xp->x_next) {
			if (mflag && (xp->x_flags & XISDIR)) {
				for (itp = inotab[INOHASH(xp->x_ino)];
				     itp; itp = itp->t_next) {
					if (itp->t_ino != xp->x_ino)
						continue;
					utime(xp->x_name, itp->t_timep);
					break;
				}
				if (itp == 0)
					fprintf(stderr, "missing dir entry\n");
			}
			if (xp->x_flags & XTRACTD)
				continue;
			if ((xp->x_flags & XLINKED) == 0) {
				fprintf(stderr, "cannot find file %s\n",
					xp->x_name);
				continue;
			}
			if (!mflag)
				continue;
			vprintf(stdout, "link %s to %s\n",
					xp->x_linkedto->x_name, xp->x_name);
			if (link(xp->x_linkedto->x_name, xp->x_name) < 0)
				fprintf(stderr, "link %s to %s failed\n",
					xp->x_linkedto->x_name, xp->x_name);
		}
	}
}

/*
 * Read the tape, bulding up a directory structure for extraction
 * by name
 */
pass1()
{
	register int i;
	register struct dinode *ip;
	struct direct nulldir;
	char buf[TP_BSIZE];
	int putdir(), null(), dirwrite();

	nulldir.d_ino = 1;
	nulldir.d_namlen = 1;
	strncpy(nulldir.d_name, "/", nulldir.d_namlen);
	nulldir.d_reclen = DIRSIZ(&nulldir);
	while (gethead(&spcl) == 0) {
		fprintf(stderr, "Can't find directory header!\n");
	}
	for (;;) {
		if (checktype(&spcl, TS_BITS) == 1) {
			readbits(&dumpmap);
			continue;
		}
		if (checktype(&spcl, TS_CLRI) == 1) {
			readbits(&clrimap);
			continue;
		}
		if (checktype(&spcl, TS_INODE) == 0) {
finish:
			fclose(df);
			dirp = opendir(dirfile);
			if (dirp == NULL)
				perror("opendir");
			resetmt();
			return;
		}
		ip = &spcl.c_dinode;
		i = ip->di_mode & IFMT;
		if (i != IFDIR) {
			goto finish;
		}
		allocinotab(spcl.c_inumber, seekpt);
		getfile(putdir, null, spcl.c_dinode.di_size);
		putent(&nulldir, dirwrite);
		flushent(dirwrite);
	}
}

/*
 * Put the directory entries in the directory file
 */
putdir(buf, size)
	char *buf;
	int size;
{
	struct direct cvtbuf;
	register struct odirect *odp;
	struct odirect *eodp;
	register struct direct *dp;
	long loc, i;

	if (cvtflag) {
		eodp = (struct odirect *)&buf[size];
		for (odp = (struct odirect *)buf; odp < eodp; odp++)
			if (odp->d_ino != 0) {
				dcvt(odp, &cvtbuf);
				putent(&cvtbuf, dirwrite);
			}
	} else {
		for (loc = 0; loc < size; ) {
			dp = (struct direct *)(buf + loc);
			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
			if (dp->d_reclen <= 0 || dp->d_reclen > i) {
				loc += i;
				continue;
			}
			loc += dp->d_reclen;
			if (dp->d_ino != 0) {
				putent(dp, dirwrite);
			}
		}
	}
}

/*
 *	Recursively find names and inumbers of all files in subtree 
 *	pname and put them in xtrlist[]
 */
getleaves(ino, pname)
	ino_t ino;
	char *pname;
{
	register struct inotab *itp;
	int namelen;
	daddr_t bpt;
	register struct direct *dp;
	char locname[BUFSIZ + 1];

	if (BIT(ino, dumpmap) == 0) {
		vprintf(stdout, "%s: not on the tape\n", pname);
		return;
	}
	for (itp = inotab[INOHASH(ino)]; itp; itp = itp->t_next) {
		if (itp->t_ino != ino)
			continue;
		/*
		 * pname is a directory name 
		 */
		(void)allocxtr(ino, pname, XISDIR);
		/*
		 * begin search through the directory
		 * skipping over "." and ".."
		 */
		strncpy(locname, pname, BUFSIZ);
		strncat(locname, "/", BUFSIZ);
		namelen = strlen(locname);
		seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
		dp = readdir(dirp);
		dp = readdir(dirp);
		dp = readdir(dirp);
		bpt = telldir(dirp);
		/*
		 * "/" signals end of directory
		 */
		while (dp != NULL && 
		    !(dp->d_namlen == 1 && dp->d_name[0] == '/')) {
			locname[namelen] = '\0';
			if (namelen + dp->d_namlen >= BUFSIZ) {
				fprintf(stderr, "%s%s: name exceeds %d char\n",
					locname, dp->d_name, BUFSIZ);
			} else {
				strncat(locname, dp->d_name, dp->d_namlen);
				getleaves(dp->d_ino, locname);
				seekdir(dirp, bpt, itp->t_seekpt);
			}
			dp = readdir(dirp);
			bpt = telldir(dirp);
		}
		return;
	}
	/*
	 * locname is name of a simple file 
	 */
	(void)allocxtr(ino, pname, XINUSE);
}

/*
 * Search the directory tree rooted at inode ROOTINO
 * for the path pointed at by n
 */
psearch(n)
	char	*n;
{
	register char *cp, *cp1;
	char c;

	ino = ROOTINO;
	if (*(cp = n) == '/')
		cp++;
next:
	cp1 = cp + 1;
	while (*cp1 != '/' && *cp1)
		cp1++;
	c = *cp1;
	*cp1 = 0;
	ino = search(ino, cp);
	if (ino == 0) {
		*cp1 = c;
		return(0);
	}
	*cp1 = c;
	if (c == '/') {
		cp = cp1+1;
		goto next;
	}
	return(ino);
}

/*
 * search the directory inode ino
 * looking for entry cp
 */
ino_t
search(inum, cp)
	ino_t	inum;
	char	*cp;
{
	register struct direct *dp;
	register struct inotab *itp;
	int len;

	for (itp = inotab[INOHASH(inum)]; itp; itp = itp->t_next)
		if (itp->t_ino == inum)
			goto found;
	return(0);
found:
	seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
	len = strlen(cp);
	do {
		dp = readdir(dirp);
		if (dp->d_namlen == 1 && dp->d_name[0] == '/')
			return(0);
	} while (dp->d_namlen != len || strncmp(dp->d_name, cp, len));
	return(dp->d_ino);
}

/*
 * Do the file extraction, calling the supplied functions
 * with the blocks
 */
getfile(f1, f2, size)
	int	(*f2)(), (*f1)();
	off_t	size;
{
	register int i;
	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
	union u_spcl addrblk;
#	define addrblock addrblk.s_spcl

	addrblock = spcl;
	for (;;) {
		for (i = 0; i < addrblock.c_count; i++) {
			if (addrblock.c_addr[i]) {
				readtape(&buf[curblk++][0]);
				if (curblk == fssize / TP_BSIZE) {
					(*f1)(buf, size > TP_BSIZE ?
					     (long) (fssize) :
					     (curblk - 1) * TP_BSIZE + size);
					curblk = 0;
				}
			} else {
				if (curblk > 0) {
					(*f1)(buf, size > TP_BSIZE ?
					     (long) (curblk * TP_BSIZE) :
					     (curblk - 1) * TP_BSIZE + size);
					curblk = 0;
				}
				(*f2)(clearedbuf, size > TP_BSIZE ?
					(long) TP_BSIZE : size);
			}
			if ((size -= TP_BSIZE) <= 0) {
				gethead(&spcl);
				goto out;
			}
		}
		if (gethead(&addrblock) == 0) {
			fprintf(stderr, "Missing address (header) block for %s\n",
				entry->x_name);
			spcl.c_magic = 0;
			goto out;
		}
		if (checktype(&addrblock, TS_ADDR) == 0) {
			spcl = addrblock;
			goto out;
		}
	}
out:
	if (curblk > 0) {
		(*f1)(buf, (curblk * TP_BSIZE) + size);
		curblk = 0;
	}
}

/*
 * The next routines are called during file extraction to
 * put the data into the right form and place.
 */
xtrfile(buf, size)
	char	*buf;
	long	size;
{

	if (write(ofile, buf, (int) size) == -1) {
		perror("extract write");
		done(1);
	}
}

xtrskip(buf, size)
	char *buf;
	long size;
{

#ifdef lint
	buf = buf;
#endif
	if (lseek(ofile, size, 1) == -1) {
		perror("extract seek");
		done(1);
	}
}

xtrcvtdir(buf, size)
	struct odirect *buf;
	long size;
{
	struct odirect *odp, *edp;
	struct direct cvtbuf;

	edp = &buf[size / sizeof(struct odirect)];
	for (odp = buf; odp < edp; odp++) {
		dcvt(odp, &cvtbuf);
		putent(&cvtbuf, xtrfile);
	}
}

xtrcvtskip(buf, size)
	char *buf;
	long size;
{

	fprintf(stderr, "unallocated block in directory %s\n",
		entry->x_name);
	xtrskip(buf, size);
}

xtrlnkfile(buf, size)
	char	*buf;
	long	size;
{

	pathlen += size;
	if (pathlen > MAXPATHLEN) {
		fprintf(stderr, "symbolic link name: %s; too long %d\n",
		    buf, size);
		done(1);
	}
	strcat(lnkbuf, buf);
}

xtrlnkskip(buf, size)
	char *buf;
	long size;
{

#ifdef lint
	buf = buf, size = size;
#endif
	fprintf(stderr, "unallocated block in symbolic link %s\n",
		entry->x_name);
	done(1);
}

null() {;}

/*
 * Do the tape i/o, dealing with volume changes
 * etc..
 */
readtape(b)
	char *b;
{
	register long i;
	struct u_spcl tmpbuf;
	char c;

	if (bct >= NTREC) {
		for (i = 0; i < NTREC; i++)
			((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0;
		bct = 0;
#ifdef RRESTOR
		if ((i = rmtread(tbf, NTREC*TP_BSIZE)) < 0) {
#else
		if ((i = read(mt, tbf, NTREC*TP_BSIZE)) < 0) {
#endif
			fprintf(stderr, "Tape read error while restoring %s\n",
				entry->x_name);
			if (!yflag) {
				fprintf(stderr, "continue? ");
				do	{
					fprintf(stderr, "[yn] ");
					c = getchar();
					while (getchar() != '\n')
						/* void */;
				} while (c != 'y' && c != 'n');
				if (c == 'n')
					done(1);
			}
			eflag++;
			i = NTREC*TP_BSIZE;
			bzero(tbf, i);
#ifdef RRESTOR
			if (rmtseek(i, 1) < 0) {
#else
			if (lseek(mt, i, 1) < 0) {
#endif
				fprintf(stderr, "continuation failed\n");
				done(1);
			}
		}
		if (i == 0) {
			bct = NTREC + 1;
			volno++;
loop:
			flsht();
#ifdef RRESTOR
			rmtclose();
#else
			close(mt);
#endif
			fprintf(stderr, "Mount volume %d\n", volno);
			while (getchar() != '\n')
				;
#ifdef RRESTOR
			if ((mt = rmtopen(magtape, 0)) == -1) {
#else
			if ((mt = open(magtape, 0)) == -1) {
#endif
				fprintf(stderr, "Cannot open tape!\n");
				goto loop;
			}
			if (readhdr(&tmpbuf) == 0) {
				fprintf(stderr, "Not a dump tape.Try again\n");
				goto loop;
			}
			if (checkvol(&tmpbuf, volno) == 0) {
				fprintf(stderr, "Wrong tape. Try again\n");
				goto loop;
			}
			readtape(b);
			return;
		}
	}
	bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
}

flsht()
{

	bct = NTREC+1;
}

resetmt()
{
	struct mtop tcom;

	if (dumpnum > 1)
		tcom.mt_op = MTBSF;
	else
		tcom.mt_op = MTREW;
	tcom.mt_count = 1;
	flsht();
#ifdef RRESTOR
	if (rmtioctl(tcom.mt_op, 1) == -1) {
		/* kludge for disk dumps */
		rmtseek((long)0, 0);
	}
#else
	if (ioctl(mt,MTIOCTOP,&tcom) == -1) {
		/* kludge for disk dumps */
		lseek(mt, (long)0, 0);
	}
#endif
	if (dumpnum > 1) {
#ifdef RRESTOR
		rmtioctl(MTFSF, 1);
#else
		tcom.mt_op = MTFSF;
		tcom.mt_count = 1;
		ioctl(mt,MTIOCTOP,&tcom);
#endif
	}
}

checkvol(b, t)
	struct s_spcl *b;
	int t;
{

	if (b->c_volume == t)
		return(1);
	return(0);
}

readhdr(b)
	struct s_spcl *b;
{

	if (gethead(b) == 0)
		return(0);
	if (checktype(b, TS_TAPE) == 0)
		return(0);
	return(1);
}

/*
 * read the tape into buf, then return whether or
 * or not it is a header block.
 */
gethead(buf)
	struct s_spcl *buf;
{
	union u_ospcl {
		char dummy[TP_BSIZE];
		struct	s_ospcl {
			int	c_type;
			time_t	c_date;
			time_t	c_ddate;
			int	c_volume;
			daddr_t	c_tapea;
			ino_t	c_inumber;
			int	c_magic;
			int	c_checksum;
			struct odinode {
				unsigned short odi_mode;
				short	odi_nlink;
				short	odi_uid;
				short	odi_gid;
				off_t	odi_size;
				daddr_t	odi_rdev;
				char	odi_addr[36];
				time_t	odi_atime;
				time_t	odi_mtime;
				time_t	odi_ctime;
			} c_dinode;
			int	c_count;
			char	c_addr[TP_NINDIR];
		} s_ospcl;
	} u_ospcl;

	if (!cvtflag) {
		readtape((char *)buf);
		if (buf->c_magic != NFS_MAGIC || checksum((int *)buf) == 0)
			return(0);
		return(1);
	}
	readtape((char *)(&u_ospcl.s_ospcl));
	bzero((char *)buf, TP_BSIZE);
	buf->c_type = u_ospcl.s_ospcl.c_type;
	buf->c_date = u_ospcl.s_ospcl.c_date;
	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
	buf->c_volume = u_ospcl.s_ospcl.c_volume;
	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
	buf->c_magic = u_ospcl.s_ospcl.c_magic;
	buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
	buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
	buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
	buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
	buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
	buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
	buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
	buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
	buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
	buf->c_count = u_ospcl.s_ospcl.c_count;
	bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, TP_NINDIR);
	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
	    checksum((int *)(&u_ospcl.s_ospcl)) == 0)
		return(0);
	buf->c_magic = NFS_MAGIC;
	return(1);
}

/*
 * return whether or not the buffer contains a header block
 */
ishead(buf)
	struct s_spcl *buf;
{

	if (buf->c_magic != NFS_MAGIC)
		return(0);
	return(1);
}

checktype(b, t)
	struct s_spcl *b;
	int	t;
{

	return(b->c_type == t);
}

/*
 * read a bit mask from the tape into m.
 */
readbits(mapp)
	char **mapp;
{
	register int i;
	char	*m;

	i = spcl.c_count;

	if (*mapp == 0)
		*mapp = (char *)calloc(i, (TP_BSIZE/(NBBY/BITS)));
	m = *mapp;
	while (i--) {
		readtape((char *) m);
		m += (TP_BSIZE/(NBBY/BITS));
	}
	while (gethead(&spcl) == 0)
		;
}

checksum(b)
	register int *b;
{
	register int i, j;

	j = sizeof(union u_spcl) / sizeof(int);
	i = 0;
	do
		i += *b++;
	while (--j);
	if (i != CHECKSUM) {
		fprintf(stderr, "Checksum error %o, file %s\n", i,
			entry->x_name);
		return(0);
	}
	return(1);
}

/*
 *	Check for access into each directory in the pathname of an extracted
 *	file and create such a directory if needed in preparation for moving 
 *	the file to its proper home.
 */
checkdir(name)
	register char *name;
{
	register char *cp;
	int i;

	for (cp = name; *cp; cp++) {
		if (*cp != '/')
			continue;
		*cp = '\0';
		if (access(name, 01) < 0 && mkdir(name, 0777) < 0)
			fprintf(stderr, "restor: "), perror(name);
		*cp = '/';
	}
}

setdir(dev)
	char *dev;
{
	struct fstab *fsp;

	if (setfsent() == 0) {
		fprintf(stderr, "Can't open checklist file: %s\n", FSTAB);
		done(1);
	}
	while ((fsp = getfsent()) != 0) {
		if (strcmp(fsp->fs_spec, dev) == 0) {
			fprintf(stderr, "%s mounted on %s\n", dev, fsp->fs_file);
			if (chdir(fsp->fs_file) >= 0)
				return;
			fprintf(stderr, "%s cannot chdir to %s\n",
			    fsp->fs_file);
			done(1);
		}
	}
	fprintf(stderr, "%s not mounted\n", dev);
	done(1);
}

/*
 * These variables are "local" to the following two functions.
 */
char dirbuf[DIRBLKSIZ];
long dirloc = 0;
long prev = 0;

/*
 * add a new directory entry to a file.
 */
putent(dp, wrtfunc)
	struct direct *dp;
	int (*wrtfunc)();
{

	if (dp->d_ino == 0)
		return;
	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
		((struct direct *)(dirbuf + prev))->d_reclen =
		    DIRBLKSIZ - prev;
		(*wrtfunc)(dirbuf, DIRBLKSIZ);
		dirloc = 0;
	}
	bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen);
	prev = dirloc;
	dirloc += dp->d_reclen;
}

/*
 * flush out a directory that is finished.
 */
flushent(wrtfunc)
	int (*wrtfunc)();
{

	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
	(*wrtfunc)(dirbuf, dirloc);
	dirloc = 0;
}

dirwrite(buf, size)
	char *buf;
	int size;
{

	fwrite(buf, 1, size, df);
	seekpt = ftell(df);
}

dcvt(odp, ndp)
	register struct odirect *odp;
	register struct direct *ndp;
{

	bzero((char *)ndp, (long)(sizeof *ndp));
	ndp->d_ino =  odp->d_ino;
	strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
	ndp->d_namlen = strlen(ndp->d_name);
	ndp->d_reclen = DIRSIZ(ndp);
	/*
	 * this quickly calculates if this inode is a directory.
	 * Currently not maintained.
	 *
	for (itp = inotab[INOHASH(odp->d_ino)]; itp; itp = itp->t_next) {
		if (itp->t_ino != odp->d_ino)
			continue;
		ndp->d_fmt = IFDIR;
		break;
	}
	 */
}

/*
 * Open a directory.
 * Modified to allow any random file to be a legal directory.
 */
DIR *
opendir(name)
	char *name;
{
	register DIR *dirp;

	dirp = (DIR *)malloc(sizeof(DIR));
	dirp->dd_fd = open(name, 0);
	if (dirp->dd_fd == -1) {
		free((char *)dirp);
		return NULL;
	}
	dirp->dd_loc = 0;
	return dirp;
}

/*
 * Seek to an entry in a directory.
 * Only values returned by ``telldir'' should be passed to seekdir.
 * Modified to have many directories based in one file.
 */
void
seekdir(dirp, loc, base)
	register DIR *dirp;
	daddr_t loc, base;
{

	if (loc == telldir(dirp))
		return;
	loc -= base;
	if (loc < 0)
		fprintf(stderr, "bad seek pointer to seekdir %d\n", loc);
	(void)lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0);
	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
	if (dirp->dd_loc != 0)
		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
}

/*
 * get next entry in a directory.
 */
struct direct *
readdir(dirp)
	register DIR *dirp;
{
	register struct direct *dp;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
		if (dp->d_reclen <= 0 ||
		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc)
			return NULL;
		dirp->dd_loc += dp->d_reclen;
		if (dp->d_ino == 0)
			continue;
		return (dp);
	}
}

allocinotab(ino, seekpt)
	ino_t ino;
	daddr_t seekpt;
{
	register struct inotab	*itp;

	itp = (struct inotab *)calloc(1, sizeof(struct inotab));
	itp->t_next = inotab[INOHASH(ino)];
	inotab[INOHASH(ino)] = itp;
	itp->t_ino = ino;
	itp->t_seekpt = seekpt;
}

struct xtrlist *
allocxtr(ino, name, flags)
	ino_t ino;
	char *name;
	char flags;
{
	register struct xtrlist	*xp, *pxp;
	int size;

	size = sizeof(struct xtrlist) + strlen(name);
	xp = (struct xtrlist *)calloc(1, size);
	xp->x_ino = ino;
	xp->x_flags = flags;
	strcpy(xp->x_name, name);
	if (flags == 0)
		return (xp);
	xp->x_next = xtrlist[INOHASH(ino)];
	xtrlist[INOHASH(ino)] = xp;
	xtrcnt++;
	for (pxp = xp->x_next; pxp; pxp = pxp->x_next)
		if (pxp->x_ino == ino && (pxp->x_flags & XLINKED) == 0) {
			xp->x_flags |= XLINKED;
			xp->x_linkedto = pxp;
			xtrcnt--;
			break;
		}
	if (!vflag && !tflag)
		return (xp);
	if (xp->x_flags & XLINKED)
		fprintf(stdout, "%s: linked to %s\n", xp->x_name, pxp->x_name);
	else if (xp->x_flags & XISDIR)
		fprintf(stdout, "%s: directory inode %u\n", xp->x_name, ino);
	else
		fprintf(stdout, "%s: inode %u\n", xp->x_name, ino);
	return (xp);
}

#ifdef RRESTOR
msg(cp, a1, a2, a3)
	char *cp;
{

	fprintf(stderr, cp, a1, a2, a3);
}
#endif RRESTOR

done(exitcode)
	int exitcode;
{

	unlink(dirfile);
	exit(exitcode);
}