/*
* make an Arena, and initialize it based upon the disk header and trailer.
*/
Arena*
initArena(Part *part, u64int base, u64int size, u32int blockSize)
{
Arena *arena;
/*
* write directory information for one clump
* must be called the arena locked
*/
int
writeClumpInfo(Arena *arena, int clump, ClumpInfo *ci)
{
CIBlock *cib, r;
/*
* read a clump of data
* n is a hint of the size of the data, not including the header
* make sure it won't run off the end, then return the number of bytes actually read
*/
u32int
readArena(Arena *arena, u64int aa, u8int *buf, long n)
{
DBlock *b;
u64int a;
u32int blockSize, off, m;
long nn;
if(n == 0)
return 0;
vtLock(arena->lock);
a = arena->size - arenaDirSize(arena, arena->clumps);
vtUnlock(arena->lock);
if(aa >= a){
setErr(EOk, "reading beyond arena clump storage: clumps=%d aa=%lld a=%lld -1 clumps=%lld\n", arena->clumps, aa, a, arena->size - arenaDirSize(arena, arena->clumps - 1));
return 0;
}
if(aa + n > a)
n = a - aa;
blockSize = arena->blockSize;
a = arena->base + aa;
off = a & (blockSize - 1);
a -= off;
nn = 0;
for(;;){
b = getDBlock(arena->part, a, 1);
if(b == nil)
return 0;
m = blockSize - off;
if(m > n - nn)
m = n - nn;
memmove(&buf[nn], &b->data[off], m);
putDBlock(b);
nn += m;
if(nn == n)
break;
off = 0;
a += blockSize;
}
return n;
}
/*
* write some data to the clump section at a given offset
* used to fix up corrupted arenas.
*/
u32int
writeArena(Arena *arena, u64int aa, u8int *clbuf, u32int n)
{
DBlock *b;
u64int a;
u32int blockSize, off, m;
long nn;
int ok;
if(n == 0)
return 0;
vtLock(arena->lock);
a = arena->size - arenaDirSize(arena, arena->clumps);
if(aa >= a || aa + n > a){
vtUnlock(arena->lock);
setErr(EOk, "writing beyond arena clump storage");
return 0;
}
blockSize = arena->blockSize;
a = arena->base + aa;
off = a & (blockSize - 1);
a -= off;
nn = 0;
for(;;){
b = getDBlock(arena->part, a, off != 0 || off + n < blockSize);
if(b == nil){
vtUnlock(arena->lock);
return 0;
}
m = blockSize - off;
if(m > n - nn)
m = n - nn;
memmove(&b->data[off], &clbuf[nn], m);
ok = writePart(arena->part, a, b->data, blockSize);
putDBlock(b);
if(!ok){
vtUnlock(arena->lock);
return 0;
}
nn += m;
if(nn == n)
break;
off = 0;
a += blockSize;
}
vtUnlock(arena->lock);
return n;
}
/*
* allocate space for the clump and write it,
* updating the arena directory
ZZZ question: should this distinguish between an arena
filling up and real errors writing the clump?
*/
u64int
writeAClump(Arena *arena, Clump *c, u8int *clbuf)
{
DBlock *b;
u64int a, aa;
u32int clump, n, nn, m, off, blockSize;
int ok;
n = c->info.size + ClumpSize;
vtLock(arena->lock);
aa = arena->used;
if(arena->sealed
|| aa + n + U32Size + arenaDirSize(arena, arena->clumps + 1) > arena->size){
if(!arena->sealed)
sealArena(arena);
vtUnlock(arena->lock);
return TWID64;
}
if(!packClump(c, &clbuf[0])){
vtUnlock(arena->lock);
return TWID64;
}
/*
* write the data out one block at a time
*/
blockSize = arena->blockSize;
a = arena->base + aa;
off = a & (blockSize - 1);
a -= off;
nn = 0;
for(;;){
b = getDBlock(arena->part, a, off != 0);
if(b == nil){
vtUnlock(arena->lock);
return TWID64;
}
m = blockSize - off;
if(m > n - nn)
m = n - nn;
memmove(&b->data[off], &clbuf[nn], m);
ok = writePart(arena->part, a, b->data, blockSize);
putDBlock(b);
if(!ok){
vtUnlock(arena->lock);
return TWID64;
}
nn += m;
if(nn == n)
break;
off = 0;
a += blockSize;
}
writeClumpInfo(arena, clump, &c->info);
//ZZZ make this an enum param
if((clump & 0x1ff) == 0x1ff){
flushCIBlocks(arena);
wbArena(arena);
}
vtUnlock(arena->lock);
return aa;
}
/*
* once sealed, an arena never has any data added to it.
* it should only be changed to fix errors.
* this also syncs the clump directory.
*/
static void
sealArena(Arena *arena)
{
flushCIBlocks(arena);
arena->sealed = 1;
wbArena(arena);
backSumArena(arena);
}
/*
* read & sum all blocks except the last one
*/
vtSha1Init(s);
b = allocZBlock(bs, 0);
e = arena->base + arena->size;
for(a = arena->base - arena->blockSize; a + arena->blockSize <= e; a += bs){
if(a + bs > e)
bs = arena->blockSize;
if(!readPart(arena->part, a, b->data, bs))
goto ReadErr;
vtSha1Update(s, b->data, bs);
}
/*
* the last one is special, since it may already have the checksum included
*/
bs = arena->blockSize;
if(!readPart(arena->part, e, b->data, bs)){
ReadErr:
logErr(EOk, "sumArena can't sum %s, read at %lld failed: %r", arena->name, a);
freeZBlock(b);
vtSha1Free(s);
return;
}