Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 = 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
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
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(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
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
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 = 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
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
	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
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
781
clrenop(Fsck*, Block*, int)
782
{
783
}
784
 
785
static void
786
closenop(Fsck*, Block*, u32int)
787
{
788
}
789
 
790
static void
791
clrinop(Fsck*, char*, MetaBlock*, int, Block*)
792
{
793
}
794
 
795
static int
796
printnop(char*, ...)
797
{
798
	return 0;
799
}