AUSAM/source/S/chk.c

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

/*
 *	chk: fast combination of icheck+dcheck
 *
 *	Original from Vrije Universiteit, Amsterdam.
 *	Modified by Kevin Hill, University of NSW.
 */ 

#include <local-system>
#include <ino.h>
#include <filsys.h>

/*
 *	`prefix' is added to the given arguments - if this cannot be opened,
 *		the argument itself is tried.
 *	OFFSET is the char offset to the X's
 *	IN is the magic interleaving number
 *	NSEC is the number of sectors per track
 *		(must be <= 256 as `indbuf' is flogged in `makefree')
 */

#ifdef	EECF
char	prefix[]	"/dev/rmsXX";
#define	OFFSET	8
#define	IN	10
#define	NSEC	21
#endif

#ifdef	AGSM
char	prefix[]	"/dev/rhpXX";
#define	OFFSET	8
#define	IN	7
#define	NSEC	22
#endif

#ifdef	EECF1140
char	prefix[]	"/dev/rrkX";
#define	OFFSET	8
#define	IN	3
#define	NSEC	12
#endif

char *dargv[]
{
#ifdef EECF
	"02", "11", "00", "01", "03", "10",
#endif
#ifdef	AGSM
	"00", "01", "02", "03", "10", "11", "12", "13",
#endif
#ifdef	EECF1140
	"0", "1",
#endif
	0
};



/*
 *	TUNABLE CONSTANTS
 */

#define	IBLKS	16		/* this many blocks of inodes read each time */
#define	MAXSPECIAL	32	/* 2nd indir blks + directory indir. blks */

#define	BISZ	88		/* size of blist and ilist arrays */
#define	NPASS	20		/* max nr of passes in orphans() */
#define	LISTSZ	20		/* nr of levels looked at per pass in orphans() */
#define	BLKS	fsize / 24	/* sorts and reads this many directory blocks per read in orphans() */
#define	MAXDEPTH	20	/* max nr dir names printed out for inums - MUST BE < MAXSPECIAL */
#define	AVLEN	7		/* average length of a directory name, plus null */
#define	IINCR	10		/* allocate this many file structures each time in process */
#define	NINCR	256		/* allocate this many characters each time in addname - MUST BE EVEN */


/*
 *	exit status bits
 */

#define	COREERR	   1
#define	OPENERR	   2
#define	NONFATAL   4
#define	FATAL	0200

/*
 *	global variables
 */

struct inode inode[IBLKS * 16];	/* buffer to read inodes */ 
struct filsys sblock;		/* superblock of filesystem */ 
int  fildes;			/* file descriptor of filesystem */ 
char *ecount;			/* used in checking link counts */
int  *parent;			/* base of parent array in orphans() */
unsigned have;			/* largest chunk of core until now */
int  ninodes;			/* number of inodes on current device */ 
unsigned fsize;			/* size of current device */
unsigned isize;			/* size of i-list on device */
unsigned ino;			/* current inumber */ 
unsigned pino;			/* curr inum when processing in-core lists */
unsigned blknr;			/* current block */
char exitcode;			/* exitstatus is collected here */ 
int  nifree;			/* counts free inodes */
int  nspcl;			/* counts special files */
int  ndir;			/* nr. of directories */
int  nfile;			/* nr. of ordinary files */
int  nlarg;			/* number of large files */
int  nvlarg;			/* number of huge files */
int  nindir;			/* number of indirect blocks */
int  nvindir;			/* number of 2nd indirect blks */
int  high;			/* high water mark */
int  nused;			/* blocks used on this device */ 
char *usedbl;			/* bitmap for used blocks */ 
char *indbl;			/* bitmap for indirect blocks */ 
char *dirbl;			/* bitmap for directory blocks */ 
char *np;			/* current position in name array */
char *namend;			/* end of name array */
int  indbuf[256];		/* buffer to read indirect blocks */ 
unsigned  blist[BISZ];		/* list for funny blocks */
unsigned  ilist[BISZ];		/* list for funny inodes */

/*
 *	flags
 *	(the c-compiler will still use ints)
 */

char headpr;			/* flag for nice output */ 
char verbose;			/* verbose output */
char dotstoo;			/* print out . and .. entries for -i option */
char freelist;			/* recreate the free list */
char quiet;			/* cut out the horsing around */
char specfind;			/* look up special files */
char blook;			/* currently looking up blk nrs */
char ilook;			/* currently looking up inums */
char checked;			/* filsystem has been checked */
char chkdot;			/* currently checking . and .. entries */
char ctrlchrs;			/* print out control-chars as `^x' pair */

/*
 *	misc
 */

