Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "stdinc.h"
#include "dat.h"
#include "fns.h"
#include "whack.h"

/*
 * Write a lump to disk.  Updates ia with an index address
 * for the newly-written lump.  Upon return, the lump will
 * have been placed in the disk cache but will likely not be on disk yet.
 */
int
storeclump(Index *ix, ZBlock *zb, u8int *sc, int type, u32int creator, IAddr *ia)
{
        ZBlock *cb;
        Clump cl;
        u64int a;
        u8int bh[VtScoreSize];
        int size, dsize;

        trace(TraceLump, "storeclump enter", sc, type);
        size = zb->len;
        if(size > VtMaxLumpSize){
                seterr(EStrange, "lump too large");
                return -1;
        }
        if(vttypevalid(type) < 0){
                seterr(EStrange, "invalid lump type");
                return -1;
        }

        if(0){
                scoremem(bh, zb->data, size);
                if(scorecmp(sc, bh) != 0){
                        seterr(ECorrupt, "storing clump: corrupted; expected=%V got=%V, size=%d", sc, bh, size);
                        return -1;
                }
        }

        cb = alloczblock(size + ClumpSize + U32Size, 0, 0);
        if(cb == nil)
                return -1;

        cl.info.type = type;
        cl.info.uncsize = size;
        cl.creator = creator;
        cl.time = now();
        scorecp(cl.info.score, sc);

        trace(TraceLump, "storeclump whackblock");
        dsize = whackblock(&cb->data[ClumpSize], zb->data, size);
        if(dsize > 0 && dsize < size){
                cl.encoding = ClumpECompress;
        }else{
                if(dsize > size){
                        fprint(2, "whack error: dsize=%d size=%d\n", dsize, size);
                        abort();
                }
                cl.encoding = ClumpENone;
                dsize = size;
                memmove(&cb->data[ClumpSize], zb->data, size);
        }
        memset(cb->data+ClumpSize+dsize, 0, 4);
        cl.info.size = dsize;

        a = writeiclump(ix, &cl, cb->data);
        trace(TraceLump, "storeclump exit %lld", a);
        freezblock(cb);
        if(a == TWID64)
                return -1;

        ia->addr = a;
        ia->type = type;
        ia->size = size;
        ia->blocks = (dsize + ClumpSize + (1 << ABlockLog) - 1) >> ABlockLog;

/*
        qlock(&stats.lock);
        stats.clumpwrites++;
        stats.clumpbwrites += size;
        stats.clumpbcomp += dsize;
        qunlock(&stats.lock);
*/

        return 0;
}

u32int
clumpmagic(Arena *arena, u64int aa)
{
        u8int buf[U32Size];

        if(readarena(arena, aa, buf, U32Size) == TWID32)
                return TWID32;
        return unpackmagic(buf);
}

/*
 * fetch a block based at addr.
 * score is filled in with the block's score.
 * blocks is roughly the length of the clump on disk;
 * if zero, the length is unknown.
 */
ZBlock*
loadclump(Arena *arena, u64int aa, int blocks, Clump *cl, u8int *score, int verify)
{
        Unwhack uw;
        ZBlock *zb, *cb;
        u8int bh[VtScoreSize], *buf;
        u32int n;
        int nunc;

/*
        qlock(&stats.lock);
        stats.clumpreads++;
        qunlock(&stats.lock);
*/

        if(blocks <= 0)
                blocks = 1;

        trace(TraceLump, "loadclump enter");

        cb = alloczblock(blocks << ABlockLog, 0, 0);
        if(cb == nil)
                return nil;
        n = readarena(arena, aa, cb->data, blocks << ABlockLog);
        if(n < ClumpSize){
                if(n != 0)
                        seterr(ECorrupt, "loadclump read less than a header");
                freezblock(cb);
                return nil;
        }
        trace(TraceLump, "loadclump unpack");
        if(unpackclump(cl, cb->data, arena->clumpmagic) < 0){
                seterr(ECorrupt, "loadclump %s %llud: %r", arena->name, aa);
                freezblock(cb);
                return nil;
        }
        if(cl->info.type == VtCorruptType){
                seterr(EOk, "clump is marked corrupt");
                freezblock(cb);
                return nil;
        }
        n -= ClumpSize;
        if(n < cl->info.size){
                freezblock(cb);
                n = cl->info.size;
                cb = alloczblock(n, 0, 0);
                if(cb == nil)
                        return nil;
                if(readarena(arena, aa + ClumpSize, cb->data, n) != n){
                        seterr(ECorrupt, "loadclump read too little data");
                        freezblock(cb);
                        return nil;
                }
                buf = cb->data;
        }else
                buf = cb->data + ClumpSize;

        scorecp(score, cl->info.score);

        zb = alloczblock(cl->info.uncsize, 0, 0);
        if(zb == nil){
                freezblock(cb);
                return nil;
        }
        switch(cl->encoding){
        case ClumpECompress:
                trace(TraceLump, "loadclump decompress");
                unwhackinit(&uw);
                nunc = unwhack(&uw, zb->data, cl->info.uncsize, buf, cl->info.size);
                if(nunc != cl->info.uncsize){
                        if(nunc < 0)
                                seterr(ECorrupt, "decompression of %llud failed: %s", aa, uw.err);
                        else
                                seterr(ECorrupt, "decompression of %llud gave partial block: %d/%d\n", aa, nunc, cl->info.uncsize);
                        freezblock(cb);
                        freezblock(zb);
                        return nil;
                }
                break;
        case ClumpENone:
                if(cl->info.size != cl->info.uncsize){
                        seterr(ECorrupt, "loading clump: bad uncompressed size for uncompressed block %llud", aa);
                        freezblock(cb);
                        freezblock(zb);
                        return nil;
                }
                scoremem(bh, buf, cl->info.uncsize);
                if(scorecmp(cl->info.score, bh) != 0)
                        seterr(ECorrupt, "pre-copy sha1 wrong at %s %llud: expected=%V got=%V", arena->name, aa, cl->info.score, bh);
                memmove(zb->data, buf, cl->info.uncsize);
                break;
        default:
                seterr(ECorrupt, "unknown encoding in loadlump %llud", aa);
                freezblock(cb);
                freezblock(zb);
                return nil;
        }
        freezblock(cb);

        if(verify){
                trace(TraceLump, "loadclump verify");
                scoremem(bh, zb->data, cl->info.uncsize);
                if(scorecmp(cl->info.score, bh) != 0){
                        seterr(ECorrupt, "loading clump: corrupted at %s %llud; expected=%V got=%V", arena->name, aa, cl->info.score, bh);
                        freezblock(zb);
                        return nil;
                }
                if(vttypevalid(cl->info.type) < 0){
                        seterr(ECorrupt, "loading lump at %s %llud: invalid lump type %d", arena->name, aa, cl->info.type);
                        freezblock(zb);
                        return nil;
                }
        }

        trace(TraceLump, "loadclump exit");
/*
        qlock(&stats.lock);
        stats.clumpbreads += cl->info.size;
        stats.clumpbuncomp += cl->info.uncsize;
        qunlock(&stats.lock);
*/
        return zb;
}