2.11BSD/src/sbin/restor/restor.c

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

/*
 * 1995/06/09 - standalone restor must be loaded split I/D because the
 *		disklabel handling increased the size of standalone programs.
 *		This is not too much of a problem since the Kernel has been
 *		required to be split I/D for several years.
 *
 *		Since split I/D is now required the NCACHE parameter is no
 *		longer ifdef'd on STANDALONE (and is always 3 instead of 1).
 */

#include <sys/param.h>
#ifdef	NONSEPARATE
#define MAXINO	1000
#else
#define MAXINO	3000
#endif
#define BITS	8
#define MAXXTR	60
#define NCACHE	3
#define	flsht()	(bct = NTREC + 1)

#ifndef STANDALONE
#include <stdio.h>
#include <signal.h>
#endif
#include <sys/inode.h>
#include <sys/fs.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <protocols/dumprestor.h>

#define	MWORD(m,i) (m[(unsigned)(i-1)/MLEN])
#define	MBIT(i)	(1<<((unsigned)(i-1)%MLEN))
#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))

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

struct	fs	sblock;

int	fi;
ino_t	ino, maxi, curino;

int	mt;
char	tapename[] = "/dev/rmt1";
char	*magtape = tapename;
#ifdef STANDALONE
char	mbuf[50];
char	module[] = "Restor";
#endif

#ifndef STANDALONE
daddr_t	seekpt;
u_int	prev;
int	df, ofile;
char	dirfile[] = "rstXXXXXX";
struct 	direct *rddir();

struct {
	ino_t	t_ino;
	daddr_t	t_seekpt;
} inotab[MAXINO];
int	ipos;

#define ONTAPE	1
#define XTRACTD	2
#define XINUSE	4
struct xtrlist {
	ino_t	x_ino;
	char	x_flags;
} xtrlist[MAXXTR];

char	name[12];

DIR	drblock;
#endif

u_char	eflag, cvtflag, isdir, volno = 1;

struct dinode tino, dino;
daddr_t	taddr[NADDR];

daddr_t	curbno;

#ifndef	STANDALONE
short	dumpmap[MSIZ];
#endif
short	clrimap[MSIZ];


int bct = NTREC+1;
char tbf[NTREC*DEV_BSIZE];

struct	cache {
	daddr_t	c_bno;
	int	c_time;
	char	c_block[DEV_BSIZE];
} cache[NCACHE];
int	curcache;

extern  long lseek();

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

#ifndef STANDALONE
	mktemp(dirfile);
	if (argc < 2) {
usage:
		printf("Usage: restor x file file..., restor r filesys, or restor t\n");
		exit(1);
	}
	argv++;
	argc -= 2;
	for (cp = *argv++; *cp; cp++) {
		switch (*cp) {
		case '-':
			break;
		case 'c':
			cvtflag = 1;
			break;
		case 'f':
			magtape = *argv++;
			argc--;
			break;
		case 'r':
		case 'R':
		case 't':
		case 'x':
			command = *cp;
			break;
		default:
			printf("Bad key character %c\n", *cp);
			goto usage;
		}
	}
	if (command == 'x') {
		if (signal(SIGINT, done) == SIG_IGN)
			signal(SIGINT, SIG_IGN);
		if (signal(SIGTERM, done) == SIG_IGN)
			signal(SIGTERM, SIG_IGN);

		df = open(dirfile, O_CREAT | O_TRUNC| O_RDWR, 0666);
		if (df < 0) {
			printf("restor: %s - cannot create directory temporary\n", dirfile);
			exit(1);
		}
	}
	doit(command, argc, argv);
	if (command == 'x')
		unlink(dirfile);
	exit(0);
#else
	printf("%s\n",module);
	magtape = "tape";
	doit('r', 1, 0);
#endif
}