char data_small[] "data (small)";
char data_lge[] "data (large)";
char indirect[] "indirect";
char indr_huge[] "indirect (huge)";
char indr2[] "2nd indirect";

char dir_inum[] "dir inum ";
char not_in[] ".' not in first 32 bytes\n";


struct specialbl
{
	int blno;		/* block number */ 
	int which;		/* 2nd indir. or direc. indir. or inum */

#define SP2IND 0
#define SPDIR  1

} specialbl[MAXSPECIAL], *nspecial, *dirblks, *ndirbp;

struct dirent
{
	int inum, pinum;
	char *namep;
} *dstart, *ilstrt, *ilp, *ilend;


main(argc, argv)
register argc;
register char **argv;
{
	register n;
	char c;

	ecount = sbrk(0) - 1;		/* points to last-used byte of core */ 
	parent = ecount - 1;		/* points to last-used word of core */

	while (**++argv == '-')		/* check for flags */
	{
		argc--;
		switch (c = argv[0][1])
		{
	    case 'a':	dotstoo++;
			continue;

	    case 'b':
	    case 'i':	while (n = number(argv[1]))
			{
				addlist(n, c == 'b' ? blist : ilist);
				argv++;
				argc--;
			}
			if (headpr) headpr = 0;
			else { argv++; argc--; }
			continue;

	    case 'q':	quiet++;
			continue;

	    case 's':	freelist++;
			continue;

	    case 'v':	verbose++;
			continue;

	    case 'x':	specfind++;
			continue;

	    default:	printf("Usage: chk [-a][-b list][-i list][-q][-s][-v][-x] [specials]\n");
			return FATAL;
		}
	}

	if (argc == 1)			 /* check default devices */
	{
		argv = dargv;
		argc = sizeof dargv / 2;
	}

	while (--argc) check(*argv++);	/* check devices given as arguments */
	return exitcode;
}

check(file)
register char *file;
{
	register i, *p;
	register char *fp;
	unsigned want;
	int initpass();
	int checklinks();
	int findbad();
	int findblks();
	int markdirs();
	int countdir();
	int srchdirs();

	fp = file;
	i = OFFSET;
	while ((prefix[i++] = *fp++) && i < sizeof prefix);
	prefix[i - 1] = 0;
	if ((fildes = open(fp = prefix, freelist ? 2 : 0)) < 0 &&
		 (fildes = open(fp = file, freelist ? 2 : 0)) < 0)
	{
		printf("Cannot open %s\n", fp);
		exitcode =| OPENERR | FATAL;
		return;
	}
	nspecial = specialbl;
	nused = 0;
	nifree = 0;
	nspcl = 0;
	ndir = 0;
	nfile = 0;
	nlarg = 0;
	nvlarg = 0;
	nindir = 0;
	nvindir = 0;
	high = 0;
	checked = 0;
	headpr = 0;
	printf("%s:\n", fp);
	sync();				/* write out in core blocks */ 
	fsize = 2;			/* bread insists on blocks in range */ 
	bread(1, &sblock, 512);		/* read super block */ 
	fsize = sblock.s_fsize;
	ninodes = (isize = sblock.s_isize) * 16;
	if (*blist == 0 && *ilist == 0)
	{
		if (verbose) printf("Fsize\t%5u\nIsize\t%5u\n", fsize, isize);
		i = freelist ? 2 : 3;
		want = i * (fsize / 8) + ninodes + i;
		if (chkcore(want)) return;	/* not enough core */
		i = want >> 1;			/* number of words to clear */ 
		p = 1 + ecount;			/* address to start */ 
		do
			*p++ = 0;
		while (--i);
		ecount[want] = 0;
		usedbl = &ecount[ninodes + 1];
		indbl = &usedbl[fsize / 8 + 1];
		dirbl = &indbl[fsize / 8 + 1];
		scaninodes(initpass);
		ino = 0;		/* inum not known on error now */
		dospecs();
		finioff();
		if (freelist)
		{
			makefree();
			return;
		}
		checkfree();
		scaninodes(checklinks);
		if ((exitcode & FATAL) == 0) orphans();
		checked++;
	}
	if (*blist)
	{
		for (p = blist; *p; p++)
			if (*p > 1 && *p < isize + 2) printf("blk %u in i-list\n", *p);
		nspecial = specialbl;
		ndirbp = dirblks = &parent[1];			/* first usable word */
		if (chkcore(BLKS * 4)) return;
		blook++;
		scaninodes(checked ? findbad : findblks);
		chkindir();
		readblks();
		for (p = blist; *p; *p++ = 0);		/* clear out blist */
		blook = 0;
	}
	if (*ilist)
	{
		if (checked)
		{
			printf("\n looking for inums");
			for (p = ilist; *p; printf(" %u", *p++));
			putchar('\n');
		}
		else scaninodes(countdir);
		for (p = ilist, nspcl = 0; *p++; nspcl++);
		nfile = ndir;
		nlarg = (nspcl + nfile) * AVLEN;
		ndir =+ (ndir + 3) / 4;			/* to improve the hashing */
		want = (ndir + nspcl) * sizeof *dstart + BLKS * sizeof *nspecial + nlarg;
		if (chkcore(want)) return;
		ilook++;
		p = dstart = ecount + 1;
		i = (ndir * sizeof *dstart) >> 1;
		do					/* clean it up */
			*p++ = 0;
		while (--i);
		nspecial = specialbl;
		ndirbp = dirblks = &ecount[ndir * sizeof *dstart + 1];
		np = &ndirbp[BLKS];
		ilstrt = ilp = namend = &np[(nlarg + 1) & ~01];
		ilend = &ilstrt[nspcl];
		scaninodes(markdirs);
		scaninodes(srchdirs);
		getdirblks();
		readblks();
		printout();
		for (p = ilist; *p; *p++ = 0);		/* clear i-list */
		ilook = 0;
	}
	close(fildes);
}


