V10/cmd/chuck/repairs.c

#include "fs.h"

repairs()
{	int i, j, k;
	if(erptr <= 0) {
		pmesg("all ok\n");
		return;
	}
	i = errcnts[Einvalid] + errcnts[Esuperino] + errcnts[Esuperfree]
		+ errcnts[Efreelist];
	if(!flags['i'] && i == erptr) {
		pmesg("fixing super block\n");
		fixfree();
		if(!flags['w'])
			return;
		if(bwrite(1, (char *)&sblk, 1))
			fatal("write failed\n");
		pmesg("done\n");
		return;
	}
	if(!flags['i'] && i + errcnts[Enullable] == erptr) {
		pmesg("clearing %d inodes safely\n", errcnts[Enullable]);
		/* even if it is a directory.  no links means Edot, Edotdot... */
		for(j = 0; j < erptr; j++)
			if(erlist[j].type == Enullable)
				clri(erlist[j].a);
		fixfree();
		return;
	}
	/* now we enter upon the realm of the speculative */
	if(!flags['i'] && !errcnts[Edup]) {
		pmesg("hi ho hi ho, it's off to work we go\n");
		if(j = errcnts[Enullable])
			pmesg("clearing %d inodes safely\n", j);
		for(j = 0; j < erptr; j++)
		switch(erlist[j].done? 0: erlist[j].type) {
		case 0:
			continue;
		default:
			pmesg("%s %d %d, not handled\n", errnm(erlist[j].type),
				erlist[j].a, erlist[j].b);
			exitcode++;
			continue;
		case Ehole:
			pmesg("%s ends with a hole, remove or copy\n", prino(erlist[j].a));
			continue;
		case Enullable:
			clri(erlist[j].a);
			erlist[j].done = 1;
			continue;
		case Enotdot: case Enotdotdot: case Edotino: case Ebadparent:
			k = erlist[j].a;
			if(k > ROOTINO)
				fixdots(k);
			continue;
		case Elinkcnt:
			k = erlist[j].a;
			if(imap[k].type == Weird) {
				if(fixweird(k))
					continue;
				pmesg("weird ino %d (strange type): fix manually\n", k);
				exitcode = 1;
				continue;
			}
			if(imap[k].nrefs == 0)
				attach(k);
			else if(imap[k].type != Unalloc)
				fixlinks(erlist[j].a);
			else if(imap[k].nrefs == 1)
				expunge(k);
			else {
				pmesg("%s has %d references!  Expunging.\n", prino(k),
					imap[k].nrefs);
				expunge(k);
				exitcode = 1;	/* things are not ok */
				continue;
			}
			erlist[j].done = 1;
			continue;
		case Ebadname:
			pmesg("%s has a weird name, look at it\n", prino(erlist[j].b));
			fixnames(erlist[j].a);
			continue;
		case Ebadino:	/* these are lost files */
			pmesg("removing bad inos from %s\n", prino(erlist[j].a));
			fixnames(erlist[j].a);
			continue;
		case Einvalid: case Efreelist: case Esuperino:
			continue;	/* fixfree() comes later */
		case Eattach:
			attach(erlist[j].a);
			erlist[j].done = 1;
			continue;
		case Efakeroot:
			attach(erlist[j].a);
			fixdots(erlist[j].a);
			continue;
		}
		fixfree();
		return;
	}
	rpterrs();
	if(flags['i'])
		interact();
	else
		exitcode = 1;
}