doit(command, argc, argv)
char	command;
int	argc;
char	*argv[];
{
	extern char *ctime();
	register i, k;
	ino_t	d;
#ifndef STANDALONE
	int	xtrfile(), skip(), null();
#endif
	int	rstrfile(), rstrskip();
	register struct dinode *ip, *ip1;

#ifndef STANDALONE
	if ((mt = open(magtape, 0)) < 0) {
		printf("%s: cannot open tape\n", magtape);
		exit(1);
	}
#else
	do {
		printf("Tape? ");
		gets(mbuf);
		mt = open(mbuf, 0);
	} while (mt == -1);
	magtape = mbuf;
#endif
	switch(command) {
#ifndef STANDALONE
	case 't':
		if (readhdr(&spcl) == 0) {
			printf("Tape is not a dump tape\n");
			exit(1);
		}
		printf("Dump   date: %s", ctime(&spcl.c_date));
		printf("Dumped from: %s", ctime(&spcl.c_ddate));
		return;
	case 'x':
		if (readhdr(&spcl) == 0) {
			printf("Tape is not a dump tape\n");
			unlink(dirfile);
			exit(1);
		}
		if (checkvol(&spcl, 1) == 0) {
			printf("Tape is not volume 1 of the dump\n");
			unlink(dirfile);
			exit(1);
		}
		pass1();  /* This sets the various maps on the way by */
		i = 0;
		while (i < MAXXTR-1 && argc--) {
			if ((d = psearch(*argv)) == 0 || BIT(d, dumpmap) == 0) {
				printf("%s: not on the tape\n", *argv++);
				continue;
			}
			xtrlist[i].x_ino = d;
			xtrlist[i].x_flags |= XINUSE;
			printf("%s: inode %u\n", *argv, d);
			argv++;
			i++;
		}
newvol:
		flsht();
		close(mt);
getvol:
		printf("Mount desired tape volume: Specify volume #: ");
		if (gets(tbf) == NULL)
			return;
		volno = atoi(tbf);
		if (volno <= 0) {
			printf("Volume numbers are positive numerics\n");
			goto getvol;
		}
		mt = open(magtape, 0);
		if (readhdr(&spcl) == 0) {
			printf("tape is not dump tape\n");
			goto newvol;
		}
		if (checkvol(&spcl, volno) == 0) {
			printf("Wrong volume (%d)\n", spcl.c_volume);
			goto newvol;
		}
rbits:
		while (gethead(&spcl) == 0)
			;
		if (checktype(&spcl, TS_INODE) == 1) {
			printf("Can't find inode mask!\n");
			goto newvol;
		}
		if (checktype(&spcl, TS_BITS) == 0)
			goto rbits;
		readbits(dumpmap);
		i = 0;
		for (k = 0; xtrlist[k].x_flags; k++) {
			if (BIT(xtrlist[k].x_ino, dumpmap)) {
				xtrlist[k].x_flags |= ONTAPE;
				i++;
			}
		}
		while (i > 0) {
again:
			if (ishead(&spcl) == 0)
				while(gethead(&spcl) == 0)
					;
			if (checktype(&spcl, TS_END) == 1) {
				printf("end of tape\n");
checkdone:
				for (k = 0; xtrlist[k].x_flags; k++)
					if ((xtrlist[k].x_flags&XTRACTD) == 0)
						goto newvol;
					return;
			}
			if (checktype(&spcl, TS_INODE) == 0) {
				gethead(&spcl);
				goto again;
			}
			d = spcl.c_inumber;
			for (k = 0; xtrlist[k].x_flags; k++) {
				if (d == xtrlist[k].x_ino) {
					printf("extract file %u\n", xtrlist[k].x_ino);
					sprintf(name, "%u", xtrlist[k].x_ino);
					if ((ofile = creat(name, 0666)) < 0) {
						printf("%s: cannot create file\n", name);
						i--;
						continue;
					}
					fchown(ofile, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
					getfile(d, xtrfile, skip, spcl.c_dinode.di_size);
					i--;
					xtrlist[k].x_flags |= XTRACTD;
					close(ofile);
					goto done;
				}
			}
			getfile(d, null, null, spcl.c_dinode.di_size);
done:
			;
		}
		goto checkdone;
#endif
	case 'r':
	case 'R':
#ifndef STANDALONE
		if ((fi = open(*argv, 2)) < 0) {
			printf("%s: cannot open\n", *argv);
			exit(1);
		}
#else
		do {
			char charbuf[50];

			printf("Disk? ");
			gets(charbuf);
			fi = open(charbuf, 2);
		} while (fi == -1);
#endif
#ifndef STANDALONE
		if (command == 'R') {
			printf("Enter starting volume number: ");
			if (gets(tbf) == EOF) {
				volno = 1;
				printf("\n");
			}
			else
				volno = atoi(tbf);
		}
		else
#endif
			volno = 1;
		printf("Last chance before scribbling on %s. ",
#ifdef STANDALONE
								"disk");
#else
								*argv);
#endif
		while (getchar() != '\n');
		dread((daddr_t)SBLOCK, (char *)&sblock, sizeof(sblock));
		maxi = (sblock.fs_isize-2)*INOPB;
		if (readhdr(&spcl) == 0) {
			printf("Missing volume record\n");
			exit(1);
		}
		if (checkvol(&spcl, volno) == 0) {
			printf("Tape is not volume %d\n", volno);
			exit(1);
		}
		gethead(&spcl);
		for (;;) {
ragain:
			if (ishead(&spcl) == 0) {
				printf("Missing header block\n");
				while (gethead(&spcl) == 0)
					;
				eflag++;
			}
			if (checktype(&spcl, TS_END) == 1) {
				printf("End of tape\n");
				close(mt);
				dwrite((daddr_t) SBLOCK, (char *) &sblock);
				return;
			}
			if (checktype(&spcl, TS_CLRI) == 1) {
				readbits(clrimap);
				for (ino = 1; ino <= maxi; ino++)
					if (BIT(ino, clrimap) == 0) {
						getdino(ino, &tino);
						if (tino.di_mode == 0)
							continue;
						itrunc(&tino);
						clri(&tino);
						putdino(ino, &tino);
					}
				dwrite((daddr_t) SBLOCK, (char *) &sblock);
				goto ragain;
			}
			if (checktype(&spcl, TS_BITS) == 1) {
#ifndef STANDALONE
				readbits(dumpmap);
#else
				readbits(0);
#endif
				goto ragain;
			}
			if (checktype(&spcl, TS_INODE) == 0) {
				printf("Unknown header type\n");
				eflag++;
				gethead(&spcl);
				goto ragain;
			}
			ino = spcl.c_inumber;
			if (eflag)
				printf("Resynced at inode %u\n", ino);
			eflag = 0;
			if (ino > maxi) {
				printf("%u: ilist too small\n", ino);
				gethead(&spcl);
				goto ragain;
			}
			dino = spcl.c_dinode;
			getdino(ino, &tino);
			curbno = 0;
			itrunc(&tino);
			clri(&tino);
			bzero(taddr, NADDR * sizeof (daddr_t));
			if (cvtflag)
				futz(dino.di_addr);
			taddr[0] = dino.di_addr[0];
			isdir = ((dino.di_mode & IFMT) == IFDIR);
			getfile(ino, rstrfile, rstrskip, dino.di_size);
			ip = &tino;
			ip1 = &dino;
			ip->di_mode = ip1->di_mode;
			ip->di_nlink = ip1->di_nlink;
			ip->di_uid = ip1->di_uid;
			ip->di_gid = ip1->di_gid;
			if (cvtflag && isdir)
				ip->di_size = curbno * DEV_BSIZE; /* XXX */
			else
				ip->di_size = ip1->di_size;
			ip->di_atime = ip1->di_atime;
			ip->di_mtime = ip1->di_mtime;
			ip->di_ctime = ip1->di_ctime;
			bcopy(taddr, ip->di_addr, NADDR * sizeof(daddr_t));
			putdino(ino, &tino);
			isdir = 0;
		}
	}
}