/*
 *	General-purpose inode scanning procedure
 */

scaninodes(f)
register (*f)();
{
	register i, j;

	for (i = 0, ino = 0; ino < ninodes; i =+ IBLKS)
	{
		bread(i+2, inode, sizeof inode);	/* read large chunk */ 
		for (j = 0; j < IBLKS * 16 && ino < ninodes; j++)
		{
			ino++;
			(*f)(&inode[j]);
		}
	}
}


/*
 *	Read inodes; for each active inode update all bitmaps.
 *	Also fill usedbl[] with all double indirect blocks and
 *	all indirect directory blocks.
 *	There won't be a lot of those but if there are some 
 *	they have to be read first
 */ 

initpass(ip)
register struct inode *ip;
{
	register mode;
	register i;
	int n, dir;

	if (((mode = ip->i_mode) & IALLOC) == 0)	/* unused inode */ 
	{
		nifree++;
		return;
	}
	if ((mode & (IFCHR & IFBLK)))			/* special file */ 
	{
		nspcl++;
		if (specfind)
		{
			printf("Spec file at %u\n", ino);
			addlist(ino, ilist);
		}
		return;
	}
	dir = 0;
	if ((mode & IFMT) == IFDIR)
	{
		dir++;
		ndir++;
		if (ip->i_size1 & 017 || ip->i_size0 || ip->i_size1 < 32)
		{
			printf("Illegal dir size; inum %u\n", ino);
			addlist(ino, ilist);
			exitcode =| FATAL;
		}
	}
	else nfile++;
	if ((mode & ILARG) == 0)
	{
		for (i = 0; i < 8; i++)
		{
			if ((n = ip->i_addr[i]) == 0) continue;
			setbit(n, usedbl, data_small);
			if (dir && freelist == 0) setbit(n, dirbl);
		}
	}
	else
	{
		nlarg++;
		for (i = 0; i < 7; i++)
		{
			if ((n = ip->i_addr[i]) == 0) continue;
			nindir++;
			setbit(n, usedbl, indirect);
			setbit(n, indbl);
			if (dir && freelist == 0) inspecial(n, SPDIR);
		}
		if ((n = ip->i_addr[7]))
		{
			nvlarg++;
			setbit(n, usedbl, indr_huge);
			if (dir)
			{
				printf("Huge directory; inum %u\n", ino);
				addlist(ino, ilist);
				exitcode =| FATAL;
				return;
			}
			inspecial(n, SP2IND);
			if (verbose)
			{
				printf("Huge file; inum %u\n", ino);
				addlist(ino, ilist);
			}
		}
		else if (dir && verbose)
		{
			printf("Large directory; inum %u\n", ino);
			addlist(ino, ilist);
		}
	}
}


/*
 *	Read all double indirect blocks and all indirect directory blocks
 *	and fill in bitmaps.
 */ 

compar(p1, p2)
struct specialbl *p1, *p2;		/* NOT registers - shorter code */
{
	return p1->blno - p2->blno;
}

dospecs()
{
	register struct specialbl *p;
	register i, n;

	if (nspecial > specialbl + 1)
		qsort(specialbl, nspecial - specialbl, sizeof specialbl[0], compar);
	for (p = specialbl; p < nspecial; p++)
	{
		bread(blknr = p->blno, indbuf, 512);
		for (i = 0; i < 256; i++)
		{
			if ((n = indbuf[i]) == 0) continue;
			if (p->which == SP2IND)
			{
				nvindir++;
				setbit(n, usedbl, indr2);
				setbit(n, indbl);
			}
			else setbit(n, dirbl);
		}
	}
}


