[net.sources] sources to recover program

bob (06/01/82)

: run this file as shell script
mkdir recover inc
echo extracting recover/README
cat > recover/README <<'E*O*F'
The recover program is a byproduct of my High Speed File Routines
(the HSFR package is available on request).
As a matter of fact, I had been editing and testing a subroutine of the HSFR
for about 5 hours when I deleted something by mistake.
I had the guts of a recover program from the HSFR subroutines,
so I just wrote a user interface and recovered my deleted file.

All of the subroutines and include files are taken unmodified from the
HSFR package.  That is why the subroutines have extra hooks that
recover does not use.

The include files in the inc directory are for V6 type filesystems.
They serve no purpose on the v7 type filesystems and can be deleted.

Recover has been tested on 4.1BSD and V7 PDP11.
The HSFR were developed on a vax 4.1bsd system.  HSFR has also been
tested on V7, PWB and ISC.  Since recover is a subset of HSFR,
I feel quite confident that recover will work on all of these.

Compile as follows or modify Makefile.vax:

VAX:	cc -o recover -DV7 *.c
PDP-v7:	cc -o recover -DV7 -DPDP11 *.c
PWB:	cc -o recover -DPWB *.c -lS
ISC:	cc -o recover -DISC *.c -lS

You may wish to make recover setuid root so that anyone can
recover files.  Remember that SUID will allow anyone to look at deleted files.

If you want diagnostics, you can set Debug to [1-7] in main.c.

On the TODO WISHLIST is a version of recover that needs no arguments.
Recover would copy blocks from the current working directory's freelist.
After you delete something precious by mistake, you don't want to
have to think much while someone may be taking your blocks.
It is easy to merge 4.1's pwd.c into recover to accomplish this,
but that code would not work on all other unix systems.

Comments, suggestions and improvements are welcome.
	Bob Gray	(duke!adiron!bob)
	PAR TECH CORP
	Seneca Plaza
	New Hartford, NY 13413		(315) 738-0600
E*O*F
echo extracting recover/Makefile.vax
cat > recover/Makefile.vax <<'E*O*F'
CC=	cc -O -DV7
SRC=	alloc.c dskino.c errexit.c getmount.c main.c rw.c
OBJ=	alloc.o dskino.o errexit.o getmount.o main.o rw.o

all:	recover recover.man

recover:	$(OBJ)
	$(CC) $(OBJ) -o recover

recover.man:	recover.1
	nroff -man recover.1 > recover.man

clean:
	rm -i recover $(OBJ) recover.man
E*O*F
echo extracting recover/recover.1
cat > recover/recover.1 <<'E*O*F'
.TH RECOVER local
.SH NAME
recover \- copy blocks from a freelist to restore a file
.SH SYNOPSIS
.B recover
name [num] [destination]
.SH DESCRIPTION
.I recover
copies
.I num
blocks (or a default number) from the freelist of
the specified
.I name
and puts them in the files data00, data01 ... data\fInum\fR.
The data files are created in
.I destination
directory if given or in /tmp.
.PP
.I Name
is an absolute pathname of the file to be recovered or the
.I special device
name of the file system of interest.
In the first case, only the beginning of the absolute pathname needs to be
given.
.I Recover
uses it to lookup the
.I special device
in the mount table.
.PP
.I Recover
will not save the day if someone takes blocks from
the freelist before you can run recover.
If recover cannot be run immediately and the file system is active,
you may wish to
.I pad
your data in the freelist by removing some
unneeded files in the same file system.
.PP
After you run recover, you will have files data00, data01 ...
data\fInum\fR
each
1 block (BSIZE bytes) long.
You may wish to use
.I file
and or an editor on the
.I data
files to
find the relevant ones.
Unless your file happens to be a multiple of BSIZE,
the last data block will have NULL bytes after your data.
Compilers will complain.
An editor can be used to truncate NULL bytes from the file.
.SH EXAMPLE
.nf
rm precious.c                   : OOPS, I meant precious.o
recover /u/bob/precious.c 100   : grab 100 blocks from freelist
cd /tmp
file data*                      : file should think that the first
                                : data files are C or text
