808 lines · 17.5 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "stdinc.h"#include "dat.h"#include "fns.h" static void	checkDirs(Fsck*);static void	checkEpochs(Fsck*);static void	checkLeak(Fsck*);static void	closenop(Fsck*, Block*, u32int);static void	clrenop(Fsck*, Block*, int);static void	clrinop(Fsck*, char*, MetaBlock*, int, Block*);static void	error(Fsck*, char*, ...);static int	getBit(uchar*, u32int);static int	printnop(char*, ...);static void	setBit(uchar*, u32int);static int	walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize],			int type, u32int tag, u32int epoch);static void	warn(Fsck*, char*, ...); #pragma varargck argpos error 2#pragma varargck argpos printnop 1#pragma varargck argpos warn 2 static Fsck*checkInit(Fsck *chk){	chk->cache = chk->fs->cache;	chk->nblocks = cacheLocalSize(chk->cache, PartData);;	chk->bsize = chk->fs->blockSize;	chk->walkdepth = 0;	chk->hint = 0;	chk->quantum = chk->nblocks/100;	if(chk->quantum == 0)		chk->quantum = 1;	if(chk->print == nil)		chk->print = printnop;	if(chk->clre == nil)		chk->clre = clrenop;	if(chk->close == nil)		chk->close = closenop;	if(chk->clri == nil)		chk->clri = clrinop;	return chk;} /* * BUG: Should merge checkEpochs and checkDirs so that * bad blocks are only reported once, and so that errors in checkEpochs * can have the affected file names attached, and so that the file system * is only read once. * * Also should summarize the errors instead of printing for every one * (e.g., XXX bad or unreachable blocks in /active/usr/rsc/foo). */ voidfsCheck(Fsck *chk){	Block *b;	Super super; 	checkInit(chk);	b = superGet(chk->cache, &super);	if(b == nil){		chk->print("could not load super block: %R");		return;	}	blockPut(b); 	chk->hint = super.active;	checkEpochs(chk); 	chk->smap = vtMemAllocZ(chk->nblocks/8+1);	checkDirs(chk);	vtMemFree(chk->smap);} static void checkEpoch(Fsck*, u32int); /* * Walk through all the blocks in the write buffer. * Then we can look for ones we missed -- those are leaks. */static voidcheckEpochs(Fsck *chk){	u32int e;	uint nb; 	nb = chk->nblocks;	chk->amap = vtMemAllocZ(nb/8+1);	chk->emap = vtMemAllocZ(nb/8+1);	chk->xmap = vtMemAllocZ(nb/8+1);	chk->errmap = vtMemAllocZ(nb/8+1); 	for(e = chk->fs->ehi; e >= chk->fs->elo; e--){		memset(chk->emap, 0, chk->nblocks/8+1);		memset(chk->xmap, 0, chk->nblocks/8+1);		checkEpoch(chk, e);	}	checkLeak(chk);	vtMemFree(chk->amap);	vtMemFree(chk->emap);	vtMemFree(chk->xmap);	vtMemFree(chk->errmap);} static voidcheckEpoch(Fsck *chk, u32int epoch){	u32int a;	Block *b;	Entry e;	Label l; 	chk->print("checking epoch %ud...\n", epoch); 	for(a=0; a<chk->nblocks; a++){		if(!readLabel(chk->cache, &l, (a+chk->hint)%chk->nblocks)){			error(chk, "could not read label for addr 0x%.8#ux", a);			continue;		}		if(l.tag == RootTag && l.epoch == epoch)			break;	} 	if(a == chk->nblocks){		chk->print("could not find root block for epoch %ud", epoch);		return;	} 	a = (a+chk->hint)%chk->nblocks;	b = cacheLocalData(chk->cache, a, BtDir, RootTag, OReadOnly, 0);	if(b == nil){		error(chk, "could not read root block 0x%.8#ux: %R", a);		return;	} 	/* no one should point at root blocks */	setBit(chk->amap, a);	setBit(chk->emap, a);	setBit(chk->xmap, a); 	/*	 * First entry is the rest of the file system.	 * Second entry is link to previous epoch root,	 * just a convenience to help the search.	 */	if(!entryUnpack(&e, b->data, 0)){		error(chk, "could not unpack root block 0x%.8#ux: %R", a);		blockPut(b);		return;	}	walkEpoch(chk, b, e.score, BtDir, e.tag, epoch);	if(entryUnpack(&e, b->data, 1))		chk->hint = globalToLocal(e.score);	blockPut(b);} /* * When b points at bb, need to check: * * (i) b.e in [bb.e, bb.eClose) * (ii) if b.e==bb.e,  then no other b' in e points at bb. * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb. * (iv) if b is active then no other active b' points at bb. * (v) if b is a past life of b' then only one of b and b' is active *	(too hard to check) */static intwalkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize], int type, u32int tag,	u32int epoch){	int i, ret;	u32int addr, ep;	Block *bb;	Entry e; 	if(b && chk->walkdepth == 0 && chk->printblocks)		chk->print("%V %d %#.8ux %#.8ux\n", b->score, b->l.type,			b->l.tag, b->l.epoch); 	if(!chk->useventi && globalToLocal(score) == NilBlock)		return 1; 	chk->walkdepth++; 	bb = cacheGlobal(chk->cache, score, type, tag, OReadOnly);	if(bb == nil){		error(chk, "could not load block %V type %d tag %ux: %R",			score, type, tag);		chk->walkdepth--;		return 0;	}	if(chk->printblocks)		chk->print("%*s%V %d %#.8ux %#.8ux\n", chk->walkdepth*2, "",			score, type, tag, bb->l.epoch); 	ret = 0;	addr = globalToLocal(score);	if(addr == NilBlock){		ret = 1;		goto Exit;	} 	if(b){		/* (i) */		if(b->l.epoch < bb->l.epoch || bb->l.epochClose <= b->l.epoch){			error(chk, "walk: block %#ux [%ud, %ud) points at %#ux [%ud, %ud)",				b->addr, b->l.epoch, b->l.epochClose,				bb->addr, bb->l.epoch, bb->l.epochClose);			goto Exit;		} 		/* (ii) */		if(b->l.epoch == epoch && bb->l.epoch == epoch){			if(getBit(chk->emap, addr)){				error(chk, "walk: epoch join detected: addr %#ux %L",					bb->addr, &bb->l);				goto Exit;			}			setBit(chk->emap, addr);		} 		/* (iii) */		if(!(b->l.state&BsCopied) && b->l.epoch == bb->l.epoch){			if(getBit(chk->xmap, addr)){				error(chk, "walk: copy join detected; addr %#ux %L",					bb->addr, &bb->l);				goto Exit;			}			setBit(chk->xmap, addr);		}	} 	/* (iv) */	if(epoch == chk->fs->ehi){		/*		 * since epoch==fs->ehi is first, amap is same as		 * ``have seen active''		 */		if(getBit(chk->amap, addr)){			error(chk, "walk: active join detected: addr %#ux %L",				bb->addr, &bb->l);			goto Exit;		}		if(bb->l.state&BsClosed)			error(chk, "walk: addr %#ux: block is in active tree but is closed",				addr);	}else		if(!getBit(chk->amap, addr))			if(!(bb->l.state&BsClosed)){				// error(chk, "walk: addr %#ux: block is not in active tree, not closed (%d)",				// addr, bb->l.epochClose);				chk->close(chk, bb, epoch+1);				chk->nclose++;			} 	if(getBit(chk->amap, addr)){		ret = 1;		goto Exit;	}	setBit(chk->amap, addr); 	if(chk->nseen++%chk->quantum == 0)		chk->print("check: visited %d/%d blocks (%.0f%%)\n",			chk->nseen, chk->nblocks, chk->nseen*100./chk->nblocks); 	b = nil;		/* make sure no more refs to parent */	USED(b); 	switch(type){	default:		/* pointer block */		for(i = 0; i < chk->bsize/VtScoreSize; i++)			if(!walkEpoch(chk, bb, bb->data + i*VtScoreSize,			    type-1, tag, epoch)){				setBit(chk->errmap, bb->addr);				chk->clrp(chk, bb, i);				chk->nclrp++;			}		break;	case BtData:		break;	case BtDir:		for(i = 0; i < chk->bsize/VtEntrySize; i++){			if(!entryUnpack(&e, bb->data, i)){				// error(chk, "walk: could not unpack entry: %ux[%d]: %R",				//	addr, i);				setBit(chk->errmap, bb->addr);				chk->clre(chk, bb, i);				chk->nclre++;				continue;			}			if(!(e.flags & VtEntryActive))				continue;if(0)			fprint(2, "%x[%d] tag=%x snap=%d score=%V\n",				addr, i, e.tag, e.snap, e.score);			ep = epoch;			if(e.snap != 0){				if(e.snap >= epoch){					// error(chk, "bad snap in entry: %ux[%d] snap = %ud: epoch = %ud",					//	addr, i, e.snap, epoch);					setBit(chk->errmap, bb->addr);					chk->clre(chk, bb, i);					chk->nclre++;					continue;				}				continue;			}			if(e.flags & VtEntryLocal){				if(e.tag < UserTag)				if(e.tag != RootTag || tag != RootTag || i != 1){					// error(chk, "bad tag in entry: %ux[%d] tag = %ux",					//	addr, i, e.tag);					setBit(chk->errmap, bb->addr);					chk->clre(chk, bb, i);					chk->nclre++;					continue;				}			}else				if(e.tag != 0){					// error(chk, "bad tag in entry: %ux[%d] tag = %ux",					//	addr, i, e.tag);					setBit(chk->errmap, bb->addr);					chk->clre(chk, bb, i);					chk->nclre++;					continue;				}			if(!walkEpoch(chk, bb, e.score, entryType(&e),			    e.tag, ep)){				setBit(chk->errmap, bb->addr);				chk->clre(chk, bb, i);				chk->nclre++;			}		}		break;	} 	ret = 1; Exit:	chk->walkdepth--;	blockPut(bb);	return ret;} /* * We've just walked the whole write buffer.  Notice blocks that * aren't marked available but that we didn't visit.  They are lost. */static voidcheckLeak(Fsck *chk){	u32int a, nfree, nlost;	Block *b;	Label l; 	nfree = 0;	nlost = 0; 	for(a = 0; a < chk->nblocks; a++){		if(!readLabel(chk->cache, &l, a)){			error(chk, "could not read label: addr 0x%ux %d %d: %R",				a, l.type, l.state);			continue;		}		if(getBit(chk->amap, a))			continue;		if(l.state == BsFree || l.epochClose <= chk->fs->elo ||		    l.epochClose == l.epoch){			nfree++;			setBit(chk->amap, a);			continue;		}		if(l.state&BsClosed)			continue;		nlost++;//		warn(chk, "unreachable block: addr 0x%ux type %d tag 0x%ux "//			"state %s epoch %ud close %ud", a, l.type, l.tag,//			bsStr(l.state), l.epoch, l.epochClose);		b = cacheLocal(chk->cache, PartData, a, OReadOnly);		if(b == nil){			error(chk, "could not read block 0x%#.8ux", a);			continue;		}		chk->close(chk, b, 0);		chk->nclose++;		setBit(chk->amap, a);		blockPut(b);	}	chk->print("fsys blocks: total=%ud used=%ud(%.1f%%) free=%ud(%.1f%%) lost=%ud(%.1f%%)\n",		chk->nblocks,		chk->nblocks - nfree-nlost,		100.*(chk->nblocks - nfree - nlost)/chk->nblocks,		nfree, 100.*nfree/chk->nblocks,		nlost, 100.*nlost/chk->nblocks);}  /* * Check that all sources in the tree are accessible. */static Source *openSource(Fsck *chk, Source *s, char *name, uchar *bm, u32int offset,	u32int gen, int dir, MetaBlock *mb, int i, Block *b){	Source *r; 	r = nil;	if(getBit(bm, offset)){		warn(chk, "multiple references to source: %s -> %d",			name, offset);		goto Err;	}	setBit(bm, offset); 	r = sourceOpen(s, offset, OReadOnly, 0);	if(r == nil){		warn(chk, "could not open source: %s -> %d: %R", name, offset);		goto Err;	} 	if(r->gen != gen){		warn(chk, "source has been removed: %s -> %d", name, offset);		goto Err;	} 	if(r->dir != dir){		warn(chk, "dir mismatch: %s -> %d", name, offset);		goto Err;	}	return r;Err:	chk->clri(chk, name, mb, i, b);	chk->nclri++;	if(r)		sourceClose(r);	return nil;} typedef struct MetaChunk MetaChunk;struct MetaChunk {	ushort	offset;	ushort	size;	ushort	index;}; static intoffsetCmp(void *s0, void *s1){	MetaChunk *mc0, *mc1; 	mc0 = s0;	mc1 = s1;	if(mc0->offset < mc1->offset)		return -1;	if(mc0->offset > mc1->offset)		return 1;	return 0;} /* * Fsck that MetaBlock has reasonable header, sorted entries, */static intchkMetaBlock(MetaBlock *mb){	MetaChunk *mc;	int oo, o, n, i;	uchar *p; 	mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));	p = mb->buf + MetaHeaderSize;	for(i = 0; i < mb->nindex; i++){		mc[i].offset = p[0]<<8 | p[1];		mc[i].size =   p[2]<<8 | p[3];		mc[i].index = i;		p += MetaIndexSize;	} 	qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp); 	/* check block looks ok */	oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;	o = oo;	n = 0;	for(i = 0; i < mb->nindex; i++){		o = mc[i].offset;		n = mc[i].size;		if(o < oo)			goto Err;

Lines 1–500 of 808.