/*
 *	Proceed through the bitmaps indbl and dirbl, letting the
 *	arm of the disk move from low to high once.
 */ 

finioff()
{
	register i, k, b;
	int ind, dir, bit, j;
	char *n;

	for (i = 0; i < (fsize / 8 + 1); i++)
	{
		ind = indbl[i];
		dir = freelist ? 0 : dirbl[i];
		if (ind || dir)
		{
			b = i * 8;
			bit = 1;
			for (j = 0; j < 8; j++)
			{
				blknr = b;
				if (ind & bit)		/* indirect block */
				{
					bread(b, indbuf, 512);
					for (k = 0; k < 256; k++)
						if (indbuf[k])
							setbit(indbuf[k], usedbl, data_lge);
				}
				else if (dir & bit)	/* directory block */
				{
					bread(b, indbuf, 512);
					for (k = 0; k < 256; k =+ 8)
					{
						if ((n = indbuf[k]) == 0) continue;
						if (n <= ninodes) ++ecount[n];
						else
						{
							if (addlist(b, blist)) printf("Bad dir inum (%u) (blk %u)\n", n, b);
							exitcode =| FATAL;
						}
					}
				}
				b++;
				bit =<< 1;
			}
		}
	}
}


/*
 *	Read free list and final icheck
 */ 

checkfree()
{
	register b, n;

	n = 0;
	if (verbose) printf("Used\t%5u\n", nused);
	while ((b = nextfree()))
	{
		setbit(b, usedbl, "free");
		n++;
	}
	if (b = fsize - isize - 2 - nused)
	{
		printf("Missing %u blocks\n", b);
		exitcode =| NONFATAL;
	}
	if (verbose)
	{
		printf("Spcl\t%5u\n", nspcl);
		printf("Files\t%5u\n", nfile);
		printf("Large\t%5u\n", nlarg);
		if (nvlarg) printf("Huge\t%5u\n", nvlarg);
		printf("Direc\t%5u\n", ndir);
		printf("Indir\t%5u\n", nindir);
		if (nvindir) printf("Indir2\t%5u\n", nvindir);
		printf("High\t%5u\n", high);
	}
	if (quiet == 0) printf("Freei\t%5u\nFree\t%5u\n", nifree, n);
}


/*
 *	Read inodes, for each active inode check link and count
 */ 

checklinks(ip)
register struct inode *ip;
{
	register i;

	i = ino;
	if ((ip->i_mode & IALLOC) == 0 && ecount[i] == 0) return;
	if (ip->i_nlink == ecount[i] && ip->i_nlink) return;
	if (headpr++ == 0) printf("\nInum\tDir-ent\tLnk-cnt\n");
	printf("%u\t%u\t%u\n", ino, ecount[i] & 0377, ip->i_nlink & 0377);
	if (ecount[i] == 0) exitcode =| NONFATAL;
	else
	{
		exitcode =| FATAL;
		addlist(i, ilist);
	}
}


/*
 *	Look for disconnected branches
 */

orphans()
{
	register j;
	register unsigned k, *lp;
	unsigned *lpp;
	int	i, notyet;
	int list[LISTSZ];
	int readir();
	int fstblk();

	k = (j = ninodes + BLKS * 2) * 2;
	if (chkcore(k)) return;
	lp = 1 + ecount;		/* clear it out */
	do
		*lp++ = 0;
	while (--j);
	ndirbp = dirblks = &parent[ninodes + 1];
	nspecial = specialbl;

	/*
	 * Now rescan inodes looking for all directories
	 * These must be read, and their child inodes marked (in parent[])
	 * as children of the directory (inum).
	 * Also, check all . and .. entries.
	 */

	parent[1] = 1;
	scaninodes(readir);
	getdirblks();
	readblks();
	ndirbp = dirblks;
	nspecial = specialbl;
	chkdot++;
	scaninodes(fstblk);
	getdirblks();
	readblks();
	chkdot = 0;

	/*
	 * Now scan through parent[] - for each slot, work back through
	 * parents until reach inode number 1 (you hope!).
	 */

	i = 0;
	do
	{
		notyet = 1;
		for (j = 2; j <= ninodes; j++)
		{
			if (parent[k = j] <= 1) continue;
			for (lp = list; lp < &list[LISTSZ]; )
				if ((*lp++ = k = parent[k]) <= 1) break;
			if (k > 1) notyet = 0;
			if (k == 0) { lp--; k = lp[-1]; }
			lpp = lp - 2;
			for (lp = list; lp < lpp; lp++) parent[*lp] = k;
			parent[j] = k;
		}
	} while (i++ < NPASS && notyet == 0);

	for (j = 2; j < ninodes; j++)
		if ((i = parent[j]) != 1)
		{
			printf("possible orphan; inum %u", j);
			if (i)
			{
				printf(" (from %u)", i);
				addlist(i, ilist);
			}
			putchar('\n');
			exitcode =| NONFATAL;
		}
}

