|  | #include "stdinc.h" | 
|  | #include "dat.h" | 
|  | #include "fns.h" | 
|  |  | 
|  | static int extra, missing, wrong; | 
|  |  | 
|  | static void | 
|  | phdr(DBlock *eb) | 
|  | { | 
|  | static int did; | 
|  |  | 
|  | if(!did){ | 
|  | did = 1; | 
|  | print("# diff actual correct\n"); | 
|  | } | 
|  | print("%s block 0x%llux\n", eb->part->name, eb->addr); | 
|  | } | 
|  |  | 
|  | static void | 
|  | pie(IEntry *ie, char c) | 
|  | { | 
|  | print("%c %V %22lld %3d %5d %3d\n", | 
|  | c, ie->score, ie->ia.addr, ie->ia.type, ie->ia.size, ie->ia.blocks); | 
|  | } | 
|  |  | 
|  | static int | 
|  | checkbucket(Index *ix, u32int buck, IBucket *ib) | 
|  | { | 
|  | ISect *is; | 
|  | DBlock *eb; | 
|  | IBucket eib; | 
|  | IEntry ie, eie; | 
|  | int i, ei, ok, c, hdr; | 
|  |  | 
|  | is = ix->sects[indexsect0(ix, buck)]; | 
|  | if(buck < is->start || buck >= is->stop){ | 
|  | seterr(EAdmin, "cannot find index section for bucket %lud\n", (ulong)buck); | 
|  | return -1; | 
|  | } | 
|  | buck -= is->start; | 
|  | eb = getdblock(is->part, is->blockbase + ((u64int)buck << is->blocklog), OREAD); | 
|  | if(eb == nil) | 
|  | return -1; | 
|  | unpackibucket(&eib, eb->data, is->bucketmagic); | 
|  |  | 
|  | ok = 0; | 
|  | ei = 0; | 
|  | hdr = 0; | 
|  | for(i = 0; i < ib->n; i++){ | 
|  | while(ei < eib.n){ | 
|  | c = ientrycmp(&ib->data[i * IEntrySize], &eib.data[ei * IEntrySize]); | 
|  | if(c == 0){ | 
|  | unpackientry(&ie, &ib->data[i * IEntrySize]); | 
|  | unpackientry(&eie, &eib.data[ei * IEntrySize]); | 
|  | if(iaddrcmp(&ie.ia, &eie.ia) != 0){ | 
|  | if(!hdr){ | 
|  | phdr(eb); | 
|  | hdr = 1; | 
|  | } | 
|  | wrong++; | 
|  | pie(&eie, '<'); | 
|  | pie(&ie, '>'); | 
|  | } | 
|  | ei++; | 
|  | goto cont; | 
|  | } | 
|  | if(c < 0) | 
|  | break; | 
|  | if(!hdr){ | 
|  | phdr(eb); | 
|  | hdr = 1; | 
|  | } | 
|  | unpackientry(&eie, &eib.data[ei*IEntrySize]); | 
|  | extra++; | 
|  | pie(&eie, '<'); | 
|  | ei++; | 
|  | ok = -1; | 
|  | } | 
|  | if(!hdr){ | 
|  | phdr(eb); | 
|  | hdr = 1; | 
|  | } | 
|  | unpackientry(&ie, &ib->data[i*IEntrySize]); | 
|  | missing++; | 
|  | pie(&ie, '>'); | 
|  | ok = -1; | 
|  | cont:; | 
|  | } | 
|  | for(; ei < eib.n; ei++){ | 
|  | if(!hdr){ | 
|  | phdr(eb); | 
|  | hdr = 1; | 
|  | } | 
|  | unpackientry(&eie, &eib.data[ei*IEntrySize]); | 
|  | pie(&eie, '<'); | 
|  | ok = -1; | 
|  | } | 
|  | putdblock(eb); | 
|  | return ok; | 
|  | } | 
|  |  | 
|  | int | 
|  | checkindex(Index *ix, Part *part, u64int off, u64int clumps, int zero) | 
|  | { | 
|  | IEStream *ies; | 
|  | IBucket ib, zib; | 
|  | ZBlock *z, *b; | 
|  | u32int next, buck; | 
|  | int ok, bok; | 
|  | u64int found = 0; | 
|  |  | 
|  | /* ZZZ make buffer size configurable */ | 
|  | b = alloczblock(ix->blocksize, 0, ix->blocksize); | 
|  | z = alloczblock(ix->blocksize, 1, ix->blocksize); | 
|  | ies = initiestream(part, off, clumps, 64*1024); | 
|  | if(b == nil || z == nil || ies == nil){ | 
|  | werrstr("allocating: %r"); | 
|  | ok = -1; | 
|  | goto out; | 
|  | } | 
|  | ok = 0; | 
|  | next = 0; | 
|  | memset(&ib, 0, sizeof ib); | 
|  | ib.data = b->data; | 
|  | zib.data = z->data; | 
|  | zib.n = 0; | 
|  | zib.buck = 0; | 
|  | for(;;){ | 
|  | buck = buildbucket(ix, ies, &ib, ix->blocksize-IBucketSize); | 
|  | found += ib.n; | 
|  | if(zero){ | 
|  | for(; next != buck; next++){ | 
|  | if(next == ix->buckets){ | 
|  | if(buck != TWID32){ | 
|  | ok = -1; | 
|  | werrstr("internal error: bucket out of range"); | 
|  | } | 
|  | if(ok < 0) | 
|  | werrstr("%d spurious entries, %d missing, %d wrong", extra, missing, wrong); | 
|  | goto out; | 
|  | } | 
|  | bok = checkbucket(ix, next, &zib); | 
|  | if(bok < 0) | 
|  | ok = -1; | 
|  | } | 
|  | } | 
|  | if(buck >= ix->buckets){ | 
|  | if(buck == TWID32) | 
|  | break; | 
|  | werrstr("internal error: bucket out of range"); | 
|  | ok = -1; | 
|  | goto out; | 
|  | } | 
|  | bok = checkbucket(ix, buck, &ib); | 
|  | if(bok < 0) | 
|  | ok = -1; | 
|  | next = buck + 1; | 
|  | } | 
|  | out: | 
|  | freeiestream(ies); | 
|  | freezblock(z); | 
|  | freezblock(b); | 
|  | return ok; | 
|  | } | 
|  |  | 
|  | int | 
|  | checkbloom(Bloom *b1, Bloom *b2, int fix) | 
|  | { | 
|  | u32int *a1, *a2; | 
|  | int i, n, extra, missing; | 
|  |  | 
|  | if(b1==nil && b2==nil) | 
|  | return 0; | 
|  | if(b1==nil || b2==nil){ | 
|  | werrstr("nil/non-nil"); | 
|  | return -1; | 
|  | } | 
|  | wbbloomhead(b1); | 
|  | wbbloomhead(b2); | 
|  | if(memcmp(b1->data, b2->data, BloomHeadSize) != 0){ | 
|  | werrstr("bloom header mismatch"); | 
|  | return -1; | 
|  | } | 
|  | a1 = (u32int*)b1->data; | 
|  | a2 = (u32int*)b2->data; | 
|  | n = b1->size/4; | 
|  | extra = 0; | 
|  | missing = 0; | 
|  | for(i=BloomHeadSize/4; i<n; i++){ | 
|  | if(a1[i] != a2[i]){ | 
|  | // print("%.8ux/%.8ux.", a1[i], a2[i]); | 
|  | extra   += countbits(a1[i] & ~a2[i]); | 
|  | missing += countbits(a2[i] & ~a1[i]); | 
|  | } | 
|  | } | 
|  | if(extra || missing) | 
|  | fprint(2, "bloom filter: %d spurious bits, %d missing bits\n", | 
|  | extra, missing); | 
|  | else | 
|  | fprint(2, "bloom filter: correct\n"); | 
|  | if(!fix && missing){ | 
|  | werrstr("missing bits"); | 
|  | return -1; | 
|  | } | 
|  | if(fix && (missing || extra)){ | 
|  | memmove(b1->data, b2->data, b1->size); | 
|  | return writebloom(b1); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: checkindex [-f] [-B blockcachesize] config tmp\n"); | 
|  | threadexitsall(0); | 
|  | } | 
|  |  | 
|  | Config conf; | 
|  |  | 
|  | void | 
|  | threadmain(int argc, char *argv[]) | 
|  | { | 
|  | Bloom *oldbloom, *newbloom; | 
|  | Part *part; | 
|  | u64int clumps, base; | 
|  | u32int bcmem; | 
|  | int fix, skipz, ok; | 
|  |  | 
|  | fix = 0; | 
|  | bcmem = 0; | 
|  | skipz = 0; | 
|  | ARGBEGIN{ | 
|  | case 'B': | 
|  | bcmem = unittoull(ARGF()); | 
|  | break; | 
|  | case 'f': | 
|  | fix++; | 
|  | break; | 
|  | case 'Z': | 
|  | skipz = 1; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | break; | 
|  | }ARGEND | 
|  |  | 
|  | if(argc != 2) | 
|  | usage(); | 
|  |  | 
|  | ventifmtinstall(); | 
|  |  | 
|  | part = initpart(argv[1], ORDWR|ODIRECT); | 
|  | if(part == nil) | 
|  | sysfatal("can't initialize temporary partition: %r"); | 
|  |  | 
|  | if(!fix) | 
|  | readonly = 1; | 
|  |  | 
|  | if(initventi(argv[0], &conf) < 0) | 
|  | sysfatal("can't init venti: %r"); | 
|  | if(mainindex->bloom && loadbloom(mainindex->bloom) < 0) | 
|  | sysfatal("can't load bloom filter: %r"); | 
|  | oldbloom = mainindex->bloom; | 
|  | newbloom = nil; | 
|  | if(oldbloom){ | 
|  | newbloom = vtmallocz(sizeof *newbloom); | 
|  | bloominit(newbloom, oldbloom->size, nil); | 
|  | newbloom->data = vtmallocz(oldbloom->size); | 
|  | } | 
|  | if(bcmem < maxblocksize * (mainindex->narenas + mainindex->nsects * 4 + 16)) | 
|  | bcmem = maxblocksize * (mainindex->narenas + mainindex->nsects * 4 + 16); | 
|  | if(0) fprint(2, "initialize %d bytes of disk block cache\n", bcmem); | 
|  | initdcache(bcmem); | 
|  |  | 
|  | fprint(2, "checkindex: building entry list\n"); | 
|  | clumps = sortrawientries(mainindex, part, &base, newbloom); | 
|  | if(clumps == TWID64) | 
|  | sysfatal("can't build sorted index: %r"); | 
|  | fprint(2, "checkindex: checking %lld entries at %lld\n", clumps, base); | 
|  | ok = 0; | 
|  | if(checkindex(mainindex, part, base, clumps, !skipz) < 0){ | 
|  | fprint(2, "checkindex: %r\n"); | 
|  | ok = -1; | 
|  | } | 
|  | if(checkbloom(oldbloom, newbloom, fix) < 0){ | 
|  | fprint(2, "checkbloom: %r\n"); | 
|  | ok = -1; | 
|  | } | 
|  | if(ok < 0) | 
|  | sysfatal("errors found"); | 
|  | fprint(2, "checkindex: index is correct\n"); | 
|  | threadexitsall(0); | 
|  | } |