33 static Devsize sbaddr;
34 static Devsize oldblock;
38 static uchar *lowstack, *startstack;
40 /* local prototypes */
41 static int amark(Off);
42 static void* chkalloc(ulong);
43 static void ckfreelist(Superb*);
44 static int fmark(Off);
45 static int fsck(Dentry*);
46 static int ftest(Off);
47 static Dentry* maked(Off, int, Off);
48 static void missing(void);
49 static void mkfreelist(Superb*);
50 static void modd(Off, int, Dentry*);
51 static void qmark(Off);
52 static void trfreelist(Superb*);
53 static void xaddfree(Device*, Off, Superb*, Iobuf*);
54 static void xflush(Device*, Superb*, Iobuf*);
55 static void xread(Off, Off);
56 static Iobuf* xtag(Off, int, Off);
61 char *p = mallocz(n, 1);
63 panic("chkalloc: out of memory");
78 Crdall = (1<<0), /* read all files */
79 Ctag = (1<<1), /* rebuild tags */
80 Cpfile = (1<<2), /* print files */
81 Cpdir = (1<<3), /* print directories */
82 Cfree = (1<<4), /* rebuild free list */
83 Csetqid = (1<<5), /* resequence qids */
84 Cream = (1<<6), /* clear all bad tags */
85 Cbad = (1<<7), /* clear all bad blocks */
86 Ctouch = (1<<8), /* touch old dir and indir */
87 Ctrim = (1<<9), /* trim fsize back to fit when checking free list */
88 Crtmp = (1<<10), /* clear temporary files (after recover) */
100 // "setqid", Csetqid,
110 cmd_check(int argc, char *argv[])
120 for(i=1; i<argc; i++) {
121 for(f=0; ckoption[f].option; f++)
122 if(strcmp(argv[i], ckoption[f].option) == 0)
124 print("unknown check option %s\n", argv[i]);
125 for(f=0; ckoption[f].option; f++)
126 print("\t%s\n", ckoption[f].option);
129 flag |= ckoption[f].flag;
134 ronly = (dev->type == Devro);
135 cwflag = (dev->type == Devcw) | (dev->type == Devro);
137 wlock(&mainlock); /* check */
140 name = abits = qbits = nil; /* in case of goto */
141 sbaddr = superaddr(dev);
142 raddr = getraddr(dev);
143 p = xtag(sbaddr, Tsuper, QPSUPER);
146 sb = (Superb*)p->iobuf;
150 /* 200 is slop since qidgen is likely to be a little bit low */
151 sizqbits = (sb->qidgen+200 + 7) / 8;
152 qbits = chkalloc(sizqbits);
155 sizabits = (fsize-fstart + 7)/8;
156 abits = chkalloc(sizabits);
159 name = chkalloc(sizname);
160 sizname -= NAMELEN+10; /* for safety */
173 /* round fsize down to start of current side */
178 for (s = 0; dsize = wormsizeside(dev, s),
179 dsize > 0 && oldblock + dsize < fsize; s++)
181 print("oldblock = %lld\n", (Wideoff)oldblock);
189 print("checking filsys: %s\n", fs->name);
193 d = maked(raddr, 0, QPROOT);
201 print("depth not zero on return\n");
210 if(sb->qidgen < maxq)
211 print("qid generator low path=%lld maxq=%lld\n",
212 (Wideoff)sb->qidgen, (Wideoff)maxq);
217 print("file system was modified\n");
218 settag(p, Tsuper, QPNONE);
221 print("nfiles = %lld\n", (Wideoff)nfiles);
222 print("fsize = %lld\n", (Wideoff)fsize);
223 print("nused = %lld\n", (Wideoff)nused);
224 print("ndup = %lld\n", (Wideoff)ndup);
225 print("nfree = %lld\n", (Wideoff)nfree);
226 print("tfree = %lld\n", (Wideoff)sb->tfree);
227 print("nfdup = %lld\n", (Wideoff)nfdup);
228 print("nmiss = %lld\n", (Wideoff)fsize-fstart-nused-nfree);
229 print("nbad = %lld\n", (Wideoff)nbad);
230 print("nqbad = %lld\n", (Wideoff)nqbad);
231 print("maxq = %lld\n", (Wideoff)maxq);
232 print("base stack=%llud\n", (vlong)startstack);
233 /* high-water mark of stack usage */
234 print("high stack=%llud\n", (vlong)lowstack);
235 print("deepest recursion=%d\n", maxdepth-1); /* one-origin */
244 name = abits = qbits = nil;
250 * if *blkp is already allocated and Cbad is set, zero it.
251 * returns *blkp if it's free, else 0.
254 blkck(Off *blkp, int *flgp)
269 * if a block address within a Dentry, *blkp, is already allocated
270 * and Cbad is set, zero it.
271 * stores 0 into *resp if already allocated, else stores *blkp.
272 * returns dmod count.
275 daddrck(Off *blkp, Off *resp)
291 * under Ctouch, read block `a' if it's in range.
292 * returns dmod count.
297 if((flags&Ctouch) && a < oldblock) {
298 Iobuf *pd = getbuf(dev, a, Brd|Bmod);
308 * if d is a directory, touch it and check all its entries in block a.
309 * if not, under Crdall, read a.
310 * returns dmod count.
313 dirck(Extdentry *ed, Off a)
317 if(ed->d->mode & DDIR) {
319 for(k=0; k<DIRPERBUF; k++) {
320 Dentry *nd = maked(a, k, ed->qpath);
332 } else if(flags & Crdall)
338 * touch a, check a's tag for Tind1, Tind2, etc.
339 * if the tag is right, validate each block number in the indirect block,
340 * and check each block (mostly in case we are reading a huge directory).
343 indirck(Extdentry *ed, Off a, int tag)
351 if (p1 = xtag(a, tag, ed->qpath)) {
352 for(i=0; i<INDPERBUF; i++) {
353 a = blkck(&((Off *)p1->iobuf)[i], &p1->flags);
356 * check each block named in this
357 * indirect(^n) block (a).
360 dmod += dirck(ed, a);
362 dmod += indirck(ed, a, tag-1);
371 indiraddrck(Extdentry *ed, Off *indirp, int tag)
376 dmod = daddrck(indirp, &a);
377 return dmod + indirck(ed, a, tag);
380 /* if result is true, *d was modified */
388 if(depth >= maxdepth)
391 startstack = lowstack = (uchar *)&edent;
392 /* more precise check, assumes downward-growing stack */
393 if ((uchar *)&edent < lowstack)
394 lowstack = (uchar *)&edent;
396 /* check that entry is allocated */
397 if(!(d->mode & DALLOC) || (ronly && (d->mode & DTMP)))
401 /* we stash qpath & ns in an Extdentry for eventual use by dirck() */
402 memset(&edent, 0, sizeof edent);
406 edent.ns = strlen(name);
409 d->name[NAMELEN-1] = 0;
410 print("%s->name (%s) not terminated\n", name, d->name);
414 if(edent.ns >= sizname) {
415 print("%s->name (%s) name too large\n", name, d->name);
418 strcat(name, d->name);
428 } else if(flags & Cpfile) {
433 edent.qpath = d->qid.path;
435 edent.qpath ^= QPDIR;
437 if(edent.qpath > maxq)
440 /* clear temporary files (after recover) */
441 if((d->mode & DTMP) && (flags & Crtmp)){
442 for(i=0; i<NDBLOCK; i++)
444 for(i=0; i<NIBLOCK; i++)
448 /* if not directory, delete file */
449 if((d->mode & DDIR) == 0)
450 memset(d, 0, sizeof(Dentry));
455 /* check direct blocks (the common case) */
460 for(i=0; i<NDBLOCK; i++) {
461 dmod += daddrck(&d->dblock[i], &a);
463 dmod += dirck(&edent, a);
466 /* check indirect^n blocks, if any */
467 for (i = 0; i < NIBLOCK; i++)
468 dmod += indiraddrck(&edent, &d->iblocks[i], Tind1+i);
472 enum { XFEN = FEPERBUF + 6 };
482 xaddfree(Device *dev, Off a, Superb *sb, Iobuf *p)
486 x = (Xfree*)p->iobuf;
487 if(x->count < XFEN) {
488 x->addr[x->count] = a;
493 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
494 sb->fbuf.free[0] = 0L;
503 xflush(Device *dev, Superb *sb, Iobuf *p)
508 x = (Xfree*)p->iobuf;
510 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
511 sb->fbuf.free[0] = 0L;
515 for(i=0; i<x->count; i++)
516 addfree(dev, x->addr[i], sb);
521 * from existing freelist
525 trfreelist(Superb *sb)
533 xp = getbuf(devnone, Cckbuf, 0);
534 memset(xp->iobuf, 0, BUFSIZE);
539 if(n < 0 || n > FEPERBUF)
544 xaddfree(dev, a, sb, xp);
551 xaddfree(dev, a, sb, xp);
554 p = xtag(a, Tfree, QPNONE);
557 fb = (Fbuf*)p->iobuf;
564 print("%lld blocks free\n", (Wideoff)sb->tfree);
568 ckfreelist(Superb *sb)
575 strcpy(name, "free list");
576 print("check %s\n", name);
584 if(n < 0 || n > FEPERBUF) {
585 print("check: nfree bad %lld\n", (Wideoff)a);
608 p = xtag(a, Tfree, QPNONE);
611 fb = (Fbuf*)p->iobuf;
616 fsize = hi--; /* fsize = hi + 1 */
619 print("set fsize to %lld\n", (Wideoff)fsize);
621 print("lo = %lld; hi = %lld\n", (Wideoff)lo, (Wideoff)hi);
625 * make freelist from scratch
628 mkfreelist(Superb *sb)
634 print("cant make freelist on ronly device\n");
637 strcpy(name, "free list");
638 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
639 sb->fbuf.free[0] = 0L;
642 for(a=fsize-fstart-1; a >= 0; a--) {
644 if(i < 0 || i >= sizabits)
649 addfree(dev, fstart+a, sb);
651 print("%lld blocks free\n", (Wideoff)sb->tfree);
656 maked(Off a, int s, Off qpath)
661 p = xtag(a, Tdir, qpath);
665 d1 = chkalloc(sizeof(Dentry));
666 memmove(d1, d, sizeof(Dentry));
672 modd(Off a, int s, Dentry *d1)
677 if(!(flags & (Cbad|Crtmp)))
679 p = getbuf(dev, a, Brd);
686 memmove(d, d1, sizeof(Dentry));
692 xread(Off a, Off qpath)
696 p = xtag(a, Tfile, qpath);
702 xtag(Off a, int tag, Off qpath)
708 p = getbuf(dev, a, Brd);
710 print("check: \"%s\": xtag: p null\n", name);
711 if(flags & (Cream|Ctag)) {
712 p = getbuf(dev, a, Bmod);
714 memset(p->iobuf, 0, RBUFSIZE);
715 settag(p, tag, qpath);
722 if(checktag(p, tag, qpath)) {
723 print("check: \"%s\": xtag: checktag\n", name);
724 if(flags & (Cream|Ctag)) {
726 memset(p->iobuf, 0, RBUFSIZE);
727 settag(p, tag, qpath);
742 if(a < fstart || a >= fsize) {
745 print("check: \"%s\": range %lld\n",
756 print("check: \"%s\": address dup %lld\n",
757 name, (Wideoff)fstart+a);
774 if(a < fstart || a >= fsize) {
775 print("check: \"%s\": range %lld\n",
784 print("check: \"%s\": address dup %lld\n",
785 name, (Wideoff)fstart+a);
800 if(a < fstart || a >= fsize)
818 for(a=fsize-fstart-1; a>=0; a--) {
821 if(!(abits[i] & b)) {
822 print("missing: %lld\n", (Wideoff)fstart+a);
840 if(i < 0 || i >= sizqbits) {
843 print("check: \"%s\": qid out of range %llux\n",
844 name, (Wideoff)qpath);
847 if((qbits[i] & b) && !ronly) {
850 print("check: \"%s\": qid dup %llux\n", name,