readir(ip)
struct inode *ip;
{
	register i, n, mode;
	int readblks();

	if (((mode = ip->i_mode) & IALLOC) == 0 || (mode & IFMT) != IFDIR)
	{
		parent[ino] = 1;
		return;
	}
	if ((mode & ILARG) == 0)
	{
		for (i = 0; i < 8; i++)
			if (n = ip->i_addr[i]) store(n, ino);
	}
	else for (i = 0; i < 7; i++)
		if (n = ip->i_addr[i]) storespec(n);
}

store(bno, inum)
unsigned bno;
{
	if (bno < isize + 2 || bno >= fsize) return;
	if (ndirbp == &dirblks[BLKS])
	{
		readblks();
		ndirbp = dirblks;
	}
	ndirbp->blno = bno;
	ndirbp->which = inum;
	ndirbp++;
}

storespec(bno)
{
	if (nspecial == &specialbl[MAXSPECIAL])
	{
		getdirblks();
		nspecial = specialbl;
	}
	nspecial->blno = bno;
	nspecial->which = ino;
	nspecial++;
}

getdirblks()
{
	register struct specialbl *p;
	register i, n;

	if (nspecial > specialbl + 1)
		qsort(specialbl, nspecial - specialbl, sizeof specialbl[0], compar);
	for (p = specialbl; p < nspecial; p++)
	{
		bread(p->blno, indbuf, 512);
		if (chkdot) store(indbuf[0], p->which);
		else for (i = 0; i < 256; i++)
			if (n = indbuf[i]) store(n, p->which);
	}
}

readblks()
{
	register struct specialbl *p;
	register k, n;

	if (ndirbp > dirblks + 1)
		qsort(dirblks, ndirbp - dirblks, sizeof dirblks[0], compar);
	for (p = dirblks; p < ndirbp; p++)
	{
		bread(p->blno, indbuf, chkdot ? 32 : 512);
		pino = p->which;			/* do NOT use ino */
		if (chkdot) { checkdots(); continue; }
		for (k = 0; k < 256; k =+ (blook ? 1 : 8))
		{
			if ((n = indbuf[k]) == 0) continue;
			if (blook)
			{
				chklist(n, pino, data_lge);
				continue;
			}
			if (ilook)
			{
				if (n >= 1 && n <= ninodes) process(&indbuf[k]);
				continue;
			}
			if ((parent[n] == 0) && ! dotname(&indbuf[k + 1])) parent[n] = pino;
		}
	}
}

dotname(s)
register *s;
{
	return (*s == '.\0' || *s == '..' && s[1] == 0);
}

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

	if (bno >= fsize)
	{
		if (checked) abort();	/* impossible! */
		exitcode =| FATAL;	/* Error message will be given in setbit() */
		p = buf;
		i = cnt;
		do
			*p++ = 0;
		while (--i);
		return;
	}
	seek(fildes, bno, 3);
	if (read(fildes, buf, cnt) != cnt)
	{
		printf("read error %u\n", bno);
		exitcode =| FATAL;
		exit(exitcode);
	}
}


/*
 *	Set the bit in the given bit-map, checking for bad and dup blocks.
 *	`string' is only valid when map == usedbl.  `string' only starts
 *	with `f' when checking blocks in the free list.
 */

setbit(bno, map, string)
char *map, *string;
register unsigned bno;
{
	register char *b;
	register bit;

	if (bno < isize + 2 || bno >= fsize)
	{
		if (map == usedbl) prerr("%u bad;", bno, string);
		exitcode =| FATAL;
		return;
	}
	b = &map[bno >> 3];		/* unsigned; don't have to mask */
	bit = 1 << (bno & 07);
	if (map == usedbl)
	{
		if ((*b) & bit)
		{
			prerr("%u dup;", bno, string);
			exitcode =| FATAL;
			return;
		}
		nused++;
		if (string[0] != 'f' && bno > high) high = bno;
	}
	*b =| bit;
}


prerr(errstrng, bno, string)
register char *string;
register unsigned bno;
char *errstrng;
{
	register printed 0;

	if (ino)
	{
		if (addlist(ino, ilist))
		{
			printf(errstrng, bno);
			printf(" inum %u;", ino);
			printed++;
		}
	}
	else
	{
		if (addlist(blknr ? blknr : bno, blist))
		{
			printf(errstrng, bno);
			if (blknr) printf(" in ind-blk %u;", bno = blknr);
			printed++;
		}
	}
	if (printed) printf(" class=%s\n", string);
}