rpterrs()
{	int i, j, k, cnts[32];
	struct dinode *dp;
	if(erptr > 30 && !flags['v']) {
		pmesg("%d errors\n", erptr);
		for(i = 0; i < 32; i++)
			cnts[i] = 0;
		for(i = 0; i < erptr; i++)
			cnts[erlist[i].type]++;
		for(i = 0; i < 32; i++)
			if(cnts[i])
				pmesg("%s\t%d\n", errnm(i), cnts[i]);
		return;
	}
	for(i = 0; i < erptr; i++)
		switch(erlist[i].type) {
		default:
			pmesg("%s %d %d\n", errnm(erlist[i].type),
				erlist[i].a, erlist[i].b);
			continue;
		case Efakeroot:
			pmesg("own parent %s\n", prino(erlist[i].a));
			continue;
		case Edup:
			j = erlist[i].b;
			k = bmap[erlist[i].a].ino;
			pmesg("dup block %d %s %s\n", erlist[i].a, prino(j), prino(k));
			continue;
		case Elinkcnt:
			j = erlist[i].a;
			dp = (struct dinode *)(buf + 2*bsize + (j-1)*sizeof(*dp));
			pmesg("Elinkcnt(%s) refs %d(parent %s)\n", prino(j), imap[j].nrefs,
				prino(imap[j].parent));
			continue;
		case Ebadparent:
			j = erlist[i].a;
			k = erlist[i].b;
			pmesg("Ebadparent: (%s) not in (%s)\n", prino(j), prino(k));
			continue;
		case Ebadname:
			j = erlist[i].a;
			k = erlist[i].b;
			pmesg("Ebadname (%s) (in %s)\n", prino(j), prino(k));
			continue;
		}
}

fixfree()
{	int i, j, *p;
	sblk.s_ninode = sblk.s_tfree = 0;
	sblk.s_tinode = tinode;
	for(i = 2, j = 0; i < ninode && j < NICINOD; i++)
		if(imap[i].type == Unalloc) {
			sblk.s_inode[j++] = i;
			sblk.s_ninode++;
		}
	sblk.s_lasti = sblk.s_inode[0];
	sblk.s_nbehind = 0;
	if(bsize < 4096) {	/* stupid test, FIX */
		fix1free();
		return;
	}
	if(pblk > lblk) {
		for(i = 0; i < BITMAP; i++)
			sblk.U.B.S_bfree[i] = 0;
		memset(freeb, 0, (pblk-lblk)*bsize);
		for(j = i = 0, p = (long *) freeb; i < pblk; i++, j++) {
			if(j >= 32) {
				j = 0;
				p++;
			}
			if(bmap[i].type == Free || bmap[i].type == Unk) {
				sblk.s_tfree++;
				*p |= (1 << j);
			}
		}
		if(bwrite(lblk, freeb, pblk-lblk)) {
			exitcode = 1;
			pmesg("bit map block write failed\n");
		}
	}
	else {
		for(i = 0, p = sblk.U.B.S_bfree; i < BITMAP; i++)
			*p++ = 0;
		for(i = fblk, j = 0, p = sblk.U.B.S_bfree; i < pblk; i++, j++) {
			if(j >= 32) {
				j = 0;
				p++;
			}
			if(bmap[i].type == Free || bmap[i].type == Unk) {
				sblk.s_tfree++;
				*p |= (1 << j);
			}
		}
	}
	sblk.U.B.S_valid = 1;
	if(!flags['w'])
		return;
	if(bwrite(1, (char *)&sblk, 1))
		fatal("super block write failed\n");
}

fix1free()
{	/* fix this someday FIX */
	pmesg("get fsck to fix the free list!\n");
	if(!flags['w'])
		return;
	if(bwrite(1, (char *)&sblk, 1))
		fatal("super block write failed\n");
}

fixweird(n)
{	struct dinode *dp;
	dp = (struct dinode *) (buf + (n-1)*sizeof(*dp) + 2*bsize);
	if(dp->di_mode)
		return(0);
	if(dp->di_size > 0)
		return(0);
	if(imap[n].nrefs > 1)
		return(0);
	pmesg("removing weird ino %s\n", prino(n));
	if(imap[n].nrefs == 1)
		expunge(n);
	else {
		clri(n);
		pmesg("and rerun chuck\n");
	}
	return(1);
}
	
fixlinks(n)
{	struct dinode *dp;
	dp = (struct dinode *) (buf + (n-1)*sizeof(*dp) + 2*bsize);
	if(imap[n].type == Unalloc) {
		pmesg("ino %d unallocated, link count unadjusted\n", n);
		return;
	}
	pmesg("%s getting %d as links\n", prino(n), imap[n].nrefs);
	dp->di_nlink = imap[n].nrefs;
	if(flags['w'])
		wrti(n);
}

