|
@@ -4,1260 +4,2049 @@
|
|
|
#include "fns.h"
|
|
|
#include "error.h"
|
|
|
|
|
|
+#define debug 0
|
|
|
+
|
|
|
/*
|
|
|
- * locking order is upwards. A thread can hold the lock for a VacFile
|
|
|
- * and then acquire the lock of its parent
|
|
|
+ * Vac file system. This is a simplified version of the same code in Fossil.
|
|
|
+ *
|
|
|
+ * The locking order in the tree is upward: a thread can hold the lock
|
|
|
+ * for a VacFile and then acquire the lock of f->up (the parent),
|
|
|
+ * but not vice-versa.
|
|
|
+ *
|
|
|
+ * A vac file is one or two venti files. Plain data files are one venti file,
|
|
|
+ * while directores are two: a venti data file containing traditional
|
|
|
+ * directory entries, and a venti directory file containing venti
|
|
|
+ * directory entries. The traditional directory entries in the data file
|
|
|
+ * contain integers indexing into the venti directory entry file.
|
|
|
+ * It's a little complicated, but it makes the data usable by standard
|
|
|
+ * tools like venti/copy.
|
|
|
+ *
|
|
|
*/
|
|
|
+
|
|
|
+static int filemetaflush(VacFile*, char*);
|
|
|
|
|
|
-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 */
|
|
|
+struct VacFile
|
|
|
+{
|
|
|
+ VacFs *fs; /* immutable */
|
|
|
|
|
|
- /* data for file */
|
|
|
- VtLock *lk; /* lock for source and msource */
|
|
|
- Source *source;
|
|
|
- Source *msource; /* for directories: meta data for children */
|
|
|
- VacFile *down; /* children */
|
|
|
+ /* meta data for file: protected by the lk in the parent */
|
|
|
+ int ref; /* holds this data structure up */
|
|
|
+
|
|
|
+ int partial; /* file was never really open */
|
|
|
+ int removed; /* file has been removed */
|
|
|
+ int dirty; /* dir is dirty with respect to meta data in block */
|
|
|
+ u32int boff; /* block offset within msource for this file's metadata */
|
|
|
+ VacDir dir; /* metadata for this file */
|
|
|
+ VacFile *up; /* parent file */
|
|
|
+ VacFile *next; /* sibling */
|
|
|
+
|
|
|
+ RWLock lk; /* lock for the following */
|
|
|
+ VtFile *source; /* actual data */
|
|
|
+ VtFile *msource; /* metadata for children in a directory */
|
|
|
+ VacFile *down; /* children */
|
|
|
+ int mode;
|
|
|
+
|
|
|
+ uvlong qidoffset; /* qid offset */
|
|
|
};
|
|
|
|
|
|
-char *vfName(VacFile *, char *);
|
|
|
-
|
|
|
-static int vfMetaFlush(VacFile*);
|
|
|
-static ulong msAlloc(Source *ms, ulong, int n);
|
|
|
+static VacFile*
|
|
|
+filealloc(VacFs *fs)
|
|
|
+{
|
|
|
+ VacFile *f;
|
|
|
+
|
|
|
+ f = vtmallocz(sizeof(VacFile));
|
|
|
+ f->ref = 1;
|
|
|
+ f->fs = fs;
|
|
|
+ f->boff = NilBlock;
|
|
|
+ f->mode = fs->mode;
|
|
|
+ return f;
|
|
|
+}
|
|
|
|
|
|
static void
|
|
|
-vfRUnlock(VacFile *vf)
|
|
|
+filefree(VacFile *f)
|
|
|
{
|
|
|
- vtRUnlock(vf->lk);
|
|
|
+ vtfileclose(f->source);
|
|
|
+ vtfileclose(f->msource);
|
|
|
+ vdcleanup(&f->dir);
|
|
|
+ memset(f, ~0, sizeof *f); /* paranoia */
|
|
|
+ vtfree(f);
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-vfRLock(VacFile *vf)
|
|
|
+chksource(VacFile *f)
|
|
|
{
|
|
|
- vtRLock(vf->lk);
|
|
|
- if(vf->source == nil) {
|
|
|
- vfRUnlock(vf);
|
|
|
- vtSetError(ERemoved);
|
|
|
+ if(f->partial)
|
|
|
return 0;
|
|
|
+
|
|
|
+ if(f->source == nil
|
|
|
+ || ((f->dir.mode & ModeDir) && f->msource == nil)){
|
|
|
+ werrstr(ERemoved);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+filelock(VacFile *f)
|
|
|
+{
|
|
|
+ wlock(&f->lk);
|
|
|
+ if(chksource(f) < 0){
|
|
|
+ wunlock(&f->lk);
|
|
|
+ return -1;
|
|
|
}
|
|
|
- return 1;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-vfUnlock(VacFile *vf)
|
|
|
+fileunlock(VacFile *f)
|
|
|
{
|
|
|
- vtUnlock(vf->lk);
|
|
|
+ wunlock(&f->lk);
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-vfLock(VacFile *vf)
|
|
|
+filerlock(VacFile *f)
|
|
|
{
|
|
|
- vtLock(vf->lk);
|
|
|
- if(vf->source == nil) {
|
|
|
- vfUnlock(vf);
|
|
|
- vtSetError(ERemoved);
|
|
|
- return 0;
|
|
|
+ rlock(&f->lk);
|
|
|
+ if(chksource(f) < 0){
|
|
|
+ runlock(&f->lk);
|
|
|
+ return -1;
|
|
|
}
|
|
|
- return 1;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-vfMetaLock(VacFile *vf)
|
|
|
+filerunlock(VacFile *f)
|
|
|
{
|
|
|
- assert(vf->up->msource != nil);
|
|
|
- vtLock(vf->up->lk);
|
|
|
+ runlock(&f->lk);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * The file metadata, like f->dir and f->ref,
|
|
|
+ * are synchronized via the parent's lock.
|
|
|
+ * This is why locking order goes up.
|
|
|
+ */
|
|
|
static void
|
|
|
-vfMetaUnlock(VacFile *vf)
|
|
|
+filemetalock(VacFile *f)
|
|
|
{
|
|
|
- vtUnlock(vf->up->lk);
|
|
|
+ assert(f->up != nil);
|
|
|
+ wlock(&f->up->lk);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static void
|
|
|
-vfRAccess(VacFile* vf)
|
|
|
+filemetaunlock(VacFile *f)
|
|
|
{
|
|
|
- vfMetaLock(vf);
|
|
|
- vf->dir.atime = time(0L);
|
|
|
- vf->dirty = 1;
|
|
|
- vfMetaUnlock(vf);
|
|
|
- vfMetaFlush(vf);
|
|
|
+ wunlock(&f->up->lk);
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-vfWAccess(VacFile* vf, char *mid)
|
|
|
+uvlong
|
|
|
+vacfilegetid(VacFile *f)
|
|
|
{
|
|
|
- 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);
|
|
|
+ /* immutable */
|
|
|
+fprint(2, "getid %s %lld+%lld = %lld\n", f->dir.elem, f->qidoffset, f->dir.qid, f->qidoffset+f->dir.qid);
|
|
|
+ return f->qidoffset + f->dir.qid;
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-vdCleanup(VacDir *dir)
|
|
|
+uvlong
|
|
|
+vacfilegetqidoffset(VacFile *f)
|
|
|
{
|
|
|
- vtMemFree(dir->elem);
|
|
|
- dir->elem = nil;
|
|
|
- vtMemFree(dir->uid);
|
|
|
- dir->uid = nil;
|
|
|
- vtMemFree(dir->gid);
|
|
|
- dir->gid = nil;
|
|
|
- vtMemFree(dir->mid);
|
|
|
- dir->mid = nil;
|
|
|
+ return f->qidoffset;
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-vdCopy(VacDir *dst, VacDir *src)
|
|
|
+ulong
|
|
|
+vacfilegetmcount(VacFile *f)
|
|
|
{
|
|
|
- *dst = *src;
|
|
|
- dst->elem = vtStrDup(src->elem);
|
|
|
- dst->uid = vtStrDup(src->uid);
|
|
|
- dst->gid = vtStrDup(src->gid);
|
|
|
- dst->mid = vtStrDup(src->mid);
|
|
|
+ ulong mcount;
|
|
|
+
|
|
|
+ filemetalock(f);
|
|
|
+ mcount = f->dir.mcount;
|
|
|
+ filemetaunlock(f);
|
|
|
+ return mcount;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
|
|
|
+ulong
|
|
|
+vacfilegetmode(VacFile *f)
|
|
|
{
|
|
|
- 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);
|
|
|
+ ulong mode;
|
|
|
|
|
|
- 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;
|
|
|
+ filemetalock(f);
|
|
|
+ mode = f->dir.mode;
|
|
|
+ filemetaunlock(f);
|
|
|
+ return mode;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-mbInit(MetaBlock *mb, uchar *p, int n)
|
|
|
+int
|
|
|
+vacfileisdir(VacFile *f)
|
|
|
{
|
|
|
- memset(mb, 0, sizeof(MetaBlock));
|
|
|
- mb->maxsize = n;
|
|
|
- mb->buf = p;
|
|
|
- mb->maxindex = n/100;
|
|
|
- mb->size = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
|
|
+ /* immutable */
|
|
|
+ return (f->dir.mode & ModeDir) != 0;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-vfMetaFlush(VacFile *vf)
|
|
|
+int
|
|
|
+vacfileisroot(VacFile *f)
|
|
|
{
|
|
|
- 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;
|
|
|
+ return f == f->fs->root;
|
|
|
}
|
|
|
|
|
|
-static VacFile *
|
|
|
-vfAlloc(VacFS *fs)
|
|
|
+/*
|
|
|
+ * The files are reference counted, and while the reference
|
|
|
+ * is bigger than zero, each file can be found in its parent's
|
|
|
+ * f->down list (chains via f->next), so that multiple threads
|
|
|
+ * end up sharing a VacFile* when referring to the same file.
|
|
|
+ *
|
|
|
+ * Each VacFile holds a reference to its parent.
|
|
|
+ */
|
|
|
+VacFile*
|
|
|
+vacfileincref(VacFile *vf)
|
|
|
{
|
|
|
- VacFile *vf;
|
|
|
-
|
|
|
- vf = vtMemAllocZ(sizeof(VacFile));
|
|
|
- vf->lk = vtLockAlloc();
|
|
|
- vf->ref = 1;
|
|
|
- vf->fs = fs;
|
|
|
+ filemetalock(vf);
|
|
|
+ assert(vf->ref > 0);
|
|
|
+ vf->ref++;
|
|
|
+ filemetaunlock(vf);
|
|
|
return vf;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-vfFree(VacFile *vf)
|
|
|
+int
|
|
|
+vacfiledecref(VacFile *f)
|
|
|
{
|
|
|
- sourceFree(vf->source);
|
|
|
- vtLockFree(vf->lk);
|
|
|
- sourceFree(vf->msource);
|
|
|
- vdCleanup(&vf->dir);
|
|
|
+ VacFile *p, *q, **qq;
|
|
|
|
|
|
- vtMemFree(vf);
|
|
|
-}
|
|
|
+ if(f->up == nil){
|
|
|
+ /* never linked in */
|
|
|
+ assert(f->ref == 1);
|
|
|
+ filefree(f);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ filemetalock(f);
|
|
|
+ f->ref--;
|
|
|
+ if(f->ref > 0){
|
|
|
+ filemetaunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ assert(f->ref == 0);
|
|
|
+ assert(f->down == nil);
|
|
|
|
|
|
-/* 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;
|
|
|
- }
|
|
|
+ if(f->source && vtfilelock(f->source, -1) >= 0){
|
|
|
+ vtfileflush(f->source);
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ }
|
|
|
+ if(f->msource && vtfilelock(f->msource, -1) >= 0){
|
|
|
+ vtfileflush(f->msource);
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Flush f's directory information to the cache.
|
|
|
+ */
|
|
|
+ filemetaflush(f, nil);
|
|
|
|
|
|
- lumpDecRef(u, 1);
|
|
|
- u = nil;
|
|
|
+ p = f->up;
|
|
|
+ qq = &p->down;
|
|
|
+ for(q = *qq; q; q = *qq){
|
|
|
+ if(q == f)
|
|
|
+ break;
|
|
|
+ qq = &q->next;
|
|
|
}
|
|
|
- vtSetError("file does not exist");
|
|
|
- /* fall through */
|
|
|
-Err:
|
|
|
- lumpDecRef(u, 1);
|
|
|
- return nil;
|
|
|
-}
|
|
|
+ assert(q != nil);
|
|
|
+ *qq = f->next;
|
|
|
|
|
|
-/* point r back at vf */
|
|
|
-static void
|
|
|
-pointback(Source *r, VacFile *vf)
|
|
|
-{
|
|
|
- assert(r->vf == nil);
|
|
|
- r->vf = vf;
|
|
|
+ filemetaunlock(f);
|
|
|
+ filefree(f);
|
|
|
+ vacfiledecref(p);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-VacFile *
|
|
|
-vfRoot(VacFS *fs, uchar *score)
|
|
|
+
|
|
|
+/*
|
|
|
+ * Construct a vacfile for the root of a vac tree, given the
|
|
|
+ * venti file for the root information. That venti file is a
|
|
|
+ * directory file containing VtEntries for three more venti files:
|
|
|
+ * the two venti files making up the root directory, and a
|
|
|
+ * third venti file that would be the metadata half of the
|
|
|
+ * "root's parent".
|
|
|
+ *
|
|
|
+ * Fossil generates slightly different vac files, due to a now
|
|
|
+ * impossible-to-change bug, which contain a VtEntry
|
|
|
+ * for just one venti file, that itself contains the expected
|
|
|
+ * three directory entries. Sigh.
|
|
|
+ */
|
|
|
+VacFile*
|
|
|
+_vacfileroot(VacFs *fs, VtFile *r)
|
|
|
{
|
|
|
- VtEntry e;
|
|
|
- Lump *u, *v;
|
|
|
- Source *r, *r0, *r1, *r2;
|
|
|
+ int redirected;
|
|
|
+ char err[ERRMAX];
|
|
|
+ VtBlock *b;
|
|
|
+ VtFile *r0, *r1, *r2;
|
|
|
MetaBlock mb;
|
|
|
MetaEntry me;
|
|
|
VacFile *root, *mr;
|
|
|
|
|
|
+ redirected = 0;
|
|
|
+Top:
|
|
|
+ b = nil;
|
|
|
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)
|
|
|
+ if(vtfilelock(r, -1) < 0)
|
|
|
+ return nil;
|
|
|
+ r0 = vtfileopen(r, 0, fs->mode);
|
|
|
+ if(debug)
|
|
|
+ fprint(2, "r0 %p\n", r0);
|
|
|
+ if(r0 == nil)
|
|
|
goto Err;
|
|
|
- if(!fs->readOnly) {
|
|
|
- v = cacheAllocLump(fs->cache, VtDirType, fs->bsize, 1);
|
|
|
- if(v == nil) {
|
|
|
- vtUnlock(u->lk);
|
|
|
- goto Err;
|
|
|
+ r2 = vtfileopen(r, 2, fs->mode);
|
|
|
+ if(debug)
|
|
|
+ fprint(2, "r2 %p\n", r2);
|
|
|
+ if(r2 == nil){
|
|
|
+ /*
|
|
|
+ * some vac files (e.g., from fossil)
|
|
|
+ * have an extra layer of indirection.
|
|
|
+ */
|
|
|
+ rerrstr(err, sizeof err);
|
|
|
+ if(!redirected && strstr(err, "not active")){
|
|
|
+ redirected = 1;
|
|
|
+ vtfileunlock(r);
|
|
|
+ r = r0;
|
|
|
+ goto Top;
|
|
|
}
|
|
|
- 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;
|
|
|
+ goto Err;
|
|
|
}
|
|
|
- lumpDecRef(u, 0);
|
|
|
- u = sourceGetLump(r2, 0, 1, 0);
|
|
|
- if(u == nil)
|
|
|
+ r1 = vtfileopen(r, 1, fs->mode);
|
|
|
+ if(debug)
|
|
|
+ fprint(2, "r1 %p\n", r1);
|
|
|
+ if(r1 == nil)
|
|
|
goto Err;
|
|
|
|
|
|
- mr = vfAlloc(fs);
|
|
|
+ mr = filealloc(fs);
|
|
|
mr->msource = r2;
|
|
|
- pointback(r2, mr);
|
|
|
r2 = nil;
|
|
|
|
|
|
- root = vfAlloc(fs);
|
|
|
+ root = filealloc(fs);
|
|
|
+ root->boff = 0;
|
|
|
root->up = mr;
|
|
|
root->source = r0;
|
|
|
- pointback(r0, root);
|
|
|
r0 = nil;
|
|
|
root->msource = r1;
|
|
|
- pointback(r1, root);
|
|
|
r1 = nil;
|
|
|
|
|
|
mr->down = root;
|
|
|
+ vtfileunlock(r);
|
|
|
|
|
|
- if(!mbUnpack(&mb, u->data, u->asize))
|
|
|
- goto Err;
|
|
|
+ if(vtfilelock(mr->msource, VtOREAD) < 0)
|
|
|
+ goto Err1;
|
|
|
+ b = vtfileblock(mr->msource, 0, VtOREAD);
|
|
|
+ vtfileunlock(mr->msource);
|
|
|
+ if(b == nil)
|
|
|
+ goto Err1;
|
|
|
|
|
|
- if(!meUnpack(&me, &mb, 0))
|
|
|
- goto Err;
|
|
|
- if(!vdUnpack(&root->dir, &me))
|
|
|
- goto Err;
|
|
|
+ if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
|
|
|
+ goto Err1;
|
|
|
|
|
|
- vfRAccess(root);
|
|
|
- lumpDecRef(u, 0);
|
|
|
- sourceFree(r2);
|
|
|
+ meunpack(&me, &mb, 0);
|
|
|
+ if(vdunpack(&root->dir, &me) < 0)
|
|
|
+ goto Err1;
|
|
|
+ vtblockput(b);
|
|
|
|
|
|
return root;
|
|
|
Err:
|
|
|
- lumpDecRef(u, 0);
|
|
|
- lumpDecRef(v, 0);
|
|
|
+ vtfileunlock(r);
|
|
|
+Err1:
|
|
|
+ vtblockput(b);
|
|
|
if(r0)
|
|
|
- sourceFree(r0);
|
|
|
+ vtfileclose(r0);
|
|
|
if(r1)
|
|
|
- sourceFree(r1);
|
|
|
+ vtfileclose(r1);
|
|
|
if(r2)
|
|
|
- sourceFree(r2);
|
|
|
- if(r)
|
|
|
- sourceFree(r);
|
|
|
+ vtfileclose(r2);
|
|
|
if(mr)
|
|
|
- vfFree(mr);
|
|
|
+ filefree(mr);
|
|
|
if(root)
|
|
|
- vfFree(root);
|
|
|
+ filefree(root);
|
|
|
|
|
|
return nil;
|
|
|
}
|
|
|
|
|
|
-VacFile *
|
|
|
-vfWalk(VacFile *vf, char *elem)
|
|
|
+/*
|
|
|
+ * Vac directories are a sequence of metablocks, each of which
|
|
|
+ * contains a bunch of metaentries sorted by file name.
|
|
|
+ * The whole sequence isn't sorted, though, so you still have
|
|
|
+ * to look at every block to find a given name.
|
|
|
+ * Dirlookup looks in f for an element name elem.
|
|
|
+ * It returns a new VacFile with the dir, boff, and mode
|
|
|
+ * filled in, but the sources (venti files) are not, and f is
|
|
|
+ * not yet linked into the tree. These details must be taken
|
|
|
+ * care of by the caller.
|
|
|
+ *
|
|
|
+ * f must be locked, f->msource must not.
|
|
|
+ */
|
|
|
+static VacFile*
|
|
|
+dirlookup(VacFile *f, char *elem)
|
|
|
{
|
|
|
- VacFile *nvf;
|
|
|
+ int i;
|
|
|
+ MetaBlock mb;
|
|
|
+ MetaEntry me;
|
|
|
+ VtBlock *b;
|
|
|
+ VtFile *meta;
|
|
|
+ VacFile *ff;
|
|
|
+ u32int bo, nb;
|
|
|
+
|
|
|
+ meta = f->msource;
|
|
|
+ b = nil;
|
|
|
+ if(vtfilelock(meta, -1) < 0)
|
|
|
+ return nil;
|
|
|
+ nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
|
|
|
+ for(bo=0; bo<nb; bo++){
|
|
|
+ b = vtfileblock(meta, bo, VtOREAD);
|
|
|
+ if(b == nil)
|
|
|
+ goto Err;
|
|
|
+ if(mbunpack(&mb, b->data, meta->dsize) < 0)
|
|
|
+ goto Err;
|
|
|
+ if(mbsearch(&mb, elem, &i, &me) >= 0){
|
|
|
+ ff = filealloc(f->fs);
|
|
|
+ if(vdunpack(&ff->dir, &me) < 0){
|
|
|
+ filefree(ff);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+fprint(2, "offset %s %lld\n", ff->dir.elem, ff->dir.qidoffset);
|
|
|
+ ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
|
|
|
+ vtfileunlock(meta);
|
|
|
+ vtblockput(b);
|
|
|
+ ff->boff = bo;
|
|
|
+ ff->mode = f->mode;
|
|
|
+ return ff;
|
|
|
+ }
|
|
|
+ vtblockput(b);
|
|
|
+ b = nil;
|
|
|
+ }
|
|
|
+ werrstr(ENoFile);
|
|
|
+ /* fall through */
|
|
|
+Err:
|
|
|
+ vtfileunlock(meta);
|
|
|
+ vtblockput(b);
|
|
|
+ return nil;
|
|
|
+}
|
|
|
|
|
|
- vfRAccess(vf);
|
|
|
+/*
|
|
|
+ * Open the venti file at offset in the directory f->source.
|
|
|
+ * f is locked.
|
|
|
+ */
|
|
|
+static VtFile *
|
|
|
+fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
|
|
|
+{
|
|
|
+ VtFile *r;
|
|
|
|
|
|
- if(elem[0] == 0) {
|
|
|
- vtSetError("illegal path element");
|
|
|
+ if((r = vtfileopen(f->source, offset, mode)) == nil)
|
|
|
+ return nil;
|
|
|
+ if(r == nil)
|
|
|
+ return nil;
|
|
|
+ if(r->gen != gen){
|
|
|
+ werrstr(ERemoved);
|
|
|
+ vtfileclose(r);
|
|
|
return nil;
|
|
|
}
|
|
|
- if(!vfIsDir(vf)) {
|
|
|
- vtSetError("not a directory");
|
|
|
+ if(r->dir != dir && r->mode != -1){
|
|
|
+ werrstr(EBadMeta);
|
|
|
+ vtfileclose(r);
|
|
|
return nil;
|
|
|
}
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+VacFile*
|
|
|
+vacfilegetparent(VacFile *f)
|
|
|
+{
|
|
|
+ if(vacfileisroot(f))
|
|
|
+ return vacfileincref(f);
|
|
|
+ return vacfileincref(f->up);
|
|
|
+}
|
|
|
|
|
|
- if(strcmp(elem, ".") == 0) {
|
|
|
- return vfIncRef(vf);
|
|
|
+/*
|
|
|
+ * Given an unlocked vacfile (directory) f,
|
|
|
+ * return the vacfile named elem in f.
|
|
|
+ * Interprets . and .. as a convenience to callers.
|
|
|
+ */
|
|
|
+VacFile*
|
|
|
+vacfilewalk(VacFile *f, char *elem)
|
|
|
+{
|
|
|
+ VacFile *ff;
|
|
|
+
|
|
|
+ if(elem[0] == 0){
|
|
|
+ werrstr(EBadPath);
|
|
|
+ return nil;
|
|
|
}
|
|
|
|
|
|
- if(strcmp(elem, "..") == 0) {
|
|
|
- if(vfIsRoot(vf))
|
|
|
- return vfIncRef(vf);
|
|
|
- return vfIncRef(vf->up);
|
|
|
+ if(!vacfileisdir(f)){
|
|
|
+ werrstr(ENotDir);
|
|
|
+ return nil;
|
|
|
}
|
|
|
|
|
|
- if(!vfLock(vf))
|
|
|
+ if(strcmp(elem, ".") == 0)
|
|
|
+ return vacfileincref(f);
|
|
|
+
|
|
|
+ if(strcmp(elem, "..") == 0)
|
|
|
+ return vacfilegetparent(f);
|
|
|
+
|
|
|
+ if(filelock(f) < 0)
|
|
|
return nil;
|
|
|
|
|
|
- for(nvf = vf->down; nvf; nvf=nvf->next) {
|
|
|
- if(strcmp(elem, nvf->dir.elem) == 0 && !nvf->removed) {
|
|
|
- nvf->ref++;
|
|
|
+ for(ff = f->down; ff; ff=ff->next){
|
|
|
+ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
|
|
|
+ ff->ref++;
|
|
|
goto Exit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- nvf = dirLookup(vf, elem);
|
|
|
- if(nvf == nil)
|
|
|
+ ff = dirlookup(f, elem);
|
|
|
+ if(ff == nil)
|
|
|
goto Err;
|
|
|
- nvf->source = sourceOpen(vf->source, nvf->dir.entry, vf->fs->readOnly);
|
|
|
- if(nvf->source == nil)
|
|
|
+
|
|
|
+ if(ff->dir.mode & ModeSnapshot)
|
|
|
+ ff->mode = VtOREAD;
|
|
|
+
|
|
|
+ if(vtfilelock(f->source, f->mode) < 0)
|
|
|
goto Err;
|
|
|
- pointback(nvf->source, nvf);
|
|
|
- if(nvf->dir.mode & ModeDir) {
|
|
|
- nvf->msource = sourceOpen(vf->source, nvf->dir.mentry, vf->fs->readOnly);
|
|
|
- if(nvf->msource == nil)
|
|
|
- goto Err;
|
|
|
- pointback(nvf->msource, nvf);
|
|
|
+ if(ff->dir.mode & ModeDir){
|
|
|
+ ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
|
|
|
+ ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
|
|
|
+ if(ff->source == nil || ff->msource == nil)
|
|
|
+ goto Err1;
|
|
|
+ }else{
|
|
|
+ ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
|
|
|
+ if(ff->source == nil)
|
|
|
+ goto Err1;
|
|
|
}
|
|
|
+ vtfileunlock(f->source);
|
|
|
|
|
|
/* link in and up parent ref count */
|
|
|
- nvf->next = vf->down;
|
|
|
- vf->down = nvf;
|
|
|
- nvf->up = vf;
|
|
|
- vfIncRef(vf);
|
|
|
+ ff->next = f->down;
|
|
|
+ f->down = ff;
|
|
|
+ ff->up = f;
|
|
|
+ vacfileincref(f);
|
|
|
Exit:
|
|
|
- vfUnlock(vf);
|
|
|
- return nvf;
|
|
|
+ fileunlock(f);
|
|
|
+ return ff;
|
|
|
+
|
|
|
+Err1:
|
|
|
+ vtfileunlock(f->source);
|
|
|
Err:
|
|
|
- vfUnlock(vf);
|
|
|
- if(nvf != nil)
|
|
|
- vfFree(nvf);
|
|
|
+ fileunlock(f);
|
|
|
+ if(ff != nil)
|
|
|
+ vacfiledecref(ff);
|
|
|
return nil;
|
|
|
}
|
|
|
|
|
|
-VacFile *
|
|
|
-vfOpen(VacFS *fs, char *path)
|
|
|
+/*
|
|
|
+ * Open a path in the vac file system:
|
|
|
+ * just walk each element one at a time.
|
|
|
+ */
|
|
|
+VacFile*
|
|
|
+vacfileopen(VacFs *fs, char *path)
|
|
|
{
|
|
|
- VacFile *vf, *nvf;
|
|
|
- char *p, elem[VtMaxStringSize];
|
|
|
+ VacFile *f, *ff;
|
|
|
+ char *p, elem[VtMaxStringSize], *opath;
|
|
|
int n;
|
|
|
|
|
|
- vf = fs->root;
|
|
|
- vfIncRef(vf);
|
|
|
- while(*path != 0) {
|
|
|
+ f = fs->root;
|
|
|
+ vacfileincref(f);
|
|
|
+ opath = path;
|
|
|
+ while(*path != 0){
|
|
|
for(p = path; *p && *p != '/'; p++)
|
|
|
;
|
|
|
n = p - path;
|
|
|
- if(n > 0) {
|
|
|
- if(n > VtMaxStringSize) {
|
|
|
- vtSetError("path element too long");
|
|
|
+ if(n > 0){
|
|
|
+ if(n > VtMaxStringSize){
|
|
|
+ werrstr("%s: element too long", EBadPath);
|
|
|
goto Err;
|
|
|
}
|
|
|
memmove(elem, path, n);
|
|
|
elem[n] = 0;
|
|
|
- nvf = vfWalk(vf, elem);
|
|
|
- if(nvf == nil)
|
|
|
+ ff = vacfilewalk(f, elem);
|
|
|
+ if(ff == nil){
|
|
|
+ werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
|
|
|
goto Err;
|
|
|
- vfDecRef(vf);
|
|
|
- vf = nvf;
|
|
|
+ }
|
|
|
+ vacfiledecref(f);
|
|
|
+ f = ff;
|
|
|
}
|
|
|
if(*p == '/')
|
|
|
p++;
|
|
|
path = p;
|
|
|
}
|
|
|
- return vf;
|
|
|
+ return f;
|
|
|
Err:
|
|
|
- vfDecRef(vf);
|
|
|
+ vacfiledecref(f);
|
|
|
return nil;
|
|
|
}
|
|
|
|
|
|
-VacFile *
|
|
|
-vfCreate(VacFile *vf, char *elem, ulong mode, char *user)
|
|
|
+/*
|
|
|
+ * Extract the score for the bn'th block in f.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfileblockscore(VacFile *f, u32int bn, u8int *score)
|
|
|
{
|
|
|
- VacFile *nvf;
|
|
|
- VacDir *dir;
|
|
|
- int n, i;
|
|
|
- uchar *p;
|
|
|
- Source *pr, *r, *mr;
|
|
|
- int isdir;
|
|
|
- MetaBlock mb;
|
|
|
- MetaEntry me;
|
|
|
- Lump *u;
|
|
|
+ VtFile *s;
|
|
|
+ uvlong size;
|
|
|
+ int dsize, ret;
|
|
|
|
|
|
- if(!vfLock(vf))
|
|
|
- return nil;
|
|
|
+ ret = -1;
|
|
|
+ if(filerlock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vtfilelock(f->source, VtOREAD) < 0)
|
|
|
+ goto out;
|
|
|
|
|
|
- r = nil;
|
|
|
- mr = nil;
|
|
|
- u = nil;
|
|
|
+ s = f->source;
|
|
|
+ dsize = s->dsize;
|
|
|
+ size = vtfilegetsize(s);
|
|
|
+ if((uvlong)bn*dsize >= size)
|
|
|
+ goto out;
|
|
|
+ ret = vtfileblockscore(f->source, bn, score);
|
|
|
+
|
|
|
+out:
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ filerunlock(f);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
|
|
|
- for(nvf = vf->down; nvf; nvf=nvf->next) {
|
|
|
- if(strcmp(elem, nvf->dir.elem) == 0 && !nvf->removed) {
|
|
|
- nvf = nil;
|
|
|
- vtSetError(EExists);
|
|
|
- goto Err;
|
|
|
- }
|
|
|
- }
|
|
|
+/*
|
|
|
+ * Read data from f.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
|
|
|
+{
|
|
|
+ int n;
|
|
|
|
|
|
- nvf = dirLookup(vf, elem);
|
|
|
- if(nvf != nil) {
|
|
|
- vtSetError(EExists);
|
|
|
- goto Err;
|
|
|
+ if(offset < 0){
|
|
|
+ werrstr(EBadOffset);
|
|
|
+ return -1;
|
|
|
}
|
|
|
-
|
|
|
- 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;
|
|
|
+ if(filerlock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vtfilelock(f->source, VtOREAD) < 0){
|
|
|
+ filerunlock(f);
|
|
|
+ return -1;
|
|
|
}
|
|
|
+ n = vtfileread(f->source, buf, cnt, offset);
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ filerunlock(f);
|
|
|
+ return n;
|
|
|
+}
|
|
|
|
|
|
- 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;
|
|
|
+static int
|
|
|
+getentry(VtFile *f, VtEntry *e)
|
|
|
+{
|
|
|
+ if(vtfilelock(f, VtOREAD) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vtfilegetentry(f, e) < 0){
|
|
|
+ vtfileunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ vtfileunlock(f);
|
|
|
+ if(vtglobaltolocal(e->score) != NilBlock){
|
|
|
+ werrstr("internal error - data not on venti");
|
|
|
+ return -1;
|
|
|
}
|
|
|
- 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;
|
|
|
- pointback(r, nvf);
|
|
|
- nvf->msource = mr;
|
|
|
- pointback(mr, vf);
|
|
|
-
|
|
|
- /* 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;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Get the VtEntries for the data contained in f.
|
|
|
+ */
|
|
|
int
|
|
|
-vfRead(VacFile *vf, void *buf, int cnt, vlong offset)
|
|
|
+vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
|
|
|
{
|
|
|
- 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))
|
|
|
+ if(filerlock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(e && getentry(f->source, e) < 0){
|
|
|
+ filerunlock(f);
|
|
|
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;
|
|
|
+ if(me){
|
|
|
+ if(f->msource == nil)
|
|
|
+ memset(me, 0, sizeof *me);
|
|
|
+ if(getentry(f->msource, me) < 0){
|
|
|
+ filerunlock(f);
|
|
|
+ return -1;
|
|
|
}
|
|
|
- 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;
|
|
|
+ }
|
|
|
+ filerunlock(f);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Get the file's size.
|
|
|
+ */
|
|
|
int
|
|
|
-vfWrite(VacFile *vf, void *buf, int cnt, vlong offset, char *user)
|
|
|
+vacfilegetsize(VacFile *f, uvlong *size)
|
|
|
{
|
|
|
- Source *s;
|
|
|
- ulong bn;
|
|
|
- int off, dsize, n;
|
|
|
- Lump *u;
|
|
|
- uchar *b;
|
|
|
+ if(filerlock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vtfilelock(f->source, VtOREAD) < 0){
|
|
|
+ filerunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ *size = vtfilegetsize(f->source);
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ filerunlock(f);
|
|
|
|
|
|
- USED(user);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- if(!vfLock(vf))
|
|
|
- return -1;
|
|
|
+/*
|
|
|
+ * Directory reading.
|
|
|
+ *
|
|
|
+ * A VacDirEnum is a buffer containing directory entries.
|
|
|
+ * Directory entries contain malloced strings and need to
|
|
|
+ * be cleaned up with vdcleanup. The invariant in the
|
|
|
+ * VacDirEnum is that the directory entries between
|
|
|
+ * vde->i and vde->n are owned by the vde and need to
|
|
|
+ * be cleaned up if it is closed. Those from 0 up to vde->i
|
|
|
+ * have been handed to the reader, and the reader must
|
|
|
+ * take care of calling vdcleanup as appropriate.
|
|
|
+ */
|
|
|
+VacDirEnum*
|
|
|
+vdeopen(VacFile *f)
|
|
|
+{
|
|
|
+ VacDirEnum *vde;
|
|
|
+ VacFile *p;
|
|
|
|
|
|
- if(vf->fs->readOnly) {
|
|
|
- vtSetError(EReadOnly);
|
|
|
- goto Err;
|
|
|
+ if(!vacfileisdir(f)){
|
|
|
+ werrstr(ENotDir);
|
|
|
+ return nil;
|
|
|
}
|
|
|
|
|
|
- if(vf->dir.mode & ModeDir) {
|
|
|
- vtSetError(ENotFile);
|
|
|
+ /*
|
|
|
+ * There might be changes to this directory's children
|
|
|
+ * that have not been flushed out into the cache yet.
|
|
|
+ * Those changes are only available if we look at the
|
|
|
+ * VacFile structures directory. But the directory reader
|
|
|
+ * is going to read the cache blocks directly, so update them.
|
|
|
+ */
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return nil;
|
|
|
+ for(p=f->down; p; p=p->next)
|
|
|
+ filemetaflush(p, nil);
|
|
|
+ fileunlock(f);
|
|
|
+
|
|
|
+ vde = vtmallocz(sizeof(VacDirEnum));
|
|
|
+ vde->file = vacfileincref(f);
|
|
|
+
|
|
|
+ return vde;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Figure out the size of the directory entry at offset.
|
|
|
+ * The rest of the metadata is kept in the data half,
|
|
|
+ * but since venti has to track the data size anyway,
|
|
|
+ * we just use that one and avoid updating the directory
|
|
|
+ * each time the file size changes.
|
|
|
+ */
|
|
|
+static int
|
|
|
+direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size)
|
|
|
+{
|
|
|
+ VtBlock *b;
|
|
|
+ ulong bn;
|
|
|
+ VtEntry e;
|
|
|
+ int epb;
|
|
|
+
|
|
|
+ epb = s->dsize/VtEntrySize;
|
|
|
+ bn = offset/epb;
|
|
|
+ offset -= bn*epb;
|
|
|
+
|
|
|
+ b = vtfileblock(s, bn, VtOREAD);
|
|
|
+ if(b == nil)
|
|
|
+ goto Err;
|
|
|
+ if(vtentryunpack(&e, b->data, offset) < 0)
|
|
|
goto Err;
|
|
|
- }
|
|
|
-if(0)fprint(2, "vfWrite: %s %d, %lld\n", vf->dir.elem, cnt, offset);
|
|
|
|
|
|
- s = vf->source;
|
|
|
- dsize = s->dsize;
|
|
|
+ /* dangling entries are returned as zero size */
|
|
|
+ if(!(e.flags & VtEntryActive) || e.gen != gen)
|
|
|
+ *size = 0;
|
|
|
+ else
|
|
|
+ *size = e.size;
|
|
|
+ vtblockput(b);
|
|
|
+ return 0;
|
|
|
|
|
|
- if(offset < 0) {
|
|
|
- vtSetError(EBadOffset);
|
|
|
+Err:
|
|
|
+ vtblockput(b);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Fill in vde with a new batch of directory entries.
|
|
|
+ */
|
|
|
+static int
|
|
|
+vdefill(VacDirEnum *vde)
|
|
|
+{
|
|
|
+ int i, n;
|
|
|
+ VtFile *meta, *source;
|
|
|
+ MetaBlock mb;
|
|
|
+ MetaEntry me;
|
|
|
+ VacFile *f;
|
|
|
+ VtBlock *b;
|
|
|
+ VacDir *de;
|
|
|
+
|
|
|
+ /* clean up first */
|
|
|
+ for(i=vde->i; i<vde->n; i++)
|
|
|
+ vdcleanup(vde->buf+i);
|
|
|
+ vtfree(vde->buf);
|
|
|
+ vde->buf = nil;
|
|
|
+ vde->i = 0;
|
|
|
+ vde->n = 0;
|
|
|
+
|
|
|
+ f = vde->file;
|
|
|
+
|
|
|
+ source = f->source;
|
|
|
+ meta = f->msource;
|
|
|
+
|
|
|
+ b = vtfileblock(meta, vde->boff, VtOREAD);
|
|
|
+ if(b == nil)
|
|
|
+ goto Err;
|
|
|
+ if(mbunpack(&mb, b->data, meta->dsize) < 0)
|
|
|
goto Err;
|
|
|
- }
|
|
|
|
|
|
- vfWAccess(vf, user);
|
|
|
+ n = mb.nindex;
|
|
|
+ vde->buf = vtmalloc(n * sizeof(VacDir));
|
|
|
|
|
|
- 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)
|
|
|
+ for(i=0; i<n; i++){
|
|
|
+ de = vde->buf + i;
|
|
|
+ meunpack(&me, &mb, i);
|
|
|
+ if(vdunpack(de, &me) < 0)
|
|
|
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))
|
|
|
+ vde->n++;
|
|
|
+ if(!(de->mode & ModeDir))
|
|
|
+ if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
|
|
|
goto Err;
|
|
|
}
|
|
|
- vfLock(vf);
|
|
|
- return b-(uchar*)buf;
|
|
|
+ vde->boff++;
|
|
|
+ vtblockput(b);
|
|
|
+ return 0;
|
|
|
Err:
|
|
|
- vfLock(vf);
|
|
|
+ vtblockput(b);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Read a single directory entry from vde into de.
|
|
|
+ * Returns -1 on error, 0 on EOF, and 1 on success.
|
|
|
+ * When it returns 1, it becomes the caller's responsibility
|
|
|
+ * to call vdcleanup(de) to free the strings contained
|
|
|
+ * inside, or else to call vdunread to give it back.
|
|
|
+ */
|
|
|
int
|
|
|
-vfGetDir(VacFile *vf, VacDir *dir)
|
|
|
+vderead(VacDirEnum *vde, VacDir *de)
|
|
|
{
|
|
|
- if(!vfRLock(vf))
|
|
|
- return 0;
|
|
|
+ int ret;
|
|
|
+ VacFile *f;
|
|
|
+ u32int nb;
|
|
|
+
|
|
|
+ f = vde->file;
|
|
|
+ if(filerlock(f) < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
|
|
|
+ filerunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
|
|
|
+
|
|
|
+ while(vde->i >= vde->n){
|
|
|
+ if(vde->boff >= nb){
|
|
|
+ ret = 0;
|
|
|
+ goto Return;
|
|
|
+ }
|
|
|
+ if(vdefill(vde) < 0){
|
|
|
+ ret = -1;
|
|
|
+ goto Return;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- vfMetaLock(vf);
|
|
|
- vdCopy(dir, &vf->dir);
|
|
|
- vfMetaUnlock(vf);
|
|
|
+ memmove(de, vde->buf + vde->i, sizeof(VacDir));
|
|
|
+ vde->i++;
|
|
|
+ ret = 1;
|
|
|
|
|
|
- if(!vfIsDir(vf))
|
|
|
- dir->size = sourceGetSize(vf->source);
|
|
|
- vfRUnlock(vf);
|
|
|
+Return:
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+ filerunlock(f);
|
|
|
|
|
|
- return 1;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-uvlong
|
|
|
-vfGetId(VacFile *vf)
|
|
|
+/*
|
|
|
+ * "Unread" the last directory entry that was read,
|
|
|
+ * so that the next vderead will return the same one.
|
|
|
+ * If the caller calls vdeunread(vde) it should not call
|
|
|
+ * vdcleanup on the entry being "unread".
|
|
|
+ */
|
|
|
+int
|
|
|
+vdeunread(VacDirEnum *vde)
|
|
|
{
|
|
|
- /* immutable */
|
|
|
- return vf->dir.qid;
|
|
|
+ if(vde->i > 0){
|
|
|
+ vde->i--;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
-ulong
|
|
|
-vfGetMcount(VacFile *vf)
|
|
|
+/*
|
|
|
+ * Close the enumerator.
|
|
|
+ */
|
|
|
+void
|
|
|
+vdeclose(VacDirEnum *vde)
|
|
|
{
|
|
|
- ulong mcount;
|
|
|
-
|
|
|
- vfMetaLock(vf);
|
|
|
- mcount = vf->dir.mcount;
|
|
|
- vfMetaUnlock(vf);
|
|
|
- return mcount;
|
|
|
+ int i;
|
|
|
+ if(vde == nil)
|
|
|
+ return;
|
|
|
+ /* free the strings */
|
|
|
+ for(i=vde->i; i<vde->n; i++)
|
|
|
+ vdcleanup(vde->buf+i);
|
|
|
+ vtfree(vde->buf);
|
|
|
+ vacfiledecref(vde->file);
|
|
|
+ vtfree(vde);
|
|
|
}
|
|
|
|
|
|
|
|
|
-int
|
|
|
-vfIsDir(VacFile *vf)
|
|
|
-{
|
|
|
- /* immutable */
|
|
|
- return (vf->dir.mode & ModeDir) != 0;
|
|
|
-}
|
|
|
+/*
|
|
|
+ * On to mutation. If the vac file system has been opened
|
|
|
+ * read-write, then the files and directories can all be edited.
|
|
|
+ * Changes are kept in the in-memory cache until flushed out
|
|
|
+ * to venti, so we must be careful to explicitly flush data
|
|
|
+ * that we're not likely to modify again.
|
|
|
+ *
|
|
|
+ * Each VacFile has its own copy of its VacDir directory entry
|
|
|
+ * in f->dir, but otherwise the cache is the authoratative source
|
|
|
+ * for data. Thus, for the most part, it suffices if we just
|
|
|
+ * call vtfileflushbefore and vtfileflush when we modify things.
|
|
|
+ * There are a few places where we have to remember to write
|
|
|
+ * changed VacDirs back into the cache. If f->dir *is* out of sync,
|
|
|
+ * then f->dirty should be set.
|
|
|
+ *
|
|
|
+ * The metadata in a directory is, to venti, a plain data file,
|
|
|
+ * but as mentioned above it is actually a sequence of
|
|
|
+ * MetaBlocks that contain sorted lists of VacDir entries.
|
|
|
+ * The filemetaxxx routines manipulate that stream.
|
|
|
+ */
|
|
|
|
|
|
-int
|
|
|
-vfIsRoot(VacFile *vf)
|
|
|
+/*
|
|
|
+ * Find space in fp for the directory entry dir (not yet written to disk)
|
|
|
+ * and write it to disk, returning NilBlock on failure,
|
|
|
+ * or the block number on success.
|
|
|
+ *
|
|
|
+ * Start is a suggested block number to try.
|
|
|
+ * The caller must have filemetalock'ed f and have
|
|
|
+ * vtfilelock'ed f->up->msource.
|
|
|
+ */
|
|
|
+static u32int
|
|
|
+filemetaalloc(VacFile *fp, VacDir *dir, u32int start)
|
|
|
{
|
|
|
- return vf == vf->fs->root;
|
|
|
+ u32int nb, bo;
|
|
|
+ VtBlock *b;
|
|
|
+ MetaBlock mb;
|
|
|
+ int nn;
|
|
|
+ uchar *p;
|
|
|
+ int i, n;
|
|
|
+ MetaEntry me;
|
|
|
+ VtFile *ms;
|
|
|
+
|
|
|
+ ms = fp->msource;
|
|
|
+ n = vdsize(dir, VacDirVersion);
|
|
|
+
|
|
|
+ /* Look for a block with room for a new entry of size n. */
|
|
|
+ nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
|
|
|
+ if(start == NilBlock){
|
|
|
+ if(nb > 0)
|
|
|
+ start = nb - 1;
|
|
|
+ else
|
|
|
+ start = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(start > nb)
|
|
|
+ start = nb;
|
|
|
+ for(bo=start; bo<nb; bo++){
|
|
|
+ if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
|
|
|
+ goto Err;
|
|
|
+ if(mbunpack(&mb, b->data, ms->dsize) < 0)
|
|
|
+ goto Err;
|
|
|
+ nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
|
|
|
+ if(n <= nn && mb.nindex < mb.maxindex){
|
|
|
+ /* reopen for writing */
|
|
|
+ vtblockput(b);
|
|
|
+ if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
|
|
|
+ goto Err;
|
|
|
+ goto Found;
|
|
|
+ }
|
|
|
+ vtblockput(b);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* No block found, extend the file by one metablock. */
|
|
|
+ vtfileflushbefore(ms, nb*(uvlong)ms->dsize);
|
|
|
+ if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
|
|
|
+ goto Err;
|
|
|
+ vtfilesetsize(ms, (nb+1)*ms->dsize);
|
|
|
+ mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
|
|
|
+
|
|
|
+Found:
|
|
|
+ /* Now we have a block; allocate space to write the entry. */
|
|
|
+ p = mballoc(&mb, n);
|
|
|
+ if(p == nil){
|
|
|
+ /* mballoc might have changed block */
|
|
|
+ mbpack(&mb);
|
|
|
+ werrstr(EBadMeta);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Figure out where to put the index entry, and write it. */
|
|
|
+ mbsearch(&mb, dir->elem, &i, &me);
|
|
|
+ assert(me.p == nil); /* not already there */
|
|
|
+ me.p = p;
|
|
|
+ me.size = n;
|
|
|
+ vdpack(dir, &me, VacDirVersion);
|
|
|
+vdunpack(dir, &me);
|
|
|
+ mbinsert(&mb, i, &me);
|
|
|
+ mbpack(&mb);
|
|
|
+ vtblockput(b);
|
|
|
+ return bo;
|
|
|
+
|
|
|
+Err:
|
|
|
+ vtblockput(b);
|
|
|
+ return NilBlock;
|
|
|
}
|
|
|
|
|
|
-int
|
|
|
-vfGetSize(VacFile *vf, uvlong *size)
|
|
|
+/*
|
|
|
+ * Update f's directory entry in the block cache.
|
|
|
+ * We look for the directory entry by name;
|
|
|
+ * if we're trying to rename the file, oelem is the old name.
|
|
|
+ *
|
|
|
+ * Assumes caller has filemetalock'ed f.
|
|
|
+ */
|
|
|
+static int
|
|
|
+filemetaflush(VacFile *f, char *oelem)
|
|
|
{
|
|
|
- if(!vfRLock(vf))
|
|
|
+ int i, n;
|
|
|
+ MetaBlock mb;
|
|
|
+ MetaEntry me, me2;
|
|
|
+ VacFile *fp;
|
|
|
+ VtBlock *b;
|
|
|
+ u32int bo;
|
|
|
+
|
|
|
+ if(!f->dirty)
|
|
|
return 0;
|
|
|
- *size = sourceGetSize(vf->source);
|
|
|
- vfRUnlock(vf);
|
|
|
|
|
|
- return 1;
|
|
|
+ if(oelem == nil)
|
|
|
+ oelem = f->dir.elem;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Locate f's old metadata in the parent's metadata file.
|
|
|
+ * We know which block it was in, but not exactly where
|
|
|
+ * in the block.
|
|
|
+ */
|
|
|
+ fp = f->up;
|
|
|
+ if(vtfilelock(fp->msource, -1) < 0)
|
|
|
+ return -1;
|
|
|
+ /* can happen if source is clri'ed out from under us */
|
|
|
+ if(f->boff == NilBlock)
|
|
|
+ goto Err1;
|
|
|
+ b = vtfileblock(fp->msource, f->boff, VtORDWR);
|
|
|
+ if(b == nil)
|
|
|
+ goto Err1;
|
|
|
+ if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
|
|
|
+ goto Err;
|
|
|
+ if(mbsearch(&mb, oelem, &i, &me) < 0)
|
|
|
+ goto Err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check whether we can resize the entry and keep it
|
|
|
+ * in this block.
|
|
|
+ */
|
|
|
+ n = vdsize(&f->dir, VacDirVersion);
|
|
|
+ if(mbresize(&mb, &me, n) >= 0){
|
|
|
+ /* Okay, can be done without moving to another block. */
|
|
|
+
|
|
|
+ /* Remove old data */
|
|
|
+ mbdelete(&mb, i, &me);
|
|
|
+
|
|
|
+ /* Find new location if renaming */
|
|
|
+ if(strcmp(f->dir.elem, oelem) != 0)
|
|
|
+ mbsearch(&mb, f->dir.elem, &i, &me2);
|
|
|
+
|
|
|
+ /* Pack new data into new location. */
|
|
|
+ vdpack(&f->dir, &me, VacDirVersion);
|
|
|
+vdunpack(&f->dir, &me);
|
|
|
+ mbinsert(&mb, i, &me);
|
|
|
+ mbpack(&mb);
|
|
|
+
|
|
|
+ /* Done */
|
|
|
+ vtblockput(b);
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
+ f->dirty = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The entry must be moved to another block.
|
|
|
+ * This can only really happen on renames that
|
|
|
+ * make the name very long.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Allocate a spot in a new block. */
|
|
|
+ if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){
|
|
|
+ /* mbresize above might have modified block */
|
|
|
+ mbpack(&mb);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ f->boff = bo;
|
|
|
+
|
|
|
+ /* Now we're committed. Delete entry in old block. */
|
|
|
+ mbdelete(&mb, i, &me);
|
|
|
+ mbpack(&mb);
|
|
|
+ vtblockput(b);
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
+
|
|
|
+ f->dirty = 0;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+Err:
|
|
|
+ vtblockput(b);
|
|
|
+Err1:
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Remove the directory entry for f.
|
|
|
+ */
|
|
|
static int
|
|
|
-vfMetaRemove(VacFile *vf, char *user)
|
|
|
+filemetaremove(VacFile *f)
|
|
|
{
|
|
|
- Lump *u;
|
|
|
+ VtBlock *b;
|
|
|
MetaBlock mb;
|
|
|
MetaEntry me;
|
|
|
int i;
|
|
|
- VacFile *vfp;
|
|
|
-
|
|
|
- vfp = vf->up;
|
|
|
+ VacFile *fp;
|
|
|
|
|
|
- vfWAccess(vfp, user);
|
|
|
+ b = nil;
|
|
|
+ fp = f->up;
|
|
|
+ filemetalock(f);
|
|
|
|
|
|
- vfMetaLock(vf);
|
|
|
-
|
|
|
- u = sourceGetLump(vfp->msource, vf->block, 0, 1);
|
|
|
- if(u == nil)
|
|
|
+ if(vtfilelock(fp->msource, VtORDWR) < 0)
|
|
|
+ goto Err;
|
|
|
+ b = vtfileblock(fp->msource, f->boff, VtORDWR);
|
|
|
+ if(b == nil)
|
|
|
goto Err;
|
|
|
|
|
|
- if(!mbUnpack(&mb, u->data, u->asize))
|
|
|
+ if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
|
|
|
goto Err;
|
|
|
- if(!mbSearch(&mb, vf->dir.elem, &i, &me) || me.p == nil)
|
|
|
+ if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
|
|
|
goto Err;
|
|
|
-print("deleting %d entry\n", i);
|
|
|
- mbDelete(&mb, i, &me);
|
|
|
- memset(me.p, 0, me.size);
|
|
|
- mbPack(&mb);
|
|
|
+ mbdelete(&mb, i, &me);
|
|
|
+ mbpack(&mb);
|
|
|
+ vtblockput(b);
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
|
|
|
- lumpDecRef(u, 1);
|
|
|
+ f->removed = 1;
|
|
|
+ f->boff = NilBlock;
|
|
|
+ f->dirty = 0;
|
|
|
|
|
|
- vf->removed = 1;
|
|
|
- vf->block = NilBlock;
|
|
|
-
|
|
|
- vfMetaUnlock(vf);
|
|
|
- return 1;
|
|
|
+ filemetaunlock(f);
|
|
|
+ return 0;
|
|
|
|
|
|
Err:
|
|
|
- lumpDecRef(u, 1);
|
|
|
- vfMetaUnlock(vf);
|
|
|
- return 0;
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
+ vtblockput(b);
|
|
|
+ filemetaunlock(f);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * That was far too much effort for directory entries.
|
|
|
+ * Now we can write code that *does* things.
|
|
|
+ */
|
|
|
|
|
|
-static int
|
|
|
-vfCheckEmpty(VacFile *vf)
|
|
|
+/*
|
|
|
+ * Flush all data associated with f out of the cache and onto venti.
|
|
|
+ * If recursive is set, flush f's children too.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfileflush(VacFile *f, int recursive)
|
|
|
{
|
|
|
- int i, n;
|
|
|
- Lump *u;
|
|
|
- MetaBlock mb;
|
|
|
- Source *r;
|
|
|
+ int ret;
|
|
|
+ VacFile **kids, *p;
|
|
|
+ int i, nkids;
|
|
|
+
|
|
|
+ if(f->mode == VtOREAD)
|
|
|
+ return 0;
|
|
|
|
|
|
- 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;
|
|
|
+ ret = 0;
|
|
|
+ filemetalock(f);
|
|
|
+ if(filemetaflush(f, nil) < 0)
|
|
|
+ ret = -1;
|
|
|
+ filemetaunlock(f);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Vacfiledecref knows how to flush source and msource too.
|
|
|
+ */
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ vtfilelock(f->source, -1);
|
|
|
+ if(vtfileflush(f->source) < 0)
|
|
|
+ ret = -1;
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ if(f->msource){
|
|
|
+ vtfilelock(f->msource, -1);
|
|
|
+ if(vtfileflush(f->msource) < 0)
|
|
|
+ ret = -1;
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Lock order prevents us from flushing kids while holding
|
|
|
+ * lock, so make a list.
|
|
|
+ */
|
|
|
+ nkids = 0;
|
|
|
+ kids = nil;
|
|
|
+ if(recursive){
|
|
|
+ nkids = 0;
|
|
|
+ for(p=f->down; p; p=p->next)
|
|
|
+ nkids++;
|
|
|
+ kids = vtmalloc(nkids*sizeof(VacFile*));
|
|
|
+ i = 0;
|
|
|
+ for(p=f->down; p; p=p->next){
|
|
|
+ kids[i++] = p;
|
|
|
+ p->ref++;
|
|
|
}
|
|
|
- lumpDecRef(u, 1);
|
|
|
}
|
|
|
- return 1;
|
|
|
-Err:
|
|
|
- lumpDecRef(u, 1);
|
|
|
- return 0;
|
|
|
+ fileunlock(f);
|
|
|
+
|
|
|
+ for(i=0; i<nkids; i++){
|
|
|
+ if(vacfileflush(kids[i], 1) < 0)
|
|
|
+ ret = -1;
|
|
|
+ vacfiledecref(kids[i]);
|
|
|
+ }
|
|
|
+ free(kids);
|
|
|
+ return ret;
|
|
|
}
|
|
|
-
|
|
|
-int
|
|
|
-vfRemove(VacFile *vf, char *user)
|
|
|
+
|
|
|
+/*
|
|
|
+ * Create a new file named elem in fp with the given mode.
|
|
|
+ * The mode can be changed later except for the ModeDir bit.
|
|
|
+ */
|
|
|
+VacFile*
|
|
|
+vacfilecreate(VacFile *fp, char *elem, ulong mode)
|
|
|
{
|
|
|
- /* can not remove the root */
|
|
|
- if(vfIsRoot(vf)) {
|
|
|
- vtSetError(ERoot);
|
|
|
- return 0;
|
|
|
+ VacFile *ff;
|
|
|
+ VacDir *dir;
|
|
|
+ VtFile *pr, *r, *mr;
|
|
|
+ int type;
|
|
|
+ u32int bo;
|
|
|
+
|
|
|
+ if(filelock(fp) < 0)
|
|
|
+ return nil;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * First, look to see that there's not a file in memory
|
|
|
+ * with the same name.
|
|
|
+ */
|
|
|
+ for(ff = fp->down; ff; ff=ff->next){
|
|
|
+ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
|
|
|
+ ff = nil;
|
|
|
+ werrstr(EExists);
|
|
|
+ goto Err1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if(!vfLock(vf))
|
|
|
- return 0;
|
|
|
+ /*
|
|
|
+ * Next check the venti blocks.
|
|
|
+ */
|
|
|
+ ff = dirlookup(fp, elem);
|
|
|
+ if(ff != nil){
|
|
|
+ werrstr(EExists);
|
|
|
+ goto Err1;
|
|
|
+ }
|
|
|
|
|
|
- if(vfIsDir(vf) && !vfCheckEmpty(vf))
|
|
|
- goto Err;
|
|
|
+ /*
|
|
|
+ * By the way, you can't create in a read-only file system.
|
|
|
+ */
|
|
|
+ pr = fp->source;
|
|
|
+ if(pr->mode != VtORDWR){
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ goto Err1;
|
|
|
+ }
|
|
|
|
|
|
- assert(vf->down == nil);
|
|
|
+ /*
|
|
|
+ * Okay, time to actually create something. Lock the two
|
|
|
+ * halves of the directory and create a file.
|
|
|
+ */
|
|
|
+ if(vtfilelock2(fp->source, fp->msource, -1) < 0)
|
|
|
+ goto Err1;
|
|
|
+ ff = filealloc(fp->fs);
|
|
|
+ ff->qidoffset = fp->qidoffset; /* hopefully fp->qidoffset == 0 */
|
|
|
+ type = VtDataType;
|
|
|
+ if(mode & ModeDir)
|
|
|
+ type = VtDirType;
|
|
|
+ mr = nil;
|
|
|
+ if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
|
|
|
+ goto Err;
|
|
|
+ if(mode & ModeDir)
|
|
|
+ if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
|
|
|
+ goto Err;
|
|
|
|
|
|
- sourceRemove(vf->source);
|
|
|
- vf->source = nil;
|
|
|
- if(vf->msource) {
|
|
|
- sourceRemove(vf->msource);
|
|
|
- vf->msource = nil;
|
|
|
+ /*
|
|
|
+ * Fill in the directory entry and write it to disk.
|
|
|
+ */
|
|
|
+ dir = &ff->dir;
|
|
|
+ dir->elem = vtstrdup(elem);
|
|
|
+ dir->entry = r->offset;
|
|
|
+ dir->gen = r->gen;
|
|
|
+ if(mode & ModeDir){
|
|
|
+ dir->mentry = mr->offset;
|
|
|
+ dir->mgen = mr->gen;
|
|
|
}
|
|
|
+ dir->size = 0;
|
|
|
+ if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
|
|
|
+ goto Err;
|
|
|
+ dir->uid = vtstrdup(fp->dir.uid);
|
|
|
+ dir->gid = vtstrdup(fp->dir.gid);
|
|
|
+ dir->mid = vtstrdup("");
|
|
|
+ dir->mtime = time(0L);
|
|
|
+ dir->mcount = 0;
|
|
|
+ dir->ctime = dir->mtime;
|
|
|
+ dir->atime = dir->mtime;
|
|
|
+ dir->mode = mode;
|
|
|
+ if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
|
|
|
+ goto Err;
|
|
|
|
|
|
- vfUnlock(vf);
|
|
|
+ /*
|
|
|
+ * Now we're committed.
|
|
|
+ */
|
|
|
+ vtfileunlock(fp->source);
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
+ ff->source = r;
|
|
|
+ ff->msource = mr;
|
|
|
+ ff->boff = bo;
|
|
|
|
|
|
- if(!vfMetaRemove(vf, user))
|
|
|
- return 0;
|
|
|
+ /* Link into tree. */
|
|
|
+ ff->next = fp->down;
|
|
|
+ fp->down = ff;
|
|
|
+ ff->up = fp;
|
|
|
+ vacfileincref(fp);
|
|
|
|
|
|
- return 1;
|
|
|
+ fileunlock(fp);
|
|
|
+ return ff;
|
|
|
|
|
|
Err:
|
|
|
- vfUnlock(vf);
|
|
|
- return 0;
|
|
|
+ vtfileunlock(fp->source);
|
|
|
+ vtfileunlock(fp->msource);
|
|
|
+ if(r){
|
|
|
+ vtfilelock(r, -1);
|
|
|
+ vtfileremove(r);
|
|
|
+ }
|
|
|
+ if(mr){
|
|
|
+ vtfilelock(mr, -1);
|
|
|
+ vtfileremove(mr);
|
|
|
+ }
|
|
|
+Err1:
|
|
|
+ if(ff)
|
|
|
+ vacfiledecref(ff);
|
|
|
+ fileunlock(fp);
|
|
|
+ return nil;
|
|
|
}
|
|
|
|
|
|
-VacFile *
|
|
|
-vfIncRef(VacFile *vf)
|
|
|
+/*
|
|
|
+ * Change the size of the file f.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfilesetsize(VacFile *f, uvlong size)
|
|
|
{
|
|
|
- vfMetaLock(vf);
|
|
|
- assert(vf->ref > 0);
|
|
|
- vf->ref++;
|
|
|
- vfMetaUnlock(vf);
|
|
|
- return vf;
|
|
|
+ if(vacfileisdir(f)){
|
|
|
+ werrstr(ENotFile);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if(f->source->mode != VtORDWR){
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ if(vtfilelock(f->source, -1) < 0)
|
|
|
+ goto Err;
|
|
|
+ if(vtfilesetsize(f->source, size) < 0){
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ fileunlock(f);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+Err:
|
|
|
+ fileunlock(f);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-vfDecRef(VacFile *vf)
|
|
|
+/*
|
|
|
+ * Write data to f.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
|
|
|
{
|
|
|
- VacFile *p, *q, **qq;
|
|
|
-
|
|
|
- if(vf->up == nil) {
|
|
|
- vfFree(vf);
|
|
|
- return;
|
|
|
+ if(vacfileisdir(f)){
|
|
|
+ werrstr(ENotFile);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(f->source->mode != VtORDWR){
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ if(offset < 0){
|
|
|
+ werrstr(EBadOffset);
|
|
|
+ goto Err;
|
|
|
}
|
|
|
|
|
|
- vfMetaLock(vf);
|
|
|
- vf->ref--;
|
|
|
- if(vf->ref > 0) {
|
|
|
- vfMetaUnlock(vf);
|
|
|
- return;
|
|
|
+ if(vtfilelock(f->source, -1) < 0)
|
|
|
+ goto Err;
|
|
|
+ if(f->dir.mode & ModeAppend)
|
|
|
+ offset = vtfilegetsize(f->source);
|
|
|
+ if(vtfilewrite(f->source, buf, cnt, offset) != cnt
|
|
|
+ || vtfileflushbefore(f->source, offset) < 0){
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ goto Err;
|
|
|
}
|
|
|
- assert(vf->ref == 0);
|
|
|
- assert(vf->down == nil);
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ fileunlock(f);
|
|
|
+ return cnt;
|
|
|
|
|
|
- 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;
|
|
|
+Err:
|
|
|
+ fileunlock(f);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
|
|
|
- vfMetaUnlock(vf);
|
|
|
- vfFree(vf);
|
|
|
+/*
|
|
|
+ * Set (!) the VtEntry for the data contained in f.
|
|
|
+ * This let's us efficiently copy data from one file to another.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
|
|
|
- vfDecRef(p);
|
|
|
+ vacfileflush(f, 0); /* flush blocks to venti, since we won't see them again */
|
|
|
+
|
|
|
+ if(!(e->flags&VtEntryActive)){
|
|
|
+ werrstr("missing entry for source");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(me && !(me->flags&VtEntryActive))
|
|
|
+ me = nil;
|
|
|
+ if(f->msource && !me){
|
|
|
+ werrstr("missing entry for msource");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(me && !f->msource){
|
|
|
+ werrstr("no msource to set");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(f->source->mode != VtORDWR
|
|
|
+ || (f->msource && f->msource->mode != VtORDWR)){
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ fileunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(vtfilelock2(f->source, f->msource, -1) < 0){
|
|
|
+ fileunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ ret = 0;
|
|
|
+ if(vtfilesetentry(f->source, e) < 0)
|
|
|
+ ret = -1;
|
|
|
+ else if(me && vtfilesetentry(f->msource, me) < 0)
|
|
|
+ ret = -1;
|
|
|
+
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ if(f->msource)
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+ fileunlock(f);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Get the directory entry for f.
|
|
|
+ */
|
|
|
int
|
|
|
-vfGetVtEntry(VacFile *vf, VtEntry *e)
|
|
|
+vacfilegetdir(VacFile *f, VacDir *dir)
|
|
|
{
|
|
|
- int res;
|
|
|
+ if(filerlock(f) < 0)
|
|
|
+ return -1;
|
|
|
|
|
|
- if(!vfRLock(vf))
|
|
|
- return 0;
|
|
|
- res = sourceGetVtEntry(vf->source, e);
|
|
|
- vfRUnlock(vf);
|
|
|
- return res;
|
|
|
+ filemetalock(f);
|
|
|
+ vdcopy(dir, &f->dir);
|
|
|
+ filemetaunlock(f);
|
|
|
+
|
|
|
+ if(!vacfileisdir(f)){
|
|
|
+ if(vtfilelock(f->source, VtOREAD) < 0){
|
|
|
+ filerunlock(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ dir->size = vtfilegetsize(f->source);
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ }
|
|
|
+ filerunlock(f);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Set the directory entry for f.
|
|
|
+ */
|
|
|
int
|
|
|
-vfGetBlockScore(VacFile *vf, ulong bn, uchar score[VtScoreSize])
|
|
|
+vacfilesetdir(VacFile *f, VacDir *dir)
|
|
|
{
|
|
|
- Lump *u;
|
|
|
- int ret, off;
|
|
|
- Source *r;
|
|
|
+ VacFile *ff;
|
|
|
+ char *oelem;
|
|
|
+ u32int mask;
|
|
|
+ u64int size;
|
|
|
+
|
|
|
+ /* can not set permissions for the root */
|
|
|
+ if(vacfileisroot(f)){
|
|
|
+ werrstr(ERoot);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
|
|
|
- if(!vfRLock(vf))
|
|
|
- return 0;
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ filemetalock(f);
|
|
|
+
|
|
|
+ if(f->source->mode != VtORDWR){
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
|
|
|
- r = vf->source;
|
|
|
+ /* On rename, check new name does not already exist */
|
|
|
+ if(strcmp(f->dir.elem, dir->elem) != 0){
|
|
|
+ for(ff = f->up->down; ff; ff=ff->next){
|
|
|
+ if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
|
|
|
+ werrstr(EExists);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ff = dirlookup(f->up, dir->elem);
|
|
|
+ if(ff != nil){
|
|
|
+ vacfiledecref(ff);
|
|
|
+ werrstr(EExists);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ werrstr(""); /* "failed" dirlookup poisoned it */
|
|
|
+ }
|
|
|
|
|
|
- u = sourceWalk(r, bn, 1, &off);
|
|
|
- if(u == nil){
|
|
|
- vfRUnlock(vf);
|
|
|
- return 0;
|
|
|
+ /* Get ready... */
|
|
|
+ if(vtfilelock2(f->source, f->msource, -1) < 0)
|
|
|
+ goto Err;
|
|
|
+ if(!vacfileisdir(f)){
|
|
|
+ size = vtfilegetsize(f->source);
|
|
|
+ if(size != dir->size){
|
|
|
+ if(vtfilesetsize(f->source, dir->size) < 0){
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ if(f->msource)
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* ... now commited to changing it. */
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ if(f->msource)
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+
|
|
|
+ oelem = nil;
|
|
|
+ if(strcmp(f->dir.elem, dir->elem) != 0){
|
|
|
+ oelem = f->dir.elem;
|
|
|
+ f->dir.elem = vtstrdup(dir->elem);
|
|
|
}
|
|
|
|
|
|
- ret = lumpGetScore(u, off, score);
|
|
|
- lumpDecRef(u, 0);
|
|
|
- vfRUnlock(vf);
|
|
|
+ if(strcmp(f->dir.uid, dir->uid) != 0){
|
|
|
+ vtfree(f->dir.uid);
|
|
|
+ f->dir.uid = vtstrdup(dir->uid);
|
|
|
+ }
|
|
|
|
|
|
- return ret;
|
|
|
-}
|
|
|
+ if(strcmp(f->dir.gid, dir->gid) != 0){
|
|
|
+ vtfree(f->dir.gid);
|
|
|
+ f->dir.gid = vtstrdup(dir->gid);
|
|
|
+ }
|
|
|
|
|
|
-VacFile *
|
|
|
-vfGetParent(VacFile *vf)
|
|
|
-{
|
|
|
- if(vfIsRoot(vf))
|
|
|
- return vfIncRef(vf);
|
|
|
- return vfIncRef(vf->up);
|
|
|
-}
|
|
|
+ f->dir.mtime = dir->mtime;
|
|
|
+ f->dir.atime = dir->atime;
|
|
|
|
|
|
-static VacDirEnum *
|
|
|
-vdeAlloc(VacFile *vf)
|
|
|
-{
|
|
|
- VacDirEnum *ds;
|
|
|
+ mask = ~(ModeDir|ModeSnapshot);
|
|
|
+ f->dir.mode &= ~mask;
|
|
|
+ f->dir.mode |= mask & dir->mode;
|
|
|
+ f->dirty = 1;
|
|
|
|
|
|
- if(!(vf->dir.mode & ModeDir)) {
|
|
|
- vtSetError(ENotDir);
|
|
|
- vfDecRef(vf);
|
|
|
- return nil;
|
|
|
+ if(filemetaflush(f, oelem) < 0){
|
|
|
+ vtfree(oelem);
|
|
|
+ goto Err; /* that sucks */
|
|
|
}
|
|
|
+ vtfree(oelem);
|
|
|
|
|
|
- ds = vtMemAllocZ(sizeof(VacDirEnum));
|
|
|
- ds->file = vf;
|
|
|
+ filemetaunlock(f);
|
|
|
+ fileunlock(f);
|
|
|
+ return 0;
|
|
|
|
|
|
- return ds;
|
|
|
+Err:
|
|
|
+ filemetaunlock(f);
|
|
|
+ fileunlock(f);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
-VacDirEnum *
|
|
|
-vdeOpen(VacFS *fs, char *path)
|
|
|
+/*
|
|
|
+ * Set the qid space.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
|
|
|
{
|
|
|
- VacFile *vf;
|
|
|
+ int ret;
|
|
|
|
|
|
- vf = vfOpen(fs, path);
|
|
|
- if(vf == nil)
|
|
|
- return nil;
|
|
|
-
|
|
|
- return vdeAlloc(vf);
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(f->source->mode != VtORDWR){
|
|
|
+ fileunlock(f);
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ filemetalock(f);
|
|
|
+ f->dir.qidspace = 1;
|
|
|
+ f->dir.qidoffset = offset;
|
|
|
+ f->dir.qidmax = max;
|
|
|
+ f->dirty = 1;
|
|
|
+ ret = filemetaflush(f, nil);
|
|
|
+ filemetaunlock(f);
|
|
|
+ fileunlock(f);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-VacDirEnum *
|
|
|
-vfDirEnum(VacFile *vf)
|
|
|
+/*
|
|
|
+ * Check that the file is empty, returning 0 if it is.
|
|
|
+ * Returns -1 on error (and not being empty is an error).
|
|
|
+ */
|
|
|
+static int
|
|
|
+filecheckempty(VacFile *f)
|
|
|
{
|
|
|
- return vdeAlloc(vfIncRef(vf));
|
|
|
+ u32int i, n;
|
|
|
+ VtBlock *b;
|
|
|
+ MetaBlock mb;
|
|
|
+ VtFile *r;
|
|
|
+
|
|
|
+ r = f->msource;
|
|
|
+ n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
|
|
|
+ for(i=0; i<n; i++){
|
|
|
+ b = vtfileblock(r, i, VtOREAD);
|
|
|
+ if(b == nil)
|
|
|
+ return -1;
|
|
|
+ if(mbunpack(&mb, b->data, r->dsize) < 0)
|
|
|
+ goto Err;
|
|
|
+ if(mb.nindex > 0){
|
|
|
+ werrstr(ENotEmpty);
|
|
|
+ goto Err;
|
|
|
+ }
|
|
|
+ vtblockput(b);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+
|
|
|
+Err:
|
|
|
+ vtblockput(b);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
|
|
|
+/*
|
|
|
+ * Remove the vac file f.
|
|
|
+ */
|
|
|
+int
|
|
|
+vacfileremove(VacFile *f)
|
|
|
{
|
|
|
- Lump *u;
|
|
|
- ulong bn;
|
|
|
- VtEntry e;
|
|
|
+ VacFile *ff;
|
|
|
|
|
|
- bn = elem/s->epb;
|
|
|
- elem -= bn*s->epb;
|
|
|
+ /* Cannot remove the root */
|
|
|
+ if(vacfileisroot(f)){
|
|
|
+ werrstr(ERoot);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
|
|
|
- u = sourceGetLump(s, bn, 1, 1);
|
|
|
- if(u == nil)
|
|
|
- goto Err;
|
|
|
- if(u->asize < (elem+1)*VtEntrySize) {
|
|
|
- vtSetError(ENoDir);
|
|
|
- goto Err;
|
|
|
+ if(filelock(f) < 0)
|
|
|
+ return -1;
|
|
|
+ if(f->source->mode != VtORDWR){
|
|
|
+ werrstr(EReadOnly);
|
|
|
+ goto Err1;
|
|
|
}
|
|
|
- vtEntryUnpack(&e, u->data, elem);
|
|
|
- if(!(e.flags & VtEntryActive) || e.gen != gen) {
|
|
|
-fprint(2, "gen mismatch\n");
|
|
|
- vtSetError(ENoDir);
|
|
|
+ if(vtfilelock2(f->source, f->msource, -1) < 0)
|
|
|
+ goto Err1;
|
|
|
+ if(vacfileisdir(f) && filecheckempty(f)<0)
|
|
|
goto Err;
|
|
|
+
|
|
|
+ for(ff=f->down; ff; ff=ff->next)
|
|
|
+ assert(ff->removed);
|
|
|
+
|
|
|
+ vtfileremove(f->source);
|
|
|
+ f->source = nil;
|
|
|
+ if(f->msource){
|
|
|
+ vtfileremove(f->msource);
|
|
|
+ f->msource = nil;
|
|
|
}
|
|
|
+ fileunlock(f);
|
|
|
|
|
|
- *size = e.size;
|
|
|
- lumpDecRef(u, 1);
|
|
|
- return 1;
|
|
|
+ if(filemetaremove(f) < 0)
|
|
|
+ return -1;
|
|
|
+ return 0;
|
|
|
|
|
|
Err:
|
|
|
- lumpDecRef(u, 1);
|
|
|
- return 0;
|
|
|
+ vtfileunlock(f->source);
|
|
|
+ if(f->msource)
|
|
|
+ vtfileunlock(f->msource);
|
|
|
+Err1:
|
|
|
+ fileunlock(f);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
-int
|
|
|
-vdeRead(VacDirEnum *ds, VacDir *dir, int n)
|
|
|
+/*
|
|
|
+ * Vac file system format.
|
|
|
+ */
|
|
|
+static char EBadVacFormat[] = "bad format for vac file";
|
|
|
+
|
|
|
+static VacFs *
|
|
|
+vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
|
|
|
{
|
|
|
- ulong nb;
|
|
|
- int i;
|
|
|
- Source *meta, *source;
|
|
|
- MetaBlock mb;
|
|
|
- MetaEntry me;
|
|
|
- Lump *u;
|
|
|
+ VacFs *fs;
|
|
|
+
|
|
|
+ fs = vtmallocz(sizeof(VacFs));
|
|
|
+ fs->z = z;
|
|
|
+ fs->bsize = bsize;
|
|
|
+ fs->mode = mode;
|
|
|
+ fs->cache = vtcachealloc(z, bsize, ncache);
|
|
|
+ return fs;
|
|
|
+}
|
|
|
|
|
|
- vfRAccess(ds->file);
|
|
|
+static int
|
|
|
+readscore(int fd, uchar score[VtScoreSize])
|
|
|
+{
|
|
|
+ char buf[45], *pref;
|
|
|
+ int n;
|
|
|
|
|
|
- if(!vfRLock(ds->file))
|
|
|
+ n = readn(fd, buf, sizeof(buf)-1);
|
|
|
+ if(n < sizeof(buf)-1) {
|
|
|
+ werrstr("short read");
|
|
|
return -1;
|
|
|
+ }
|
|
|
+ buf[n] = 0;
|
|
|
|
|
|
- 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;
|
|
|
+ if(vtparsescore(buf, &pref, score) < 0){
|
|
|
+ werrstr(EBadVacFormat);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(pref==nil || strcmp(pref, "vac") != 0) {
|
|
|
+ werrstr("not a vac file");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- 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;
|
|
|
+VacFs*
|
|
|
+vacfsopen(VtConn *z, char *file, int mode, int ncache)
|
|
|
+{
|
|
|
+ int fd;
|
|
|
+ uchar score[VtScoreSize];
|
|
|
+ char *prefix;
|
|
|
+
|
|
|
+ if(vtparsescore(file, &prefix, score) >= 0){
|
|
|
+ if(strcmp(prefix, "vac") != 0){
|
|
|
+ werrstr("not a vac file");
|
|
|
+ return nil;
|
|
|
}
|
|
|
- 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;
|
|
|
+ }else{
|
|
|
+ fd = open(file, OREAD);
|
|
|
+ if(fd < 0)
|
|
|
+ return nil;
|
|
|
+ if(readscore(fd, score) < 0){
|
|
|
+ close(fd);
|
|
|
+ return nil;
|
|
|
}
|
|
|
- ds->index++;
|
|
|
+ close(fd);
|
|
|
}
|
|
|
-Exit:
|
|
|
- lumpDecRef(u, 1);
|
|
|
- vfRUnlock(ds->file);
|
|
|
- return i;
|
|
|
+ return vacfsopenscore(z, score, mode, ncache);
|
|
|
+}
|
|
|
+
|
|
|
+VacFs*
|
|
|
+vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
|
|
|
+{
|
|
|
+ VacFs *fs;
|
|
|
+ int n;
|
|
|
+ VtRoot rt;
|
|
|
+ uchar buf[VtRootSize];
|
|
|
+ VacFile *root;
|
|
|
+ VtFile *r;
|
|
|
+ VtEntry e;
|
|
|
+
|
|
|
+ n = vtread(z, score, VtRootType, buf, VtRootSize);
|
|
|
+ if(n < 0)
|
|
|
+ return nil;
|
|
|
+ if(n != VtRootSize){
|
|
|
+ werrstr("vtread on root too short");
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(vtrootunpack(&rt, buf) < 0)
|
|
|
+ return nil;
|
|
|
+
|
|
|
+ if(strcmp(rt.type, "vac") != 0) {
|
|
|
+ werrstr("not a vac root");
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+
|
|
|
+ fs = vacfsalloc(z, rt.blocksize, ncache, mode);
|
|
|
+ memmove(fs->score, score, VtScoreSize);
|
|
|
+ fs->mode = mode;
|
|
|
+
|
|
|
+ memmove(e.score, rt.score, VtScoreSize);
|
|
|
+ e.gen = 0;
|
|
|
+ e.psize = (rt.blocksize/VtEntrySize)*VtEntrySize;
|
|
|
+ e.dsize = rt.blocksize;
|
|
|
+ e.type = VtDirType;
|
|
|
+ e.flags = VtEntryActive;
|
|
|
+ e.size = 3*VtEntrySize;
|
|
|
+
|
|
|
+ root = nil;
|
|
|
+ if((r = vtfileopenroot(fs->cache, &e)) == nil)
|
|
|
+ goto Err;
|
|
|
+ if(debug)
|
|
|
+ fprint(2, "r %p\n", r);
|
|
|
+ root = _vacfileroot(fs, r);
|
|
|
+ if(debug)
|
|
|
+ fprint(2, "root %p\n", root);
|
|
|
+ vtfileclose(r);
|
|
|
+ if(root == nil)
|
|
|
+ goto Err;
|
|
|
+ fs->root = root;
|
|
|
+ return fs;
|
|
|
Err:
|
|
|
- lumpDecRef(u, 1);
|
|
|
- vfRUnlock(ds->file);
|
|
|
- n = i;
|
|
|
- for(i=0; i<n ; i++)
|
|
|
- vdCleanup(&dir[i]);
|
|
|
- return -1;
|
|
|
+ if(root)
|
|
|
+ vacfiledecref(root);
|
|
|
+ vacfsclose(fs);
|
|
|
+ return nil;
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-vdeFree(VacDirEnum *ds)
|
|
|
+int
|
|
|
+vacfsmode(VacFs *fs)
|
|
|
{
|
|
|
- if(ds == nil)
|
|
|
- return;
|
|
|
- vfDecRef(ds->file);
|
|
|
- vtMemFree(ds);
|
|
|
+ return fs->mode;
|
|
|
}
|
|
|
|
|
|
-static ulong
|
|
|
-msAlloc(Source *ms, ulong start, int n)
|
|
|
+VacFile*
|
|
|
+vacfsgetroot(VacFs *fs)
|
|
|
{
|
|
|
- ulong nb, i;
|
|
|
- Lump *u;
|
|
|
- MetaBlock mb;
|
|
|
+ return vacfileincref(fs->root);
|
|
|
+}
|
|
|
|
|
|
- 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;
|
|
|
+int
|
|
|
+vacfsgetblocksize(VacFs *fs)
|
|
|
+{
|
|
|
+ return fs->bsize;
|
|
|
}
|
|
|
|
|
|
-VacFS *
|
|
|
-vacfs(VacFile *vf)
|
|
|
+int
|
|
|
+vacfsgetscore(VacFs *fs, u8int *score)
|
|
|
{
|
|
|
- if (vf == nil)
|
|
|
- return nil;
|
|
|
- return vf->fs;
|
|
|
+ memmove(score, fs->score, VtScoreSize);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+_vacfsnextqid(VacFs *fs, uvlong *qid)
|
|
|
+{
|
|
|
+ ++fs->qid;
|
|
|
+ *qid = fs->qid;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+vacfsjumpqid(VacFs *fs, uvlong step)
|
|
|
+{
|
|
|
+ fs->qid += step;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * path may be nil; it's the right-hand part of the path so far.
|
|
|
- * result is malloced, path must be malloced or nil.
|
|
|
+ * Set *maxqid to the maximum qid expected in this file system.
|
|
|
+ * In newer vac archives, the maximum qid is stored in the
|
|
|
+ * qidspace VacDir annotation. In older vac archives, the root
|
|
|
+ * got created last, so it had the maximum qid.
|
|
|
*/
|
|
|
-char *
|
|
|
-vfName(VacFile *vf, char *path)
|
|
|
+int
|
|
|
+vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
|
|
|
{
|
|
|
- char *nname, *rname, *elem;
|
|
|
-
|
|
|
- if (vf == nil || vf == vf->up) { /* at the root? */
|
|
|
- if (path == nil)
|
|
|
- return strdup("/");
|
|
|
- return path;
|
|
|
- }
|
|
|
- elem = vf->dir.elem;
|
|
|
- if (elem != nil && path != nil)
|
|
|
- rname = smprint("%s/%s", elem, path);
|
|
|
- else if (elem != nil)
|
|
|
- rname = strdup(elem);
|
|
|
+ VacDir vd;
|
|
|
+
|
|
|
+ if(vacfilegetdir(fs->root, &vd) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vd.qidspace)
|
|
|
+ *maxqid = vd.qidmax;
|
|
|
else
|
|
|
- return vfName(vf->up, path);
|
|
|
- nname = vfName(vf->up, rname);
|
|
|
- if (nname != rname)
|
|
|
- free(rname);
|
|
|
- return nname;
|
|
|
+ *maxqid = vd.qid;
|
|
|
+ vdcleanup(&vd);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+vacfsclose(VacFs *fs)
|
|
|
+{
|
|
|
+ if(fs->root)
|
|
|
+ vacfiledecref(fs->root);
|
|
|
+ fs->root = nil;
|
|
|
+ vtcachefree(fs->cache);
|
|
|
+ vtfree(fs);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Create a fresh vac fs.
|
|
|
+ */
|
|
|
+VacFs *
|
|
|
+vacfscreate(VtConn *z, int bsize, int ncache)
|
|
|
+{
|
|
|
+ VacFs *fs;
|
|
|
+ VtFile *f;
|
|
|
+ uchar buf[VtEntrySize], metascore[VtScoreSize];
|
|
|
+ VtEntry e;
|
|
|
+ VtBlock *b;
|
|
|
+ MetaBlock mb;
|
|
|
+ VacDir vd;
|
|
|
+ MetaEntry me;
|
|
|
+ int psize;
|
|
|
+
|
|
|
+ if((fs = vacfsalloc(z, bsize, ncache, VtORDWR)) == nil)
|
|
|
+ return nil;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Fake up an empty vac fs.
|
|
|
+ */
|
|
|
+ psize = bsize/VtEntrySize*VtEntrySize;
|
|
|
+ f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
|
|
|
+ vtfilelock(f, VtORDWR);
|
|
|
+
|
|
|
+ /* Write metablock containing root directory VacDir. */
|
|
|
+ b = vtcacheallocblock(fs->cache, VtDataType);
|
|
|
+ mbinit(&mb, b->data, bsize, bsize/BytesPerEntry);
|
|
|
+ memset(&vd, 0, sizeof vd);
|
|
|
+ vd.elem = "/";
|
|
|
+ vd.mode = 0777|ModeDir;
|
|
|
+ vd.uid = "vac";
|
|
|
+ vd.gid = "vac";
|
|
|
+ vd.mid = "";
|
|
|
+ me.size = vdsize(&vd, VacDirVersion);
|
|
|
+ me.p = mballoc(&mb, me.size);
|
|
|
+ vdpack(&vd, &me, VacDirVersion);
|
|
|
+ mbinsert(&mb, 0, &me);
|
|
|
+ mbpack(&mb);
|
|
|
+ vtblockwrite(b);
|
|
|
+ memmove(metascore, b->score, VtScoreSize);
|
|
|
+ vtblockput(b);
|
|
|
+
|
|
|
+ /* First entry: empty venti directory stream. */
|
|
|
+ memset(&e, 0, sizeof e);
|
|
|
+ e.flags = VtEntryActive;
|
|
|
+ e.psize = psize;
|
|
|
+ e.dsize = bsize;
|
|
|
+ e.type = VtDirType;
|
|
|
+ memmove(e.score, vtzeroscore, VtScoreSize);
|
|
|
+ vtentrypack(&e, buf, 0);
|
|
|
+ vtfilewrite(f, buf, VtEntrySize, 0);
|
|
|
+
|
|
|
+ /* Second entry: empty metadata stream. */
|
|
|
+ e.type = VtDataType;
|
|
|
+ vtentrypack(&e, buf, 0);
|
|
|
+ vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
|
|
|
+
|
|
|
+ /* Third entry: metadata stream with root directory. */
|
|
|
+ memmove(e.score, metascore, VtScoreSize);
|
|
|
+ e.size = bsize;
|
|
|
+ vtentrypack(&e, buf, 0);
|
|
|
+ vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
|
|
|
+
|
|
|
+ vtfileflush(f);
|
|
|
+ vtfileunlock(f);
|
|
|
+
|
|
|
+ /* Now open it as a vac fs. */
|
|
|
+ fs->root = _vacfileroot(fs, f);
|
|
|
+ if(fs->root == nil){
|
|
|
+ werrstr("vacfileroot: %r");
|
|
|
+ vacfsclose(fs);
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+
|
|
|
+ return fs;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+vacfssync(VacFs *fs)
|
|
|
+{
|
|
|
+ uchar buf[1024];
|
|
|
+ VtEntry e;
|
|
|
+ VtFile *f;
|
|
|
+ VtRoot root;
|
|
|
+
|
|
|
+ /* Sync the entire vacfs to disk. */
|
|
|
+ if(vacfileflush(fs->root, 1) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vtfilelock(fs->root->up->msource, -1) < 0)
|
|
|
+ return -1;
|
|
|
+ if(vtfileflush(fs->root->up->msource) < 0){
|
|
|
+ vtfileunlock(fs->root->up->msource);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ vtfileunlock(fs->root->up->msource);
|
|
|
+
|
|
|
+ /* Prepare the dir stream for the root block. */
|
|
|
+ if(getentry(fs->root->source, &e) < 0)
|
|
|
+ return -1;
|
|
|
+ vtentrypack(&e, buf, 0);
|
|
|
+ if(getentry(fs->root->msource, &e) < 0)
|
|
|
+ return -1;
|
|
|
+ vtentrypack(&e, buf, 1);
|
|
|
+ if(getentry(fs->root->up->msource, &e) < 0)
|
|
|
+ return -1;
|
|
|
+ vtentrypack(&e, buf, 2);
|
|
|
+
|
|
|
+ f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
|
|
|
+ vtfilelock(f, VtORDWR);
|
|
|
+ if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
|
|
|
+ || vtfileflush(f) < 0){
|
|
|
+ vtfileunlock(f);
|
|
|
+ vtfileclose(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ vtfileunlock(f);
|
|
|
+ if(getentry(f, &e) < 0){
|
|
|
+ vtfileclose(f);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ vtfileclose(f);
|
|
|
+
|
|
|
+ /* Build a root block. */
|
|
|
+ memset(&root, 0, sizeof root);
|
|
|
+ strcpy(root.type, "vac");
|
|
|
+ strcpy(root.name, fs->name);
|
|
|
+ memmove(root.score, e.score, VtScoreSize);
|
|
|
+ root.blocksize = fs->bsize;
|
|
|
+ memmove(root.prev, fs->score, VtScoreSize);
|
|
|
+ vtrootpack(&root, buf);
|
|
|
+ if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
|
|
|
+ werrstr("writing root: %r");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if(vtsync(fs->z) < 0)
|
|
|
+ return -1;
|
|
|
+ return 0;
|
|
|
}
|