cat data00 data01 data02 > XYZ  : It turns out I only need 3 blocks
vi XYZ                          : look at it and truncate NULLS
rm data*                        : clean up
mv XYZ /u/bob/precious.c
.SH FILES
/usr/local/recover
/etc/mtab - to look up on which file system
.I name
was mounted.
.br
data00, data01, data02, ...
.SH DIAGNOSTICS
A usage message if you forget one of the arguments.
A message if you do not have permission to read disk.
.SH AUTHOR
Bob Gray (duke!adiron!bob)
.SH BUGS
The algorithm used for looking up where file systems are mounted
only works if file systems are mounted at the top level.
This should be fixed.
If an absolute pathname is given that refers to
the root, getmount will fail.  Use the root's special file.
.I Num
is checked for what the author thinks is reasonable.
E*O*F
echo extracting recover/alloc.c
cat > recover/alloc.c <<'E*O*F'
/* @(#)alloc.c	1.1	82/05/12 */
#include "defs.h"

/*
 * alloc will return the next available
 * free disk block from the free list of
 * the specified device.
 * The super block has up to NICFREE remembered
 * free blocks; the last of these is read to
 * obtain NICFREE more . . .
 */

daddr_t
alloc(fd, fp)
int fd;				/* raw file descriptor of filesystem */
register struct filsys *fp;	/* pointer to incore superblock */
{
	daddr_t bno;
	register i;
	struct fblk fblk;	/* struct will contain freeblock list */

	do {
		if (fp->s_nfree <= 0)
			goto nospace;
		if (fp->s_nfree > NICFREE) {
			fprintf(stderr, "bad free count");
			goto nospace;
		}
		bno = fp->s_free[--fp->s_nfree];
		fp->s_free[fp->s_nfree] = (daddr_t)0; /* safety */
		DEBUG(7,"s_nfree=%d",fp->s_nfree);
		DEBUG(7,"   s_free[s_nfree]=%ld",(long)bno);

		if (bno == 0)
			goto nospace;
	} while (badblock(fp, bno));
	if (fp->s_nfree <= 0) {
		bread(fd, bno, (char *) &fblk, sizeof(fblk) );
		fp->s_nfree = fblk.df_nfree;
		if(fp->s_nfree <=0 || fp->s_nfree > NICFREE)
			goto nospace;
		for(i=0;i<NICFREE;i++)
			fp->s_free[i] = fblk.df_free[i];

	}
	fp->s_tfree--;		/* total free blocks */
	DEBUG(7,"   s_tfree=%ld\n",(long)fp->s_tfree);
	return (bno);

nospace:
	fprintf(stderr, "file system full nfree=%d, tfree=%d\n",
				fp->s_nfree, fp->s_tfree);
	fp->s_nfree = 0;
	fp->s_tfree = 0;
	return ((daddr_t) 0 );
}

/*
 * Check that a block number is in the
 * range between the I list and the size
 * of the device.
 * This is used mainly to check that a
 * garbage file system has not been mounted.
 */
badblock(fp, bn)
	register struct filsys *fp;
	daddr_t bn;
{

	if (bn < fp->s_isize || bn >= fp->s_fsize) {
		fprintf(stderr, "bad block");
		return(1);
	}
	return(0);
}
E*O*F
echo extracting recover/defs.h
cat > recover/defs.h <<'E*O*F'
/* @(#)defs.h	1.1 82/05/12 */
#include <stdio.h>
extern Debug;
#define DEBUG(l,f,s) if(Debug>=l) fprintf(stderr,f,s)
/* shorthand */
#define ISIZE(f)	finfo[f].i.i_size
#define IPOS(f)		finfo[f].pos
#define F(f,in)		finfo[f].ch[in].

/*#define SMOUNT */	/* defining this means mounts will be fork/execed */
			/* make sure MOUNT and UMOUNT have proper pathnames */
			/* they are used with SMOUNT code */

#ifdef ISC
#define MOUNT	"/priv/mount"
#define UMOUNT	"/priv/unmount"
/* Of the 8 bits for a minor dev, low 5 select a logical sub dsk, the next
 2 bits select a drive on a controller and hi bit selects 1 of 2 controllers*/
#define DRIVE_CTL_MASK	0340 /* these 2 are for safety - to lessen mistakes */
/*#define SCANNERDSK	0200*/  /* allow writing to only certain filesystems*/
#define ILARG	010000
#define MTAB "/etc/mtab"
#define MSIZE	64
#define MOFF	32
struct mnt
	{
	char path[MOFF];
	char spec[MOFF];
	};
