Ultrix-3.1/src/cmd/restor/restor.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * ULTRIX-11 file system restore program (restor)
 *
 * Modified for use with the Micro/pdp-11.
 * The m option restores from RX50 diskettes.
 * The e option restores from RX33 diskettes.
 *
 * Fred Canter 5/25/83

 * 512restor program: compiled by defining REST_512,
 * used to restore from 512-byte block file system.
 * George Mathew 6/11/85
 *
 * New option (s file#) to restore individual files if there
 * is more than one dump file on a tape. It uses tape ioctls
 * to position the tape correctly.
 * George Mathew 9/4/85
 *
 * Modifications to restart restor with any floppy in the
 * middle for STANDALONE version only.
 * George Mathew 11/11/85
 */

#ifndef STANDALONE
static char Sccsid[] = "@(#)restor.c 3.2 7/11/87";
#endif STANDALONE
#ifdef	RESTOR40
#define	MAXINO	2000
#else
#define MAXINO	3000
#endif

#define BITS	8
#define MAXXTR	60
#define NCACHE	3

#ifndef STANDALONE
#define	NORMAL	0
#define	FATAL	1
#include <stdio.h>
#include <signal.h>
#else
#include "/usr/sys/sas/sa_defs.h"	/* exit status codes for SDLOAD */
#endif
#include <sys/param.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/fblk.h>
#include <sys/filsys.h>
#include <sys/dir.h>
#include <sys/mtio.h>
#include <dumprestor.h>


#ifdef REST_512        /* 512restor */

/* from old ino.h */

#define	OINOPB	8	/* 8 inodes per block */

#endif REST_512

#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))

struct	filsys	sblock;

int	fi;
ino_t	ino, maxi, curino;

int	mt;
int	trn;
int	rx_trn;		/* max # of 10240 byte blocks on floppy */
char	tapename[] = "/dev/rht0";
char	*magtape = tapename;
char	*nota = "Media is not a dump volume\n";
char	*notv = "Media is not volume ";
char	*eodm = "End of dump media\n";
char	*prtbrt = "Press <RETURN> to begin restoring to";
#ifdef STANDALONE
char	mbuf[30];
char	vbuf[30];
#endif

#ifndef STANDALONE
daddr_t	seekpt;
int	df, ofile;
char	dirfile[] = "rstXXXXXX";

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];

char	drblock[BSIZE];
int	bpt;
#endif

int	eflag;
int	mflag;
int	rflag;	/* load device is RC25 or RL02 */

int	volno = 1;

#ifdef STANDALONE
int	restart = 0;	/* flag indicating restarted from within the process */
#endif

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

daddr_t	curbno;

short	dumpmap[MSIZ];
short	clrimap[MSIZ];


int bct = NTREC+1;

#ifdef REST_512
char tbf[NTREC*OBSIZE];
#else
char tbf[NTREC*BSIZE];
#endif

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

#ifdef STANDALONE
int	argflag;	/* 0=interactive, 1=called by SDLOAD, see prf.c */
int	afstat;		/* save argflag during volume change */
#endif
int	seqno = 0;	/* file number if more than 1 restored on a single vol */
struct  mtop mtop;

