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