#include "../inc/param.h"
#include "../inc/inode.h"
#include "../inc/stat.h"
#include "../inc/filsys.h"
#include "../inc/fblk.h"
#endif

#ifdef PWB
#define MOUNT	"/etc/mount"
#define UMOUNT	"/etc/umount"
#define ILARG	010000
#define MTAB "/etc/mnttab"
#define MSIZE	26
#define MOFF	10
struct mnt
	{
	char spec[MOFF];
	char path[16];
	};
#include "../inc/param.h"
#include "../inc/inode.h"
#include "../inc/stat.h"
#include "../inc/filsys.h"
#include "../inc/fblk.h"
#endif

#ifdef V7		/* v7 or VAX 4.1BAD */
#define MOUNT	"xmount"
#define UMOUNT	"xumount"
#define NEWFS
#define MTAB	"/etc/mtab"
#define MSIZE	64
#define MOFF	32
struct mnt
	{
	char path[MOFF];
	char spec[MOFF];
	};
#include <sys/param.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/stat.h>
#include <sys/filsys.h>
#include <sys/fblk.h>
#endif

#ifdef NEWFS
#define HIBLK (1<<30)	/* this should be big enough */
#else
#define HIBLK (daddr_t) ~0	/* biggest number - all 1 bits */
#endif

struct chunk
	{
	daddr_t	physblk;	/* physical block of start of chunk */
	daddr_t logblk;		/* logical ....				*/
	unsigned nblks;		/* number of blocks in chunk 		*/
	};

#define CHLSTSZ 50
struct info		/* one of these for each possible file opened */
	{
	struct inode i;
	struct chunk ch[CHLSTSZ];/*eventually make this a pointer to dynamic */
	off_t	pos;		/* R/W pointer - lseek  */
	};

extern struct info finfo[];
long lseek();

/* alloc.c */
daddr_t alloc();	/* return next freeblock given raw fd and superb */
int	badblock();	/* return 1 if block is out of range */

/* bchunk.c */
/* bchunk()   get blocks and build a chunklist */

/* bmap.c */
daddr_t	bmap();	/* map logical to physical blocks */

/* buildf.c */
daddr_t	findphys();	/* return physical block given logical block */
/* buildf()	build a file by setting up indirect block pointers */

/* coal.c */
/* coalesce()	take a chunklist and attempt to coalesce it */
int indircnt();	/* return # of indir blocks needed to represent a file */

/* errexit.c */
/* errexit()	print message on stderr and exit with code */

/* dskino.c */
/* getdskino()	given inum fill in inode info */
/* iexpand()	expand disk inode info into incore inode */
/* setdskino()  put incore inode info on disk */

/* getmount.c */
struct mnt * getmount();	/* get mount info */
char * rindex();	/* reverse index for PWB and ISC */
char * rawname();	/* make block dev name raw */

/* rw.c */
/* bread()	block read */
/* bwrite()	block write */

int	c_creat();
int	c_open();
int	c_close();
int	c_read();
int	c_write();
long	c_lseek();

#	ifndef MIN
#	define	MIN(a,b) ((((long)a)<((long)b))?((long)a):((long)b))
#	endif
E*O*F
echo extracting recover/dskino.c
cat > recover/dskino.c <<'E*O*F'
/* @(#)dskino.c	1.1	82/05/12 */
#include	"defs.h"

#ifndef NEWFS
#define dinode i6node
/* Inode structure as it appears on the disk. (V6 type ) */
struct	i6node
{
	int	i6_mode;
	char	i6_nlink;
	char	i6_uid;
	char	i6_gid;
	char	i6_size0;
	unsigned i6_size1;
	daddr_t	i6_addr[8];
	time_t	i6_atime;
	time_t	i6_mtime;
};
#endif
			/* given inumber on filesystem, fill in inode */
getdskino(fd,inum,ip)
struct inode *ip;
ino_t	inum;
	{
	struct dinode *dp;
	char buf[BSIZE];
	bread(fd,itod(inum),buf,BSIZE);
	dp = (struct dinode *) &buf[0];
	dp += itoo(inum);
	iexpand(ip,dp);
	}