main(argc, argv)
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(FATAL);
	}
	argv++;
	argc -= 2;
	for (cp = *argv++; *cp; cp++) {
		switch (*cp) {
		case '-':
			break;
		case 'f':
			magtape = *argv++;
			argc--;
			break;
		case 'r':
		case 'R':
		case 't':
		case 'x':
			command = *cp;
			break;
		case 's':
			if ((seqno = atoi(*argv++)) <= 0) {
				printf("file number must be > 0\n");
				goto usage;
			}
			argc--;
			break;
		case 'm':		/* restore from RX50 diskettes */
			mflag++;
			rx_trn = 40;
			break;
		case 'e':		/* restore from RX33 diskettes */
			mflag++;
			rx_trn = 120;
			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 = creat(dirfile, 0666);
		if (df < 0) {
			printf("%s - can't create directory temporary\n", dirfile);
			exit(FATAL);
		}
		close(df);
		df = open(dirfile, 2);
	}
	doit(command, argc, argv);
	/* if (command == 'x')
		unlink(dirfile);
	exit(NORMAL); */
	rstexit(NORMAL);
#else
	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();
#endif
	int	rstrfile(), rstrskip();
	struct dinode *ip, *ip1;
	int ret;

#ifndef STANDALONE
	if ((mt = open(magtape, 0)) < 0) {
		printf("%s: can't open\n", magtape);
		rstexit(FATAL);
	}
	if (seqno > 0) {
		mtop.mt_op = MTFSF;
		mtop.mt_count = seqno - 1;
		if (ioctl(mt,MTIOCTOP,&mtop) < 0) {
			printf("cannot position tape at file %d\n",seqno);
			rstexit(FATAL);
		}
	}
#else
	if(!restart) {
		do {
			printf("Input: ");
			gets(mbuf);
			mt = open(mbuf, 0);
			if(argflag && (mt == -1))
				rstexit(FATAL);
		} while (mt == -1);
		if((mbuf[0] == 'r') && ((mbuf[1] == 'a') || (mbuf[1] == 'x')))
		{
		    while (1) {
			printf("Diskette type <33 or 50>: ");
			gets(vbuf);
			i = atoi(vbuf);
			if(i == 50) {
			    rx_trn = 40;
			    break;
			} else if(i == 33) {
			    rx_trn = 120;
			    break;
			} else
			    continue;
		    }
		    mflag++;		/* restore is from RX50/RX33 */
		}
		if((mbuf[0] == 'r') && ((mbuf[1] == 'l') || (mbuf[1] == 'c')))
			rflag++;	/* restore from RC25/RL02 */
		magtape = mbuf;
		if(mflag) {
			printf("Starting volume number <1>: ");
			gets(vbuf);
			if(vbuf[0] == 0)
				volno = 1;
			else
				volno = atoi(vbuf);
		}
	}
#endif
	switch(command) {
#ifndef STANDALONE
	case 't':
		if (readhdr(&spcl) == 0) {
			printf("%s", nota); /* not a dump volume */
			rstexit(FATAL);
		}
		printf("Dump   date: %s", ctime(&spcl.c_date));
		printf("Dumped from: %s", ctime(&spcl.c_ddate));
		return;
	case 'x':
		if (readhdr(&spcl) == 0) {
			printf("%s", nota); /* not a dump volume */
			rstexit(FATAL);
		}
		if (checkvol(&spcl, 1) == 0) {
			printf("%s1 of the dump\n", notv);
			rstexit(FATAL);
		}
		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 volume\n", *argv++);
				continue;
			}
			xtrlist[i].x_ino = d;
			xtrlist[i].x_flags |= XINUSE;
			printf("%s: inode %u\n", *argv, d);
			argv++;
			i++;
		}
newvol:
		flsht();
		if (seqno > 0) {
			filcntl();
		} else
			close(mt);
		trn = 0;	/* fake rewind RX50/RX33 diskette */
getvol:
		if (seqno == 0) {
			printf("Mount desired volume: Specify volume #: ");
			fflush(stdout);
			if (gets(tbf) == NULL)
				return;
			volno = atoi(tbf);
			if (volno <= 0) {
				printf("Volume number should be > 0\n");
				goto getvol;
			}
			mt = open(magtape, 0);
		}
		if (readhdr(&spcl) == 0) {
			printf("%s", nota); /* not a dump volume */
			goto newvol;
		}
		if (seqno == 0) {
			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("%s", eodm);
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: can't create file\n", name);
						i--;
						continue;
					}
					chown(name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
					getfile(ino, xtrfile, skip, spcl.c_dinode.di_size);
					i--;
					xtrlist[k].x_flags |= XTRACTD;
					close(ofile);
					goto done;
				}
			}
			gethead(&spcl);
done:
			;
		}
		goto checkdone;
#endif
	case 'r':
	case 'R':
