blob: ca95573024d3695075757d5496f3cc729865cff3 [file] [log] [blame]
#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);
}