/*
 * Read the tape, bulding up a directory structure for extraction
 * by name
 */
#ifndef STANDALONE
pass1()
{
	register i;
	struct dinode *ip;
	struct direct nulldir;
	int	putdir(), null();

	while (gethead(&spcl) == 0) {
		printf("Can't find directory header!\n");
	}
	nulldir.d_ino = 0;
	nulldir.d_namlen = 1;
	strcpy(nulldir.d_name, "/");
	nulldir.d_reclen = DIRSIZ(&nulldir);
	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:
			close(mt);
			return;
		}
		ip = &spcl.c_dinode;
		i = ip->di_mode & IFMT;
		if (i != IFDIR) {
			goto finish;
		}
		inotab[ipos].t_ino = spcl.c_inumber;
		inotab[ipos++].t_seekpt = seekpt;
		getfile(spcl.c_inumber, putdir, null, spcl.c_dinode.di_size);
		putent(&nulldir);
		flushent();
	}
}
#endif

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

	bzero(ndp, sizeof (struct direct));
	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);
}

/*
 * Do the file extraction, calling the supplied functions
 * with the blocks
 */
getfile(n, f1, f2, size)
ino_t	n;
int	(*f2)(), (*f1)();
long	size;
{
	register i;
	struct spcl addrblock;
	char buf[DEV_BSIZE];

	addrblock = spcl;
	curino = n;
	goto start;
	for (;;) {
		if (gethead(&addrblock) == 0) {
			printf("Missing address (header) block\n");
			goto eloop;
		}
		if (checktype(&addrblock, TS_ADDR) == 0) {
			spcl = addrblock;
			curino = 0;
			return;
		}
start:
		for (i = 0; i < addrblock.c_count; i++) {
			if (addrblock.c_addr[i]) {
				readtape(buf);
				(*f1)(buf, size > DEV_BSIZE ? (long) DEV_BSIZE : size);
			}
			else {
				bzero(buf, DEV_BSIZE);
				(*f2)(buf, size > DEV_BSIZE ? (long) DEV_BSIZE : size);
			}
			if ((size -= DEV_BSIZE) <= 0) {
eloop:
				while (gethead(&spcl) == 0)
					;
				if (checktype(&spcl, TS_ADDR) == 1)
					goto eloop;
				curino = 0;
				return;
			}
		}
	}
}