#ifdef NEWFS
iexpand(ip, dp)			/* expand disk inode info into incore inode*/
register struct inode *ip;
register struct dinode *dp;
{
	register char *p1, *p2;
	register int i;

	ip->i_mode = dp->di_mode;
	ip->i_nlink = dp->di_nlink;
	ip->i_uid = dp->di_uid;
	ip->i_gid = dp->di_gid;
	ip->i_size = dp->di_size;
	p1 = (char *)ip->i_un.i_addr;
	p2 = (char *)dp->di_addr;
	for(i=0; i<NADDR; i++) {
#ifdef PDP11
		*p1++ = *p2++;
		*p1++ = 0;
		*p1++ = *p2++;
		*p1++ = *p2++;
#else
		*p1++ = *p2++;
		*p1++ = *p2++;
		*p1++ = *p2++;
		*p1++ = 0;
#endif
	}
}
#else

iexpand(ip, dp)
register struct inode *ip;
register struct i6node *dp;
{
	register int i;

	ip->i_mode = dp->i6_mode;
	ip->i_nlink = dp->i6_nlink;
	ip->i_uid = dp->i6_uid;
	ip->i_gid = dp->i6_gid;
	ip->i_size = ((long) (dp->i6_size0 & 0377)) << 16;
	ip->i_size += (long)(unsigned) dp->i6_size1;
	DEBUG(6,"m=0%o    ",ip->i_mode); DEBUG(6,"sz=%D\n",ip->i_size);
	for(i=0; i<8; i++)
		{
		ip->i_un.i_addr[i]= dp->i6_addr[i];
		DEBUG(6,"a=%ld\n",(long)ip->i_un.i_addr[i]);
		}
}
#endif

setdskino(fd,inum,ip) /* given a pointer to incore inode, put data on disk */
struct inode *ip;
ino_t	inum;
	{
	register char *p1, *p2;
	struct dinode *dp;
	register int i;
	char buf[BSIZE];
	bread(fd,itod(inum),buf,BSIZE);	/* read block containing inode */
	dp = (struct dinode *) &buf[0];
	dp += itoo(inum);		/* get offset to our inode */
#ifdef NEWFS
	dp->di_size = ip->i_size; /* copy size */
	p2 = (char *)ip->i_un.i_addr;
	p1 = (char *)dp->di_addr;
	for(i=0; i<NADDR; i++)		/* copy addresses */
		{
		DEBUG(4,"addr=%ld\n",(long)ip->i_un.i_addr[i]);
#ifdef PDP11
		*p1++ = *p2++;	/* copy 32 bit addresses to 24 bit addresses */
			 p2++;
		*p1++ = *p2++;
		*p1++ = *p2++;
#else
		*p1++ = *p2++;	/* copy 32 bit addresses to 24 bit addresses */
		*p1++ = *p2++;
		*p1++ = *p2++;
			 p2++;
#endif
		}
#else
	dp->i6_mode  = ip->i_mode;
	dp->i6_size0 = ( ip->i_size >> 16 ) & 0377;
	dp->i6_size1 =   ip->i_size & 0177777;
	for(i=0; i<8; i++)
		{
		DEBUG(4,"addr=%ld\n",(long)ip->i_un.i_addr[i]);
		dp->i6_addr[i] =    ip->i_un.i_addr[i];
		}

#endif
	bwrite(fd,itod(inum),buf,BSIZE);  /* read block containing inode */
	}

