xref: /plan9/sys/src/cmd/fossil/check.c (revision 0c6300e705c776baceaa75d1b64fc63b9564225a)
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*
checkInit(Fsck * chk)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
fsCheck(Fsck * chk)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 = vtMemAllocZ(chk->nblocks/8+1);
73 	checkDirs(chk);
74 	vtMemFree(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
checkEpochs(Fsck * chk)84 checkEpochs(Fsck *chk)
85 {
86 	u32int e;
87 	uint nb;
88 
89 	nb = chk->nblocks;
90 	chk->amap = vtMemAllocZ(nb/8+1);
91 	chk->emap = vtMemAllocZ(nb/8+1);
92 	chk->xmap = vtMemAllocZ(nb/8+1);
93 	chk->errmap = vtMemAllocZ(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 	vtMemFree(chk->amap);
102 	vtMemFree(chk->emap);
103 	vtMemFree(chk->xmap);
104 	vtMemFree(chk->errmap);
105 }
106 
107 static void
checkEpoch(Fsck * chk,u32int epoch)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
walkEpoch(Fsck * chk,Block * b,uchar score[VtScoreSize],int type,u32int tag,u32int epoch)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
checkLeak(Fsck * chk)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 *
openSource(Fsck * chk,Source * s,char * name,uchar * bm,u32int offset,u32int gen,int dir,MetaBlock * mb,int i,Block * b)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
offsetCmp(void * s0,void * s1)449 offsetCmp(void *s0, void *s1)
450 {
451 	MetaChunk *mc0, *mc1;
452 
453 	mc0 = s0;
454 	mc1 = 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
chkMetaBlock(MetaBlock * mb)466 chkMetaBlock(MetaBlock *mb)
467 {
468 	MetaChunk *mc;
469 	int oo, o, n, i;
470 	uchar *p;
471 
472 	mc = vtMemAlloc(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 	vtMemFree(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 	vtMemFree(mc);
513 	return 0;
514 }
515 
516 static void
scanSource(Fsck * chk,char * name,Source * r)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
chkDir(Fsck * chk,char * name,Source * source,Source * meta)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 = vtMemAllocZ(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 			vtMemFree(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 		vtMemFree(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 	vtMemFree(bm);
714 }
715 
716 static void
checkDirs(Fsck * chk)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
setBit(uchar * bmap,u32int addr)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
getBit(uchar * bmap,u32int addr)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
error(Fsck * chk,char * fmt,...)750 error(Fsck *chk, char *fmt, ...)
751 {
752 	char buf[256];
753 	va_list arg;
754 	static int nerr;
755 
756 	va_start(arg, fmt);
757 	vseprint(buf, buf+sizeof buf, fmt, arg);
758 	va_end(arg);
759 
760 	chk->print("error: %s\n", buf);
761 
762 //	if(nerr++ > 20)
763 //		vtFatal("too many errors");
764 }
765 
766 static void
warn(Fsck * chk,char * fmt,...)767 warn(Fsck *chk, char *fmt, ...)
768 {
769 	char buf[256];
770 	va_list arg;
771 	static int nerr;
772 
773 	va_start(arg, fmt);
774 	vseprint(buf, buf+sizeof buf, fmt, arg);
775 	va_end(arg);
776 
777 	chk->print("error: %s\n", buf);
778 }
779 
780 static void
clrenop(Fsck *,Block *,int)781 clrenop(Fsck*, Block*, int)
782 {
783 }
784 
785 static void
closenop(Fsck *,Block *,u32int)786 closenop(Fsck*, Block*, u32int)
787 {
788 }
789 
790 static void
clrinop(Fsck *,char *,MetaBlock *,int,Block *)791 clrinop(Fsck*, char*, MetaBlock*, int, Block*)
792 {
793 }
794 
795 static int
printnop(char *,...)796 printnop(char*, ...)
797 {
798 	return 0;
799 }
800