/*
 * Do the tape i\/o, dealling with volume changes
 * etc..
 */
readtape(b)
char *b;
{
	register i;
	struct spcl tmpbuf;

	if (bct >= NTREC) {
		for (i = 0; i < NTREC; i++)
			((struct spcl *)&tbf[i*DEV_BSIZE])->c_magic = 0;
		bct = 0;
		if ((i = read(mt, tbf, NTREC*DEV_BSIZE)) < 0) {
			printf("Tape read error: inode %u\n", curino);
			eflag++;
			for (i = 0; i < NTREC; i++)
				bzero(&tbf[i*DEV_BSIZE], DEV_BSIZE);
		}
		if (i == 0) {
			bct = NTREC + 1;
			volno++;
loop:
			flsht();
			close(mt);
			printf("Mount volume %d\n", volno);
			while (getchar() != '\n')
				;
			if ((mt = open(magtape, 0)) == -1) {
				printf("Cannot open tape!\n");
				goto loop;
			}
			if (readhdr(&tmpbuf) == 0) {
				printf("Not a dump tape.Try again\n");
				goto loop;
			}
			if (checkvol(&tmpbuf, volno) == 0) {
				printf("Wrong tape. Try again\n");
				goto loop;
			}
			readtape(b);
			return;
		}
	}
#ifdef	STANDALONE
	if(b)
#endif
	bcopy(&tbf[(bct++*DEV_BSIZE)], b, DEV_BSIZE);
}

