mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
1215 lines
18 KiB
C
1215 lines
18 KiB
C
|
#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;
|
||
|
}
|
||
|
|