#ifndef STANDALONE
		if ((fi = open(*argv, 2)) < 0) {
			printf("%s: can't open\n", *argv);
			rstexit(FATAL);
		}
#else
		if(!restart) {
			do {
				char charbuf[50];
	
				printf("Disk: ");
				gets(charbuf);
				fi = open(charbuf, 2);
				if(argflag && (fi == -1))
					rstexit(FATAL);
			} while (fi == -1);
		}
#endif
#ifndef STANDALONE
		if (command == 'R') {
			printf("Enter starting volume number: ");
			fflush(stdout);
			if (gets(tbf) == EOF) {
				volno = 1;
				printf("\n");
			}
			else
				volno = atoi(tbf);
		}
		else
#endif
			if(mflag == 0)
				volno = 1;
#ifdef STANDALONE
		if(!restart) {
			if(argflag == 0)
				printf("%s disk. ", prtbrt);
		} else {
			printf("Restarting: Mount volume %d <type return when ready>",volno);
			/* make sure we are interactive, user hits return */
			afstat = argflag;
			argflag = 0;
		}
#else
		printf("%s %s. ", prtbrt, *argv);
		fflush(stdout);
#endif
		while (getchar() != '\n');
#ifdef STANDALONE
		if(restart)
		{
			argflag = afstat;
			mt = open(magtape,0);
		}
#endif
		dread((daddr_t)1, (char *)&sblock, sizeof(sblock));
		maxi = (sblock.s_isize-2)*INOPB;
		if (readhdr(&spcl) == 0) {
			printf("Missing volume record\n");
			rstexit(FATAL);
		}
		if (checkvol(&spcl, volno) == 0) {
			printf("%s%d\n", notv, volno);
			rstexit(FATAL);
		}
		gethead(&spcl);
		for (;;) {
ragain:
			if (ishead(&spcl) == 0) {
#ifdef STANDALONE
				if(!restart) {
					printf("Missing header block\n");
					printf("type:%d\n",spcl.c_type);
				}
#else
				printf("Missing header block\n");
				printf("type:%d\n",spcl.c_type);
#endif
				while (gethead(&spcl) == 0)
					;
				eflag++;
			}
			if (checktype(&spcl, TS_END) == 1) {
				printf("%s", eodm);
				close(mt);
				dwrite( (daddr_t) 1, (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) 1, (char *) &sblock);
				goto ragain;
			}
			if (checktype(&spcl, TS_BITS) == 1) {
				readbits(dumpmap);
				goto ragain;
			}
			if (checktype(&spcl, TS_INODE) == 0) {
#ifdef STANDALONE
				if(!restart)
					printf("Unknown header type\n");
#else
				printf("Unknown header type\n");
#endif
				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);
			for (i = 0; i < NADDR; i++)
				taddr[i] = 0;
			l3tol(taddr, dino.di_addr, 1);
			getfile(ino, rstrfile, rstrskip, dino.di_size);
			ip = &tino;
			ltol3(ip->di_addr, taddr, NADDR);
			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;
			ip->di_size = ip1->di_size;
			ip->di_atime = ip1->di_atime;
			ip->di_mtime = ip1->di_mtime;
			ip->di_ctime = ip1->di_ctime;
			putdino(ino, &tino);
		}
	}
}

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

	while (gethead(&spcl) == 0) {
		printf("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:
			flsh();
			if (seqno > 0) {
				filcntl();
			} else
				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("\000\000/");
	}
}
#endif

/*
 * 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[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:

#ifdef REST_512
		
		for (i = 0; i < addrblock.c_count; i += 2) {
			if (addrblock.c_addr[i])
				readtape(buf, 0);
			else
				clearbuf(buf, 0);
			if (size > OBSIZE && addrblock.c_addr[i+1])
				readtape(buf, 1);
			else
				clearbuf(buf, 1);
			if (addrblock.c_addr[i] || size > OBSIZE && addrblock.c_addr[i + 1])
				(*f1)(buf, size > BSIZE ? (long) BSIZE : size);
			else
				(*f2)(buf, size > BSIZE ? (long) BSIZE : size);
#else
		for (i = 0; i < addrblock.c_count; i++) {
			if (addrblock.c_addr[i]) {
				readtape(buf);
				(*f1)(buf, size > BSIZE ? (long) BSIZE : size);
			}
			else {
				clearbuf(buf);
				(*f2)(buf, size > BSIZE ? (long) BSIZE : size);
			}
#endif REST_512
			if ((size -= 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..
 */