E*O*F
echo extracting recover/errexit.c
cat > recover/errexit.c <<'E*O*F'
/* @(#)errexit.c	1.1	82/05/12 */
#include <stdio.h>
/* VARARGS2 */
errexit(code, fmt, arg1)
char fmt[];
char arg1[];
{
	fflush(stdout);
	_doprnt(fmt, &arg1, stderr);    /* print the error message */
	perror("If sys call made error then:");
	exit (code);                    /* consider it fatal and exit */
}
E*O*F
echo extracting recover/main.c
cat > recover/main.c <<'E*O*F'
char sccsid[]=	"@(#)main.c	1.5	82/06/01";
/* a program to recover files from the free list */
/* Bob Gray  PAR CORP  (315) 738-0600     duke!adiron!bob */
#define	DEFCNT	10		/* give him 10 blocks if he doesnt specify */
#define MAXBLK	1024		/* Is there any reason to allow more? */
#include	"defs.h"
int Debug = 0;			/* default debug level - higher gives more*/
main(argc,argv)
char **argv;
	{
	struct filsys filsys;	/* to hold superblock */
	int fd;			/* file desc for disk */
	int i;
	int f;			/* file desc for each dataXXX file created */
	int count = DEFCNT;	/* number of blocks to grab from freelist */
	daddr_t physical;
	char databuf[BSIZE];
	char stringbuf[60];
	char *disk;		/* filesystem we want is on this disk */
	char *dest;		/* where do we creat dataXXX files? */
	struct mnt *mountp;	/* pointer to a /etc/mtab entry */

	if(argc<2 || argc>4)
		errexit(7,"Usage: recover name [num] [destination]\n");

	/* from first arg, we determine where desired filesystem is */
	if( strncmp("/dev/",argv[1],5) == 0 )
		disk = argv[1];    /* assume he gave us a char or blk dev */
	else			   /* look up pathname to get disk */
		{
		if( (mountp=getmount(argv[1]) ) == (struct mnt *) 0)
			errexit(9,"getmount info failed\n");
		disk = rawname(mountp->spec);
		}

	count=atoi(argv[2]);
	if(count<1 || count>MAXBLK)
		errexit(8,"a count of %d seems strange\n",count);

	if(argc == 4)
		dest = argv[3];
	else
		dest = "/tmp";

	printf("copy %d blocks from %s and stick in %s\n",count,disk,dest);
	sync();		/* force cache buffers out to disk - for PDP11s */
	if((fd=open(disk,0)) <= 0 )
		errexit(10,"cannot open %s\n",disk);

	bread(fd, SUPERB, (char *) &filsys, sizeof(filsys) );
	DEBUG(6,"s_tfree= %u\n",filsys.s_tfree);
	DEBUG(6,"s_tinode= %u\n",filsys.s_tinode);

	for(i=0;i<count;i++)
		{
		physical = alloc(fd, &filsys);
		sprintf(stringbuf,"%s/data%02d",dest,i);
		if( (f = creat(stringbuf,0644) ) < 0 )
			errexit(41,"cannot creat in getmount\n");
		bread(fd, physical, databuf, BSIZE);
		if(write(f,databuf,BSIZE) != BSIZE)
			errexit(33,"bad write\n");
		close(f);
		}
	}

#ifndef V7
strncmp(s1, s2, n)
register char *s1, *s2;
register n;
{
	while (--n >= 0 && *s1 == *s2++)
		if (*s1++ == '\0')
			return(0);
	return(n<0 ? 0 : *s1 - *--s2);
}
#endif
E*O*F
echo extracting recover/rw.c
cat > recover/rw.c <<'E*O*F'
/* 82/05/12	@(#)rw.c	1.1 */
#include <stdio.h>
#include "defs.h"

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

#ifdef PWB
	seek(fi,bno,3);
#else
	if( lseek(fi,(long) bno*BSIZE, 0) != (long)bno*BSIZE)
		errexit(32,"lseek failed %ld\n",(long)bno);
#endif
	if (read(fi, buf, cnt) != cnt)
		errexit(1,"read error %ld\n", bno);
}

bwrite(fi, bno, buf, cnt)
daddr_t bno;
char	*buf;
{
#ifdef PWB
	seek(fi,bno,3);
#else
	if( lseek(fi,(long) bno*BSIZE, 0) != (long)bno*BSIZE)
		errexit(32,"lseek failed %ld\n",(long)bno);
#endif
	if (write(fi, buf, cnt) != cnt)
		errexit(2,"write error %ld\n", bno);
}
E*O*F
echo extracting recover/getmount.c
cat > recover/getmount.c <<'E*O*F'
/* @(#)getmount.c	1.1 82/05/12 */
/*given ful pathname sp of a file, return pointer to mnt struct or NULL */
/* special file has to be mounted on a top level directory - PWB restriction */
#include "defs.h"
char *strcpy(), *strcat();

