#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; idata, 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; idata, 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) 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; iindex >= 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; ifile); 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; idata, 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; }