#ifndef	STANDALONE
/*
 * search the directory inode ino
 * looking for entry cp
 */
ino_t
search(inum, cp)
ino_t	inum;
char	*cp;
{
	register i, len;
	register struct direct *dp;

	for (i = 0; i < MAXINO; i++)
		if (inotab[i].t_ino == inum) {
			goto found;
		}
	return(0);
found:
	lseek(df, inotab[i].t_seekpt, 0);
	drblock.dd_loc = 0;
	len = strlen(cp);
	do {
		dp = rddir();
		if (dp == NULL || dp->d_ino == 0)
			return(0);
	} while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0);
	return((ino_t)dp->d_ino);
}

/*
 * Search the directory tree rooted at inode 2
 * for the path pointed at by n
 */
psearch(n)
char	*n;
{
	register char *cp, *cp1;
	register 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);
}
#endif	STANDALONE

/*
 * read/write a disk block, be sure to update the buffer
 * cache if needed.
 */
dwrite(bno, b)
daddr_t	bno;
char	*b;
{
	register i;

	for (i = 0; i < NCACHE; i++) {
		if (cache[i].c_bno == bno) {
			bcopy(b, cache[i].c_block, DEV_BSIZE);
			cache[i].c_time = 0;
			break;
		}
		else
			cache[i].c_time++;
	}
	lseek(fi, bno*DEV_BSIZE, 0);
	if(write(fi, b, DEV_BSIZE) != DEV_BSIZE) {
#ifdef STANDALONE
		printf("disk write error:  block %D\n", bno);
#else
		fprintf(stderr, "disk write error:  block %ld\n", bno);
#endif
		exit(1);
	}
}

dread(bno, buf, cnt)
daddr_t bno;
char *buf;
{
	register i, j;

	j = 0;
	for (i = 0; i < NCACHE; i++) {
		if (++curcache >= NCACHE)
			curcache = 0;
		if (cache[curcache].c_bno == bno) {
			bcopy(cache[curcache].c_block, buf, cnt);
			cache[curcache].c_time = 0;
			return;
		}
		else {
			cache[curcache].c_time++;
			if (cache[j].c_time < cache[curcache].c_time)
				j = curcache;
		}
	}

	lseek(fi, bno*DEV_BSIZE, 0);
	if (read(fi, cache[j].c_block, DEV_BSIZE) != DEV_BSIZE) {
#ifdef STANDALONE
		printf("read error:  block %D\n", bno);
#else
		printf("read error:  block %ld\n", bno);
#endif
		exit(1);
	}
	bcopy(cache[j].c_block, buf, cnt);
	cache[j].c_time = 0;
	cache[j].c_bno = bno;
}

/*
 * the inode manpulation routines. Like the system.
 *
 * clri zeros the inode
 */
clri(ip)
register struct dinode *ip;
{
	if (ip->di_mode&IFMT)
		sblock.fs_tinode++;
	bzero(ip, sizeof (*ip));
}

/*
 * itrunc/tloop/bfree free all of the blocks pointed at by the inode
 */
itrunc(ip)
register struct dinode *ip;
{
	register i;
	daddr_t bn;

	if (ip->di_mode == 0)
		return;
	i = ip->di_mode & IFMT;
	if (i != IFDIR && i != IFREG)
		return;
	for(i=NADDR-1;i>=0;i--) {
		bn = ip->di_addr[i];
		if(bn == 0) continue;
		switch(i) {

		default:
			bfree(bn);
			break;

		case NADDR-3:
			tloop(bn, 0, 0);
			break;

		case NADDR-2:
			tloop(bn, 1, 0);
			break;

		case NADDR-1:
			tloop(bn, 1, 1);
		}
	}
	ip->di_size = 0;
}