inspecial(bno, wflag)
unsigned bno;
{
	if (nspecial == &specialbl[MAXSPECIAL])
	{
		/*
		 * There are more than MAXSPECIAL strange blocks.
		 * Now we are forced to make an extra pass.
		 */ 

		printf("More than %u special blocks (recompile?)\n", MAXSPECIAL);
		dospecs();
		nspecial = specialbl;
	}
	nspecial->blno = bno;
	nspecial->which = wflag;
	nspecial++;
}

nextfree()
{
	register unsigned b, i;

	i = --sblock.s_nfree;
	if (i >= 100)		/* gets < 0 too! */
	{
		printf("illegal nfree (%u)\n", i + 1);
		exitcode =| FATAL;
		return(0);
	}
	b = sblock.s_free[i];
	if (b == 0) return(0);
	if (sblock.s_nfree <= 0)
	{
		bread(b, indbuf, 512);
		sblock.s_nfree = indbuf[0];
		for (i = 0; i < 100; i++) sblock.s_free[i] = indbuf[i+1];
	}
	return(b);
}

makefree()
{
	register unsigned i, j, low;
	int *flag, adr[NSEC];

	flag = &indbuf[0];
	for (i = 0; i < NSEC; flag[i++] = 0);
	for (i = 0, j = 0; i < NSEC; i++)
	{
		while (flag[j]) j = (j + 1) % NSEC;
		adr[i] = j;
		flag[j]++;
		j = (j + IN) % NSEC;
	}
	sblock.s_nfree = sblock.s_ninode = 0;
	sblock.s_flock = sblock.s_ilock = sblock.s_fmod = 0;
	for (i = 101; i < 256; indbuf[i++] = 0);
	low = isize + 2;		/* lowest potentially usable blk */
	free(0);				/* end-of-list sentinel */
	j = lrem(0, i = sblock.s_fsize, NSEC);
	while (j-- && --i >= low) freebl(i);
	i--;
	while (i >= low + NSEC - 1)
	{
		for (j = 0; j < NSEC; j++) freebl(i - adr[j]);
		i =- NSEC;
	}
	while (i >= low) freebl(i--);
	bwrite(1, &sblock);			/* do NOT sync (may be root device) */
	close(fildes);
}

free(bno)
{
	register i;

	if (sblock.s_nfree >= 100)
	{
		indbuf[0] = sblock.s_nfree;
		for (i = 0; i < 100; i++) indbuf[i + 1] = sblock.s_free[i];
		sblock.s_nfree = 0;
		bwrite(bno, indbuf);
	}
	sblock.s_free[sblock.s_nfree++] = bno;
}

freebl(bno)
{
	if ((usedbl[(bno >> 3) & 017777] & (1 << (bno & 07))) == 0) free(bno);
}

bwrite(bno, buf)
register unsigned bno;
{
	seek(fildes, bno, 3);
	if (write(fildes, buf, 512) != 512)
	{
		printf("write error %u\n", bno);
		exitcode =| FATAL;
		exit(exitcode);
	}
}


/*
 *	findbad looks up blocks in `blist' to find their inode nrs
 *	(if they have been put there as a result of the checks).
 *	Note that it is not necessary to check indirect blocks of
 *	ordinary files, as their inums will already be known.
 */

findbad(ip)
struct inode *ip;
{
	register i, n, mode;
	int chkindir();

	if (((mode = ip->i_mode) & IALLOC) == 0 ||
		(mode & (IFCHR & IFBLK))) return;
	for (i = 0; i < 8; i++)
		if (n = ip->i_addr[i]) chklist(n, ino, 0);
	if ((mode & ILARG) == 0) return;
	if ((mode & IFMT) == IFDIR)
	{
		for (i = 0; i < 7; i++)
			if (n = ip->i_addr[i]) build(n);
		return;
	}
	if (n = ip->i_addr[7]) build(n);
}

build(bno)
register unsigned bno;
{
	if (bno < isize + 2 || bno >= fsize) return;
	if (nspecial == &specialbl[MAXSPECIAL])
	{
		chkindir();
		nspecial = specialbl;
	}
	nspecial->blno = bno;
	nspecial->which = ino;
	nspecial++;
}

