xref: /plan9/sys/src/cmd/disk/kfs/chk.c (revision 7611b47f1229c7132642a0d31563e92d74d44f86)
1 #include	"all.h"
2 
3 #define	DSIZE		546000
4 #define	MAXDEPTH	100
5 
6 static	char*	abits;
7 static	long	sizabits;
8 static	char*	qbits;
9 static	long	sizqbits;
10 static	char*	name;
11 static	long	sizname;
12 static	long	fstart;
13 static	long	fsize;
14 static	long	nfiles;
15 static	long	maxq;
16 static	char*	fence;
17 static	char*	fencebase;
18 static	Device	dev;
19 static	long	ndup;
20 static	long	nused;
21 static	long	nfdup;
22 static	long	nqbad;
23 static	long	nfree;
24 static	long	nbad;
25 static	int	mod;
26 static	int	flags;
27 static	int	ronly;
28 static	int	cwflag;
29 static	long	sbaddr;
30 static	long	oldblock;
31 static	int	depth;
32 static	int	maxdepth;
33 
34 /* local prototypes */
35 static	int	fsck(Dentry*);
36 static	void	ckfreelist(Superb*);
37 static	void	mkfreelist(Superb*);
38 static	Dentry*	maked(long, int, long);
39 static	void	modd(long, int, Dentry*);
40 static	void	xread(long, long);
41 static	int	amark(long);
42 static	int	fmark(long);
43 static	void	missing(void);
44 static	void	qmark(long);
45 static	void*	zalloc(ulong);
46 static	void*	dalloc(ulong);
47 static	Iobuf*	xtag(long, int, long);
48 
49 static
50 void*
zalloc(ulong n)51 zalloc(ulong n)
52 {
53 	char *p;
54 
55 	p = malloc(n);
56 	if(p == 0)
57 		panic("zalloc: out of memory\n");
58 	memset(p, '\0', n);
59 	return p;
60 }
61 
62 static
63 void*
dalloc(ulong n)64 dalloc(ulong n)
65 {
66 	char *p;
67 
68 	if(fencebase == 0)
69 		fence = fencebase = zalloc(MAXDEPTH*sizeof(Dentry));
70 	p = fence;
71 	fence += n;
72 	if(fence > fencebase+MAXDEPTH*sizeof(Dentry))
73 		panic("dalloc too much memory\n");
74 	return p;
75 }
76 
77 void
check(Filsys * fs,long flag)78 check(Filsys *fs, long flag)
79 {
80 	Iobuf *p;
81 	Superb *sb;
82 	Dentry *d;
83 	long raddr;
84 	long nqid;
85 
86 	wlock(&mainlock);
87 	dev = fs->dev;
88 	flags = flag;
89 	fence = fencebase;
90 
91 	sizname = 4000;
92 	name = zalloc(sizname);
93 	sizname -= NAMELEN+10;	/* for safety */
94 
95 	sbaddr = superaddr(dev);
96 	raddr = getraddr(dev);
97 	p = xtag(sbaddr, Tsuper, QPSUPER);
98 	if(!p){
99 		cprint("bad superblock\n");
100 		goto out;
101 	}
102 	sb = (Superb*)p->iobuf;
103 	fstart = 1;
104 
105 	fsize = sb->fsize;
106 	sizabits = (fsize-fstart + 7)/8;
107 	abits = zalloc(sizabits);
108 
109 	nqid = sb->qidgen+100;		/* not as much of a botch */
110 	if(nqid > 1024*1024*8)
111 		nqid = 1024*1024*8;
112 	if(nqid < 64*1024)
113 		nqid = 64*1024;
114 
115 	sizqbits = (nqid+7)/8;
116 	qbits = zalloc(sizqbits);
117 
118 	mod = 0;
119 	nfree = 0;
120 	nfdup = 0;
121 	nused = 0;
122 	nbad = 0;
123 	ndup = 0;
124 	nqbad = 0;
125 	depth = 0;
126 	maxdepth = 0;
127 
128 	if(flags & Ctouch) {
129 		oldblock = fsize/DSIZE;
130 		oldblock *= DSIZE;
131 		if(oldblock < 0)
132 			oldblock = 0;
133 		cprint("oldblock = %ld\n", oldblock);
134 	}
135 	if(amark(sbaddr))
136 		{}
137 	if(cwflag) {
138 		if(amark(sb->roraddr))
139 			{}
140 		if(amark(sb->next))
141 			{}
142 	}
143 
144 	if(!(flags & Cquiet))
145 		cprint("checking file system: %s\n", fs->name);
146 	nfiles = 0;
147 	maxq = 0;
148 
149 	d = maked(raddr, 0, QPROOT);
150 	if(d) {
151 		if(amark(raddr))
152 			{}
153 		if(fsck(d))
154 			modd(raddr, 0, d);
155 		depth--;
156 		fence -= sizeof(Dentry);
157 		if(depth)
158 			cprint("depth not zero on return\n");
159 	}
160 
161 	if(flags & Cfree) {
162 		mkfreelist(sb);
163 		sb->qidgen = maxq;
164 		settag(p, Tsuper, QPNONE);
165 	}
166 
167 	if(sb->qidgen < maxq)
168 		cprint("qid generator low path=%ld maxq=%ld\n",
169 			sb->qidgen, maxq);
170 	if(!(flags & Cfree))
171 		ckfreelist(sb);
172 	if(mod) {
173 		cprint("file system was modified\n");
174 		settag(p, Tsuper, QPNONE);
175 	}
176 
177 	if(!(flags & Cquiet)){
178 		cprint("%8ld files\n", nfiles);
179 		cprint("%8ld blocks in the file system\n", fsize-fstart);
180 		cprint("%8ld used blocks\n", nused);
181 		cprint("%8ld free blocks\n", sb->tfree);
182 	}
183 	if(!(flags & Cfree)){
184 		if(nfree != sb->tfree)
185 			cprint("%8ld free blocks found\n", nfree);
186 		if(nfdup)
187 			cprint("%8ld blocks duplicated in the free list\n", nfdup);
188 		if(fsize-fstart-nused-nfree)
189 			cprint("%8ld missing blocks\n", fsize-fstart-nused-nfree);
190 	}
191 	if(ndup)
192 		cprint("%8ld address duplications\n", ndup);
193 	if(nbad)
194 		cprint("%8ld bad block addresses\n", nbad);
195 	if(nqbad)
196 		cprint("%8ld bad qids\n", nqbad);
197 	if(!(flags & Cquiet))
198 		cprint("%8ld maximum qid path\n", maxq);
199 	missing();
200 
201 out:
202 	if(p)
203 		putbuf(p);
204 	free(abits);
205 	free(name);
206 	free(qbits);
207 	wunlock(&mainlock);
208 }
209 
210 static
211 int
touch(long a)212 touch(long a)
213 {
214 	Iobuf *p;
215 
216 	if((flags&Ctouch) && a && a < oldblock){
217 		p = getbuf(dev, a, Bread|Bmod);
218 		if(p)
219 			putbuf(p);
220 		return 1;
221 	}
222 	return 0;
223 }
224 
225 static
226 int
checkdir(long a,long qpath)227 checkdir(long a, long qpath)
228 {
229 	Dentry *nd;
230 	int i, ns, dmod;
231 
232 	ns = strlen(name);
233 	dmod = touch(a);
234 	for(i=0; i<DIRPERBUF; i++) {
235 		nd = maked(a, i, qpath);
236 		if(!nd)
237 			break;
238 		if(fsck(nd)) {
239 			modd(a, i, nd);
240 			dmod++;
241 		}
242 		depth--;
243 		fence -= sizeof(Dentry);
244 		name[ns] = 0;
245 	}
246 	name[ns] = 0;
247 	return dmod;
248 }
249 
250 static
251 int
checkindir(long a,Dentry * d,long qpath)252 checkindir(long a, Dentry *d, long qpath)
253 {
254 	Iobuf *p;
255 	int i, dmod;
256 
257 	dmod = touch(a);
258 	p = xtag(a, Tind1, qpath);
259 	if(!p)
260 		return dmod;
261 	for(i=0; i<INDPERBUF; i++) {
262 		a = ((long*)p->iobuf)[i];
263 		if(!a)
264 			continue;
265 		if(amark(a)) {
266 			if(flags & Cbad) {
267 				((long*)p->iobuf)[i] = 0;
268 				p->flags |= Bmod;
269 			}
270 			continue;
271 		}
272 		if(d->mode & DDIR)
273 			dmod += checkdir(a, qpath);
274 		else if(flags & Crdall)
275 			xread(a, qpath);
276 	}
277 	putbuf(p);
278 	return dmod;
279 }
280 
281 static
282 int
fsck(Dentry * d)283 fsck(Dentry *d)
284 {
285 	char *s;
286 	Rune r;
287 	Iobuf *p;
288 	int l, i, ns, dmod;
289 	long a, qpath;
290 
291 	depth++;
292 	if(depth >= maxdepth){
293 		maxdepth = depth;
294 		if(maxdepth >= MAXDEPTH){
295 			cprint("max depth exceeded: %s\n", name);
296 			return 0;
297 		}
298 	}
299 	dmod = 0;
300 	if(!(d->mode & DALLOC))
301 		return 0;
302 	nfiles++;
303 
304 	ns = strlen(name);
305 	i = strlen(d->name);
306 	if(i >= NAMELEN){
307 		d->name[NAMELEN-1] = 0;
308 		cprint("%s->name (%s) not terminated\n", name, d->name);
309 		return 0;
310 	}
311 	ns += i;
312 	if(ns >= sizname){
313 		cprint("%s->name (%s) name too large\n", name, d->name);
314 		return 0;
315 	}
316 	for (s = d->name; *s; s += l){
317 		l = chartorune(&r, s);
318 		if (r == Runeerror)
319 			for (i = 0; i < l; i++){
320 				s[i] = '_';
321 				cprint("%s->name (%s) bad UTF\n", name, d->name);
322 				dmod++;
323 			}
324 	}
325 	strcat(name, d->name);
326 
327 	if(d->mode & DDIR){
328 		if(ns > 1)
329 			strcat(name, "/");
330 		if(flags & Cpdir)
331 			cprint("%s\n", name);
332 	} else
333 	if(flags & Cpfile)
334 		cprint("%s\n", name);
335 
336 	qpath = d->qid.path & ~QPDIR;
337 	qmark(qpath);
338 	if(qpath > maxq)
339 		maxq = qpath;
340 	for(i=0; i<NDBLOCK; i++) {
341 		a = d->dblock[i];
342 		if(!a)
343 			continue;
344 		if(amark(a)) {
345 			d->dblock[i] = 0;
346 			dmod++;
347 			continue;
348 		}
349 		if(d->mode & DDIR)
350 			dmod += checkdir(a, qpath);
351 		else if(flags & Crdall)
352 			xread(a, qpath);
353 	}
354 	a = d->iblock;
355 	if(a && amark(a)) {
356 		d->iblock = 0;
357 		dmod++;
358 	}
359 	else if(a)
360 		dmod += checkindir(a, d, qpath);
361 
362 	a = d->diblock;
363 	if(a && amark(a)) {
364 		d->diblock = 0;
365 		return dmod + 1;
366 	}
367 	dmod += touch(a);
368 	if(p = xtag(a, Tind2, qpath)){
369 		for(i=0; i<INDPERBUF; i++){
370 			a = ((long*)p->iobuf)[i];
371 			if(!a)
372 				continue;
373 			if(amark(a)) {
374 				if(flags & Cbad) {
375 					((long*)p->iobuf)[i] = 0;
376 					p->flags |= Bmod;
377 				}
378 				continue;
379 			}
380 			dmod += checkindir(a, d, qpath);
381 		}
382 		putbuf(p);
383 	}
384 	return dmod;
385 }
386 
387 static
388 void
ckfreelist(Superb * sb)389 ckfreelist(Superb *sb)
390 {
391 	long a, lo, hi;
392 	int n, i;
393 	Iobuf *p;
394 	Fbuf *fb;
395 
396 
397 	strcpy(name, "free list");
398 	cprint("check %s\n", name);
399 	fb = &sb->fbuf;
400 	a = sbaddr;
401 	p = 0;
402 	lo = 0;
403 	hi = 0;
404 	for(;;) {
405 		n = fb->nfree;
406 		if(n < 0 || n > FEPERBUF) {
407 			cprint("check: nfree bad %ld\n", a);
408 			break;
409 		}
410 		for(i=1; i<n; i++) {
411 			a = fb->free[i];
412 			if(a && !fmark(a)) {
413 				if(!lo || lo > a)
414 					lo = a;
415 				if(!hi || hi < a)
416 					hi = a;
417 			}
418 		}
419 		a = fb->free[0];
420 		if(!a)
421 			break;
422 		if(fmark(a))
423 			break;
424 		if(!lo || lo > a)
425 			lo = a;
426 		if(!hi || hi < a)
427 			hi = a;
428 		if(p)
429 			putbuf(p);
430 		p = xtag(a, Tfree, QPNONE);
431 		if(!p)
432 			break;
433 		fb = (Fbuf*)p->iobuf;
434 	}
435 	if(p)
436 		putbuf(p);
437 	cprint("lo = %ld; hi = %ld\n", lo, hi);
438 }
439 
440 /*
441  * make freelist from scratch
442  */
443 static
444 void
mkfreelist(Superb * sb)445 mkfreelist(Superb *sb)
446 {
447 	long a;
448 	int i, b;
449 
450 	strcpy(name, "free list");
451 	memset(&sb->fbuf, 0, sizeof(sb->fbuf));
452 	sb->fbuf.nfree = 1;
453 	sb->tfree = 0;
454 	for(a=fsize-fstart-1; a >= 0; a--) {
455 		i = a/8;
456 		if(i < 0 || i >= sizabits)
457 			continue;
458 		b = 1 << (a&7);
459 		if(abits[i] & b)
460 			continue;
461 		addfree(dev, fstart+a, sb);
462 		abits[i] |= b;
463 	}
464 }
465 
466 static
467 Dentry*
maked(long a,int s,long qpath)468 maked(long a, int s, long qpath)
469 {
470 	Iobuf *p;
471 	Dentry *d, *d1;
472 
473 	p = xtag(a, Tdir, qpath);
474 	if(!p)
475 		return 0;
476 	d = getdir(p, s);
477 	d1 = dalloc(sizeof(Dentry));
478 	memmove(d1, d, sizeof(Dentry));
479 	putbuf(p);
480 	return d1;
481 }
482 
483 static
484 void
modd(long a,int s,Dentry * d1)485 modd(long a, int s, Dentry *d1)
486 {
487 	Iobuf *p;
488 	Dentry *d;
489 
490 	if(!(flags & Cbad))
491 		return;
492 	p = getbuf(dev, a, Bread);
493 	d = getdir(p, s);
494 	if(!d) {
495 		if(p)
496 			putbuf(p);
497 		return;
498 	}
499 	memmove(d, d1, sizeof(Dentry));
500 	p->flags |= Bmod;
501 	putbuf(p);
502 }
503 
504 static
505 void
xread(long a,long qpath)506 xread(long a, long qpath)
507 {
508 	Iobuf *p;
509 
510 	p = xtag(a, Tfile, qpath);
511 	if(p)
512 		putbuf(p);
513 }
514 
515 static
516 Iobuf*
xtag(long a,int tag,long qpath)517 xtag(long a, int tag, long qpath)
518 {
519 	Iobuf *p;
520 
521 	if(a == 0)
522 		return 0;
523 	p = getbuf(dev, a, Bread);
524 	if(!p) {
525 		cprint("check: \"%s\": xtag: p null\n", name);
526 		if(flags & (Cream|Ctag)) {
527 			p = getbuf(dev, a, Bmod);
528 			if(p) {
529 				memset(p->iobuf, 0, RBUFSIZE);
530 				settag(p, tag, qpath);
531 				mod++;
532 				return p;
533 			}
534 		}
535 		return 0;
536 	}
537 	if(checktag(p, tag, qpath)) {
538 		cprint("check: \"%s\": xtag: checktag\n", name);
539 		if(flags & Cream)
540 			memset(p->iobuf, 0, RBUFSIZE);
541 		if(flags & (Cream|Ctag)) {
542 			settag(p, tag, qpath);
543 			mod++;
544 		}
545 		return p;
546 	}
547 	return p;
548 }
549 
550 static
551 int
amark(long a)552 amark(long a)
553 {
554 	long i;
555 	int b;
556 
557 	if(a < fstart || a >= fsize) {
558 		cprint("check: \"%s\": range %ld\n",
559 			name, a);
560 		nbad++;
561 		return 1;
562 	}
563 	a -= fstart;
564 	i = a/8;
565 	b = 1 << (a&7);
566 	if(abits[i] & b) {
567 		if(!ronly) {
568 			if(ndup < 10)
569 				cprint("check: \"%s\": address dup %ld\n",
570 					name, fstart+a);
571 			else
572 			if(ndup == 10)
573 				cprint("...");
574 		}
575 		ndup++;
576 		return 0;	/* really?? */
577 	}
578 	abits[i] |= b;
579 	nused++;
580 	return 0;
581 }
582 
583 static
584 int
fmark(long a)585 fmark(long a)
586 {
587 	long i;
588 	int b;
589 
590 	if(a < fstart || a >= fsize) {
591 		cprint("check: \"%s\": range %ld\n",
592 			name, a);
593 		nbad++;
594 		return 1;
595 	}
596 	a -= fstart;
597 	i = a/8;
598 	b = 1 << (a&7);
599 	if(abits[i] & b) {
600 		cprint("check: \"%s\": address dup %ld\n",
601 			name, fstart+a);
602 		nfdup++;
603 		return 1;
604 	}
605 	abits[i] |= b;
606 	nfree++;
607 	return 0;
608 }
609 
610 static
611 void
missing(void)612 missing(void)
613 {
614 	long a, i;
615 	int b, n;
616 
617 	n = 0;
618 	for(a=fsize-fstart-1; a>=0; a--) {
619 		i = a/8;
620 		b = 1 << (a&7);
621 		if(!(abits[i] & b)) {
622 			cprint("missing: %ld\n", fstart+a);
623 			n++;
624 		}
625 		if(n > 10) {
626 			cprint(" ...\n");
627 			break;
628 		}
629 	}
630 }
631 
632 static
633 void
qmark(long qpath)634 qmark(long qpath)
635 {
636 	int i, b;
637 
638 	i = qpath/8;
639 	b = 1 << (qpath&7);
640 	if(i < 0 || i >= sizqbits) {
641 		nqbad++;
642 		if(nqbad < 20)
643 			cprint("check: \"%s\": qid out of range %lux\n",
644 				name, qpath);
645 		return;
646 	}
647 	if((qbits[i] & b) && !ronly) {
648 		nqbad++;
649 		if(nqbad < 20)
650 			cprint("check: \"%s\": qid dup %lux\n",
651 				name, qpath);
652 	}
653 	qbits[i] |= b;
654 }
655