391 lines
6.6 KiB
C
391 lines
6.6 KiB
C
#include "stdinc.h"
|
|
#include "vac.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "error.h"
|
|
|
|
static int sizeToDepth(uvlong s, int psize, int dsize);
|
|
|
|
static int
|
|
sizeToDepth(uvlong s, int psize, int dsize)
|
|
{
|
|
int np;
|
|
int d;
|
|
|
|
/* determine pointer depth */
|
|
np = psize/VtScoreSize;
|
|
s = (s + dsize - 1)/dsize;
|
|
for(d = 0; s > 1; d++)
|
|
s = (s + np - 1)/np;
|
|
return d;
|
|
}
|
|
|
|
/* assumes u is lock? */
|
|
Source *
|
|
sourceAlloc(Cache *c, Lump *u, ulong block, int entry, int readOnly)
|
|
{
|
|
Source *r;
|
|
VtEntry d;
|
|
|
|
if(u->asize < (entry+1)*VtEntrySize) {
|
|
vtSetError(ENoDir);
|
|
return nil;
|
|
}
|
|
|
|
if(!vtEntryUnpack(&d, u->data, entry))
|
|
return nil;
|
|
|
|
if(!(d.flags & VtEntryActive)) {
|
|
fprint(2, "bad flags %#ux %V\n", d.flags, d.score);
|
|
vtSetError(ENoDir);
|
|
return nil;
|
|
}
|
|
|
|
/* HACK for backwards compatiblity - should go away at some point */
|
|
if(d.depth == 0) {
|
|
if(d.size > d.dsize) fprint(2, "depth == 0! size = %ulld\n", d.size);
|
|
d.depth = sizeToDepth(d.size, d.psize, d.dsize);
|
|
}
|
|
|
|
if(d.depth < sizeToDepth(d.size, d.psize, d.dsize)) {
|
|
vtSetError(EBadDir);
|
|
return nil;
|
|
}
|
|
|
|
r = vtMemAllocZ(sizeof(Source));
|
|
r->lk = vtLockAlloc();
|
|
r->cache = c;
|
|
r->readOnly = readOnly;
|
|
r->lump = lumpIncRef(u);
|
|
r->block = block;
|
|
r->entry = entry;
|
|
r->gen = d.gen;
|
|
r->dir = (d.flags & VtEntryDir) != 0;
|
|
r->depth = d.depth;
|
|
r->psize = d.psize;
|
|
r->dsize = d.dsize;
|
|
r->size = d.size;
|
|
|
|
r->epb = r->dsize/VtEntrySize;
|
|
|
|
return r;
|
|
}
|
|
|
|
Source *
|
|
sourceOpen(Source *r, ulong entry, int readOnly)
|
|
{
|
|
ulong bn;
|
|
Lump *u;
|
|
|
|
if(0)fprint(2, "sourceOpen: %V:%d: %lud\n", r->lump->score, r->entry, entry);
|
|
if(r->readOnly && !readOnly) {
|
|
vtSetError(EReadOnly);
|
|
return nil;
|
|
}
|
|
|
|
bn = entry/r->epb;
|
|
|
|
u = sourceGetLump(r, bn, readOnly, 1);
|
|
if(u == nil)
|
|
return nil;
|
|
|
|
r = sourceAlloc(r->cache, u, bn, entry%r->epb, readOnly);
|
|
lumpDecRef(u, 1);
|
|
return r;
|
|
}
|
|
|
|
Source *
|
|
sourceCreate(Source *r, int psize, int dsize, int isdir, ulong entry)
|
|
{
|
|
Source *rr;
|
|
int i;
|
|
Lump *u;
|
|
ulong bn;
|
|
VtEntry dir;
|
|
|
|
if(r->readOnly) {
|
|
vtSetError(EReadOnly);
|
|
return nil;
|
|
}
|
|
|
|
if(entry == 0) {
|
|
/*
|
|
* look at a random block to see if we can find an empty entry
|
|
*/
|
|
entry = sourceGetDirSize(r);
|
|
entry = r->epb*lnrand(entry/r->epb+1);
|
|
}
|
|
|
|
/*
|
|
* need to loop since multiple threads could be trying to allocate
|
|
*/
|
|
for(;;) {
|
|
bn = entry/r->epb;
|
|
sourceSetDepth(r, (uvlong)(bn+1)*r->dsize);
|
|
u = sourceGetLump(r, bn, 0, 1);
|
|
if(u == nil)
|
|
return nil;
|
|
for(i=entry%r->epb; i<r->epb; i++) {
|
|
vtEntryUnpack(&dir, u->data, i);
|
|
if((dir.flags&VtEntryActive) == 0 && dir.gen != ~0)
|
|
goto Found;
|
|
}
|
|
lumpDecRef(u, 1);
|
|
entry = sourceGetDirSize(r);
|
|
}
|
|
Found:
|
|
/* found an entry */
|
|
dir.psize = psize;
|
|
dir.dsize = dsize;
|
|
dir.flags = VtEntryActive;
|
|
if(isdir)
|
|
dir.flags |= VtEntryDir;
|
|
dir.depth = 0;
|
|
dir.size = 0;
|
|
memmove(dir.score, vtZeroScore, VtScoreSize);
|
|
vtEntryPack(&dir, u->data, i);
|
|
|
|
sourceSetDirSize(r, bn*r->epb + i + 1);
|
|
rr = sourceAlloc(r->cache, u, bn, i, 0);
|
|
|
|
lumpDecRef(u, 1);
|
|
return rr;
|
|
}
|
|
|
|
void
|
|
sourceRemove(Source *r)
|
|
{
|
|
lumpFreeEntry(r->lump, r->entry);
|
|
sourceFree(r);
|
|
}
|
|
|
|
int
|
|
sourceSetDepth(Source *r, uvlong size)
|
|
{
|
|
Lump *u, *v;
|
|
VtEntry dir;
|
|
int depth;
|
|
|
|
if(r->readOnly){
|
|
vtSetError(EReadOnly);
|
|
return 0;
|
|
}
|
|
|
|
depth = sizeToDepth(size, r->psize, r->dsize);
|
|
|
|
assert(depth >= 0);
|
|
|
|
if(depth > VtPointerDepth) {
|
|
vtSetError(ETooBig);
|
|
return 0;
|
|
}
|
|
|
|
vtLock(r->lk);
|
|
|
|
if(r->depth >= depth) {
|
|
vtUnlock(r->lk);
|
|
return 1;
|
|
}
|
|
|
|
u = r->lump;
|
|
vtLock(u->lk);
|
|
if(!vtEntryUnpack(&dir, u->data, r->entry)) {
|
|
vtUnlock(u->lk);
|
|
vtUnlock(r->lk);
|
|
return 0;
|
|
}
|
|
while(dir.depth < depth) {
|
|
v = cacheAllocLump(r->cache, VtPointerType0+r->depth, r->psize, r->dir);
|
|
if(v == nil)
|
|
break;
|
|
memmove(v->data, dir.score, VtScoreSize);
|
|
memmove(dir.score, v->score, VtScoreSize);
|
|
dir.depth++;
|
|
vtUnlock(v->lk);
|
|
}
|
|
vtEntryPack(&dir, u->data, r->entry);
|
|
vtUnlock(u->lk);
|
|
|
|
r->depth = dir.depth;
|
|
vtUnlock(r->lk);
|
|
|
|
return dir.depth == depth;
|
|
}
|
|
|
|
int
|
|
sourceGetVtEntry(Source *r, VtEntry *dir)
|
|
{
|
|
Lump *u;
|
|
|
|
u = r->lump;
|
|
vtLock(u->lk);
|
|
if(!vtEntryUnpack(dir, u->data, r->entry)) {
|
|
vtUnlock(u->lk);
|
|
return 0;
|
|
}
|
|
vtUnlock(u->lk);
|
|
return 1;
|
|
}
|
|
|
|
uvlong
|
|
sourceGetSize(Source *r)
|
|
{
|
|
uvlong size;
|
|
|
|
vtLock(r->lk);
|
|
size = r->size;
|
|
vtUnlock(r->lk);
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
int
|
|
sourceSetSize(Source *r, uvlong size)
|
|
{
|
|
Lump *u;
|
|
VtEntry dir;
|
|
int depth;
|
|
|
|
if(r->readOnly) {
|
|
vtSetError(EReadOnly);
|
|
return 0;
|
|
}
|
|
|
|
if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize) {
|
|
vtSetError(ETooBig);
|
|
return 0;
|
|
}
|
|
|
|
vtLock(r->lk);
|
|
depth = sizeToDepth(size, r->psize, r->dsize);
|
|
if(size < r->size) {
|
|
vtUnlock(r->lk);
|
|
return 1;
|
|
}
|
|
if(depth > r->depth) {
|
|
vtSetError(EBadDir);
|
|
vtUnlock(r->lk);
|
|
return 0;
|
|
}
|
|
|
|
u = r->lump;
|
|
vtLock(u->lk);
|
|
vtEntryUnpack(&dir, u->data, r->entry);
|
|
dir.size = size;
|
|
vtEntryPack(&dir, u->data, r->entry);
|
|
vtUnlock(u->lk);
|
|
r->size = size;
|
|
vtUnlock(r->lk);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
sourceSetDirSize(Source *r, ulong ds)
|
|
{
|
|
uvlong size;
|
|
|
|
size = (uvlong)r->dsize*(ds/r->epb);
|
|
size += VtEntrySize*(ds%r->epb);
|
|
return sourceSetSize(r, size);
|
|
}
|
|
|
|
ulong
|
|
sourceGetDirSize(Source *r)
|
|
{
|
|
ulong ds;
|
|
uvlong size;
|
|
|
|
size = sourceGetSize(r);
|
|
ds = r->epb*(size/r->dsize);
|
|
ds += (size%r->dsize)/VtEntrySize;
|
|
return ds;
|
|
}
|
|
|
|
ulong
|
|
sourceGetNumBlocks(Source *r)
|
|
{
|
|
return (sourceGetSize(r)+r->dsize-1)/r->dsize;
|
|
}
|
|
|
|
Lump *
|
|
sourceWalk(Source *r, ulong block, int readOnly, int *off)
|
|
{
|
|
int depth;
|
|
int i, np;
|
|
Lump *u, *v;
|
|
int elem[VtPointerDepth+1];
|
|
ulong b;
|
|
|
|
if(r->readOnly && !readOnly) {
|
|
vtSetError(EReadOnly);
|
|
return nil;
|
|
}
|
|
|
|
vtLock(r->lk);
|
|
np = r->psize/VtScoreSize;
|
|
b = block;
|
|
for(i=0; i<r->depth; i++) {
|
|
elem[i] = b % np;
|
|
b /= np;
|
|
}
|
|
if(b != 0) {
|
|
vtUnlock(r->lk);
|
|
vtSetError(EBadOffset);
|
|
return nil;
|
|
}
|
|
elem[i] = r->entry;
|
|
u = lumpIncRef(r->lump);
|
|
depth = r->depth;
|
|
*off = elem[0];
|
|
vtUnlock(r->lk);
|
|
|
|
for(i=depth; i>0; i--) {
|
|
v = lumpWalk(u, elem[i], VtPointerType0+i-1, r->psize, readOnly, 0);
|
|
lumpDecRef(u, 0);
|
|
if(v == nil)
|
|
return nil;
|
|
u = v;
|
|
}
|
|
|
|
return u;
|
|
}
|
|
|
|
Lump *
|
|
sourceGetLump(Source *r, ulong block, int readOnly, int lock)
|
|
{
|
|
int type, off;
|
|
Lump *u, *v;
|
|
|
|
if(r->readOnly && !readOnly) {
|
|
vtSetError(EReadOnly);
|
|
return nil;
|
|
}
|
|
if(block == NilBlock) {
|
|
vtSetError(ENilBlock);
|
|
return nil;
|
|
}
|
|
if(0)fprint(2, "sourceGetLump: %V:%d %lud\n", r->lump->score, r->entry, block);
|
|
u = sourceWalk(r, block, readOnly, &off);
|
|
if(u == nil)
|
|
return nil;
|
|
if(r->dir)
|
|
type = VtDirType;
|
|
else
|
|
type = VtDataType;
|
|
v = lumpWalk(u, off, type, r->dsize, readOnly, lock);
|
|
lumpDecRef(u, 0);
|
|
return v;
|
|
}
|
|
|
|
void
|
|
sourceFree(Source *k)
|
|
{
|
|
if(k == nil)
|
|
return;
|
|
lumpDecRef(k->lump, 0);
|
|
vtLockFree(k->lk);
|
|
memset(k, ~0, sizeof(*k));
|
|
vtMemFree(k);
|
|
}
|