struct mnt *
getmount(sp)
char *sp;
	{
	char name[60];
	struct mnt mbuf[NMOUNT];
	static struct mnt mnt;
	int mfd;
	int i;
	char *p;
	char *s;	/* slide along pathname and zap a NULL to terminate*/
	mnt.spec[0] = 0;/* strcat depends on this buffer being empty */
	strcpy(name,sp);
	p = &name[0];
	s = p;	/* p will always point to start of string */
	if(*p != '/')
		return(NULL);	/* check that full pathname was given */
			/* keep only the first absolute directory -see above */
			/* eg: given /image/landsat/band1 -> /image  */
	s++;		/* skip over initial slash */
				/* MTAB might have entries that look like: */
				/* /image000000000000000hp1a000000000000000 */
	while(*s)
		{
		if(*s == '/')
			{
			*s = NULL;
			break;
			}
		s++;
		}
	if( (mfd=open(MTAB,0)) < 0)	/* see mtab(5) for details */
		{
		fprintf(stderr,"cannot open %s\n",MTAB);
		return(NULL);
		}
	if(read(mfd,(char *)mbuf,sizeof(mbuf)) <= 0 )
		errexit(29,"bad read of %s\n",MTAB);
	if(close(mfd) < 0)
		errexit(30,"bad close of %s\n",MTAB);
	for(i=0;i<NMOUNT;i++)
		{
		DEBUG(3,"spec=%s.  ",mbuf[i].spec);
		DEBUG(3,"path=%s.\n",mbuf[i].path);
		if(strcmp(p,mbuf[i].path) == 0) /* FOUND */
			break;
		}
	if(i>=NMOUNT)
		return( (struct mnt *) 0);	/* NOT FOUND */

	strcpy(mnt.path, mbuf[i].path);

#ifndef ISC
	strcpy(mnt.spec,"/dev/");
#endif
	strcat(mnt.spec, mbuf[i].spec);
	DEBUG(3,"spec=%s.	",mnt.spec); DEBUG(3,"path=%s.\n",mnt.path);
	return(&mnt);
	}


#ifndef V7
/*
 * Return the ptr in sp at which the character c last
 * appears; NULL if not found
*/

char *
rindex(sp, c)
register char *sp, c;
{
	register char *r;

	r = NULL;
	do {
		if (*sp == c)
			r = sp;
	} while (*sp++);
	return(r);
}
#endif

char *		/* make block dev name raw dev name  - EG: */
rawname(cp)	/*  /dev/hp1a ==>  /dev/rhp1a		   */
	char *cp;
	{
	char *rindex();
	static char rawbuf[32];
	char *dp = rindex(cp, '/');

	if (dp == 0)
		return (0);
	*dp = 0;
	strcpy(rawbuf, cp);
	*dp = '/';
	strcat(rawbuf, "/r");
	strcat(rawbuf, dp+1);
	return (rawbuf);
	}
E*O*F
echo extracting inc/fblk.h
cat > inc/fblk.h <<'E*O*F'
/*	fblk.h	4.2	81/02/19	*/

struct fblk
{
	int    	df_nfree;
	daddr_t	df_free[NICFREE];
};
E*O*F
echo extracting inc/filsys.h
cat > inc/filsys.h <<'E*O*F'
/*	filsys.h	4.3	81/03/03	*/

/*
 * Structure of the super-block
 */
struct	filsys
{
	unsigned  s_isize;		/* size in blocks of i-list */
	daddr_t	s_fsize;   		/* size in blocks of entire volume */
	int  	s_nfree;   		/* number of addresses in s_free */
	daddr_t	s_free[NICFREE];	/* free block list */
	int  	s_ninode;  		/* number of i-nodes in s_inode */
	ino_t  	s_inode[NICINOD];	/* free i-node list */
	char   	s_flock;   		/* lock during free list manipulation */
	char   	s_ilock;   		/* lock during i-list manipulation */
	char   	s_fmod;    		/* super block modified flag */
	char   	s_ronly;   		/* mounted read-only flag */
	time_t 	s_time;    		/* last super block update */

#ifdef NEWFS

	daddr_t	s_tfree;   		/* total free blocks*/
	ino_t  	s_tinode;  		/* total free inodes */
	int	s_dinfo[2];		/* interleave stuff */
#define	s_m	s_dinfo[0]
#define	s_n	s_dinfo[1]
	char   	s_fsmnt[12];		/* ordinary file mounted on */
	/* end not maintained */
	ino_t	s_lasti;		/* start place for circular search */
	ino_t	s_nbehind;		/* est # free inodes before s_lasti */
#else

	int     pad[35];
	long    s_dtime;        /* time file system was last dumped */
	char    s_dfile[6];     /* where last dump was taken to     */
	daddr_t	s_tfree;	/* Total free, for subsystem examination */
	int	s_tinode;	/* Free inodes, for subsystem examination */
	char	s_fname[6];	/* File system name */
	char	s_fpack[6];	/* File system pack name */
#endif

};

#ifdef KERNEL
struct	filsys *getfs();
#endif
E*O*F
echo extracting inc/param.h
cat > inc/param.h <<'E*O*F'
#define	SUPERB	((daddr_t)1)	/* block number of the super block */
#define NOFILE	15		/* this many files opened at once */
#define NMOUNT	20

