#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, "%s: bad flags %#ux %V\n", argv0, 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, "%s: depth == 0! size = %ulld\n", argv0, 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, "%s: sourceOpen: %V:%d: %lud\n", argv0, 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; iepb; 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; idepth; 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, "%s: sourceGetLump: %V:%d %lud\n", argv0, 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); }