plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

check.c (17620B)


      1 #include "stdinc.h"
      2 #include "dat.h"
      3 #include "fns.h"
      4 
      5 static void	checkDirs(Fsck*);
      6 static void	checkEpochs(Fsck*);
      7 static void	checkLeak(Fsck*);
      8 static void	closenop(Fsck*, Block*, u32int);
      9 static void	clrenop(Fsck*, Block*, int);
     10 static void	clrinop(Fsck*, char*, MetaBlock*, int, Block*);
     11 static void	error(Fsck*, char*, ...);
     12 static int	getBit(uchar*, u32int);
     13 static int	printnop(char*, ...);
     14 static void	setBit(uchar*, u32int);
     15 static int	walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize],
     16 			int type, u32int tag, u32int epoch);
     17 static void	warn(Fsck*, char*, ...);
     18 
     19 #pragma varargck argpos error 2
     20 #pragma varargck argpos printnop 1
     21 #pragma varargck argpos warn 2
     22 
     23 static Fsck*
     24 checkInit(Fsck *chk)
     25 {
     26 	chk->cache = chk->fs->cache;
     27 	chk->nblocks = cacheLocalSize(chk->cache, PartData);;
     28 	chk->bsize = chk->fs->blockSize;
     29 	chk->walkdepth = 0;
     30 	chk->hint = 0;
     31 	chk->quantum = chk->nblocks/100;
     32 	if(chk->quantum == 0)
     33 		chk->quantum = 1;
     34 	if(chk->print == nil)
     35 		chk->print = printnop;
     36 	if(chk->clre == nil)
     37 		chk->clre = clrenop;
     38 	if(chk->close == nil)
     39 		chk->close = closenop;
     40 	if(chk->clri == nil)
     41 		chk->clri = clrinop;
     42 	return chk;
     43 }
     44 
     45 /*
     46  * BUG: Should merge checkEpochs and checkDirs so that
     47  * bad blocks are only reported once, and so that errors in checkEpochs
     48  * can have the affected file names attached, and so that the file system
     49  * is only read once.
     50  *
     51  * Also should summarize the errors instead of printing for every one
     52  * (e.g., XXX bad or unreachable blocks in /active/usr/rsc/foo).
     53  */
     54 
     55 void
     56 fsCheck(Fsck *chk)
     57 {
     58 	Block *b;
     59 	Super super;
     60 
     61 	checkInit(chk);
     62 	b = superGet(chk->cache, &super);
     63 	if(b == nil){
     64 		chk->print("could not load super block: %r");
     65 		return;
     66 	}
     67 	blockPut(b);
     68 
     69 	chk->hint = super.active;
     70 	checkEpochs(chk);
     71 
     72 	chk->smap = vtmallocz(chk->nblocks/8+1);
     73 	checkDirs(chk);
     74 	vtfree(chk->smap);
     75 }
     76 
     77 static void checkEpoch(Fsck*, u32int);
     78 
     79 /*
     80  * Walk through all the blocks in the write buffer.
     81  * Then we can look for ones we missed -- those are leaks.
     82  */
     83 static void
     84 checkEpochs(Fsck *chk)
     85 {
     86 	u32int e;
     87 	uint nb;
     88 
     89 	nb = chk->nblocks;
     90 	chk->amap = vtmallocz(nb/8+1);
     91 	chk->emap = vtmallocz(nb/8+1);
     92 	chk->xmap = vtmallocz(nb/8+1);
     93 	chk->errmap = vtmallocz(nb/8+1);
     94 
     95 	for(e = chk->fs->ehi; e >= chk->fs->elo; e--){
     96 		memset(chk->emap, 0, chk->nblocks/8+1);
     97 		memset(chk->xmap, 0, chk->nblocks/8+1);
     98 		checkEpoch(chk, e);
     99 	}
    100 	checkLeak(chk);
    101 	vtfree(chk->amap);
    102 	vtfree(chk->emap);
    103 	vtfree(chk->xmap);
    104 	vtfree(chk->errmap);
    105 }
    106 
    107 static void
    108 checkEpoch(Fsck *chk, u32int epoch)
    109 {
    110 	u32int a;
    111 	Block *b;
    112 	Entry e;
    113 	Label l;
    114 
    115 	chk->print("checking epoch %ud...\n", epoch);
    116 
    117 	for(a=0; a<chk->nblocks; a++){
    118 		if(!readLabel(chk->cache, &l, (a+chk->hint)%chk->nblocks)){
    119 			error(chk, "could not read label for addr 0x%.8#ux", a);
    120 			continue;
    121 		}
    122 		if(l.tag == RootTag && l.epoch == epoch)
    123 			break;
    124 	}
    125 
    126 	if(a == chk->nblocks){
    127 		chk->print("could not find root block for epoch %ud", epoch);
    128 		return;
    129 	}
    130 
    131 	a = (a+chk->hint)%chk->nblocks;
    132 	b = cacheLocalData(chk->cache, a, BtDir, RootTag, OReadOnly, 0);
    133 	if(b == nil){
    134 		error(chk, "could not read root block 0x%.8#ux: %r", a);
    135 		return;
    136 	}
    137 
    138 	/* no one should point at root blocks */
    139 	setBit(chk->amap, a);
    140 	setBit(chk->emap, a);
    141 	setBit(chk->xmap, a);
    142 
    143 	/*
    144 	 * First entry is the rest of the file system.
    145 	 * Second entry is link to previous epoch root,
    146 	 * just a convenience to help the search.
    147 	 */
    148 	if(!entryUnpack(&e, b->data, 0)){
    149 		error(chk, "could not unpack root block 0x%.8#ux: %r", a);
    150 		blockPut(b);
    151 		return;
    152 	}
    153 	walkEpoch(chk, b, e.score, BtDir, e.tag, epoch);
    154 	if(entryUnpack(&e, b->data, 1))
    155 		chk->hint = globalToLocal(e.score);
    156 	blockPut(b);
    157 }
    158 
    159 /*
    160  * When b points at bb, need to check:
    161  *
    162  * (i) b.e in [bb.e, bb.eClose)
    163  * (ii) if b.e==bb.e,  then no other b' in e points at bb.
    164  * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb.
    165  * (iv) if b is active then no other active b' points at bb.
    166  * (v) if b is a past life of b' then only one of b and b' is active
    167  *	(too hard to check)
    168  */
    169 static int
    170 walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize], int type, u32int tag,
    171 	u32int epoch)
    172 {
    173 	int i, ret;
    174 	u32int addr, ep;
    175 	Block *bb;
    176 	Entry e;
    177 
    178 	if(b && chk->walkdepth == 0 && chk->printblocks)
    179 		chk->print("%V %d %#.8ux %#.8ux\n", b->score, b->l.type,
    180 			b->l.tag, b->l.epoch);
    181 
    182 	if(!chk->useventi && globalToLocal(score) == NilBlock)
    183 		return 1;
    184 
    185 	chk->walkdepth++;
    186 
    187 	bb = cacheGlobal(chk->cache, score, type, tag, OReadOnly);
    188 	if(bb == nil){
    189 		error(chk, "could not load block %V type %d tag %ux: %r",
    190 			score, type, tag);
    191 		chk->walkdepth--;
    192 		return 0;
    193 	}
    194 	if(chk->printblocks)
    195 		chk->print("%*s%V %d %#.8ux %#.8ux\n", chk->walkdepth*2, "",
    196 			score, type, tag, bb->l.epoch);
    197 
    198 	ret = 0;
    199 	addr = globalToLocal(score);
    200 	if(addr == NilBlock){
    201 		ret = 1;
    202 		goto Exit;
    203 	}
    204 
    205 	if(b){
    206 		/* (i) */
    207 		if(b->l.epoch < bb->l.epoch || bb->l.epochClose <= b->l.epoch){
    208 			error(chk, "walk: block %#ux [%ud, %ud) points at %#ux [%ud, %ud)",
    209 				b->addr, b->l.epoch, b->l.epochClose,
    210 				bb->addr, bb->l.epoch, bb->l.epochClose);
    211 			goto Exit;
    212 		}
    213 
    214 		/* (ii) */
    215 		if(b->l.epoch == epoch && bb->l.epoch == epoch){
    216 			if(getBit(chk->emap, addr)){
    217 				error(chk, "walk: epoch join detected: addr %#ux %L",
    218 					bb->addr, &bb->l);
    219 				goto Exit;
    220 			}
    221 			setBit(chk->emap, addr);
    222 		}
    223 
    224 		/* (iii) */
    225 		if(!(b->l.state&BsCopied) && b->l.epoch == bb->l.epoch){
    226 			if(getBit(chk->xmap, addr)){
    227 				error(chk, "walk: copy join detected; addr %#ux %L",
    228 					bb->addr, &bb->l);
    229 				goto Exit;
    230 			}
    231 			setBit(chk->xmap, addr);
    232 		}
    233 	}
    234 
    235 	/* (iv) */
    236 	if(epoch == chk->fs->ehi){
    237 		/*
    238 		 * since epoch==fs->ehi is first, amap is same as
    239 		 * ``have seen active''
    240 		 */
    241 		if(getBit(chk->amap, addr)){
    242 			error(chk, "walk: active join detected: addr %#ux %L",
    243 				bb->addr, &bb->l);
    244 			goto Exit;
    245 		}
    246 		if(bb->l.state&BsClosed)
    247 			error(chk, "walk: addr %#ux: block is in active tree but is closed",
    248 				addr);
    249 	}else
    250 		if(!getBit(chk->amap, addr))
    251 			if(!(bb->l.state&BsClosed)){
    252 				// error(chk, "walk: addr %#ux: block is not in active tree, not closed (%d)",
    253 				// addr, bb->l.epochClose);
    254 				chk->close(chk, bb, epoch+1);
    255 				chk->nclose++;
    256 			}
    257 
    258 	if(getBit(chk->amap, addr)){
    259 		ret = 1;
    260 		goto Exit;
    261 	}
    262 	setBit(chk->amap, addr);
    263 
    264 	if(chk->nseen++%chk->quantum == 0)
    265 		chk->print("check: visited %d/%d blocks (%.0f%%)\n",
    266 			chk->nseen, chk->nblocks, chk->nseen*100./chk->nblocks);
    267 
    268 	b = nil;		/* make sure no more refs to parent */
    269 	USED(b);
    270 
    271 	switch(type){
    272 	default:
    273 		/* pointer block */
    274 		for(i = 0; i < chk->bsize/VtScoreSize; i++)
    275 			if(!walkEpoch(chk, bb, bb->data + i*VtScoreSize,
    276 			    type-1, tag, epoch)){
    277 				setBit(chk->errmap, bb->addr);
    278 				chk->clrp(chk, bb, i);
    279 				chk->nclrp++;
    280 			}
    281 		break;
    282 	case BtData:
    283 		break;
    284 	case BtDir:
    285 		for(i = 0; i < chk->bsize/VtEntrySize; i++){
    286 			if(!entryUnpack(&e, bb->data, i)){
    287 				// error(chk, "walk: could not unpack entry: %ux[%d]: %r",
    288 				//	addr, i);
    289 				setBit(chk->errmap, bb->addr);
    290 				chk->clre(chk, bb, i);
    291 				chk->nclre++;
    292 				continue;
    293 			}
    294 			if(!(e.flags & VtEntryActive))
    295 				continue;
    296 if(0)			fprint(2, "%x[%d] tag=%x snap=%d score=%V\n",
    297 				addr, i, e.tag, e.snap, e.score);
    298 			ep = epoch;
    299 			if(e.snap != 0){
    300 				if(e.snap >= epoch){
    301 					// error(chk, "bad snap in entry: %ux[%d] snap = %ud: epoch = %ud",
    302 					//	addr, i, e.snap, epoch);
    303 					setBit(chk->errmap, bb->addr);
    304 					chk->clre(chk, bb, i);
    305 					chk->nclre++;
    306 					continue;
    307 				}
    308 				continue;
    309 			}
    310 			if(e.flags & VtEntryLocal){
    311 				if(e.tag < UserTag)
    312 				if(e.tag != RootTag || tag != RootTag || i != 1){
    313 					// error(chk, "bad tag in entry: %ux[%d] tag = %ux",
    314 					//	addr, i, e.tag);
    315 					setBit(chk->errmap, bb->addr);
    316 					chk->clre(chk, bb, i);
    317 					chk->nclre++;
    318 					continue;
    319 				}
    320 			}else
    321 				if(e.tag != 0){
    322 					// error(chk, "bad tag in entry: %ux[%d] tag = %ux",
    323 					//	addr, i, e.tag);
    324 					setBit(chk->errmap, bb->addr);
    325 					chk->clre(chk, bb, i);
    326 					chk->nclre++;
    327 					continue;
    328 				}
    329 			if(!walkEpoch(chk, bb, e.score, entryType(&e),
    330 			    e.tag, ep)){
    331 				setBit(chk->errmap, bb->addr);
    332 				chk->clre(chk, bb, i);
    333 				chk->nclre++;
    334 			}
    335 		}
    336 		break;
    337 	}
    338 
    339 	ret = 1;
    340 
    341 Exit:
    342 	chk->walkdepth--;
    343 	blockPut(bb);
    344 	return ret;
    345 }
    346 
    347 /*
    348  * We've just walked the whole write buffer.  Notice blocks that
    349  * aren't marked available but that we didn't visit.  They are lost.
    350  */
    351 static void
    352 checkLeak(Fsck *chk)
    353 {
    354 	u32int a, nfree, nlost;
    355 	Block *b;
    356 	Label l;
    357 
    358 	nfree = 0;
    359 	nlost = 0;
    360 
    361 	for(a = 0; a < chk->nblocks; a++){
    362 		if(!readLabel(chk->cache, &l, a)){
    363 			error(chk, "could not read label: addr 0x%ux %d %d: %r",
    364 				a, l.type, l.state);
    365 			continue;
    366 		}
    367 		if(getBit(chk->amap, a))
    368 			continue;
    369 		if(l.state == BsFree || l.epochClose <= chk->fs->elo ||
    370 		    l.epochClose == l.epoch){
    371 			nfree++;
    372 			setBit(chk->amap, a);
    373 			continue;
    374 		}
    375 		if(l.state&BsClosed)
    376 			continue;
    377 		nlost++;
    378 //		warn(chk, "unreachable block: addr 0x%ux type %d tag 0x%ux "
    379 //			"state %s epoch %ud close %ud", a, l.type, l.tag,
    380 //			bsStr(l.state), l.epoch, l.epochClose);
    381 		b = cacheLocal(chk->cache, PartData, a, OReadOnly);
    382 		if(b == nil){
    383 			error(chk, "could not read block 0x%#.8ux", a);
    384 			continue;
    385 		}
    386 		chk->close(chk, b, 0);
    387 		chk->nclose++;
    388 		setBit(chk->amap, a);
    389 		blockPut(b);
    390 	}
    391 	chk->print("fsys blocks: total=%ud used=%ud(%.1f%%) free=%ud(%.1f%%) lost=%ud(%.1f%%)\n",
    392 		chk->nblocks,
    393 		chk->nblocks - nfree-nlost,
    394 		100.*(chk->nblocks - nfree - nlost)/chk->nblocks,
    395 		nfree, 100.*nfree/chk->nblocks,
    396 		nlost, 100.*nlost/chk->nblocks);
    397 }
    398 
    399 
    400 /*
    401  * Check that all sources in the tree are accessible.
    402  */
    403 static Source *
    404 openSource(Fsck *chk, Source *s, char *name, uchar *bm, u32int offset,
    405 	u32int gen, int dir, MetaBlock *mb, int i, Block *b)
    406 {
    407 	Source *r;
    408 
    409 	r = nil;
    410 	if(getBit(bm, offset)){
    411 		warn(chk, "multiple references to source: %s -> %d",
    412 			name, offset);
    413 		goto Err;
    414 	}
    415 	setBit(bm, offset);
    416 
    417 	r = sourceOpen(s, offset, OReadOnly, 0);
    418 	if(r == nil){
    419 		warn(chk, "could not open source: %s -> %d: %r", name, offset);
    420 		goto Err;
    421 	}
    422 
    423 	if(r->gen != gen){
    424 		warn(chk, "source has been removed: %s -> %d", name, offset);
    425 		goto Err;
    426 	}
    427 
    428 	if(r->dir != dir){
    429 		warn(chk, "dir mismatch: %s -> %d", name, offset);
    430 		goto Err;
    431 	}
    432 	return r;
    433 Err:
    434 	chk->clri(chk, name, mb, i, b);
    435 	chk->nclri++;
    436 	if(r)
    437 		sourceClose(r);
    438 	return nil;
    439 }
    440 
    441 typedef struct MetaChunk MetaChunk;
    442 struct MetaChunk {
    443 	ushort	offset;
    444 	ushort	size;
    445 	ushort	index;
    446 };
    447 
    448 static int
    449 offsetCmp(const void *s0, const void *s1)
    450 {
    451 	MetaChunk *mc0, *mc1;
    452 
    453 	mc0 = (MetaChunk*)s0;
    454 	mc1 = (MetaChunk*)s1;
    455 	if(mc0->offset < mc1->offset)
    456 		return -1;
    457 	if(mc0->offset > mc1->offset)
    458 		return 1;
    459 	return 0;
    460 }
    461 
    462 /*
    463  * Fsck that MetaBlock has reasonable header, sorted entries,
    464  */
    465 static int
    466 chkMetaBlock(MetaBlock *mb)
    467 {
    468 	MetaChunk *mc;
    469 	int oo, o, n, i;
    470 	uchar *p;
    471 
    472 	mc = vtmalloc(mb->nindex*sizeof(MetaChunk));
    473 	p = mb->buf + MetaHeaderSize;
    474 	for(i = 0; i < mb->nindex; i++){
    475 		mc[i].offset = p[0]<<8 | p[1];
    476 		mc[i].size =   p[2]<<8 | p[3];
    477 		mc[i].index = i;
    478 		p += MetaIndexSize;
    479 	}
    480 
    481 	qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
    482 
    483 	/* check block looks ok */
    484 	oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
    485 	o = oo;
    486 	n = 0;
    487 	for(i = 0; i < mb->nindex; i++){
    488 		o = mc[i].offset;
    489 		n = mc[i].size;
    490 		if(o < oo)
    491 			goto Err;
    492 		oo += n;
    493 	}
    494 	if(o+n > mb->size || mb->size - oo != mb->free)
    495 		goto Err;
    496 
    497 	vtfree(mc);
    498 	return 1;
    499 
    500 Err:
    501 if(0){
    502 	fprint(2, "metaChunks failed!\n");
    503 	oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
    504 	for(i=0; i<mb->nindex; i++){
    505 		fprint(2, "\t%d: %d %d\n", i, mc[i].offset,
    506 			mc[i].offset + mc[i].size);
    507 		oo += mc[i].size;
    508 	}
    509 	fprint(2, "\tused=%d size=%d free=%d free2=%d\n",
    510 		oo, mb->size, mb->free, mb->size - oo);
    511 }
    512 	vtfree(mc);
    513 	return 0;
    514 }
    515 
    516 static void
    517 scanSource(Fsck *chk, char *name, Source *r)
    518 {
    519 	u32int a, nb, o;
    520 	Block *b;
    521 	Entry e;
    522 
    523 	if(!chk->useventi && globalToLocal(r->score)==NilBlock)
    524 		return;
    525 	if(!sourceGetEntry(r, &e)){
    526 		error(chk, "could not get entry for %s", name);
    527 		return;
    528 	}
    529 	a = globalToLocal(e.score);
    530 	if(!chk->useventi && a==NilBlock)
    531 		return;
    532 	if(getBit(chk->smap, a))
    533 		return;
    534 	setBit(chk->smap, a);
    535 
    536 	nb = (sourceGetSize(r) + r->dsize-1) / r->dsize;
    537 	for(o = 0; o < nb; o++){
    538 		b = sourceBlock(r, o, OReadOnly);
    539 		if(b == nil){
    540 			error(chk, "could not read block in data file %s", name);
    541 			continue;
    542 		}
    543 		if(b->addr != NilBlock && getBit(chk->errmap, b->addr)){
    544 			warn(chk, "previously reported error in block %ux is in file %s",
    545 				b->addr, name);
    546 		}
    547 		blockPut(b);
    548 	}
    549 }
    550 
    551 /*
    552  * Walk the source tree making sure that the BtData
    553  * sources containing directory entries are okay.
    554  */
    555 static void
    556 chkDir(Fsck *chk, char *name, Source *source, Source *meta)
    557 {
    558 	int i;
    559 	u32int a1, a2, nb, o;
    560 	char *s, *nn;
    561 	uchar *bm;
    562 	Block *b, *bb;
    563 	DirEntry de;
    564 	Entry e1, e2;
    565 	MetaBlock mb;
    566 	MetaEntry me;
    567 	Source *r, *mr;
    568 
    569 	if(!chk->useventi && globalToLocal(source->score)==NilBlock &&
    570 	    globalToLocal(meta->score)==NilBlock)
    571 		return;
    572 
    573 	if(!sourceLock2(source, meta, OReadOnly)){
    574 		warn(chk, "could not lock sources for %s: %r", name);
    575 		return;
    576 	}
    577 	if(!sourceGetEntry(source, &e1) || !sourceGetEntry(meta, &e2)){
    578 		warn(chk, "could not load entries for %s: %r", name);
    579 		return;
    580 	}
    581 	a1 = globalToLocal(e1.score);
    582 	a2 = globalToLocal(e2.score);
    583 	if((!chk->useventi && a1==NilBlock && a2==NilBlock)
    584 	|| (getBit(chk->smap, a1) && getBit(chk->smap, a2))){
    585 		sourceUnlock(source);
    586 		sourceUnlock(meta);
    587 		return;
    588 	}
    589 	setBit(chk->smap, a1);
    590 	setBit(chk->smap, a2);
    591 
    592 	bm = vtmallocz(sourceGetDirSize(source)/8 + 1);
    593 
    594 	nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
    595 	for(o = 0; o < nb; o++){
    596 		b = sourceBlock(meta, o, OReadOnly);
    597 		if(b == nil){
    598 			error(chk, "could not read block in meta file: %s[%ud]: %r",
    599 				name, o);
    600 			continue;
    601 		}
    602 if(0)		fprint(2, "source %V:%d block %d addr %d\n", source->score,
    603 			source->offset, o, b->addr);
    604 		if(b->addr != NilBlock && getBit(chk->errmap, b->addr))
    605 			warn(chk, "previously reported error in block %ux is in %s",
    606 				b->addr, name);
    607 
    608 		if(!mbUnpack(&mb, b->data, meta->dsize)){
    609 			error(chk, "could not unpack meta block: %s[%ud]: %r",
    610 				name, o);
    611 			blockPut(b);
    612 			continue;
    613 		}
    614 		if(!chkMetaBlock(&mb)){
    615 			error(chk, "bad meta block: %s[%ud]: %r", name, o);
    616 			blockPut(b);
    617 			continue;
    618 		}
    619 		s = nil;
    620 		for(i=mb.nindex-1; i>=0; i--){
    621 			meUnpack(&me, &mb, i);
    622 			if(!deUnpack(&de, &me)){
    623 				error(chk,
    624 				  "could not unpack dir entry: %s[%ud][%d]: %r",
    625 					name, o, i);
    626 				continue;
    627 			}
    628 			if(s && strcmp(s, de.elem) <= 0)
    629 				error(chk,
    630 			   "dir entry out of order: %s[%ud][%d] = %s last = %s",
    631 					name, o, i, de.elem, s);
    632 			vtfree(s);
    633 			s = vtstrdup(de.elem);
    634 			nn = smprint("%s/%s", name, de.elem);
    635 			if(nn == nil){
    636 				error(chk, "out of memory");
    637 				continue;
    638 			}
    639 			if(chk->printdirs)
    640 				if(de.mode&ModeDir)
    641 					chk->print("%s/\n", nn);
    642 			if(chk->printfiles)
    643 				if(!(de.mode&ModeDir))
    644 					chk->print("%s\n", nn);
    645 			if(!(de.mode & ModeDir)){
    646 				r = openSource(chk, source, nn, bm, de.entry,
    647 					de.gen, 0, &mb, i, b);
    648 				if(r != nil){
    649 					if(sourceLock(r, OReadOnly)){
    650 						scanSource(chk, nn, r);
    651 						sourceUnlock(r);
    652 					}
    653 					sourceClose(r);
    654 				}
    655 				deCleanup(&de);
    656 				free(nn);
    657 				continue;
    658 			}
    659 
    660 			r = openSource(chk, source, nn, bm, de.entry,
    661 				de.gen, 1, &mb, i, b);
    662 			if(r == nil){
    663 				deCleanup(&de);
    664 				free(nn);
    665 				continue;
    666 			}
    667 
    668 			mr = openSource(chk, source, nn, bm, de.mentry,
    669 				de.mgen, 0, &mb, i, b);
    670 			if(mr == nil){
    671 				sourceClose(r);
    672 				deCleanup(&de);
    673 				free(nn);
    674 				continue;
    675 			}
    676 
    677 			if(!(de.mode&ModeSnapshot) || chk->walksnapshots)
    678 				chkDir(chk, nn, r, mr);
    679 
    680 			sourceClose(mr);
    681 			sourceClose(r);
    682 			deCleanup(&de);
    683 			free(nn);
    684 			deCleanup(&de);
    685 
    686 		}
    687 		vtfree(s);
    688 		blockPut(b);
    689 	}
    690 
    691 	nb = sourceGetDirSize(source);
    692 	for(o=0; o<nb; o++){
    693 		if(getBit(bm, o))
    694 			continue;
    695 		r = sourceOpen(source, o, OReadOnly, 0);
    696 		if(r == nil)
    697 			continue;
    698 		warn(chk, "non referenced entry in source %s[%d]", name, o);
    699 		if((bb = sourceBlock(source, o/(source->dsize/VtEntrySize),
    700 		    OReadOnly)) != nil){
    701 			if(bb->addr != NilBlock){
    702 				setBit(chk->errmap, bb->addr);
    703 				chk->clre(chk, bb, o%(source->dsize/VtEntrySize));
    704 				chk->nclre++;
    705 			}
    706 			blockPut(bb);
    707 		}
    708 		sourceClose(r);
    709 	}
    710 
    711 	sourceUnlock(source);
    712 	sourceUnlock(meta);
    713 	vtfree(bm);
    714 }
    715 
    716 static void
    717 checkDirs(Fsck *chk)
    718 {
    719 	Source *r, *mr;
    720 
    721 	sourceLock(chk->fs->source, OReadOnly);
    722 	r = sourceOpen(chk->fs->source, 0, OReadOnly, 0);
    723 	mr = sourceOpen(chk->fs->source, 1, OReadOnly, 0);
    724 	sourceUnlock(chk->fs->source);
    725 	chkDir(chk, "", r, mr);
    726 
    727 	sourceClose(r);
    728 	sourceClose(mr);
    729 }
    730 
    731 static void
    732 setBit(uchar *bmap, u32int addr)
    733 {
    734 	if(addr == NilBlock)
    735 		return;
    736 
    737 	bmap[addr>>3] |= 1 << (addr & 7);
    738 }
    739 
    740 static int
    741 getBit(uchar *bmap, u32int addr)
    742 {
    743 	if(addr == NilBlock)
    744 		return 0;
    745 
    746 	return (bmap[addr>>3] >> (addr & 7)) & 1;
    747 }
    748 
    749 static void
    750 error(Fsck *chk, char *fmt, ...)
    751 {
    752 	char buf[256];
    753 	va_list arg;
    754 
    755 	va_start(arg, fmt);
    756 	vseprint(buf, buf+sizeof buf, fmt, arg);
    757 	va_end(arg);
    758 
    759 	chk->print("error: %s\n", buf);
    760 
    761 //	if(nerr++ > 20)
    762 //		sysfatal("too many errors");
    763 }
    764 
    765 static void
    766 warn(Fsck *chk, char *fmt, ...)
    767 {
    768 	char buf[256];
    769 	va_list arg;
    770 
    771 	va_start(arg, fmt);
    772 	vseprint(buf, buf+sizeof buf, fmt, arg);
    773 	va_end(arg);
    774 
    775 	chk->print("error: %s\n", buf);
    776 }
    777 
    778 static void
    779 clrenop(Fsck *chk, Block *b, int i)
    780 {
    781 	USED(chk);
    782 	USED(b);
    783 	USED(i);
    784 }
    785 
    786 static void
    787 closenop(Fsck *chk, Block *b, u32int i)
    788 {
    789 	USED(chk);
    790 	USED(b);
    791 	USED(i);
    792 }
    793 
    794 static void
    795 clrinop(Fsck *chk, char *c, MetaBlock *mb, int i, Block *b)
    796 {
    797 	USED(chk);
    798 	USED(c);
    799 	USED(mb);
    800 	USED(i);
    801 	USED(b);
    802 }
    803 
    804 static int
    805 printnop(char *c, ...)
    806 {
    807 	USED(c);
    808 	return 0;
    809 }