chkindir()
{
	register struct specialbl *p;
	register i, n;

	if (nspecial > specialbl + 1)
		qsort(specialbl, nspecial - specialbl, sizeof specialbl[0], compar);
	for (p = specialbl; p < nspecial; p++)
	{
		bread(p->blno, indbuf, 512);
		for (i = 0; i < 256; i++)
			if (n = indbuf[i])
			{
				chklist(n, p->which, blook ? indr2 : 0);
				if (blook) store(n, p->which);
			}
	}
}

chklist(bno, inum, s)
register bno;
char *s;
{
	register unsigned *lp blist;

	while (*lp != bno && *++lp);
	if (*lp)
	{
		printf("blk %u from inum %u", bno, inum);
		if (s) printf("; class=%s", s);
		putchar('\n');
		addlist(inum, ilist);
	}
}

findblks(ip)
struct inode *ip;
{
	register i, n, mode;

	if (((mode = ip->i_mode) & IALLOC) == 0 ||
		(mode & (IFCHR & IFBLK))) return;
	if ((mode & ILARG) == 0)
	{
		for (i = 0; i < 8; i++)
			if (n = ip->i_addr[i]) chklist(n, ino, data_small);
	}
	else
	{
		for (i = 0; i < 7; i++)
		{
			if ((n = ip->i_addr[i]) == 0) continue;
			chklist(n, ino, indirect);
			store(n, ino);
		}
		if (n = ip->i_addr[7])
		{
			chklist(n, ino, indr_huge);
			build(n);
		}
	}
}

fstblk(ip)
register struct inode *ip;
{
	register mode;

	if (((mode = ip->i_mode) & IALLOC) == 0 || (mode & IFMT) != IFDIR) return;
	if ((mode & ILARG) == 0) store(ip->i_addr[0], ino);
	else storespec(ip->i_addr[0]);
}

checkdots()
{
	register *dot, *dotdot, *s;

	dot = dotdot = 0;
	s = indbuf;
	do
	{
		if (s[0])
			if (s[1] == '.\0') dot = s;
			else if (s[1] == '..' && s[2] == 0) dotdot = s;
		s =+ 8;
	} while (s != &indbuf[16]);
	s = 0;
	if (dot == 0)
	{
		printf("%s%u: `%s", dir_inum, pino, not_in);
		s++;
	}
	else if (*dot != pino)
	{
		printf("%s%u: `.' inum %u\n", dir_inum, pino, *dot);
		addlist(*dot, ilist);
		s++;
	}
	if (dotdot == 0)
	{
		printf("%s%u: `.%s", dir_inum, pino, not_in);
		s++;
	}
	else if (*dotdot != parent[pino])
	{
		printf("%s%u: from %u; `..' inum %u\n", dir_inum, pino, parent[pino], *dotdot);
		addlist(*dotdot, ilist);
		s++;
	}
	if (s)
	{
		addlist(pino, ilist);
		exitcode =| FATAL;
	}
}

countdir(ip)
struct inode *ip;
{
	register mode;

	if (((mode = ip->i_mode) & IALLOC) && (mode & IFMT) == IFDIR) ndir++;
}

markdirs(ip)
struct inode *ip;
{
	register mode;

	if (((mode = ip->i_mode) & IALLOC) && (mode & IFMT) == IFDIR) lookup(ino, 1);
}

lookup(inum, add)
register inum;
{
	register struct dirent *d, *dend;

	d = &dstart[inum % ndir];
	dend = &dstart[ndir];
	while (d->inum)
	{
		if (d->inum == inum) return d;
		if (++d >= dend) d = dstart;
	}
	if (add) d->inum = inum;
	return 0;
}

srchdirs(ip)
struct inode *ip;
{
	register i, n, mode;

	if (((mode = ip->i_mode) & IALLOC) == 0 || (mode & IFMT) != IFDIR) return;
	if ((mode & ILARG) == 0)
	{
		for (i = 0; i < 8; i++)
			if (n = ip->i_addr[i]) store(n, ino);
	}
	else
	{
		for (i = 0; i < 7; i++)
			if (n = ip->i_addr[i]) storespec(n);
	}
}

process(dp)
register *dp;
{
	register i, *ip;

	if ((i = dotname(dp + 1)) && ! dotstoo) return;
	if (i == 0 && (ip = lookup(*dp, 0)))		/* it's a directory */
	{
		ip->pinum = pino;
		ip->namep = addname(dp + 1);
	}
	ip = ilist;
	while (*ip != *dp && *++ip);
	if (*ip)
	{
		if (ilp == ilend)
		{
			if ((i = more(&ilend, &namend, IINCR * sizeof *ilend)) == -1) return;
			if (i)
			{
				ilp--;
				ilend = i;
				ilend->inum = ilp->inum;
				ilend->pinum = ilp->pinum;
				ilend->namep = ilp->namep;
				ilp->inum = 0;
				ilp = ilp->pinum = i;
				ilp++;
			}
			ilend =+ IINCR;
		}
		ilp->inum = *dp;
		ilp->pinum = pino;
		ilp->namep = addname(dp + 1);
		ilp++;
	}
}