long	boff;
#ifdef REST_512
readtape(b,part)
#else
readtape(b)
#endif
char *b;
{
	register i;
	struct spcl tmpbuf;

	if (bct >= NTREC) {
		for (i = 0; i < NTREC; i++)
#ifdef REST_512
			((struct spcl *)&tbf[i*OBSIZE])->c_magic = 0;
#else
			((struct spcl *)&tbf[i*BSIZE])->c_magic = 0;
#endif
		bct = 0;
		if(mflag || rflag) {	/* restoring from a disk */
		    if(mflag) {		/* RX50/RX33 restore */
			if(trn >= rx_trn)
				goto newvol;
		    }
		    boff = (long) trn;
#ifdef REST_512
		    boff *= (long)(NTREC*OBSIZE);
#else
		    boff *= (long)(NTREC*BSIZE);
#endif
		    lseek(mt, boff, 0);
		}
#ifdef REST_512
		if ((i = read(mt, tbf, NTREC*OBSIZE)) < 0) {
#else
		if ((i = read(mt, tbf, NTREC*BSIZE)) < 0) {
#endif
			printf("Input read error: inode %u\n", curino);
			eflag++;
#ifdef STANDALONE
			if (mflag)  {
				if (volno >1 )
					volno -= 1;
				else
					rstexit(FATAL);
				if (++restart > 1) {
					printf("Exceeded 1 restart. Exit\n");
					rstexit(FATAL);
				}
				if( ichecks())
					rstexit(FATAL);
				close(mt);
				trn = 0;
				bct = NTREC +1;
				doit('r',1,0);
				rstexit(NORMAL);
			}
#endif
#ifdef REST_512
			rstexit(FATAL);
#else
			for (i = 0; i < NTREC; i++)
				clearbuf(&tbf[i*BSIZE]);
#endif
		}
		trn++;
		if(i == 0) {
newvol:
			bct = NTREC + 1;
			volno++;
			trn = 0;
loop:
			flsht();
			close(mt);
			printf("Mount volume %d <type return when ready>", volno);
#ifdef STANDALONE
			/* make sure we are interactive, user hits return */
			afstat = argflag;
			argflag = 0;
#else
			fflush(stdout);
#endif
			while (getchar() != '\n')
				;
#ifdef STANDALONE
			argflag = afstat;
#endif
			if ((mt = open(magtape, 0)) == -1) {
				printf("Can't open volume!\n");
				goto loop;
			}
			if (readhdr(&tmpbuf) == 0) {
				printf("Not a dump volume. Try again\n");
				goto loop;
			}
			if (checkvol(&tmpbuf, volno) == 0) {
				printf("Wrong volume. Try again\n");
				goto loop;
			}
#ifdef REST_512
			readtape(b, part);
#else
			readtape(b);
#endif
			return;
		}
	}
#ifdef REST_512
	copy(&tbf[(bct++*OBSIZE)], b + part * OBSIZE, OBSIZE);
#else
	copy(&tbf[(bct++*BSIZE)], b, BSIZE);
#endif
}

flsht()
{
	bct = NTREC+1;
}

copy(f, t, s)
register char *f, *t;
{
	register i;

	i = s;
	do
		*t++ = *f++;
	while (--i);
}

#ifdef REST_512
clearbuf(cp, part)
#else
clearbuf(cp)
#endif
register char *cp;
{
	register i;

#ifdef REST_512
	cp += part * OBSIZE;
	i = OBSIZE;
#else
	i = BSIZE;
#endif
	do
		*cp++ = 0;
	while (--i);
}

/*
 * Put and get the directory entries from the compressed
 * directory file
 */
#ifndef STANDALONE
putent(cp)
char	*cp;
{
	register i;

	for (i = 0; i < sizeof(ino_t); i++)
		writec(*cp++);
	for (i = 0; i < DIRSIZ; i++) {
		writec(*cp);
		if (*cp++ == 0)
			return;
	}
	return;
}

getent(bf)
register char *bf;
{
	register i;

	for (i = 0; i < sizeof(ino_t); i++)
		*bf++ = readc();
	for (i = 0; i < DIRSIZ; i++)
		if ((*bf++ = readc()) == 0)
			return;
	return;
}

/*
 * read/write te directory file
 */
writec(c)
char c;
{
	drblock[bpt++] = c;
	seekpt++;
	if (bpt >= BSIZE) {
		bpt = 0;
		write(df, drblock, BSIZE);
	}
}

readc()
{
	if (bpt >= BSIZE) {
		read(df, drblock, BSIZE);
		bpt = 0;
	}
	return(drblock[bpt++]);
}

mseek(pt)
daddr_t pt;
{
	bpt = BSIZE;
	lseek(df, pt, 0);
}

flsh()
{
	write(df, drblock, bpt+1);
}

/*
 * search the directory inode ino
 * looking for entry cp
 */
ino_t
search(inum, cp)
ino_t	inum;
char	*cp;
{
	register i;
	struct direct dir;

	for (i = 0; i < MAXINO; i++)
		if (inotab[i].t_ino == inum) {
			goto found;
		}
	return(0);
found:
	mseek(inotab[i].t_seekpt);
	do {
		getent((char *)&dir);
		if (direq(dir.d_name, "/"))
			return(0);
	} while (direq(dir.d_name, cp) == 0);
	return(dir.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;
	char c;

	ino = 2;
	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);
}

direq(s1, s2)
register char *s1, *s2;
{
	register i;

	for (i = 0; i < DIRSIZ; i++)
		if (*s1++ == *s2) {
			if (*s2++ == 0)
				return(1);
		} else
			return(0);
	return(1);
}
#endif

/*
 * 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) {
			copy(b, cache[i].c_block, BSIZE);
			cache[i].c_time = 0;
			break;
		}
		else
			cache[i].c_time++;
	}
	lseek(fi, bno*BSIZE, 0);
	if(write(fi, b, BSIZE) != BSIZE) {
#ifdef STANDALONE
		printf("disk write error %D\n", bno);
#else
		fprintf(stderr, "disk write error %ld\n", bno);
#endif
		rstexit(FATAL);
	}
}

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) {
			copy(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*BSIZE, 0);
	if (read(fi, cache[j].c_block, BSIZE) != BSIZE) {
#ifdef STANDALONE
		printf("read error %D\n", bno);
#else
		printf("read error %ld\n", bno);
#endif
		rstexit(FATAL);
	}
	copy(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)
struct dinode *ip;
{
	int i, *p;

	if(ip->di_mode&IFMT)
		sblock.s_tinode++;
	i = sizeof(struct dinode)/sizeof(int);
	p = (int *)ip;
	do
		*p++ = 0;
	while(--i);
}

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

	if (ip->di_mode == 0)
		return;
	i = ip->di_mode & IFMT;
	if (i != IFDIR && i != IFREG)
		return;
	l3tol(iaddr, ip->di_addr, NADDR);
	for(i=NADDR-1;i>=0;i--) {
		bn = iaddr[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[BSIZE];
		daddr_t	indir[NINDIR];
	} ibuf;

	dread(bn, ibuf.data, 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[BSIZE];
		struct	fblk frees;
	} fbuf;

	if(sblock.s_nfree >= NICFREE) {
		fbuf.df_nfree = sblock.s_nfree;
		for(i=0;i<NICFREE;i++)
			fbuf.df_free[i] = sblock.s_free[i];
		sblock.s_nfree = 0;
		dwrite(bn, fbuf.data);
	}
	sblock.s_free[sblock.s_nfree++] = bn;
	sblock.s_tfree++;
}

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

	if(sblock.s_nfree == 0 || (bno=sblock.s_free[--sblock.s_nfree]) == 0) {
#ifdef STANDALONE
		printf("Out of space\n");
#else
		fprintf(stderr, "Out of space\n");
#endif
		rstexit(FATAL);
	}
	if(sblock.s_nfree == 0) {
		dread(bno, fbuf.data, BSIZE);
		sblock.s_nfree = fbuf.df_nfree;
		for(i=0;i<NICFREE;i++)
			sblock.s_free[i] = fbuf.df_free[i];
	}
	dwrite(bno, zeroes);
	sblock.s_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;
	int j, sh;
	daddr_t nb, nnb;
	daddr_t indir[NINDIR];

	/*
	 * blocks 0..NADDR-4 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, 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;
{
#ifdef REST_512
	readtape((char *)buf, 0);
#else
	readtape((char *)buf);
#endif
/*
printf("\ngethead: M=%o cs=%o",buf->c_magic, checksum((int *) buf));
 */
	if (buf->c_magic != MAGIC || checksum((int *) buf) == 0)
		return(0);
	return(1);
}