tloop(bn, f1, f2)
daddr_t	bn;
int	f1, f2;
{
	register i;
	daddr_t nb;
	union {
		char	data[DEV_BSIZE];
		daddr_t	indir[NINDIR];
	} ibuf;

	dread(bn, ibuf.data, DEV_BSIZE);
	for(i=NINDIR-1;i>=0;i--) {
		nb = ibuf.indir[i];
		if(nb) {
			if(f1)
				tloop(nb, f2, 0);
			else
				bfree(nb);
		}
	}
	bfree(bn);
}

bfree(bn)
daddr_t	bn;
{
	register i;
	union {
		char	data[DEV_BSIZE];
		struct	fblk frees;
	} fbuf;

	if(sblock.fs_nfree >= NICFREE) {
		fbuf.frees.df_nfree = sblock.fs_nfree;
		for(i=0;i<NICFREE;i++)
			fbuf.frees.df_free[i] = sblock.fs_free[i];
		sblock.fs_nfree = 0;
		dwrite(bn, fbuf.data);
	}
	sblock.fs_free[sblock.fs_nfree++] = bn;
	sblock.fs_tfree++;
}

/*
 * allocate a block off the free list.
 */
daddr_t
balloc()
{
	daddr_t	bno;
	register i;
	union {
		char	data[DEV_BSIZE];
		struct	fblk frees;
	} fbuf;

	if(sblock.fs_nfree == 0 || (bno=sblock.fs_free[--sblock.fs_nfree]) == 0) {
#ifdef STANDALONE
		printf("Out of space\n");
#else
		fprintf(stderr, "Out of space.\n");
#endif
		exit(1);
	}
	if(sblock.fs_nfree == 0) {
		dread(bno, fbuf.data, DEV_BSIZE);
		sblock.fs_nfree = fbuf.frees.df_nfree;
		for(i=0;i<NICFREE;i++)
			sblock.fs_free[i] = fbuf.frees.df_free[i];
	}
	bzero(fbuf.data, DEV_BSIZE);
	dwrite(bno, fbuf.data);
	sblock.fs_tfree--;
	return(bno);
}

/*
 * map a block number into a block address, ensuring
 * all of the correct indirect blocks are around. Allocate
 * the block requested.
 */
daddr_t
bmap(iaddr, bn)
daddr_t	iaddr[NADDR];
daddr_t	bn;
{
	register i;
	register int j, sh;
	daddr_t nb, nnb;
	daddr_t indir[NINDIR];

	/*
	 * blocks 0...NADDR-3 are direct blocks
	 */
	if(bn < NADDR-3) {
		iaddr[bn] = nb = balloc();
		return(nb);
	}

	/*
	 * addresses NADDR-3, NADDR-2, and NADDR-1
	 * have single, double, triple indirect blocks.
	 * the first step is to determine
	 * how many levels of indirection.
	 */
	sh = 0;
	nb = 1;
	bn -= NADDR-3;
	for(j=3; j>0; j--) {
		sh += NSHIFT;
		nb <<= NSHIFT;
		if(bn < nb)
			break;
		bn -= nb;
	}
	if(j == 0) {
		return((daddr_t)0);
	}

	/*
	 * fetch the address from the inode
	 */
	if((nb = iaddr[NADDR-j]) == 0) {
		iaddr[NADDR-j] = nb = balloc();
	}

	/*
	 * fetch through the indirect blocks
	 */
	for(; j<=3; j++) {
		dread(nb, (char *)indir, DEV_BSIZE);
		sh -= NSHIFT;
		i = (bn>>sh) & NMASK;
		nnb = indir[i];
		if(nnb == 0) {
			nnb = balloc();
			indir[i] = nnb;
			dwrite(nb, (char *)indir);
		}
		nb = nnb;
	}
	return(nb);
}

/*
 * read the tape into buf, then return whether or
 * or not it is a header block.
 */
gethead(buf)
struct spcl *buf;
{
	readtape((char *)buf);
	return(ishead(buf));
}

/*
 * return whether or not the buffer contains a header block
 */
