plan9port/src/cmd/vac/file.c

1215 lines
18 KiB
C
Raw Normal View History

#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
/*
* locking order is upwards. A thread can hold the lock for a VacFile
* and then acquire the lock of its parent
*/
struct VacFile {
/* meta data for file: protected by the lk in the parent */
int ref; /* holds this data structure up */
VacFS *fs; /* immutable */
int removed; /* file has been removed */
int dirty; /* dir is dirty with respect to meta data in block */
ulong block; /* block offset withing msource for this file's meta data */
VacDir dir; /* meta data for this file */
VacFile *up; /* parent file */
VacFile *next; /* sibling */
/* data for file */
VtLock *lk; /* lock for source and msource */
Source *source;
Source *msource; /* for directories: meta data for children */
VacFile *down; /* children */
};
static int vfMetaFlush(VacFile*);
static ulong msAlloc(Source *ms, ulong, int n);
static void
vfRUnlock(VacFile *vf)
{
vtRUnlock(vf->lk);
}
static int
vfRLock(VacFile *vf)
{
vtRLock(vf->lk);
if(vf->source == nil) {
vfRUnlock(vf);
vtSetError(ERemoved);
return 0;
}
return 1;
}
static void
vfUnlock(VacFile *vf)
{
vtUnlock(vf->lk);
}
static int
vfLock(VacFile *vf)
{
vtLock(vf->lk);
if(vf->source == nil) {
vfUnlock(vf);
vtSetError(ERemoved);
return 0;
}
return 1;
}
static void
vfMetaLock(VacFile *vf)
{
assert(vf->up->msource != nil);
vtLock(vf->up->lk);
}
static void
vfMetaUnlock(VacFile *vf)
{
vtUnlock(vf->up->lk);
}
static void
vfRAccess(VacFile* vf)
{
vfMetaLock(vf);
vf->dir.atime = time(0L);
vf->dirty = 1;
vfMetaUnlock(vf);
vfMetaFlush(vf);
}
static void
vfWAccess(VacFile* vf, char *mid)
{
vfMetaLock(vf);
vf->dir.atime = vf->dir.mtime = time(0L);
if(strcmp(vf->dir.mid, mid) != 0) {
vtMemFree(vf->dir.mid);
vf->dir.mid = vtStrDup(mid);
}
vf->dir.mcount++;
vf->dirty = 1;
vfMetaUnlock(vf);
vfMetaFlush(vf);
}
void
vdCleanup(VacDir *dir)
{
vtMemFree(dir->elem);
dir->elem = nil;
vtMemFree(dir->uid);
dir->uid = nil;
vtMemFree(dir->gid);
dir->gid = nil;
vtMemFree(dir->mid);
dir->mid = nil;
}
void
vdCopy(VacDir *dst, VacDir *src)
{
*dst = *src;
dst->elem = vtStrDup(src->elem);
dst->uid = vtStrDup(src->uid);
dst->gid = vtStrDup(src->gid);
dst->mid = vtStrDup(src->mid);
}
static int
mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
{
int i;
int b, t, x;
/* binary search within block */
b = 0;
t = mb->nindex;
while(b < t) {
i = (b+t)>>1;
if(!meUnpack(me, mb, i))
return 0;
if(mb->unbotch)
x = meCmpNew(me, elem);
else
x = meCmp(me, elem);
if(x == 0) {
*ri = i;
return 1;
}
if(x < 0)
b = i+1;
else /* x > 0 */
t = i;
}
assert(b == t);
*ri = b; /* b is the index to insert this entry */
memset(me, 0, sizeof(*me));
return 1;
}
static void
mbInit(MetaBlock *mb, uchar *p, int n)
{
memset(mb, 0, sizeof(MetaBlock));
mb->maxsize = n;
mb->buf = p;
mb->maxindex = n/100;
mb->size = MetaHeaderSize + mb->maxindex*MetaIndexSize;
}
static int
vfMetaFlush(VacFile *vf)
{
VacFile *vfp;
Lump *u;
MetaBlock mb;
MetaEntry me, nme;
uchar *p;
int i, n, moved;
//print("vfMetaFlush %s\n", vf->dir.elem);
/* assume name has not changed for the moment */
vfMetaLock(vf);
vfp = vf->up;
moved = 0;
u = sourceGetLump(vfp->msource, vf->block, 0, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
if(!mbSearch(&mb, vf->dir.elem, &i, &me) || me.p == nil)
goto Err;
nme = me;
n = vdSize(&vf->dir);
//print("old size %d new size %d\n", me.size, n);
if(n <= nme.size) {
nme.size = n;
} else {
/* try expand entry? */
p = mbAlloc(&mb, n);
//print("alloced %ld\n", p - mb.buf);
if(p == nil) {
assert(0);
/* much more work */
}
nme.p = p;
nme.size = n;
}
mbDelete(&mb, i, &me);
memset(me.p, 0, me.size);
if(!moved) {
vdPack(&vf->dir, &nme);
mbInsert(&mb, i, &nme);
}
mbPack(&mb);
lumpDecRef(u, 1);
vf->dirty = 0;
vfMetaUnlock(vf);
return 1;
Err:
lumpDecRef(u, 1);
vfMetaUnlock(vf);
return 0;
}
static VacFile *
vfAlloc(VacFS *fs)
{
VacFile *vf;
vf = vtMemAllocZ(sizeof(VacFile));
vf->lk = vtLockAlloc();
vf->ref = 1;
vf->fs = fs;
return vf;
}
static void
vfFree(VacFile *vf)
{
sourceFree(vf->source);
vtLockFree(vf->lk);
sourceFree(vf->msource);
vdCleanup(&vf->dir);
vtMemFree(vf);
}
/* the file is locked already */
static VacFile *
dirLookup(VacFile *vf, char *elem)
{
int i, j, nb;
MetaBlock mb;
MetaEntry me;
Lump *u;
Source *meta;
VacFile *nvf;
meta = vf->msource;
u = nil;
nb = sourceGetNumBlocks(meta);
for(i=0; i<nb; i++) {
u = sourceGetLump(meta, i, 1, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
if(!mbSearch(&mb, elem, &j, &me))
goto Err;
if(me.p != nil) {
nvf = vfAlloc(vf->fs);
if(!vdUnpack(&nvf->dir, &me)) {
vfFree(nvf);
goto Err;
}
lumpDecRef(u, 1);
nvf->block = i;
return nvf;
}
lumpDecRef(u, 1);
u = nil;
}
vtSetError("file does not exist");
/* fall through */
Err:
lumpDecRef(u, 1);
return nil;
}
VacFile *
vfRoot(VacFS *fs, uchar *score)
{
VtEntry e;
Lump *u, *v;
Source *r, *r0, *r1, *r2;
MetaBlock mb;
MetaEntry me;
VacFile *root, *mr;
root = nil;
mr = nil;
r0 = nil;
r1 = nil;
r2 = nil;
v = nil;
r = nil;
u = cacheGetLump(fs->cache, score, VtDirType, fs->bsize);
if(u == nil)
goto Err;
if(!fs->readOnly) {
v = cacheAllocLump(fs->cache, VtDirType, fs->bsize, 1);
if(v == nil) {
vtUnlock(u->lk);
goto Err;
}
v->gen = u->gen;
v->asize = u->asize;
v->state = LumpActive;
memmove(v->data, u->data, v->asize);
lumpDecRef(u, 1);
u = v;
v = nil;
}
vtUnlock(u->lk);
vtEntryUnpack(&e, u->data, 2);
if(e.flags == 0){ /* just one entry */
r = sourceAlloc(fs->cache, u, 0, 0, fs->readOnly);
if(r == nil)
goto Err;
r0 = sourceOpen(r, 0, fs->readOnly);
if(r0 == nil)
goto Err;
r1 = sourceOpen(r, 1, fs->readOnly);
if(r1 == nil)
goto Err;
r2 = sourceOpen(r, 2, fs->readOnly);
if(r2 == nil)
goto Err;
sourceFree(r);
r = nil;
}else{
r0 = sourceAlloc(fs->cache, u, 0, 0, fs->readOnly);
if(r0 == nil)
goto Err;
r1 = sourceAlloc(fs->cache, u, 0, 1, fs->readOnly);
if(r1 == nil)
goto Err;
r2 = sourceAlloc(fs->cache, u, 0, 2, fs->readOnly);
if(r2 == nil)
goto Err;
}
lumpDecRef(u, 0);
u = sourceGetLump(r2, 0, 1, 0);
if(u == nil)
goto Err;
mr = vfAlloc(fs);
mr->msource = r2;
r2 = nil;
root = vfAlloc(fs);
root->up = mr;
root->source = r0;
r0 = nil;
root->msource = r1;
r1 = nil;
mr->down = root;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
if(!meUnpack(&me, &mb, 0))
goto Err;
if(!vdUnpack(&root->dir, &me))
goto Err;
vfRAccess(root);
lumpDecRef(u, 0);
sourceFree(r2);
return root;
Err:
lumpDecRef(u, 0);
lumpDecRef(v, 0);
if(r0)
sourceFree(r0);
if(r1)
sourceFree(r1);
if(r2)
sourceFree(r2);
if(r)
sourceFree(r);
if(mr)
vfFree(mr);
if(root)
vfFree(root);
return nil;
}
VacFile *
vfWalk(VacFile *vf, char *elem)
{
VacFile *nvf;
vfRAccess(vf);
if(elem[0] == 0) {
vtSetError("illegal path element");
return nil;
}
if(!vfIsDir(vf)) {
vtSetError("not a directory");
return nil;
}
if(strcmp(elem, ".") == 0) {
return vfIncRef(vf);
}
if(strcmp(elem, "..") == 0) {
if(vfIsRoot(vf))
return vfIncRef(vf);
return vfIncRef(vf->up);
}
if(!vfLock(vf))
return nil;
for(nvf = vf->down; nvf; nvf=nvf->next) {
if(strcmp(elem, nvf->dir.elem) == 0 && !nvf->removed) {
nvf->ref++;
goto Exit;
}
}
nvf = dirLookup(vf, elem);
if(nvf == nil)
goto Err;
nvf->source = sourceOpen(vf->source, nvf->dir.entry, vf->fs->readOnly);
if(nvf->source == nil)
goto Err;
if(nvf->dir.mode & ModeDir) {
nvf->msource = sourceOpen(vf->source, nvf->dir.mentry, vf->fs->readOnly);
if(nvf->msource == nil)
goto Err;
}
/* link in and up parent ref count */
nvf->next = vf->down;
vf->down = nvf;
nvf->up = vf;
vfIncRef(vf);
Exit:
vfUnlock(vf);
return nvf;
Err:
vfUnlock(vf);
if(nvf != nil)
vfFree(nvf);
return nil;
}
VacFile *
vfOpen(VacFS *fs, char *path)
{
VacFile *vf, *nvf;
char *p, elem[VtMaxStringSize];
int n;
vf = fs->root;
vfIncRef(vf);
while(*path != 0) {
for(p = path; *p && *p != '/'; p++)
;
n = p - path;
if(n > 0) {
if(n > VtMaxStringSize) {
vtSetError("path element too long");
goto Err;
}
memmove(elem, path, n);
elem[n] = 0;
nvf = vfWalk(vf, elem);
if(nvf == nil)
goto Err;
vfDecRef(vf);
vf = nvf;
}
if(*p == '/')
p++;
path = p;
}
return vf;
Err:
vfDecRef(vf);
return nil;
}
VacFile *
vfCreate(VacFile *vf, char *elem, ulong mode, char *user)
{
VacFile *nvf;
VacDir *dir;
int n, i;
uchar *p;
Source *pr, *r, *mr;
int isdir;
MetaBlock mb;
MetaEntry me;
Lump *u;
if(!vfLock(vf))
return nil;
r = nil;
mr = nil;
u = nil;
for(nvf = vf->down; nvf; nvf=nvf->next) {
if(strcmp(elem, nvf->dir.elem) == 0 && !nvf->removed) {
nvf = nil;
vtSetError(EExists);
goto Err;
}
}
nvf = dirLookup(vf, elem);
if(nvf != nil) {
vtSetError(EExists);
goto Err;
}
nvf = vfAlloc(vf->fs);
isdir = mode & ModeDir;
pr = vf->source;
r = sourceCreate(pr, pr->psize, pr->dsize, isdir, 0);
if(r == nil)
goto Err;
if(isdir) {
mr = sourceCreate(pr, pr->psize, pr->dsize, 0, r->block*pr->epb + r->entry);
if(mr == nil)
goto Err;
}
dir = &nvf->dir;
dir->elem = vtStrDup(elem);
dir->entry = r->block*pr->epb + r->entry;
dir->gen = r->gen;
if(isdir) {
dir->mentry = mr->block*pr->epb + mr->entry;
dir->mgen = mr->gen;
}
dir->size = 0;
dir->qid = vf->fs->qid++;
dir->uid = vtStrDup(user);
dir->gid = vtStrDup(vf->dir.gid);
dir->mid = vtStrDup(user);
dir->mtime = time(0L);
dir->mcount = 0;
dir->ctime = dir->mtime;
dir->atime = dir->mtime;
dir->mode = mode;
n = vdSize(dir);
nvf->block = msAlloc(vf->msource, 0, n);
if(nvf->block == NilBlock)
goto Err;
u = sourceGetLump(vf->msource, nvf->block, 0, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
p = mbAlloc(&mb, n);
if(p == nil)
goto Err;
if(!mbSearch(&mb, elem, &i, &me))
goto Err;
assert(me.p == nil);
me.p = p;
me.size = n;
vdPack(dir, &me);
mbInsert(&mb, i, &me);
mbPack(&mb);
lumpDecRef(u, 1);
nvf->source = r;
nvf->msource = mr;
/* link in and up parent ref count */
nvf->next = vf->down;
vf->down = nvf;
nvf->up = vf;
vfIncRef(vf);
vfWAccess(vf, user);
vfUnlock(vf);
return nvf;
Err:
lumpDecRef(u, 1);
if(r)
sourceRemove(r);
if(mr)
sourceRemove(mr);
if(nvf)
vfFree(nvf);
vfUnlock(vf);
return 0;
}
int
vfRead(VacFile *vf, void *buf, int cnt, vlong offset)
{
Source *s;
uvlong size;
ulong bn;
int off, dsize, n, nn;
Lump *u;
uchar *b;
if(0)fprint(2, "vfRead: %s %d, %lld\n", vf->dir.elem, cnt, offset);
if(!vfRLock(vf))
return -1;
s = vf->source;
dsize = s->dsize;
size = sourceGetSize(s);
if(offset < 0) {
vtSetError(EBadOffset);
goto Err;
}
vfRAccess(vf);
if(offset >= size)
offset = size;
if(cnt > size-offset)
cnt = size-offset;
bn = offset/dsize;
off = offset%dsize;
b = buf;
while(cnt > 0) {
u = sourceGetLump(s, bn, 1, 0);
if(u == nil)
goto Err;
if(u->asize <= off) {
lumpDecRef(u, 0);
goto Err;
}
n = cnt;
if(n > dsize-off)
n = dsize-off;
nn = u->asize-off;
if(nn > n)
nn = n;
memmove(b, u->data+off, nn);
memset(b+nn, 0, n-nn);
off = 0;
bn++;
cnt -= n;
b += n;
lumpDecRef(u, 0);
}
vfRUnlock(vf);
return b-(uchar*)buf;
Err:
vfRUnlock(vf);
return -1;
}
int
vfWrite(VacFile *vf, void *buf, int cnt, vlong offset, char *user)
{
Source *s;
ulong bn;
int off, dsize, n;
Lump *u;
uchar *b;
USED(user);
if(!vfLock(vf))
return -1;
if(vf->fs->readOnly) {
vtSetError(EReadOnly);
goto Err;
}
if(vf->dir.mode & ModeDir) {
vtSetError(ENotFile);
goto Err;
}
if(0)fprint(2, "vfWrite: %s %d, %lld\n", vf->dir.elem, cnt, offset);
s = vf->source;
dsize = s->dsize;
if(offset < 0) {
vtSetError(EBadOffset);
goto Err;
}
vfWAccess(vf, user);
bn = offset/dsize;
off = offset%dsize;
b = buf;
while(cnt > 0) {
n = cnt;
if(n > dsize-off)
n = dsize-off;
if(!sourceSetDepth(s, offset+n))
goto Err;
u = sourceGetLump(s, bn, 0, 0);
if(u == nil)
goto Err;
if(u->asize < dsize) {
vtSetError("runt block");
lumpDecRef(u, 0);
goto Err;
}
memmove(u->data+off, b, n);
off = 0;
cnt -= n;
b += n;
offset += n;
bn++;
lumpDecRef(u, 0);
if(!sourceSetSize(s, offset))
goto Err;
}
vfLock(vf);
return b-(uchar*)buf;
Err:
vfLock(vf);
return -1;
}
int
vfGetDir(VacFile *vf, VacDir *dir)
{
if(!vfRLock(vf))
return 0;
vfMetaLock(vf);
vdCopy(dir, &vf->dir);
vfMetaUnlock(vf);
if(!vfIsDir(vf))
dir->size = sourceGetSize(vf->source);
vfRUnlock(vf);
return 1;
}
uvlong
vfGetId(VacFile *vf)
{
/* immutable */
return vf->dir.qid;
}
ulong
vfGetMcount(VacFile *vf)
{
ulong mcount;
vfMetaLock(vf);
mcount = vf->dir.mcount;
vfMetaUnlock(vf);
return mcount;
}
int
vfIsDir(VacFile *vf)
{
/* immutable */
return (vf->dir.mode & ModeDir) != 0;
}
int
vfIsRoot(VacFile *vf)
{
return vf == vf->fs->root;
}
int
vfGetSize(VacFile *vf, uvlong *size)
{
if(!vfRLock(vf))
return 0;
*size = sourceGetSize(vf->source);
vfRUnlock(vf);
return 1;
}
static int
vfMetaRemove(VacFile *vf, char *user)
{
Lump *u;
MetaBlock mb;
MetaEntry me;
int i;
VacFile *vfp;
vfp = vf->up;
vfWAccess(vfp, user);
vfMetaLock(vf);
u = sourceGetLump(vfp->msource, vf->block, 0, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
if(!mbSearch(&mb, vf->dir.elem, &i, &me) || me.p == nil)
goto Err;
print("deleting %d entry\n", i);
mbDelete(&mb, i, &me);
memset(me.p, 0, me.size);
mbPack(&mb);
lumpDecRef(u, 1);
vf->removed = 1;
vf->block = NilBlock;
vfMetaUnlock(vf);
return 1;
Err:
lumpDecRef(u, 1);
vfMetaUnlock(vf);
return 0;
}
static int
vfCheckEmpty(VacFile *vf)
{
int i, n;
Lump *u;
MetaBlock mb;
Source *r;
r = vf->msource;
n = sourceGetNumBlocks(r);
for(i=0; i<n; i++) {
u = sourceGetLump(r, i, 1, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
if(mb.nindex > 0) {
vtSetError(ENotEmpty);
goto Err;
}
lumpDecRef(u, 1);
}
return 1;
Err:
lumpDecRef(u, 1);
return 0;
}
int
vfRemove(VacFile *vf, char *user)
{
/* can not remove the root */
if(vfIsRoot(vf)) {
vtSetError(ERoot);
return 0;
}
if(!vfLock(vf))
return 0;
if(vfIsDir(vf) && !vfCheckEmpty(vf))
goto Err;
assert(vf->down == nil);
sourceRemove(vf->source);
vf->source = nil;
if(vf->msource) {
sourceRemove(vf->msource);
vf->msource = nil;
}
vfUnlock(vf);
if(!vfMetaRemove(vf, user))
return 0;
return 1;
Err:
vfUnlock(vf);
return 0;
}
VacFile *
vfIncRef(VacFile *vf)
{
vfMetaLock(vf);
assert(vf->ref > 0);
vf->ref++;
vfMetaUnlock(vf);
return vf;
}
void
vfDecRef(VacFile *vf)
{
VacFile *p, *q, **qq;
if(vf->up == nil) {
vfFree(vf);
return;
}
vfMetaLock(vf);
vf->ref--;
if(vf->ref > 0) {
vfMetaUnlock(vf);
return;
}
assert(vf->ref == 0);
assert(vf->down == nil);
p = vf->up;
qq = &p->down;
for(q = *qq; q; qq=&q->next,q=*qq)
if(q == vf)
break;
assert(q != nil);
*qq = vf->next;
vfMetaUnlock(vf);
vfFree(vf);
vfDecRef(p);
}
int
vfGetVtEntry(VacFile *vf, VtEntry *e)
{
int res;
if(!vfRLock(vf))
return 0;
res = sourceGetVtEntry(vf->source, e);
vfRUnlock(vf);
return res;
}
int
vfGetBlockScore(VacFile *vf, ulong bn, uchar score[VtScoreSize])
{
Lump *u;
int ret, off;
Source *r;
if(!vfRLock(vf))
return 0;
r = vf->source;
u = sourceWalk(r, bn, 1, &off);
if(u == nil){
vfRUnlock(vf);
return 0;
}
ret = lumpGetScore(u, off, score);
lumpDecRef(u, 0);
vfRUnlock(vf);
return ret;
}
VacFile *
vfGetParent(VacFile *vf)
{
if(vfIsRoot(vf))
return vfIncRef(vf);
return vfIncRef(vf->up);
}
static VacDirEnum *
vdeAlloc(VacFile *vf)
{
VacDirEnum *ds;
if(!(vf->dir.mode & ModeDir)) {
vtSetError(ENotDir);
vfDecRef(vf);
return nil;
}
ds = vtMemAllocZ(sizeof(VacDirEnum));
ds->file = vf;
return ds;
}
VacDirEnum *
vdeOpen(VacFS *fs, char *path)
{
VacFile *vf;
vf = vfOpen(fs, path);
if(vf == nil)
return nil;
return vdeAlloc(vf);
}
VacDirEnum *
vfDirEnum(VacFile *vf)
{
return vdeAlloc(vfIncRef(vf));
}
static int
dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
{
Lump *u;
ulong bn;
VtEntry e;
bn = elem/s->epb;
elem -= bn*s->epb;
u = sourceGetLump(s, bn, 1, 1);
if(u == nil)
goto Err;
if(u->asize < (elem+1)*VtEntrySize) {
vtSetError(ENoDir);
goto Err;
}
vtEntryUnpack(&e, u->data, elem);
if(!(e.flags & VtEntryActive) || e.gen != gen) {
fprint(2, "gen mismatch\n");
vtSetError(ENoDir);
goto Err;
}
*size = e.size;
lumpDecRef(u, 1);
return 1;
Err:
lumpDecRef(u, 1);
return 0;
}
int
vdeRead(VacDirEnum *ds, VacDir *dir, int n)
{
ulong nb;
int i;
Source *meta, *source;
MetaBlock mb;
MetaEntry me;
Lump *u;
vfRAccess(ds->file);
if(!vfRLock(ds->file))
return -1;
i = 0;
u = nil;
source = ds->file->source;
meta = ds->file->msource;
nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
if(ds->block >= nb)
goto Exit;
u = sourceGetLump(meta, ds->block, 1, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
for(i=0; i<n; i++) {
while(ds->index >= mb.nindex) {
lumpDecRef(u, 1);
u = nil;
ds->index = 0;
ds->block++;
if(ds->block >= nb)
goto Exit;
u = sourceGetLump(meta, ds->block, 1, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, u->asize))
goto Err;
}
if(!meUnpack(&me, &mb, ds->index))
goto Err;
if(dir != nil) {
if(!vdUnpack(&dir[i], &me))
goto Err;
if(!(dir[i].mode & ModeDir))
if(!dirEntrySize(source, dir[i].entry, dir[i].gen, &dir[i].size))
goto Err;
}
ds->index++;
}
Exit:
lumpDecRef(u, 1);
vfRUnlock(ds->file);
return i;
Err:
lumpDecRef(u, 1);
vfRUnlock(ds->file);
n = i;
for(i=0; i<n ; i++)
vdCleanup(&dir[i]);
return -1;
}
void
vdeFree(VacDirEnum *ds)
{
if(ds == nil)
return;
vfDecRef(ds->file);
vtMemFree(ds);
}
static ulong
msAlloc(Source *ms, ulong start, int n)
{
ulong nb, i;
Lump *u;
MetaBlock mb;
nb = sourceGetNumBlocks(ms);
u = nil;
if(start > nb)
start = nb;
for(i=start; i<nb; i++) {
u = sourceGetLump(ms, i, 1, 1);
if(u == nil)
goto Err;
if(!mbUnpack(&mb, u->data, ms->dsize))
goto Err;
if(mb.maxsize - mb.size + mb.free >= n && mb.nindex < mb.maxindex)
break;
lumpDecRef(u, 1);
u = nil;
}
/* add block to meta file */
if(i == nb) {
if(!sourceSetDepth(ms, (i+1)*ms->dsize))
goto Err;
u = sourceGetLump(ms, i, 0, 1);
if(u == nil)
goto Err;
sourceSetSize(ms, (nb+1)*ms->dsize);
mbInit(&mb, u->data, u->asize);
mbPack(&mb);
}
lumpDecRef(u, 1);
return i;
Err:
lumpDecRef(u, 1);
return NilBlock;
}