wrti(n)	/* write the block of inodes containing inode n */
{	int i;
	i = (n-1)/inopb + 2;
	if(bwrite(i, buf + i*bsize, 1))
		pmesg("write of block containing ino %d failed\n", n);
}

clri(n)
{	struct dinode *dp;
	static struct dinode nild;
	dp = (struct dinode *) (buf + (n-1)*sizeof(*dp) + 2*bsize);
	switch(imap[n].type) {
	case Dir: case Reg: case Lnk:
		tossblocks(n);
	}
	*dp = nild;
	imap[n].type = Unalloc;
	tinode++;
	if(flags['w'])
		wrti(n);
}

tossblocks(n)
{	struct dinode *dp;
	int addr[NADDR], i;
	dp = (struct dinode *) (buf + (n-1)*sizeof(*dp) + 2*bsize);
	l3tol(addr, dp->di_addr, NADDR);
	for(i = 1; i < NADDR-3; i++)
		if(addr[i]) {
			bmap[addr[i]].type = Free;
			tfree++;
		}
	if(addr[10])
		freeind(addr[10]);
	if(addr[11])
		freedbl(addr[10]);
	if(addr[12])
		pmesg("ino %d, triply indirect block?  rerun chuck to fix free list\n",
			n);
}

freeind(n)
{	int i, *p;
	bmap[n].type = Free;
	if(bread(n, buf, 1))
		fatal("freeind, could't read block %d\n", n);
	for(i = 0, p = (int *)buf; i < bsize/sizeof(int); i++, p++)
		if(*p) {
			bmap[*p].type = Free;
			tfree++;
		}
}

freedbl(n)
{	int i, *p;
	bmap[n].type = Free;
	if(bread(n, buf+bsize, 1))
		fatal("freeind2, couldn't read %d\n", n);
	for(i = 0, p = (int *)(buf+bsize); i < bsize/sizeof(int); i++, p++)
		if(*p)
			freeind(n);
}

interact()
{	int i, j;
	struct dinode *dp;
	for(i = 0; i < erptr; i++)
	switch(erlist[i].type) {
	default:
		pmesg("%s not yet doable\n", errnm(erlist[i].type));
		continue;
	case Enotdot:
		pmesg("Enotdot (dot not self) %s\n", prino(erlist[i].a));
		continue;
	case Edotino:
		pmesg("Edotino (dot illegal) %s\n", prino(erlist[i].a));
		continue;
	case Enotdotdot:
		pmesg("Enotdotdot (dotdot illegal) %s\n", prino(erlist[i].a));
		continue;
	case Ebadparent:
		pmesg("Ebadparent (dotdot not parent) %s\n", prino(erlist[i].a));
		continue;
	case Elinkcnt:
		j = erlist[i].a;
		dp = (struct dinode *) (buf + 2*bsize + (j-1)*sizeof(*dp));
		pmesg("Elinkcnt, %s, %d links %d refs\n", prino(j), dp->di_nlink,
			imap[j].nrefs);
		if(imap[j].nrefs > 0 || imap[j].type == Weird) 
			switch(qry("clri, links-changed-to-refs, skip:\n")) {
			case 's':
				continue;
			case 'c':
				pmesg("clearing ino %d\n", j);
				clri(j);
				continue;
			case 'l':
				pmesg("fixing links ino %d\n", j);
				fixlinks(j);
				continue;
			}
		switch(qry("attach to /lost+found, expunge:\n")) {
		case 'a':
			attach(j);
			continue;
		case 'e':
			expunge(j);
			continue;
		}
		continue;
	case Enullable:
		j = erlist[i].a;
		pmesg("Enullable ino %s\n", prino(j));
		if(qry("clri, or skip\n") == 'c') {
			pmesg("clearing ino %d\n", j);
			clri(j);
		}
		continue;
	case Einvalid:
		if(qry("valid flag not set, set it?\n") == 'y') {
			sblk.U.B.S_valid = 1;
			if(bwrite(1, (char *) &sblk, 1)) 
				pmesg("couldn't rewrite super block\n");
		}
		continue;
	}
	if(qry("fix the super block?\n") == 'y') {
		pmesg("fixing the superblock\n");
		fixfree();
	}
}