addname(nm)
char *nm;
{
	register i, j;
	register char *cp;
	char *new;

	i = 0;
	cp = np;
	do
	{
		if (cp == namend)
		{
			if ((j = more(&namend, &ilend, NINCR)) == -1) return;
			if (new = j)
			{
				nm =- i;
				namend = np = cp = new;
				i = 0;
			}
			namend =+ NINCR;
		}
		*cp = *nm++;
	} while (*cp++ && i++ < 14);
	cp[-1] = 0;			/* in case name is 14 chars long */
	nm = np;
	np = cp;
	return nm;
}

more(me, him, incr)
register unsigned *me, *him, incr;
{
	if (*him < *me)		/* contiguous allocation possible */
	{
		if (chkcore(*me - ecount - 1 + incr)) return -1;
		return 0;
	}
	if (chkcore(*him - ecount - 1 + incr)) return -1;
	return *him;
}

printout()
{
	register n;
	register struct dirent *d, *dp;

	for (d = ilstrt; d != ilp; d++)
	{
		if ((dp = d)->inum == 0) d = dp = dp->pinum;	/* chain along */
		nspecial = specialbl;
		n = 0;
		do
			storespec(dp);
		while dp->pinum != 1 && ++n != MAXDEPTH && (dp = lookup(ino = dp->pinum, 0)) != 0;
		pname(dp);
	}
}

pname(dp)
register struct dirent *dp;
{
	register struct specialbl *p;

	p = nspecial - 1;
	if (dp == 0) printf("?%u?", ino);
	else if (dp->pinum != 1) printf(".../%u", dp->pinum);
	else putchar('1');
	while (p >= specialbl) printf("/%u", (p--)->blno->inum);
	printf(":\t");
	p = nspecial - 1;
	ctrlchrs++;
	if (dp == 0) printf("?%u?", ino);
	else if (dp->pinum != 1) printf("...");
	else printf("%s", (p--)->blno->namep);
	while (p >= specialbl) printf("/%s", (p--)->blno->namep);
	ctrlchrs = 0;
	putchar('\n');
}

/*
 *	addlist returns 1 if the given element is not already
 *	in the list, or if it is in the list and the verbose option is on
 */

addlist(n, list)
register unsigned n;
unsigned list[];
{
	register unsigned *lp list;
	static ioflw, boflw;

	if (*lp)
	{
		while (*lp != n && *++lp);
		if (*lp) return verbose;
		if (lp >= &list[BISZ - 1])
		{
			if (list == blist && boflw++ == 0) printf("b-list overflow\n");
			if (list == ilist && ioflw++ == 0) printf("i-list overflow\n");
			return 1;
		}
	}
	*lp = n;
	return 1;
}

number(s)
register char *s;
{
	register n 0;

	if (*s < '0' || *s > '9')
	{
		headpr++;
		return 0;
	}
	while (*s) n = n * 10 + *s++ - '0';
	return n;
}

chkcore(want)
register unsigned want;
{
	if (want > have)
	{
		if (sbrk(want - have) == -1)
		{
			printf("Not enough core\n");
			exitcode =| COREERR | FATAL;
			return 1;
		}
		have = want;
	}
	return 0;
}

/*
 *	This printf is smaller than the C-library version.
 *	It supports only %s, %u, and %nu (n a single digit).
 */

printf(s, a)
register char *s;
unsigned a;
{
	register c;
	register unsigned *p;
	int fw;

	p = &a;
	while (c = *s++)
	{
		if (c == '%')
		{
			if ((c = *s++) >= '0' && c <= '9')
			{
				fw = c - '0';
				c = *s++;
			}
			else fw = 0;
			switch (c)
			{
		    case 's':	printf(*p++);		/* CARE! */
				continue;

		    case 'u':	printl(*p++, fw);
				continue;

		    case 0:	return;
			}
		}
		putchar(c);
	}
}

printl(n, fw)
register unsigned n;
register fw;
{
	register char *s;
	char stack[10];

	s = &stack[sizeof stack];
	*--s = 0;
	do
	{
		*--s = n % 10 + '0';
		n =/ 10;
		fw--;
	} while (n);
	while (fw-- > 0) *--s = ' ';
	while (*s) putchar(*s++);
}

putchar(c)
{
	if ((c =& 0177) < ' ' && ctrlchrs)
	{
		putchar('^');
		c =| 0100;
	}
	write(1, &c, 1);
}