Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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);
}