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