| #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; |
| } |
| |