#define	CLSIZE	1		/* this many sectors per fs block */
#define	BSIZE	512		/* size of secondary block (bytes) */
#define	INOPB	16		/* 16 inodes per block */
#define	BMASK	0777		/* BSIZE-1 */
#define	BSHIFT	9		/* LOG2(BSIZE) */
#define	NICINOD	100		/* number of superblock inodes */
#define	NICFREE	100		/* number of superblock free blocks */

/* inumber to disk address and inumber to disk offset */
#define	itod(x)	((daddr_t)((((unsigned)(x)+2*INOPB-1)/INOPB)))
#define	itoo(x)	((int)(((x)+2*INOPB-1)%INOPB))

/* file system blocks to disk blocks and back */
#define	fsbtodb(b)	((b)*CLSIZE)
#define	dbtofsb(b)	((b)/CLSIZE)

#ifdef PWB	/* C compiler on PWB needs help for daddr_t x[NINDIR] */
#define NINDIR 256
#else
#define	NINDIR	(BSIZE/sizeof(daddr_t))
#endif

#define	MAX(a,b) (((a)>(b))?(a):(b))
#define	MIN(a,b) ((((long)a)<((long)b))?((long)a):((long)b))

typedef	unsigned	daddr_t;
typedef	char *		caddr_t;
typedef	unsigned 	ino_t;
typedef	long		time_t;
typedef	long		off_t;
typedef int		dev_t;
E*O*F
echo extracting inc/inode.h
cat > inc/inode.h <<'E*O*F'
/*	inode.h	4.5	81/03/09	*/

#define	NADDR	13
struct	inode
{
	char	i_flag;
	char	i_count;	/* reference count */
	dev_t	i_dev;		/* device where inode resides */
	ino_t	i_number;	/* i number, 1-to-1 with device address */
	unsigned  i_mode;
	int	i_nlink;	/* directory entries */
	int	i_uid;		/* owner */
	int	i_gid;		/* group of owner */
	off_t	i_size;		/* size of file */
	union {
		daddr_t	i_addr[NADDR];	/* if normal file/directory */
	} i_un;
};

/* modes */
#define	IFMT	0170000		/* type of file */
#define		IFDIR	0040000	/* directory */
#define		IFCHR	0020000	/* character special */
#define		IFBLK	0060000	/* block special */
#define		IFREG	0100000	/* regular */
#define		IFMPC	0030000	/* multiplexed char special */
#define		IFMPB	0070000	/* multiplexed block special */
#define	ISUID	04000		/* set user id on execution */
#define	ISGID	02000		/* set group id on execution */
#define	ISVTX	01000		/* save swapped text even after use */
#define	IREAD	0400		/* read, write, execute permissions */
#define	IWRITE	0200
#define	IEXEC	0100
E*O*F
echo extracting inc/stat.h
cat > inc/stat.h <<'E*O*F'
/*	stat.h	4.2	81/02/19	*/

struct	stat
{
	dev_t	st_dev;
	ino_t	st_ino;
	unsigned  st_mode;
	int	st_nlink;
	int	st_uid;
	int	st_gid;
	dev_t	st_rdev;
	off_t	st_size;
	time_t	st_atime;
	time_t	st_mtime;
	time_t	st_ctime;
};

#define	S_IFMT	0170000		/* type of file */
#define		S_IFDIR	0040000	/* directory */
#define		S_IFCHR	0020000	/* character special */
#define		S_IFBLK	0060000	/* block special */
#define		S_IFREG	0100000	/* regular */
#define		S_IFMPC	0030000	/* multiplexed char special */
#define		S_IFMPB	0070000	/* multiplexed block special */
#define	S_ISUID	0004000		/* set user id on execution */
#define	S_ISGID	0002000		/* set group id on execution */
#define	S_ISVTX	0001000		/* save swapped text even after use */
#define	S_IREAD	0000400		/* read permission, owner */
#define	S_IWRITE 0000200	/* write permission, owner */
#define	S_IEXEC	0000100		/* execute/search permission, owner */
E*O*F
echo extracting inc/README
cat > inc/README <<'E*O*F'
These files are used by v6 type systems (including ISC and PWB).
V7 type systems get (including 4.1BSD) get theirs from /usr/include/sys.
E*O*F