diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/cwfs/chk.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cwfs/chk.c')
-rwxr-xr-x | sys/src/cmd/cwfs/chk.c | 838 |
1 files changed, 838 insertions, 0 deletions
diff --git a/sys/src/cmd/cwfs/chk.c b/sys/src/cmd/cwfs/chk.c new file mode 100755 index 000000000..8ee9a980f --- /dev/null +++ b/sys/src/cmd/cwfs/chk.c @@ -0,0 +1,838 @@ +#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 */ +}; + +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, + 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(); + +out: + cons.noage = 0; + putbuf(p); + 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)) + 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); + prflush(); + } + } else if(flags & Cpfile) { + print("%s\n", name); + prflush(); + } + + /* check qid */ + edent.qpath = d->qid.path & ~QPDIR; + qmark(edent.qpath); + if(edent.qpath > maxq) + maxq = edent.qpath; + + /* 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)) + 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; +} |