ishead(buf)
register struct spcl *buf;
{
register int ret = 0;

	if (buf->c_magic == OFS_MAGIC) {
		if (cvtflag == 0)
			printf("Convert old direct format to new\n");
		ret = cvtflag = 1;
	}
	else if (buf->c_magic == NFS_MAGIC) {
		if (cvtflag)
			printf("Was converting old direct format, not now\n");
		cvtflag = 0;
		ret = 1;
	}
	if (ret == 0)
		return(ret);
	return(checksum((int *) buf));
}

checktype(b, t)
struct	spcl *b;
int	t;
{
	return(b->c_type == t);
}


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

	j = DEV_BSIZE/sizeof(int);
	i = 0;
	do
		i += *b++;
	while (--j);
	if (i != CHECKSUM) {
		printf("Checksum error %o\n", i);
		return(0);
	}
	return(1);
}

checkvol(b, t)
struct spcl *b;
int t;
{
	if (b->c_volume == t)
		return(1);
	return(0);
}

readhdr(b)
struct	spcl *b;
{
	if (gethead(b) == 0)
		return(0);
	if (checktype(b, TS_TAPE) == 0)
		return(0);
	return(1);
}

/*
 * The next routines are called during file extraction to
 * put the data into the right form and place.
 */
#ifndef STANDALONE
xtrfile(b, size)
char	*b;
long	size;
{
	write(ofile, b, (int) size);
}

null() {;}

skip()
{
	lseek(ofile, (long) DEV_BSIZE, 1);
}
#endif


rstrfile(b, s)
char *b;
long s;
{
	daddr_t d;

	if (isdir && cvtflag)
		return(olddirect(b, s));
	d = bmap(taddr, curbno);
	dwrite(d, b);
	curbno += 1;
}

rstrskip(b, s)
char *b;
long s;
{
	curbno += 1;
}

#ifndef STANDALONE
putdir(b)
char *b;
{
	register struct direct *dp;
	struct direct cvtbuf;
	struct odirect *odp, *eodp;
	u_int	loc;
	register int i;

	if (cvtflag) {
		eodp = (struct odirect *)&b[DEV_BSIZE];
		for (odp = (struct odirect *)b; odp < eodp; odp++) {
			if (odp->d_ino) {
				dcvt(odp, &cvtbuf);
				putent(&cvtbuf);
			}
		}
	return;
	}

	for (loc = 0; loc < DEV_BSIZE; ) {
		dp = (struct direct *)(b + 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)
			putent(dp);
	}
}

putent(dp)
register struct direct *dp;
{

	dp->d_reclen = DIRSIZ(dp);
	if (drblock.dd_loc + dp->d_reclen > DIRBLKSIZ) {
		((struct direct *)(drblock.dd_buf + prev))->d_reclen = 
			DIRBLKSIZ - prev;
		write(df, drblock.dd_buf, DIRBLKSIZ);
		drblock.dd_loc = 0;
	}
	bcopy(dp, drblock.dd_buf + drblock.dd_loc, dp->d_reclen);
	prev = drblock.dd_loc;
	drblock.dd_loc += dp->d_reclen;
}

flushent()
{

	((struct direct *)(drblock.dd_buf + prev))->d_reclen =DIRBLKSIZ - prev;
	write(df, drblock.dd_buf, DIRBLKSIZ);
	prev = drblock.dd_loc = 0;
	seekpt = lseek(df, 0L, 1);
}

struct direct *
rddir()
{
register struct direct *dp;

	for (;;) {
		if (drblock.dd_loc == 0) {
			drblock.dd_size = read(df, drblock.dd_buf, DIRBLKSIZ);
			if (drblock.dd_size <= 0) {
				printf("error reading directory\n");
				return(NULL);
			}
		}
		if (drblock.dd_loc >= drblock.dd_size) {
			drblock.dd_loc = 0;
			continue;
		}
		dp = (struct direct *)(drblock.dd_buf + drblock.dd_loc);
		if (dp->d_reclen == 0 ||
			dp->d_reclen > DIRBLKSIZ + 1 - drblock.dd_loc) {
				printf("corrupted directory: bad reclen %d\n",
					dp->d_reclen);
				return(NULL);
		}
		drblock.dd_loc += dp->d_reclen;
		if (dp->d_ino == 0 && strcmp(dp->d_name, "/"))
			continue;
	return(dp);
	}
}

