ref: 04c4a9ca8b06e34a203f7ddacc2263cf2077e1aa
dir: /sys/src/cmd/cwfs/chk.c/
#include "all.h" /* augmented Dentry */ typedef struct { Dentry *d; Off qpath; int ns; } Extdentry; static char* abits; static long sizabits; static char* qbits; static long sizqbits; static char* name; static long sizname; static Off fstart; static Off fsize; static Off nfiles; static Off maxq; static Device* dev; static Off ndup; static Off nused; static Off nfdup; static Off nqbad; static Off nfree; static Off nbad; static int mod; static int flags; static int ronly; static int cwflag; static Devsize sbaddr; static Devsize oldblock; static int depth; static int maxdepth; static uchar *lowstack, *startstack; /* local prototypes */ static int amark(Off); static void* chkalloc(ulong); static void ckfreelist(Superb*); static int fmark(Off); static int fsck(Dentry*); static int ftest(Off); static Dentry* maked(Off, int, Off); static void missing(void); static void mkfreelist(Superb*); static void modd(Off, int, Dentry*); static void qmark(Off); static void trfreelist(Superb*); static void xaddfree(Device*, Off, Superb*, Iobuf*); static void xflush(Device*, Superb*, Iobuf*); static void xread(Off, Off); static Iobuf* xtag(Off, int, Off); static void * chkalloc(ulong n) { char *p = mallocz(n, 1); if (p == nil) panic("chkalloc: out of memory"); return p; } void chkfree(void *p) { free(p); } /* * check flags */ enum { Crdall = (1<<0), /* read all files */ Ctag = (1<<1), /* rebuild tags */ Cpfile = (1<<2), /* print files */ Cpdir = (1<<3), /* print directories */ Cfree = (1<<4), /* rebuild free list */ Csetqid = (1<<5), /* resequence qids */ Cream = (1<<6), /* clear all bad tags */ Cbad = (1<<7), /* clear all bad blocks */ Ctouch = (1<<8), /* touch old dir and indir */ Ctrim = (1<<9), /* trim fsize back to fit when checking free list */ Crtmp = (1<<10), /* clear temporary files (after recover) */ }; static struct { char* option; long flag; } ckoption[] = { "rdall", Crdall, "tag", Ctag, "pfile", Cpfile, "pdir", Cpdir, "free", Cfree, // "setqid", Csetqid, "ream", Cream, "bad", Cbad, "touch", Ctouch, "trim", Ctrim, "rtmp", Crtmp, 0, }; void cmd_check(int argc, char *argv[]) { long f, i, flag; Off raddr; Filsys *fs; Iobuf *p; Superb *sb; Dentry *d; flag = 0; for(i=1; i<argc; i++) { for(f=0; ckoption[f].option; f++) if(strcmp(argv[i], ckoption[f].option) == 0) goto found; print("unknown check option %s\n", argv[i]); for(f=0; ckoption[f].option; f++) print("\t%s\n", ckoption[f].option); return; found: flag |= ckoption[f].flag; } fs = cons.curfs; dev = fs->dev; ronly = (dev->type == Devro); cwflag = (dev->type == Devcw) | (dev->type == Devro); if(!ronly) wlock(&mainlock); /* check */ flags = flag; name = abits = qbits = nil; /* in case of goto */ sbaddr = superaddr(dev); raddr = getraddr(dev); p = xtag(sbaddr, Tsuper, QPSUPER); if(!p) goto out; sb = (Superb*)p->iobuf; fstart = 2; cons.noage = 1; /* 200 is slop since qidgen is likely to be a little bit low */ sizqbits = (sb->qidgen+200 + 7) / 8; qbits = chkalloc(sizqbits); fsize = sb->fsize; sizabits = (fsize-fstart + 7)/8; abits = chkalloc(sizabits); sizname = 4000; name = chkalloc(sizname); sizname -= NAMELEN+10; /* for safety */ mod = 0; nfree = 0; nfdup = 0; nused = 0; nbad = 0; ndup = 0; nqbad = 0; depth = 0; maxdepth = 0; if(flags & Ctouch) { /* round fsize down to start of current side */ int s; Devsize dsize; oldblock = 0; for (s = 0; dsize = wormsizeside(dev, s), dsize > 0 && oldblock + dsize < fsize; s++) oldblock += dsize; print("oldblock = %lld\n", (Wideoff)oldblock); } amark(sbaddr); if(cwflag) { amark(sb->roraddr); amark(sb->next); } print("checking filsys: %s\n", fs->name); nfiles = 0; maxq = 0; d = maked(raddr, 0, QPROOT); if(d) { amark(raddr); if(fsck(d)) modd(raddr, 0, d); chkfree(d); depth--; if(depth) print("depth not zero on return\n"); } if(flags & Cfree) if(cwflag) trfreelist(sb); else mkfreelist(sb); if(sb->qidgen < maxq) print("qid generator low path=%lld maxq=%lld\n", (Wideoff)sb->qidgen, (Wideoff)maxq); if(!(flags & Cfree)) ckfreelist(sb); if(mod) { sb->qidgen = maxq; print("file system was modified\n"); settag(p, Tsuper, QPNONE); } print("nfiles = %lld\n", (Wideoff)nfiles); print("fsize = %lld\n", (Wideoff)fsize); print("nused = %lld\n", (Wideoff)nused); print("ndup = %lld\n", (Wideoff)ndup); print("nfree = %lld\n", (Wideoff)nfree); print("tfree = %lld\n", (Wideoff)sb->tfree); print("nfdup = %lld\n", (Wideoff)nfdup); print("nmiss = %lld\n", (Wideoff)fsize-fstart-nused-nfree); print("nbad = %lld\n", (Wideoff)nbad); print("nqbad = %lld\n", (Wideoff)nqbad); print("maxq = %lld\n", (Wideoff)maxq); print("base stack=%llud\n", (vlong)startstack); /* high-water mark of stack usage */ print("high stack=%llud\n", (vlong)lowstack); print("deepest recursion=%d\n", maxdepth-1); /* one-origin */ if(!cwflag) missing(); putbuf(p); out: cons.noage = 0; chkfree(name); chkfree(abits); chkfree(qbits); name = abits = qbits = nil; if(!ronly) wunlock(&mainlock); } /* * if *blkp is already allocated and Cbad is set, zero it. * returns *blkp if it's free, else 0. */ static Off blkck(Off *blkp, int *flgp) { Off a = *blkp; if(amark(a)) { if(flags & Cbad) { *blkp = 0; *flgp |= Bmod; } a = 0; } return a; } /* * if a block address within a Dentry, *blkp, is already allocated * and Cbad is set, zero it. * stores 0 into *resp if already allocated, else stores *blkp. * returns dmod count. */ static int daddrck(Off *blkp, Off *resp) { int dmod = 0; if(amark(*blkp)) { if(flags & Cbad) { *blkp = 0; dmod++; } *resp = 0; } else *resp = *blkp; return dmod; } /* * under Ctouch, read block `a' if it's in range. * returns dmod count. */ static int touch(Off a) { if((flags&Ctouch) && a < oldblock) { Iobuf *pd = getbuf(dev, a, Brd|Bmod); if(pd) putbuf(pd); return 1; } return 0; } /* * if d is a directory, touch it and check all its entries in block a. * if not, under Crdall, read a. * returns dmod count. */ static int dirck(Extdentry *ed, Off a) { int k, dmod = 0; if(ed->d->mode & DDIR) { dmod += touch(a); for(k=0; k<DIRPERBUF; k++) { Dentry *nd = maked(a, k, ed->qpath); if(nd == nil) break; if(fsck(nd)) { modd(a, k, nd); dmod++; } chkfree(nd); depth--; name[ed->ns] = 0; } } else if(flags & Crdall) xread(a, ed->qpath); return dmod; } /* * touch a, check a's tag for Tind1, Tind2, etc. * if the tag is right, validate each block number in the indirect block, * and check each block (mostly in case we are reading a huge directory). */ static int indirck(Extdentry *ed, Off a, int tag) { int i, dmod = 0; Iobuf *p1; if (a == 0) return dmod; dmod = touch(a); if (p1 = xtag(a, tag, ed->qpath)) { for(i=0; i<INDPERBUF; i++) { a = blkck(&((Off *)p1->iobuf)[i], &p1->flags); if (a) { /* * check each block named in this * indirect(^n) block (a). */ if (tag == Tind1) dmod += dirck(ed, a); else dmod += indirck(ed, a, tag-1); } } putbuf(p1); } return dmod; } static int indiraddrck(Extdentry *ed, Off *indirp, int tag) { int dmod; Off a; dmod = daddrck(indirp, &a); return dmod + indirck(ed, a, tag); } /* if result is true, *d was modified */ static int fsck(Dentry *d) { int i, dmod; Extdentry edent; depth++; if(depth >= maxdepth) maxdepth = depth; if (lowstack == nil) startstack = lowstack = (uchar *)&edent; /* more precise check, assumes downward-growing stack */ if ((uchar *)&edent < lowstack) lowstack = (uchar *)&edent; /* check that entry is allocated */ if(!(d->mode & DALLOC) || (ronly && (d->mode & DTMP))) return 0; nfiles++; /* we stash qpath & ns in an Extdentry for eventual use by dirck() */ memset(&edent, 0, sizeof edent); edent.d = d; /* check name */ edent.ns = strlen(name); i = strlen(d->name); if(i >= NAMELEN) { d->name[NAMELEN-1] = 0; print("%s->name (%s) not terminated\n", name, d->name); return 0; } edent.ns += i; if(edent.ns >= sizname) { print("%s->name (%s) name too large\n", name, d->name); return 0; } strcat(name, d->name); if(d->mode & DDIR) { if(edent.ns > 1) { strcat(name, "/"); edent.ns++; } if(flags & Cpdir) { print("%s\n", name); } } else if(flags & Cpfile) { print("%s\n", name); } /* check qid */ edent.qpath = d->qid.path; if(d->mode & DDIR) edent.qpath ^= QPDIR; qmark(edent.qpath); if(edent.qpath > maxq) maxq = edent.qpath; /* clear temporary files (after recover) */ if((d->mode & DTMP) && (flags & Crtmp)){ for(i=0; i<NDBLOCK; i++) d->dblock[i] = 0; for(i=0; i<NIBLOCK; i++) d->iblocks[i] = 0; d->size = 0; /* if not directory, delete file */ if((d->mode & DDIR) == 0) memset(d, 0, sizeof(Dentry)); return 1; } /* check direct blocks (the common case) */ dmod = 0; { Off a; for(i=0; i<NDBLOCK; i++) { dmod += daddrck(&d->dblock[i], &a); if (a) dmod += dirck(&edent, a); } } /* check indirect^n blocks, if any */ for (i = 0; i < NIBLOCK; i++) dmod += indiraddrck(&edent, &d->iblocks[i], Tind1+i); return dmod; } enum { XFEN = FEPERBUF + 6 }; typedef struct { int flag; int count; int next; Off addr[XFEN]; } Xfree; static void xaddfree(Device *dev, Off a, Superb *sb, Iobuf *p) { Xfree *x; x = (Xfree*)p->iobuf; if(x->count < XFEN) { x->addr[x->count] = a; x->count++; return; } if(!x->flag) { memset(&sb->fbuf, 0, sizeof(sb->fbuf)); sb->fbuf.free[0] = 0L; sb->fbuf.nfree = 1; sb->tfree = 0; x->flag = 1; } addfree(dev, a, sb); } static void xflush(Device *dev, Superb *sb, Iobuf *p) { int i; Xfree *x; x = (Xfree*)p->iobuf; if(!x->flag) { memset(&sb->fbuf, 0, sizeof(sb->fbuf)); sb->fbuf.free[0] = 0L; sb->fbuf.nfree = 1; sb->tfree = 0; } for(i=0; i<x->count; i++) addfree(dev, x->addr[i], sb); } /* * make freelist * from existing freelist * (cw devices) */ static void trfreelist(Superb *sb) { Off a, n; int i; Iobuf *p, *xp; Fbuf *fb; xp = getbuf(devnone, Cckbuf, 0); memset(xp->iobuf, 0, BUFSIZE); fb = &sb->fbuf; p = 0; for(;;) { n = fb->nfree; if(n < 0 || n > FEPERBUF) break; for(i=1; i<n; i++) { a = fb->free[i]; if(a && !ftest(a)) xaddfree(dev, a, sb, xp); } a = fb->free[0]; if(!a) break; if(ftest(a)) break; xaddfree(dev, a, sb, xp); if(p) putbuf(p); p = xtag(a, Tfree, QPNONE); if(!p) break; fb = (Fbuf*)p->iobuf; } if(p) putbuf(p); xflush(dev, sb, xp); putbuf(xp); mod++; print("%lld blocks free\n", (Wideoff)sb->tfree); } static void ckfreelist(Superb *sb) { Off a, lo, hi; int n, i; Iobuf *p; Fbuf *fb; strcpy(name, "free list"); print("check %s\n", name); fb = &sb->fbuf; a = sbaddr; p = 0; lo = 0; hi = 0; for(;;) { n = fb->nfree; if(n < 0 || n > FEPERBUF) { print("check: nfree bad %lld\n", (Wideoff)a); break; } for(i=1; i<n; i++) { a = fb->free[i]; if(a && !fmark(a)) { if(!lo || lo > a) lo = a; if(!hi || hi < a) hi = a; } } a = fb->free[0]; if(!a) break; if(fmark(a)) break; if(!lo || lo > a) lo = a; if(!hi || hi < a) hi = a; if(p) putbuf(p); p = xtag(a, Tfree, QPNONE); if(!p) break; fb = (Fbuf*)p->iobuf; } if(p) putbuf(p); if (flags & Ctrim) { fsize = hi--; /* fsize = hi + 1 */ sb->fsize = fsize; mod++; print("set fsize to %lld\n", (Wideoff)fsize); } print("lo = %lld; hi = %lld\n", (Wideoff)lo, (Wideoff)hi); } /* * make freelist from scratch */ static void mkfreelist(Superb *sb) { Off a; int i, b; if(ronly) { print("cant make freelist on ronly device\n"); return; } strcpy(name, "free list"); memset(&sb->fbuf, 0, sizeof(sb->fbuf)); sb->fbuf.free[0] = 0L; sb->fbuf.nfree = 1; sb->tfree = 0; for(a=fsize-fstart-1; a >= 0; a--) { i = a/8; if(i < 0 || i >= sizabits) continue; b = 1 << (a&7); if(abits[i] & b) continue; addfree(dev, fstart+a, sb); } print("%lld blocks free\n", (Wideoff)sb->tfree); mod++; } static Dentry* maked(Off a, int s, Off qpath) { Iobuf *p; Dentry *d, *d1; p = xtag(a, Tdir, qpath); if(!p) return 0; d = getdir(p, s); d1 = chkalloc(sizeof(Dentry)); memmove(d1, d, sizeof(Dentry)); putbuf(p); return d1; } static void modd(Off a, int s, Dentry *d1) { Iobuf *p; Dentry *d; if(!(flags & (Cbad|Crtmp))) return; p = getbuf(dev, a, Brd); d = getdir(p, s); if(!d) { if(p) putbuf(p); return; } memmove(d, d1, sizeof(Dentry)); p->flags |= Bmod; putbuf(p); } static void xread(Off a, Off qpath) { Iobuf *p; p = xtag(a, Tfile, qpath); if(p) putbuf(p); } static Iobuf* xtag(Off a, int tag, Off qpath) { Iobuf *p; if(a == 0) return 0; p = getbuf(dev, a, Brd); if(!p) { print("check: \"%s\": xtag: p null\n", name); if(flags & (Cream|Ctag)) { p = getbuf(dev, a, Bmod); if(p) { memset(p->iobuf, 0, RBUFSIZE); settag(p, tag, qpath); mod++; return p; } } return 0; } if(checktag(p, tag, qpath)) { print("check: \"%s\": xtag: checktag\n", name); if(flags & (Cream|Ctag)) { if(flags & Cream) memset(p->iobuf, 0, RBUFSIZE); settag(p, tag, qpath); mod++; return p; } return p; } return p; } static int amark(Off a) { Off i; int b; if(a < fstart || a >= fsize) { if(a == 0) return 0; print("check: \"%s\": range %lld\n", name, (Wideoff)a); nbad++; return 1; } a -= fstart; i = a/8; b = 1 << (a&7); if(abits[i] & b) { if(!ronly) if(ndup < 10) print("check: \"%s\": address dup %lld\n", name, (Wideoff)fstart+a); else if(ndup == 10) print("..."); ndup++; return 1; } abits[i] |= b; nused++; return 0; } static int fmark(Off a) { Off i; int b; if(a < fstart || a >= fsize) { print("check: \"%s\": range %lld\n", name, (Wideoff)a); nbad++; return 1; } a -= fstart; i = a/8; b = 1 << (a&7); if(abits[i] & b) { print("check: \"%s\": address dup %lld\n", name, (Wideoff)fstart+a); nfdup++; return 1; } abits[i] |= b; nfree++; return 0; } static int ftest(Off a) { Off i; int b; if(a < fstart || a >= fsize) return 1; a -= fstart; i = a/8; b = 1 << (a&7); if(abits[i] & b) return 1; abits[i] |= b; return 0; } static void missing(void) { Off a, i; int b, n; n = 0; for(a=fsize-fstart-1; a>=0; a--) { i = a/8; b = 1 << (a&7); if(!(abits[i] & b)) { print("missing: %lld\n", (Wideoff)fstart+a); n++; } if(n > 10) { print(" ...\n"); break; } } } static void qmark(Off qpath) { int b; Off i; i = qpath/8; b = 1 << (qpath&7); if(i < 0 || i >= sizqbits) { nqbad++; if(nqbad < 20) print("check: \"%s\": qid out of range %llux\n", name, (Wideoff)qpath); return; } if((qbits[i] & b) && !ronly) { nqbad++; if(nqbad < 20) print("check: \"%s\": qid dup %llux\n", name, (Wideoff)qpath); } qbits[i] |= b; }