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