#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;
ia->addr = 0;
ia->type = type;
ia->size = size;
ia->blocks = (dsize + ClumpSize + (1 << ABlockLog) - 1) >> ABlockLog;
a = writeiclump(ix, &cl, cb->data, &ia->addr);
trace(TraceLump, "storeclump exit %lld", a);
freezblock(cb);
if(a == TWID64)
return -1;
/*
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;
}
|