#endif

olddirect(buf, s)
char  *buf;
long	s;
{
	char ndirbuf[DEV_BSIZE];
	register char *dp;
	struct direct cvtbuf;
	register struct odirect *odp, *eodp;
	daddr_t d;
	u_int dirloc, prev;

	inidbuf(ndirbuf);
	dp = ndirbuf;
	odp = (struct odirect *)buf;
	eodp = (struct odirect *)&buf[(int)s];
	for (prev = 0, dirloc = 0; odp < eodp; odp++ ) {
		if (odp->d_ino == 0)
			continue;
		dcvt(odp, &cvtbuf);
		if (dirloc + cvtbuf.d_reclen > DIRBLKSIZ) {
			((struct direct *)(dp + prev))->d_reclen =
					DIRBLKSIZ - prev;
			if (dp != ndirbuf) {
				d = bmap(taddr, curbno);
				dwrite(d, ndirbuf);
				curbno++;
				inidbuf(ndirbuf);
				dp = ndirbuf;
			}
			else
				dp += DIRBLKSIZ;
			dirloc = 0;
		}
		bcopy(&cvtbuf, dp + dirloc, cvtbuf.d_reclen);
		prev = dirloc;
		dirloc += cvtbuf.d_reclen;
	}
	if (dirloc) {
		((struct direct *)(dp + prev))->d_reclen = 
			DIRBLKSIZ - prev;
		d = bmap(taddr, curbno);
		dwrite(d, ndirbuf);
		curbno++;
	}
	return(0);
}

struct direct *
inidbuf(buf)
register char *buf;
{
	register int i;
	register struct direct *dp;

	bzero(buf, DEV_BSIZE);
	for (i = 0; i < DEV_BSIZE; i += DIRBLKSIZ) {
		dp = (struct direct *)&buf[i];
		dp->d_reclen = DIRBLKSIZ;
	}
	return((struct direct *)buf);
}

/*
 * read/write an inode from the disk
 */
getdino(inum, b)
ino_t	inum;
struct	dinode *b;
{
	daddr_t	bno;
	char buf[DEV_BSIZE];

	bno = (inum - 1)/INOPB;
	bno += 2;
	dread(bno, buf, DEV_BSIZE);
	bcopy(&buf[((inum-1)%INOPB)*sizeof(struct dinode)], (char *) b, sizeof(struct dinode));
}

putdino(inum, b)
ino_t	inum;
struct	dinode *b;
{
	daddr_t bno;
	char buf[DEV_BSIZE];

	if (b->di_mode&IFMT)
		sblock.fs_tinode--;
	bno = ((inum - 1)/INOPB) + 2;
	dread(bno, buf, DEV_BSIZE);
	bcopy((char *) b, &buf[((inum-1)%INOPB)*sizeof(struct dinode)], sizeof(struct dinode));
	dwrite(bno, buf);
}

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

	i = spcl.c_count;

	while (i--) {
		readtape((char *) m);
#ifdef	STANDALONE
		if(m)
#endif
		m += (DEV_BSIZE/(MLEN/BITS));
	}
	while (gethead(&spcl) == 0)
		;
}

done()
{
#ifndef STANDALONE
	unlink(dirfile);
#endif
	exit(0);
}

futz(addr)
	char	*addr;
	{
	daddr_t	l;
	register char *a, *b;

	a = (char *)&l;
	b = addr;
	*a++ = *b++;
	*a++ = 0;
	*a++ = *b++;
	*a++ = *b++;
	*(daddr_t *)addr = l;
	}