/*
 * return whether or not the buffer contains a header block
 */
ishead(buf)
struct spcl *buf;
{
	if (buf->c_magic != MAGIC || checksum((int *) buf) == 0) {
/* 
printf("\nishead: M=%o cs=%o",buf->c_magic,checksum((int *) buf));
*/
		return(0);
	}
	return(1);
}

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


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

#ifdef REST_512
	j = OBSIZE/sizeof(int);
#else
	j = BSIZE/sizeof(int);
#endif
	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()
{
#ifdef REST_512
	lseek(ofile, (long) OBSIZE, 1);
#else
	lseek(ofile, (long) BSIZE, 1);
#endif
}
#endif


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

	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;
	register i;

	for (dp = (struct direct *) b, i = 0; i < BSIZE; dp++, i += sizeof(*dp)) {
		if (dp->d_ino == 0)
			continue;
		putent((char *) dp);
	}
}
#endif

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

	bno = (ino - 1)/INOPB;
	bno += 2;
	dread(bno, buf, BSIZE);
	copy(&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[BSIZE];

	if(b->di_mode&IFMT)
		sblock.s_tinode--;
	bno = ((ino - 1)/INOPB) + 2;
	dread(bno, buf, BSIZE);
	copy((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)
short	*m;
{
	register i;

	i = spcl.c_count;

	while (i--) {
#ifdef REST_512
		readtape((char *) m, 0);
		m += (OBSIZE/(MLEN/BITS));
#else
		readtape((char *) m);
		m += (BSIZE/(MLEN/BITS));
#endif
	}
	while (gethead(&spcl) == 0)
		;
}

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

filcntl()
{
		mtop.mt_op = MTBSF;
		mtop.mt_count = 1;
		if (ioctl(mt,MTIOCTOP,&mtop) < 0) {
			/* abbreviation like BOF for beginning of file used to squeeze the code for restor40*/
			printf("cannot move to BOF: file %d\n",seqno);
			rstexit(FATAL);
		}
		if (seqno > 1) {
			mtop.mt_op = MTFSF;
			if (ioctl(mt,MTIOCTOP,&mtop) < 0) {
				printf("cannot skip TM: file %d\n");
				rstexit(FATAL);
			}
		}
		return(0);
}

rstexit(status)
int status;
{
#ifndef STANDALONE
	unlink(dirfile);
#endif
	exit(status);
}

/****  icheck for standalone version ***/

#ifdef STANDALONE
/*
 * icheck program with -s option for standalone version
 * included here for restarting restor in the middle.
 *
 * George Mathew 11/11/85
 *
 */

#define	NI	8
/* #define	MAXFN	714 */
#define	MAXFN	170
struct	dinode	itab[INOPB*NI];
daddr_t	saiaddr[NADDR];
char	*sabmap;
/*
 * Standalone buffer used for checking dups
 * and salvaging the free list.
 */

int	nerror;
/* #define	SABUFSIZ 16384 */
#define	SABUFSIZ 1200
char	sabuf[SABUFSIZ];
ino_t	saino;
daddr_t	nfree;
long	atol();
daddr_t	alloc();

ichecks()
{
	register i, j;
	ino_t mino;
	daddr_t d;
	long n;

	printf("Wait: icheck being done\n");
	bread((daddr_t)1, (char *)&sblock, sizeof(sblock));
	mino = (sblock.s_isize-2) * INOPB;
	saino = 0;
	n = (sblock.s_fsize - sblock.s_isize + BITS-1) / BITS;
	if (n != (unsigned)n) {
		printf("Check fsize and isize: %D, %u\n",
		   sblock.s_fsize, sblock.s_isize);
		nerror = 1;
	}
	if(n <= SABUFSIZ)
		sabmap = &sabuf;
	else
		sabmap = NULL;
	if (sabmap==NULL) {
		printf("Not enough core; duplicates unchecked\n");
		return(-1);
	}
	for(i=0; i<(unsigned)n; i++)
		sabmap[i] = 0;
	for(i=2;; i+=NI) {
		if(saino >= mino)
			break;
		bread((daddr_t)i, (char *)itab, sizeof(itab));
		for(j=0; j<INOPB*NI; j++) {
			if(saino >= mino)
				break;
			saino++;
			pass2(&itab[j]);
		}
	}
	saino = 0;
	bread((daddr_t)1, (char *)&sblock, sizeof(sblock));
	makefree();
	if (nerror)
		return(-1);
	else
		return(0);
	
}

pass2(ip)
register struct dinode *ip;
{
	daddr_t ind1[NINDIR];
	daddr_t ind2[NINDIR];
	daddr_t ind3[NINDIR];
	register i, j;
	int k, l;

	i = ip->di_mode & IFMT;
	if( (i == 0) || (i == IFIFO) || (i == IFCHR) || (i == IFBLK) )
		return;
	if( (i != IFDIR) && (i != IFREG)
#ifdef  UCB_SYMLINKS
		&&(i != IFLNK)
#endif
		) {
			printf("bad mode %u\n", saino);
			nerror = 1;
			return(1);
		}
	l3tol(saiaddr, ip->di_addr, NADDR);
	for(i=0; i<NADDR; i++) {
		if(saiaddr[i] == 0)
			continue;
		if(i < NADDR-3) {
			chk(saiaddr[i], "data (small)");
			continue;
		}
		if (chk(saiaddr[i], "1st indirect"))
				continue;
		bread(saiaddr[i], (char *)ind1, BSIZE);
		for(j=0; j<NINDIR; j++) {
			if(ind1[j] == 0)
				continue;
			if(i == NADDR-3) {
				chk(ind1[j], "data (large)");
				continue;
			}
			if(chk(ind1[j], "2nd indirect"))
				continue;
			bread(ind1[j], (char *)ind2, BSIZE);
			for(k=0; k<NINDIR; k++) {
				if(ind2[k] == 0)
					continue;
				if(i == NADDR-2) {
					chk(ind2[k], "data (huge)");
					continue;
				}
				if(chk(ind2[k], "3rd indirect"))
					continue;
				bread(ind2[k], (char *)ind3, BSIZE);
				for(l=0; l<NINDIR; l++)
					if(ind3[l]) {
						chk(ind3[l], "data (garg)");
					}
			}
		}
	}
}

chk(bno, s)
daddr_t bno;
char *s;
{
	register n;

	if (bno<sblock.s_isize || bno>=sblock.s_fsize) {
		printf("%D bad; inode=%u, class=%s\n", bno, saino, s);
		nerror =1;
		return(1);
	}
	if(duped(bno)) {
		printf("%D dup; inode=%u, class=%s\n", bno, saino, s);
		nerror = 1;
	}
	return(0);
}

duped(bno)
daddr_t bno;
{
	daddr_t d;
	register m, n;

	d = bno - sblock.s_isize;
	m = 1 << (d%BITS);
	n = (d/BITS);
	if(sabmap[n] & m)
		return(1);
	sabmap[n] |= m;
	return(0);
}

daddr_t
alloc()
{
	int i;
	daddr_t bno;
	union {
		char	data[BSIZE];
		struct	fblk fb;
	} buf;

	sblock.s_tfree--;
	if (sblock.s_nfree<=0)
		return(0);
	if (sblock.s_nfree>NICFREE) {
		printf("Bad free list, s.b. count = %d\n", sblock.s_nfree);
		nerror = 1;
		return(0);
	}
	bno = sblock.s_free[--sblock.s_nfree];
	sblock.s_free[sblock.s_nfree] = (daddr_t)0;
	if(bno == 0)
		return(bno);
	if(sblock.s_nfree <= 0) {
		bread(bno, buf.data, BSIZE);
		sblock.s_nfree = buf.fb.df_nfree;
		if (sblock.s_nfree<0 || sblock.s_nfree>NICFREE) {
			printf("Bad free list, entry count of block %D = %d\n",
				bno, sblock.s_nfree);
			sblock.s_nfree = 0;
			nerror = 1;
			return(0);
		}
		for(i=0; i<NICFREE; i++)
			sblock.s_free[i] = buf.fb.df_free[i];
	}
	return(bno);
}

sabfree(bno)
daddr_t bno;
{
	union {
		char	data[BSIZE];
		struct	fblk fb;
	} buf;
	int i;

	if(bno != 0)
		sblock.s_tfree++;
	if(sblock.s_nfree >= NICFREE) {
		for(i=0; i<BSIZE; i++)
			buf.data[i] = 0;
		buf.fb.df_nfree = sblock.s_nfree;
		for(i=0; i<NICFREE; i++)
			buf.fb.df_free[i] = sblock.s_free[i];
		bwrite(bno, buf.data);
		sblock.s_nfree = 0;
	}
	sblock.s_free[sblock.s_nfree] = bno;
	sblock.s_nfree++;
}

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

	lseek(fi, bno*BSIZE, 0);
	if (read(fi, buf, cnt) != cnt) {
		printf("read error %D\n", bno);
		nerror = 1;
			printf("No update\n");
		for(i=0; i<BSIZE; i++)
			buf[i] = 0;
	}
}

bwrite(bno, buf)
daddr_t bno;
char	*buf;
{

	lseek(fi, bno*BSIZE, 0);
	if (write(fi, buf, BSIZE) != BSIZE) {
		printf("write error %D\n", bno);
		nerror = 1;
	}
}

makefree()
{
	char flg[MAXFN];
	int adr[MAXFN];
	register i, j;
	daddr_t f, d;
	int m, n;

	n = sblock.s_n;
	if(n <= 0 || n > MAXFN)
		n = MAXFN;
	sblock.s_n = n;
	m = sblock.s_m;
	if(m <= 0 || m > sblock.s_n)
		m = 9;
	sblock.s_m = m;

	for(i=0; i<n; i++)
		flg[i] = 0;
	i = 0;
	for(j=0; j<n; j++) {
		while(flg[i])
			i = (i+1)%n;
		adr[j] = i+1;
		flg[i]++;
		i = (i+m)%n;
	}

	sblock.s_nfree = 0;
	sblock.s_ninode = 0;
	sblock.s_flock = 0;
	sblock.s_ilock = 0;
	sblock.s_fmod = 0;
	sblock.s_ronly = 0;
	sblock.s_tfree = 0;

	sabfree((daddr_t)0);
	d = sblock.s_fsize-1;
	if(d % sblock.s_n == 0)
		d++;	/* so last block will not be missing (see mkfs.c) */
	while(d%sblock.s_n)
		d++;
	for(; d > 0; d -= sblock.s_n)
	for(i=0; i<sblock.s_n; i++) {
		f = d - adr[i];
		if(f < sblock.s_fsize && f >= sblock.s_isize)
			if(!duped(f))
				sabfree(f);
	}
	bwrite((daddr_t)1, (char *)&sblock);
	return(0);
}
#endif STANDALONE