aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vac
diff options
context:
space:
mode:
authorRuss Cox <rsc@swtch.com>2008-06-14 23:08:50 -0400
committerRuss Cox <rsc@swtch.com>2008-06-14 23:08:50 -0400
commitecc0a1b0e77605dee8d9d33d2bc0d3d5ce7ca951 (patch)
tree5de980d531effa86b0222c3da20c7f163dd5d419 /src/cmd/vac
parente9b70a5f4ca38a98b64c9e0cea528693f5297376 (diff)
downloadplan9port-ecc0a1b0e77605dee8d9d33d2bc0d3d5ce7ca951.tar.gz
plan9port-ecc0a1b0e77605dee8d9d33d2bc0d3d5ce7ca951.tar.bz2
plan9port-ecc0a1b0e77605dee8d9d33d2bc0d3d5ce7ca951.zip
vac: major cleanup and bug fixes
Diffstat (limited to 'src/cmd/vac')
-rw-r--r--src/cmd/vac/dat.h2
-rw-r--r--src/cmd/vac/file.c2375
-rw-r--r--src/cmd/vac/fns.h3
-rw-r--r--src/cmd/vac/fs.c185
-rw-r--r--src/cmd/vac/pack.c4
-rw-r--r--src/cmd/vac/vac.c2
-rw-r--r--src/cmd/vac/vac.h22
-rw-r--r--src/cmd/vac/vacfs.c4
8 files changed, 1375 insertions, 1222 deletions
diff --git a/src/cmd/vac/dat.h b/src/cmd/vac/dat.h
index 1967c436..000a762a 100644
--- a/src/cmd/vac/dat.h
+++ b/src/cmd/vac/dat.h
@@ -36,5 +36,3 @@ struct VacDirEnum
VacDir *buf;
};
-void _mbinit(MetaBlock*, u8int*, uint, uint);
-int _mbsearch(MetaBlock*, char*, int*, MetaEntry*);
diff --git a/src/cmd/vac/file.c b/src/cmd/vac/file.c
index b7384b7f..035b2777 100644
--- a/src/cmd/vac/file.c
+++ b/src/cmd/vac/file.c
@@ -7,9 +7,24 @@
#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
{
VacFs *fs; /* immutable */
@@ -32,22 +47,6 @@ struct VacFile
int mode;
};
-static int filelock(VacFile*);
-static u32int filemetaalloc(VacFile*, VacDir*, u32int);
-static int filemetaflush2(VacFile*, char*);
-static void filemetalock(VacFile*);
-static void filemetaunlock(VacFile*);
-static void fileraccess(VacFile*);
-static int filerlock(VacFile*);
-static void filerunlock(VacFile*);
-static void fileunlock(VacFile*);
-static void filewaccess(VacFile*, char*);
-
-void mbinit(MetaBlock*, u8int*, uint, uint);
-int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
-int mbresize(MetaBlock*, MetaEntry*, int);
-VacFile *vdlookup(VacFile*, char*);
-
static VacFile*
filealloc(VacFs *fs)
{
@@ -71,56 +70,197 @@ filefree(VacFile *f)
vtfree(f);
}
+static int
+chksource(VacFile *f)
+{
+ 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 0;
+}
+
+static void
+fileunlock(VacFile *f)
+{
+ wunlock(&f->lk);
+}
+
+static int
+filerlock(VacFile *f)
+{
+ rlock(&f->lk);
+ if(chksource(f) < 0){
+ runlock(&f->lk);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+filerunlock(VacFile *f)
+{
+ runlock(&f->lk);
+}
+
/*
- * the file is locked already
- * f->msource is unlocked
+ * The file metadata, like f->dir and f->ref,
+ * are synchronized via the parent's lock.
+ * This is why locking order goes up.
*/
-static VacFile*
-dirlookup(VacFile *f, char *elem)
+static void
+filemetalock(VacFile *f)
{
- int i;
- MetaBlock mb;
- MetaEntry me;
- VtBlock *b;
- VtFile *meta;
- VacFile *ff;
- u32int bo, nb;
+ assert(f->up != nil);
+ wlock(&f->up->lk);
+}
- 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;
- }
- vtfileunlock(meta);
- vtblockput(b);
- ff->boff = bo;
- ff->mode = f->mode;
- return ff;
- }
+static void
+filemetaunlock(VacFile *f)
+{
+ wunlock(&f->up->lk);
+}
- vtblockput(b);
- b = nil;
+uvlong
+vacfilegetid(VacFile *f)
+{
+ /* immutable */
+ return f->dir.qid;
+}
+
+ulong
+vacfilegetmcount(VacFile *f)
+{
+ ulong mcount;
+
+ filemetalock(f);
+ mcount = f->dir.mcount;
+ filemetaunlock(f);
+ return mcount;
+}
+
+ulong
+vacfilegetmode(VacFile *f)
+{
+ ulong mode;
+
+ filemetalock(f);
+ mode = f->dir.mode;
+ filemetaunlock(f);
+ return mode;
+}
+
+int
+vacfileisdir(VacFile *f)
+{
+ /* immutable */
+ return (f->dir.mode & ModeDir) != 0;
+}
+
+int
+vacfileisroot(VacFile *f)
+{
+ return f == f->fs->root;
+}
+
+/*
+ * 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)
+{
+ filemetalock(vf);
+ assert(vf->ref > 0);
+ vf->ref++;
+ filemetaunlock(vf);
+ return vf;
+}
+
+int
+vacfiledecref(VacFile *f)
+{
+ VacFile *p, *q, **qq;
+
+ if(f->up == nil){
+ /* never linked in */
+ assert(f->ref == 1);
+ filefree(f);
+ return 0;
}
- werrstr(ENoFile);
- /* fall through */
-Err:
- vtfileunlock(meta);
- vtblockput(b);
- return nil;
+
+ filemetalock(f);
+ f->ref--;
+ if(f->ref > 0){
+ filemetaunlock(f);
+ return -1;
+ }
+ assert(f->ref == 0);
+ assert(f->down == nil);
+
+ 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);
+
+ p = f->up;
+ qq = &p->down;
+ for(q = *qq; q; q = *qq){
+ if(q == f)
+ break;
+ qq = &q->next;
+ }
+ assert(q != nil);
+ *qq = f->next;
+
+ filemetaunlock(f);
+ filefree(f);
+ vacfiledecref(p);
+ return 0;
}
+
+/*
+ * 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)
{
@@ -183,26 +323,27 @@ Top:
r1 = nil;
mr->down = root;
+ vtfileunlock(r);
- if(vtfilelock(mr->msource, -1) < 0)
- goto Err;
+ if(vtfilelock(mr->msource, VtOREAD) < 0)
+ goto Err1;
b = vtfileblock(mr->msource, 0, VtOREAD);
vtfileunlock(mr->msource);
if(b == nil)
- goto Err;
+ goto Err1;
if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
- goto Err;
+ goto Err1;
meunpack(&me, &mb, 0);
if(vdunpack(&root->dir, &me) < 0)
- goto Err;
+ goto Err1;
vtblockput(b);
- vtfileunlock(r);
- fileraccess(root);
return root;
Err:
+ vtfileunlock(r);
+Err1:
vtblockput(b);
if(r0)
vtfileclose(r0);
@@ -214,43 +355,111 @@ Err:
filefree(mr);
if(root)
filefree(root);
- vtfileunlock(r);
return nil;
}
+/*
+ * 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)
+{
+ 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;
+ }
+ 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;
+}
+
+/*
+ * 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(vtfilelock(f->source, mode) < 0)
+ if((r = vtfileopen(f->source, offset, mode)) == nil)
return nil;
- r = vtfileopen(f->source, offset, mode);
- vtfileunlock(f->source);
if(r == nil)
return nil;
if(r->gen != gen){
werrstr(ERemoved);
- goto Err;
+ vtfileclose(r);
+ return nil;
}
if(r->dir != dir && r->mode != -1){
-fprint(2, "fileopensource: dir mismatch %d %d\n", r->dir, dir);
werrstr(EBadMeta);
- goto Err;
+ vtfileclose(r);
+ return nil;
}
return r;
-Err:
- vtfileclose(r);
- return nil;
}
VacFile*
-_filewalk(VacFile *f, char *elem, int partial)
+vacfilegetparent(VacFile *f)
{
- VacFile *ff;
+ if(vacfileisroot(f))
+ return vacfileincref(f);
+ return vacfileincref(f->up);
+}
- fileraccess(f);
+/*
+ * 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);
@@ -262,15 +471,11 @@ _filewalk(VacFile *f, char *elem, int partial)
return nil;
}
- if(strcmp(elem, ".") == 0){
+ if(strcmp(elem, ".") == 0)
return vacfileincref(f);
- }
- if(strcmp(elem, "..") == 0){
- if(vacfileisroot(f))
- return vacfileincref(f);
- return vacfileincref(f->up);
- }
+ if(strcmp(elem, "..") == 0)
+ return vacfilegetparent(f);
if(filelock(f) < 0)
return nil;
@@ -289,26 +494,19 @@ _filewalk(VacFile *f, char *elem, int partial)
if(ff->dir.mode & ModeSnapshot)
ff->mode = VtOREAD;
- if(partial){
- /*
- * Do nothing. We're opening this file only so we can clri it.
- * Usually the sources can't be opened, hence we won't even bother.
- * Be VERY careful with the returned file. If you hand it to a routine
- * expecting ff->source and/or ff->msource to be non-nil, we're
- * likely to dereference nil. FileClri should be the only routine
- * setting partial.
- */
- ff->partial = 1;
- }else if(ff->dir.mode & ModeDir){
+ if(vtfilelock(f->source, f->mode) < 0)
+ goto Err;
+ 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 Err;
+ goto Err1;
}else{
ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
if(ff->source == nil)
- goto Err;
+ goto Err1;
}
+ vtfileunlock(f->source);
/* link in and up parent ref count */
ff->next = f->down;
@@ -318,6 +516,9 @@ _filewalk(VacFile *f, char *elem, int partial)
Exit:
fileunlock(f);
return ff;
+
+Err1:
+ vtfileunlock(f->source);
Err:
fileunlock(f);
if(ff != nil)
@@ -325,14 +526,12 @@ Err:
return nil;
}
+/*
+ * Open a path in the vac file system:
+ * just walk each element one at a time.
+ */
VacFile*
-vacfilewalk(VacFile *f, char *elem)
-{
- return _filewalk(f, elem, 0);
-}
-
-VacFile*
-_fileopen(VacFs *fs, char *path, int partial)
+vacfileopen(VacFs *fs, char *path)
{
VacFile *f, *ff;
char *p, elem[VtMaxStringSize], *opath;
@@ -352,7 +551,7 @@ _fileopen(VacFs *fs, char *path, int partial)
}
memmove(elem, path, n);
elem[n] = 0;
- ff = _filewalk(f, elem, partial && *p=='\0');
+ ff = vacfilewalk(f, elem);
if(ff == nil){
werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
goto Err;
@@ -370,57 +569,677 @@ Err:
return nil;
}
-VacFile*
-vacfileopen(VacFs *fs, char *path)
+/*
+ * Extract the score for the bn'th block in f.
+ */
+int
+vacfileblockscore(VacFile *f, u32int bn, u8int *score)
{
- return _fileopen(fs, path, 0);
+ VtFile *s;
+ uvlong size;
+ int dsize, ret;
+
+ ret = -1;
+ if(filerlock(f) < 0)
+ return -1;
+ if(vtfilelock(f->source, VtOREAD) < 0)
+ goto out;
+
+ 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;
}
-#if 0
-static void
-filesettmp(VacFile *f, int istmp)
+/*
+ * Read data from f.
+ */
+int
+vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
{
- int i;
+ int n;
+
+ if(offset < 0){
+ werrstr(EBadOffset);
+ return -1;
+ }
+ 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;
+}
+
+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;
+ }
+ return 0;
+}
+
+/*
+ * Get the VtEntries for the data contained in f.
+ */
+int
+vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
+{
+ if(filerlock(f) < 0)
+ return -1;
+ if(e && getentry(f->source, e) < 0){
+ filerunlock(f);
+ return -1;
+ }
+ if(me){
+ if(f->msource == nil)
+ memset(me, 0, sizeof *me);
+ if(getentry(f->msource, me) < 0){
+ filerunlock(f);
+ return -1;
+ }
+ }
+ filerunlock(f);
+ return 0;
+}
+
+/*
+ * Get the file's size.
+ */
+int
+vacfilegetsize(VacFile *f, uvlong *size)
+{
+ 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);
+
+ return 0;
+}
+
+/*
+ * 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(!vacfileisdir(f)){
+ werrstr(ENotDir);
+ return nil;
+ }
+
+ /*
+ * 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;
- VtFile *r;
+ int epb;
- for(i=0; i<2; i++){
- if(i==0)
- r = f->source;
- else
- r = f->msource;
- if(r == nil)
- continue;
- if(vtfilegetentry(r, &e) < 0){
- fprint(2, "vtfilegetentry failed (cannot happen): %r\n");
- continue;
+ 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;
+
+ /* dangling entries are returned as zero size */
+ if(!(e.flags & VtEntryActive) || e.gen != gen)
+ *size = 0;
+ else
+ *size = e.size;
+ vtblockput(b);
+ return 0;
+
+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;
+
+ n = mb.nindex;
+ vde->buf = vtmalloc(n * sizeof(VacDir));
+
+ for(i=0; i<n; i++){
+ de = vde->buf + i;
+ meunpack(&me, &mb, i);
+ if(vdunpack(de, &me) < 0)
+ goto Err;
+ vde->n++;
+ if(!(de->mode & ModeDir))
+ if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
+ goto Err;
+ }
+ vde->boff++;
+ vtblockput(b);
+ return 0;
+Err:
+ 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
+vderead(VacDirEnum *vde, VacDir *de)
+{
+ 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;
}
- if(istmp)
- e.flags |= VtEntryNoArchive;
+ }
+
+ memmove(de, vde->buf + vde->i, sizeof(VacDir));
+ vde->i++;
+ ret = 1;
+
+Return:
+ vtfileunlock(f->source);
+ vtfileunlock(f->msource);
+ filerunlock(f);
+
+ return ret;
+}
+
+/*
+ * "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)
+{
+ if(vde->i > 0){
+ vde->i--;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * Close the enumerator.
+ */
+void
+vdeclose(VacDirEnum *vde)
+{
+ 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);
+}
+
+
+/*
+ * 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.
+ */
+
+/*
+ * 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)
+{
+ 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
- e.flags &= ~VtEntryNoArchive;
- if(vtfilesetentry(r, &e) < 0){
- fprint(2, "vtfilesetentry failed (cannot happen): %r\n");
- continue;
+ start = 0;
+ }
+
+ b = nil;
+ 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);
+ b = nil;
+ }
+
+ /* 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);
+ mbinsert(&mb, i, &me);
+ mbpack(&mb);
+ vtblockput(b);
+ return bo;
+
+Err:
+ vtblockput(b);
+ return NilBlock;
+}
+
+/*
+ * 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)
+{
+ int i, n;
+ MetaBlock mb;
+ MetaEntry me, me2;
+ VacFile *fp;
+ VtBlock *b;
+ u32int bo;
+
+ if(!f->dirty)
+ return 0;
+
+ 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);
+ 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;
}
-#endif
+/*
+ * Remove the directory entry for f.
+ */
+static int
+filemetaremove(VacFile *f)
+{
+ VtBlock *b;
+ MetaBlock mb;
+ MetaEntry me;
+ int i;
+ VacFile *fp;
+
+ b = nil;
+ fp = f->up;
+ filemetalock(f);
+
+ if(vtfilelock(fp->msource, VtORDWR) < 0)
+ goto Err;
+ b = vtfileblock(fp->msource, f->boff, VtORDWR);
+ if(b == nil)
+ goto Err;
+
+ if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
+ goto Err;
+ if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
+ goto Err;
+ mbdelete(&mb, i, &me);
+ mbpack(&mb);
+ vtblockput(b);
+ vtfileunlock(fp->msource);
+
+ f->removed = 1;
+ f->boff = NilBlock;
+ f->dirty = 0;
+
+ filemetaunlock(f);
+ return 0;
+
+Err:
+ 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.
+ */
+
+/*
+ * 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 ret;
+ VacFile **kids, *p;
+ int i, nkids;
+
+ if(f->mode == VtOREAD)
+ return 0;
+
+ 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;
+ fileunlock(f);
+ 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++;
+ }
+ }
+ fileunlock(f);
+
+ for(i=0; i<nkids; i++){
+ if(vacfileflush(kids[i], 1) < 0)
+ ret = -1;
+ vacfiledecref(kids[i]);
+ }
+ free(kids);
+ return ret;
+}
+
+/*
+ * 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 *f, char *elem, ulong mode, char *uid)
+vacfilecreate(VacFile *fp, char *elem, ulong mode)
{
VacFile *ff;
VacDir *dir;
VtFile *pr, *r, *mr;
- int isdir;
+ int type;
+ u32int bo;
- if(filelock(f) < 0)
+ if(filelock(fp) < 0)
return nil;
- r = nil;
- mr = nil;
- for(ff = f->down; ff; ff=ff->next){
+ /*
+ * 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);
@@ -428,91 +1247,87 @@ vacfilecreate(VacFile *f, char *elem, ulong mode, char *uid)
}
}
- ff = dirlookup(f, elem);
+ /*
+ * Next check the venti blocks.
+ */
+ ff = dirlookup(fp, elem);
if(ff != nil){
werrstr(EExists);
goto Err1;
}
- pr = f->source;
+ /*
+ * By the way, you can't create in a read-only file system.
+ */
+ pr = fp->source;
if(pr->mode != VtORDWR){
werrstr(EReadOnly);
goto Err1;
}
- if(vtfilelock2(f->source, f->msource, -1) < 0)
+ /*
+ * 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(f->fs);
- isdir = mode & ModeDir;
-
- r = vtfilecreate(pr, pr->psize, pr->dsize, isdir ? VtDirType : VtDataType);
- if(r == nil)
+ ff = filealloc(fp->fs);
+ 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;
- if(isdir){
- mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType);
- if(mr == nil)
- goto Err;
- }
+ /*
+ * 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(isdir){
+ if(mode & ModeDir){
dir->mentry = mr->offset;
dir->mgen = mr->gen;
}
dir->size = 0;
- if(_vacfsnextqid(f->fs, &dir->qid) < 0)
+ if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
goto Err;
- dir->uid = vtstrdup(uid);
- dir->gid = vtstrdup(f->dir.gid);
- dir->mid = vtstrdup(uid);
+ 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;
-
- ff->boff = filemetaalloc(f, dir, 0);
- if(ff->boff == NilBlock)
+ if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) < 0)
goto Err;
- vtfileunlock(f->source);
- vtfileunlock(f->msource);
-
+ /*
+ * Now we're committed.
+ */
+ vtfileunlock(fp->source);
+ vtfileunlock(fp->msource);
ff->source = r;
ff->msource = mr;
+ ff->boff = bo;
-#if 0
- if(mode&ModeTemporary){
- if(vtfilelock2(r, mr, -1) < 0)
- goto Err1;
- filesettmp(ff, 1);
- vtfileunlock(r);
- if(mr)
- vtfileunlock(mr);
- }
-#endif
-
- /* committed */
+ /* Link into tree. */
+ ff->next = fp->down;
+ fp->down = ff;
+ ff->up = fp;
+ vacfileincref(fp);
- /* link in and up parent ref count */
- ff->next = f->down;
- f->down = ff;
- ff->up = f;
- vacfileincref(f);
-
- filewaccess(f, uid);
-
- fileunlock(f);
+ fileunlock(fp);
return ff;
Err:
- vtfileunlock(f->source);
- vtfileunlock(f->msource);
-Err1:
+ vtfileunlock(fp->source);
+ vtfileunlock(fp->msource);
if(r){
vtfilelock(r, -1);
vtfileremove(r);
@@ -521,147 +1336,58 @@ Err1:
vtfilelock(mr, -1);
vtfileremove(mr);
}
+Err1:
if(ff)
vacfiledecref(ff);
- fileunlock(f);
+ fileunlock(fp);
return nil;
}
+/*
+ * Change the size of the file f.
+ */
int
-vacfileblockscore(VacFile *f, u32int bn, u8int *score)
-{
- VtFile *s;
- uvlong size;
- int dsize, ret;
-
- ret = -1;
- if(filerlock(f) < 0)
- return -1;
- fileraccess(f);
- if(vtfilelock(f->source, VtOREAD) < 0)
- goto out;
-
- 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;
-}
-
-int
-vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
+vacfilesetsize(VacFile *f, uvlong size)
{
- VtFile *s;
- uvlong size;
- u32int bn;
- int off, dsize, n, nn;
- VtBlock *b;
- uchar *p;
-
- if(filerlock(f) < 0)
+ if(vacfileisdir(f)){
+ werrstr(ENotFile);
return -1;
-
- if(offset < 0){
- werrstr(EBadOffset);
- goto Err1;
- }
-
- fileraccess(f);
-
- if(vtfilelock(f->source, VtOREAD) < 0)
- goto Err1;
-
- s = f->source;
- dsize = s->dsize;
- size = vtfilegetsize(s);
-
- if(offset >= size)
- offset = size;
-
- if(cnt > size-offset)
- cnt = size-offset;
- bn = offset/dsize;
- off = offset%dsize;
- p = buf;
- while(cnt > 0){
- b = vtfileblock(s, bn, OREAD);
- if(b == nil)
- goto Err;
- n = cnt;
- if(n > dsize-off)
- n = dsize-off;
- nn = dsize-off;
- if(nn > n)
- nn = n;
- memmove(p, b->data+off, nn);
- memset(p+nn, 0, nn-n);
- off = 0;
- bn++;
- cnt -= n;
- p += n;
- vtblockput(b);
}
- vtfileunlock(s);
- filerunlock(f);
- return p-(uchar*)buf;
-
-Err:
- vtfileunlock(s);
-Err1:
- filerunlock(f);
- return -1;
-}
-
-int
-vacfilesetsize(VacFile *f, uvlong size)
-{
- int r;
-
+
if(filelock(f) < 0)
return -1;
- r = 0;
- if(f->dir.mode & ModeDir){
- werrstr(ENotFile);
- goto Err;
- }
+
if(f->source->mode != VtORDWR){
werrstr(EReadOnly);
goto Err;
}
if(vtfilelock(f->source, -1) < 0)
goto Err;
- r = vtfilesetsize(f->source, size);
+ if(vtfilesetsize(f->source, size) < 0){
+ vtfileunlock(f->source);
+ goto Err;
+ }
vtfileunlock(f->source);
+ fileunlock(f);
+ return 0;
+
Err:
fileunlock(f);
- return r;
+ return -1;
}
+/*
+ * Write data to f.
+ */
int
-filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
+vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
{
- VtFile *s;
- ulong bn;
- int off, dsize, n;
- VtBlock *b;
- uchar *p;
- vlong eof;
-
- if(filelock(f) < 0)
- return -1;
-
- s = nil;
- if(f->dir.mode & ModeDir){
+ if(vacfileisdir(f)){
werrstr(ENotFile);
- goto Err;
+ return -1;
}
-
+ if(filelock(f) < 0)
+ return -1;
if(f->source->mode != VtORDWR){
werrstr(EReadOnly);
goto Err;
@@ -671,108 +1397,106 @@ filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
goto Err;
}
- filewaccess(f, uid);
-
if(vtfilelock(f->source, -1) < 0)
goto Err;
- s = f->source;
- dsize = s->dsize;
-
- eof = vtfilegetsize(s);
if(f->dir.mode & ModeAppend)
- offset = eof;
- bn = offset/dsize;
- off = offset%dsize;
- p = buf;
- while(cnt > 0){
- n = cnt;
- if(n > dsize-off)
- n = dsize-off;
- b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
- if(b == nil){
- if(offset > eof)
- vtfilesetsize(s, offset);
- goto Err;
- }
- memmove(b->data+off, p, n);
- off = 0;
- cnt -= n;
- p += n;
- offset += n;
- bn++;
- /* vtblockdirty(b); */
- vtblockput(b);
- }
- if(offset > eof && vtfilesetsize(s, offset) < 0)
+ offset = vtfilegetsize(f->source);
+ if(vtfilewrite(f->source, buf, cnt, offset) != cnt
+ || vtfileflushbefore(f->source, offset) < 0){
+ vtfileunlock(f->source);
goto Err;
- vtfileunlock(s);
+ }
+ vtfileunlock(f->source);
fileunlock(f);
- return p-(uchar*)buf;
+ return cnt;
+
Err:
- if(s)
- vtfileunlock(s);
fileunlock(f);
return -1;
}
+/*
+ * Set (!) the VtEntry for the data contained in f.
+ * This let's us efficiently copy data from one file to another.
+ */
int
-vacfilegetdir(VacFile *f, VacDir *dir)
+vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
{
- if(filerlock(f) < 0)
- return -1;
+ int ret;
- filemetalock(f);
- vdcopy(dir, &f->dir);
- filemetaunlock(f);
+ vacfileflush(f, 0); /* flush blocks to venti, since we won't see them again */
- if(!vacfileisdir(f)){
- if(vtfilelock(f->source, VtOREAD) < 0){
- filerunlock(f);
- return -1;
- }
- dir->size = vtfilegetsize(f->source);
- vtfileunlock(f->source);
+ if(!(e->flags&VtEntryActive)){
+ werrstr("missing entry for source");
+ return -1;
}
- filerunlock(f);
-
- return 0;
-}
-
-int
-vacfiletruncate(VacFile *f, char *uid)
-{
- if(vacfileisdir(f)){
- werrstr(ENotFile);
+ 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){
+ if(f->source->mode != VtORDWR
+ || (f->msource && f->msource->mode != VtORDWR)){
werrstr(EReadOnly);
fileunlock(f);
return -1;
}
- if(vtfilelock(f->source, -1) < 0){
- fileunlock(f);
- return -1;
- }
- if(vtfiletruncate(f->source) < 0){
- vtfileunlock(f->source);
+ 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
+vacfilegetdir(VacFile *f, VacDir *dir)
+{
+ if(filerlock(f) < 0)
+ return -1;
+
+ filemetalock(f);
+ vdcopy(dir, &f->dir);
+ filemetaunlock(f);
- filewaccess(f, uid);
+ 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
-vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
+vacfilesetdir(VacFile *f, VacDir *dir)
{
VacFile *ff;
char *oelem;
@@ -787,16 +1511,14 @@ vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
if(filelock(f) < 0)
return -1;
-
+ filemetalock(f);
+
if(f->source->mode != VtORDWR){
werrstr(EReadOnly);
- fileunlock(f);
- return -1;
+ goto Err;
}
- filemetalock(f);
-
- /* check new name does not already exist */
+ /* 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){
@@ -804,15 +1526,16 @@ vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
goto Err;
}
}
-
ff = dirlookup(f->up, dir->elem);
if(ff != nil){
vacfiledecref(ff);
werrstr(EExists);
goto Err;
}
+ werrstr(""); /* "failed" dirlookup poisoned it */
}
+ /* Get ready... */
if(vtfilelock2(f->source, f->msource, -1) < 0)
goto Err;
if(!vacfileisdir(f)){
@@ -824,14 +1547,9 @@ vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
vtfileunlock(f->msource);
goto Err;
}
- /* commited to changing it now */
}
}
- /* commited to changing it now */
-#if 0
- if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
- filesettmp(f, dir->mode&ModeTemporary);
-#endif
+ /* ... now commited to changing it. */
vtfileunlock(f->source);
if(f->msource)
vtfileunlock(f->msource);
@@ -855,28 +1573,30 @@ vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
f->dir.mtime = dir->mtime;
f->dir.atime = dir->atime;
-/*fprint(2, "mode %x %x ", f->dir.mode, dir->mode); */
mask = ~(ModeDir|ModeSnapshot);
f->dir.mode &= ~mask;
f->dir.mode |= mask & dir->mode;
f->dirty = 1;
-/*fprint(2, "->%x\n", f->dir.mode); */
- filemetaflush2(f, oelem);
+ if(filemetaflush(f, oelem) < 0){
+ vtfree(oelem);
+ goto Err; /* that sucks */
+ }
vtfree(oelem);
filemetaunlock(f);
fileunlock(f);
-
- filewaccess(f->up, uid);
-
return 0;
+
Err:
filemetaunlock(f);
fileunlock(f);
return -1;
}
+/*
+ * Set the qid space.
+ */
int
vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
{
@@ -888,254 +1608,16 @@ vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
f->dir.qidspace = 1;
f->dir.qidoffset = offset;
f->dir.qidmax = max;
- ret = filemetaflush2(f, nil);
+ ret = filemetaflush(f, nil);
filemetaunlock(f);
fileunlock(f);
return ret;
}
-uvlong
-vacfilegetid(VacFile *f)
-{
- /* immutable */
- return f->dir.qid;
-}
-
-ulong
-vacfilegetmcount(VacFile *f)
-{
- ulong mcount;
-
- filemetalock(f);
- mcount = f->dir.mcount;
- filemetaunlock(f);
- return mcount;
-}
-
-ulong
-vacfilegetmode(VacFile *f)
-{
- ulong mode;
-
- filemetalock(f);
- mode = f->dir.mode;
- filemetaunlock(f);
- return mode;
-}
-
-int
-vacfileisdir(VacFile *f)
-{
- /* immutable */
- return (f->dir.mode & ModeDir) != 0;
-}
-
-int
-vacfileisroot(VacFile *f)
-{
- return f == f->fs->root;
-}
-
-int
-vacfilegetsize(VacFile *f, uvlong *size)
-{
- if(filerlock(f) < 0)
- return 0;
- if(vtfilelock(f->source, VtOREAD) < 0){
- filerunlock(f);
- return -1;
- }
- *size = vtfilegetsize(f->source);
- vtfileunlock(f->source);
- filerunlock(f);
-
- return 0;
-}
-
-int
-vacfilegetvtentry(VacFile *f, VtEntry *e)
-{
- if(filerlock(f) < 0)
- return 0;
- if(vtfilelock(f->source, VtOREAD) < 0){
- filerunlock(f);
- return -1;
- }
- vtfilegetentry(f->source, e);
- vtfileunlock(f->source);
- filerunlock(f);
-
- return 0;
-}
-
-void
-vacfilemetaflush(VacFile *f, int rec)
-{
- VacFile **kids, *p;
- int nkids;
- int i;
-
- filemetalock(f);
- filemetaflush2(f, nil);
- filemetaunlock(f);
-
- if(!rec || !vacfileisdir(f))
- return;
-
- if(filelock(f) < 0)
- return;
- 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++;
- }
- fileunlock(f);
-
- for(i=0; i<nkids; i++){
- vacfilemetaflush(kids[i], 1);
- vacfiledecref(kids[i]);
- }
- vtfree(kids);
-}
-
-/* assumes metalock is held */
-static int
-filemetaflush2(VacFile *f, char *oelem)
-{
- VacFile *fp;
- VtBlock *b, *bb;
- MetaBlock mb;
- MetaEntry me, me2;
- int i, n;
- u32int boff;
-
- if(!f->dirty)
- return 0;
-
- if(oelem == nil)
- oelem = f->dir.elem;
-
- fp = f->up;
-
- if(vtfilelock(fp->msource, -1) < 0)
- return 0;
- /* 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;
-
- n = vdsize(&f->dir, VacDirVersion);
-
- if(mbresize(&mb, &me, n) >= 0){
- /* fits in the block */
- mbdelete(&mb, i, &me);
- if(strcmp(f->dir.elem, oelem) != 0)
- mbsearch(&mb, f->dir.elem, &i, &me2);
- vdpack(&f->dir, &me, VacDirVersion);
- mbinsert(&mb, i, &me);
- mbpack(&mb);
- /* vtblockdirty(b); */
- vtblockput(b);
- vtfileunlock(fp->msource);
- f->dirty = 0;
- return -1;
- }
-
- /*
- * moving entry to another block
- * it is feasible for the fs to crash leaving two copies
- * of the directory entry. This is just too much work to
- * fix. Given that entries are only allocated in a block that
- * is less than PercentageFull, most modifications of meta data
- * will fit within the block. i.e. this code should almost
- * never be executed.
- */
- boff = filemetaalloc(fp, &f->dir, f->boff+1);
- if(boff == NilBlock){
- /* mbresize might have modified block */
- mbpack(&mb);
- /* vtblockdirty(b); */
- goto Err;
- }
- f->boff = boff;
-
- /* make sure deletion goes to disk after new entry */
- bb = vtfileblock(fp->msource, f->boff, VtORDWR);
- mbdelete(&mb, i, &me);
- mbpack(&mb);
- vtblockput(bb);
- /* vtblockdirty(b); */
- vtblockput(b);
- vtfileunlock(fp->msource);
-
- f->dirty = 0;
-
- return 0;
-
-Err:
- vtblockput(b);
-Err1:
- vtfileunlock(fp->msource);
- return -1;
-}
-
-static int
-filemetaremove(VacFile *f, char *uid)
-{
- VtBlock *b;
- MetaBlock mb;
- MetaEntry me;
- int i;
- VacFile *up;
-
- b = nil;
- up = f->up;
- filewaccess(up, uid);
- filemetalock(f);
-
- if(vtfilelock(up->msource, VtORDWR) < 0)
- goto Err;
- b = vtfileblock(up->msource, f->boff, VtORDWR);
- if(b == nil)
- goto Err;
-
- if(mbunpack(&mb, b->data, up->msource->dsize) < 0)
- goto Err;
- if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
- goto Err;
- mbdelete(&mb, i, &me);
- mbpack(&mb);
- vtfileunlock(up->msource);
-
- /* vtblockdirty(b); */
- vtblockput(b);
-
- f->removed = 1;
- f->boff = NilBlock;
- f->dirty = 0;
-
- filemetaunlock(f);
- return 0;
-
-Err:
- vtfileunlock(up->msource);
- vtblockput(b);
- filemetaunlock(f);
- return -1;
-}
-
-/* assume file is locked, assume f->msource is locked */
+/*
+ * 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)
{
@@ -1147,9 +1629,9 @@ filecheckempty(VacFile *f)
r = f->msource;
n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
for(i=0; i<n; i++){
- b = vtfileblock(r, i, VtORDWR);
+ b = vtfileblock(r, i, VtOREAD);
if(b == nil)
- goto Err;
+ return -1;
if(mbunpack(&mb, b->data, r->dsize) < 0)
goto Err;
if(mb.nindex > 0){
@@ -1159,17 +1641,21 @@ filecheckempty(VacFile *f)
vtblockput(b);
}
return 0;
+
Err:
vtblockput(b);
return -1;
}
+/*
+ * Remove the vac file f.
+ */
int
-vacfileremove(VacFile *f, char *muid)
+vacfileremove(VacFile *f)
{
VacFile *ff;
- /* can not remove the root */
+ /* Cannot remove the root */
if(vacfileisroot(f)){
werrstr(ERoot);
return -1;
@@ -1177,7 +1663,6 @@ vacfileremove(VacFile *f, char *muid)
if(filelock(f) < 0)
return -1;
-
if(f->source->mode != VtORDWR){
werrstr(EReadOnly);
goto Err1;
@@ -1196,12 +1681,10 @@ vacfileremove(VacFile *f, char *muid)
vtfileremove(f->msource);
f->msource = nil;
}
-
fileunlock(f);
- if(filemetaremove(f, muid) < 0)
+ if(filemetaremove(f) < 0)
return -1;
-
return 0;
Err:
@@ -1213,451 +1696,305 @@ Err1:
return -1;
}
-static int
-clri(VacFile *f, char *uid)
-{
- int r;
-
- if(f == nil)
- return -1;
- if(f->up->source->mode != VtORDWR){
- werrstr(EReadOnly);
- vacfiledecref(f);
- return -1;
- }
- r = filemetaremove(f, uid);
- vacfiledecref(f);
- return r;
-}
-
-int
-vacfileclripath(VacFs *fs, char *path, char *uid)
-{
- return clri(_fileopen(fs, path, 1), uid);
-}
-
-int
-vacfileclri(VacFile *dir, char *elem, char *uid)
-{
- return clri(_filewalk(dir, elem, 1), uid);
-}
+/*
+ * Vac file system format.
+ */
+static char EBadVacFormat[] = "bad format for vac file";
-VacFile*
-vacfileincref(VacFile *vf)
+static VacFs *
+vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
{
- filemetalock(vf);
- assert(vf->ref > 0);
- vf->ref++;
- filemetaunlock(vf);
- return vf;
+ VacFs *fs;
+
+ fs = vtmallocz(sizeof(VacFs));
+ fs->z = z;
+ fs->bsize = bsize;
+ fs->mode = mode;
+ fs->cache = vtcachealloc(z, bsize, ncache);
+ return fs;
}
-int
-vacfiledecref(VacFile *f)
+static int
+readscore(int fd, uchar score[VtScoreSize])
{
- VacFile *p, *q, **qq;
+ char buf[45], *pref;
+ int n;
- if(f->up == nil){
- /* never linked in */
- assert(f->ref == 1);
- filefree(f);
- return 0;
+ n = readn(fd, buf, sizeof(buf)-1);
+ if(n < sizeof(buf)-1) {
+ werrstr("short read");
+ return -1;
}
+ buf[n] = 0;
- filemetalock(f);
- f->ref--;
- if(f->ref > 0){
- filemetaunlock(f);
+ if(vtparsescore(buf, &pref, score) < 0){
+ werrstr(EBadVacFormat);
return -1;
}
- assert(f->ref == 0);
- assert(f->down == nil);
-
- filemetaflush2(f, nil);
-
- p = f->up;
- qq = &p->down;
- for(q = *qq; q; q = *qq){
- if(q == f)
- break;
- qq = &q->next;
+ if(pref==nil || strcmp(pref, "vac") != 0) {
+ werrstr("not a vac file");
+ return -1;
}
- assert(q != nil);
- *qq = f->next;
-
- filemetaunlock(f);
- filefree(f);
- vacfiledecref(p);
return 0;
}
-VacFile*
-vacfilegetparent(VacFile *f)
-{
- if(vacfileisroot(f))
- return vacfileincref(f);
- return vacfileincref(f->up);
-}
-
-int
-vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid)
+VacFs*
+vacfsopen(VtConn *z, char *file, int mode, int ncache)
{
- werrstr("read only file system");
- return -1;
+ 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;
+ }
+ }else{
+ fd = open(file, OREAD);
+ if(fd < 0)
+ return nil;
+ if(readscore(fd, score) < 0){
+ close(fd);
+ return nil;
+ }
+ close(fd);
+ }
+ return vacfsopenscore(z, score, mode, ncache);
}
-VacDirEnum*
-vdeopen(VacFile *f)
+VacFs*
+vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
{
- VacDirEnum *vde;
- VacFile *p;
+ VacFs *fs;
+ int n;
+ VtRoot rt;
+ uchar buf[VtRootSize];
+ VacFile *root;
+ VtFile *r;
+ VtEntry e;
- if(!vacfileisdir(f)){
- werrstr(ENotDir);
+ n = vtread(z, score, VtRootType, buf, VtRootSize);
+ if(n < 0)
+ return nil;
+ if(n != VtRootSize){
+ werrstr("vtread on root too short");
return nil;
}
- /* flush out meta data */
- if(filelock(f) < 0)
+ if(vtrootunpack(&rt, buf) < 0)
return nil;
- for(p=f->down; p; p=p->next)
- filemetaflush2(p, nil);
- fileunlock(f);
- vde = vtmallocz(sizeof(VacDirEnum));
- vde->file = vacfileincref(f);
+ if(strcmp(rt.type, "vac") != 0) {
+ werrstr("not a vac root");
+ return nil;
+ }
- return vde;
-}
+ fs = vacfsalloc(z, rt.blocksize, ncache, mode);
+ memmove(fs->score, score, VtScoreSize);
+ fs->mode = mode;
-static int
-direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
-{
- VtBlock *b;
- ulong bn;
- VtEntry e;
- int epb;
-
- epb = s->dsize/VtEntrySize;
- bn = elem/epb;
- elem -= bn*epb;
+ 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;
- b = vtfileblock(s, bn, VtOREAD);
- if(b == nil)
+ root = nil;
+ if((r = vtfileopenroot(fs->cache, &e)) == nil)
goto Err;
- if(vtentryunpack(&e, b->data, elem) < 0)
+ 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;
-
- /* hanging entries are returned as zero size */
- if(!(e.flags & VtEntryActive) || e.gen != gen)
- *size = 0;
- else
- *size = e.size;
- vtblockput(b);
- return 0;
-
+ fs->root = root;
+ return fs;
Err:
- vtblockput(b);
- return -1;
+ if(root)
+ vacfiledecref(root);
+ vacfsclose(fs);
+ return nil;
}
-static int
-vdefill(VacDirEnum *vde)
+int
+vacfsmode(VacFs *fs)
{
- 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;
-
- n = mb.nindex;
- vde->buf = vtmalloc(n * sizeof(VacDir));
+ return fs->mode;
+}
- for(i=0; i<n; i++){
- de = vde->buf + i;
- meunpack(&me, &mb, i);
- if(vdunpack(de, &me) < 0)
- goto Err;
- vde->n++;
- if(!(de->mode & ModeDir))
- if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
- goto Err;
- }
- vde->boff++;
- vtblockput(b);
- return 0;
-Err:
- vtblockput(b);
- return -1;
+VacFile*
+vacfsgetroot(VacFs *fs)
+{
+ return vacfileincref(fs->root);
}
int
-vderead(VacDirEnum *vde, VacDir *de)
+vacfsgetblocksize(VacFs *fs)
{
- int ret, didread;
- 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;
-
- didread = 0;
- while(vde->i >= vde->n){
- if(vde->boff >= nb){
- ret = 0;
- goto Return;
- }
- didread = 1;
- if(vdefill(vde) < 0){
- ret = -1;
- goto Return;
- }
- }
-
- memmove(de, vde->buf + vde->i, sizeof(VacDir));
- vde->i++;
- ret = 1;
-
-Return:
- vtfileunlock(f->source);
- vtfileunlock(f->msource);
- filerunlock(f);
+ return fs->bsize;
+}
- if(didread)
- fileraccess(f);
- return ret;
+int
+vacfsgetscore(VacFs *fs, u8int *score)
+{
+ memmove(score, fs->score, VtScoreSize);
+ return 0;
}
int
-vdeunread(VacDirEnum *vde)
+_vacfsnextqid(VacFs *fs, uvlong *qid)
{
- if(vde->i > 0){
- vde->i--;
- return 0;
- }
- return -1;
+ ++fs->qid;
+ *qid = fs->qid;
+ return 0;
}
void
-vdeclose(VacDirEnum *vde)
+vacfsclose(VacFs *fs)
{
- int i;
- if(vde == nil)
- return;
- for(i=vde->i; i<vde->n; i++)
- vdcleanup(vde->buf+i);
- vtfree(vde->buf);
- vacfiledecref(vde->file);
- vtfree(vde);
+ if(fs->root)
+ vacfiledecref(fs->root);
+ fs->root = nil;
+ vtcachefree(fs->cache);
+ vtfree(fs);
}
/*
- * caller must lock f->source and f->msource
- * caller must NOT lock the source and msource
- * referenced by dir.
+ * Create a fresh vac fs.
*/
-static u32int
-filemetaalloc(VacFile *f, VacDir *dir, u32int start)
+VacFs *
+vacfscreate(VtConn *z, int bsize, int ncache)
{
- u32int nb, bo;
+ VacFs *fs;
+ VtFile *f;
+ uchar buf[VtEntrySize], metascore[VtScoreSize];
+ VtEntry e;
VtBlock *b;
MetaBlock mb;
- int nn;
- uchar *p;
- int i, n;
+ VacDir vd;
MetaEntry me;
- VtFile *s, *ms;
-
- s = f->source;
- ms = f->msource;
-
- n = vdsize(dir, VacDirVersion);
- nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
- b = nil;
- if(start > nb)
- start = nb;
- for(bo=start; bo<nb; bo++){
- b = vtfileblock(ms, bo, VtORDWR);
- if(b == 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)
- break;
- vtblockput(b);
- b = nil;
- }
-
- /* add block to meta file */
- if(b == nil){
- b = vtfileblock(ms, bo, VtORDWR);
- if(b == nil)
- goto Err;
- vtfilesetsize(ms, (nb+1)*ms->dsize);
- mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
- }
-
- p = mballoc(&mb, n);
- if(p == nil){
- /* mballoc might have changed block */
- mbpack(&mb);
- werrstr(EBadMeta);
- goto Err;
- }
-
- mbsearch(&mb, dir->elem, &i, &me);
- assert(me.p == nil);
- me.p = p;
- me.size = n;
- vdpack(dir, &me, VacDirVersion);
- mbinsert(&mb, i, &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);
- return bo;
-Err:
- vtblockput(b);
- return NilBlock;
+
+ /* 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;
}
-static int
-chksource(VacFile *f)
+int
+vacfssync(VacFs *fs)
{
- if(f->partial)
- return 0;
+ uchar buf[1024];
+ VtEntry e;
+ VtFile *f;
+ VtRoot root;
- if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
- werrstr(ERemoved);
+ /* Sync the entire vacfs to disk. */
+ if(vacfileflush(fs->root, 1) < 0)
return -1;
- }
- return 0;
-}
-static int
-filerlock(VacFile *f)
-{
-/* assert(!canwlock(&f->fs->elk)); */
- rlock(&f->lk);
- if(chksource(f) < 0){
- runlock(&f->lk);
+ /* 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;
}
- return 0;
-}
-
-static void
-filerunlock(VacFile *f)
-{
- runlock(&f->lk);
-}
-
-static int
-filelock(VacFile *f)
-{
-/* assert(!canwlock(&f->fs->elk)); */
- wlock(&f->lk);
- if(chksource(f) < 0){
- wunlock(&f->lk);
+ vtfileunlock(f);
+ if(getentry(f, &e) < 0){
+ vtfileclose(f);
return -1;
}
- return 0;
-}
-
-static void
-fileunlock(VacFile *f)
-{
- wunlock(&f->lk);
-}
-
-/*
- * f->source and f->msource must NOT be locked.
- * vacfilemetaflush locks the filemeta and then the source (in filemetaflush2).
- * We have to respect that ordering.
- */
-static void
-filemetalock(VacFile *f)
-{
- assert(f->up != nil);
-/* assert(!canwlock(&f->fs->elk)); */
- wlock(&f->up->lk);
-}
-
-static void
-filemetaunlock(VacFile *f)
-{
- wunlock(&f->up->lk);
-}
-
-/*
- * f->source and f->msource must NOT be locked.
- * see filemetalock.
- */
-static void
-fileraccess(VacFile* f)
-{
- if(f->mode == VtOREAD)
- return;
-
- filemetalock(f);
- f->dir.atime = time(0L);
- f->dirty = 1;
- filemetaunlock(f);
-}
-
-/*
- * f->source and f->msource must NOT be locked.
- * see filemetalock.
- */
-static void
-filewaccess(VacFile* f, char *mid)
-{
- if(f->mode == VtOREAD)
- return;
-
- filemetalock(f);
- f->dir.atime = f->dir.mtime = time(0L);
- if(strcmp(f->dir.mid, mid) != 0){
- vtfree(f->dir.mid);
- f->dir.mid = vtstrdup(mid);
+ 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;
}
- f->dir.mcount++;
- f->dirty = 1;
- filemetaunlock(f);
-
-/*RSC: let's try this */
-/*presotto - lets not
- if(f->up)
- filewaccess(f->up, mid);
-*/
+ if(vtsync(fs->z) < 0)
+ return -1;
+ return 0;
}
-
diff --git a/src/cmd/vac/fns.h b/src/cmd/vac/fns.h
index afcb7f7b..27cd479f 100644
--- a/src/cmd/vac/fns.h
+++ b/src/cmd/vac/fns.h
@@ -3,6 +3,9 @@ void mbinsert(MetaBlock *mb, int i, MetaEntry*);
void mbdelete(MetaBlock *mb, int i, MetaEntry*);
void mbpack(MetaBlock *mb);
uchar *mballoc(MetaBlock *mb, int n);
+void mbinit(MetaBlock *mb, uchar *p, int n, int entries);
+int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
+int mbresize(MetaBlock*, MetaEntry*, int);
int meunpack(MetaEntry*, MetaBlock *mb, int i);
int mecmp(MetaEntry*, char *s);
diff --git a/src/cmd/vac/fs.c b/src/cmd/vac/fs.c
index 9d3bb349..e69de29b 100644
--- a/src/cmd/vac/fs.c
+++ b/src/cmd/vac/fs.c
@@ -1,185 +0,0 @@
-#include "stdinc.h"
-#include "vac.h"
-#include "dat.h"
-#include "fns.h"
-
-#define debug 0
-
-static char EBadVacFormat[] = "bad format for vac file";
-
-static VacFs *
-vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
-{
- VacFs *fs;
-
- fs = vtmallocz(sizeof(VacFs));
- fs->ref = 1;
- fs->z = z;
- fs->bsize = bsize;
- fs->cache = vtcachealloc(z, bsize, ncache);
- return fs;
-}
-
-static int
-readscore(int fd, uchar score[VtScoreSize])
-{
- char buf[45], *pref;
- int n;
-
- n = readn(fd, buf, sizeof(buf)-1);
- if(n < sizeof(buf)-1) {
- werrstr("short read");
- return -1;
- }
- buf[n] = 0;
-
- 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;
-}
-
-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;
- }
- }else{
- fd = open(file, OREAD);
- if(fd < 0)
- return nil;
- if(readscore(fd, score) < 0){
- close(fd);
- return nil;
- }
- close(fd);
- }
- 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:
- if(root)
- vacfiledecref(root);
- vacfsclose(fs);
- return nil;
-}
-
-VacFs *
-vacfscreate(VtConn *z, int bsize, int ncache)
-{
- return vacfsalloc(z, bsize, ncache, VtORDWR);
-}
-
-int
-vacfsmode(VacFs *fs)
-{
- return fs->mode;
-}
-
-VacFile*
-vacfsgetroot(VacFs *fs)
-{
- return vacfileincref(fs->root);
-}
-
-int
-vacfsgetblocksize(VacFs *fs)
-{
- return fs->bsize;
-}
-
-int
-vacfsgetscore(VacFs *fs, u8int *score)
-{
- memmove(score, fs->score, VtScoreSize);
- return 0;
-}
-
-int
-_vacfsnextqid(VacFs *fs, uvlong *qid)
-{
- ++fs->qid;
- *qid = fs->qid;
- return 0;
-}
-
-int
-vacfssync(VacFs *fs)
-{
- return 0;
-}
-
-void
-vacfsclose(VacFs *fs)
-{
- if(fs->root)
- vacfiledecref(fs->root);
- fs->root = nil;
- vtcachefree(fs->cache);
- vtfree(fs);
-}
-
diff --git a/src/cmd/vac/pack.c b/src/cmd/vac/pack.c
index 79811dfe..cb92fad8 100644
--- a/src/cmd/vac/pack.c
+++ b/src/cmd/vac/pack.c
@@ -694,12 +694,12 @@ mbsearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
}
void
-mbinit(MetaBlock *mb, uchar *p, int n)
+mbinit(MetaBlock *mb, uchar *p, int n, int entries)
{
memset(mb, 0, sizeof(MetaBlock));
mb->maxsize = n;
mb->buf = p;
- mb->maxindex = n/100;
+ mb->maxindex = entries;
mb->size = MetaHeaderSize + mb->maxindex*MetaIndexSize;
}
diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c
index 7637e034..71244f43 100644
--- a/src/cmd/vac/vac.c
+++ b/src/cmd/vac/vac.c
@@ -1044,7 +1044,7 @@ dirsinkwritefile(DirSink *k, VacFile *vf)
{
VtEntry dir;
- if(vacfilegetvtentry(vf, &dir) < 0)
+ if(vacfilegetentries(vf, &dir, nil) < 0)
return -1;
dirsinkwrite(k, &dir);
return 0;
diff --git a/src/cmd/vac/vac.h b/src/cmd/vac/vac.h
index a0c4a91f..5ce69734 100644
--- a/src/cmd/vac/vac.h
+++ b/src/cmd/vac/vac.h
@@ -80,10 +80,9 @@ struct VacDir
uvlong qidmax; /* qid maximum */
};
-
struct VacFs
{
- int ref;
+ char name[128];
uchar score[VtScoreSize];
VacFile *root;
VtConn *z;
@@ -103,28 +102,28 @@ int vacfsgetscore(VacFs *fs, u8int *score);
VacFile *vacfsgetroot(VacFs *fs);
VacFile *vacfileopen(VacFs *fs, char *path);
-VacFile *vacfilecreate(VacFile *file, char *elem, ulong perm, char *muid);
+VacFile *vacfilecreate(VacFile *file, char *elem, ulong perm);
VacFile *vacfilewalk(VacFile *file, char *elem);
-int vacfileremove(VacFile *file, char *muid);
+int vacfileremove(VacFile *file);
int vacfileread(VacFile *file, void *buf, int n, vlong offset);
int vacfileblockscore(VacFile *file, u32int, u8int*);
-int vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid);
-int vacfilereadpacket(VacFile *file, Packet **pp, vlong offset);
-int vacfilewritepacket(VacFile *file, Packet *p, vlong offset, char *muid);
+int vacfilewrite(VacFile *file, void *buf, int n, vlong offset);
uvlong vacfilegetid(VacFile *file);
ulong vacfilegetmcount(VacFile *file);
int vacfileisdir(VacFile *file);
int vacfileisroot(VacFile *file);
ulong vacfilegetmode(VacFile *file);
-int vacfilegetblocksize(VacFile *file, u32int bn, u8int *score);
int vacfilegetsize(VacFile *file, uvlong *size);
int vacfilegetdir(VacFile *file, VacDir *dir);
-int vacfilesetdir(VacFile *file, VacDir *dir, char *muid);
-int vacfilegetvtentry(VacFile *file, VtEntry *entry);
+int vacfilesetdir(VacFile *file, VacDir *dir);
VacFile *vacfilegetparent(VacFile *file);
-int vacfilesync(VacFile*);
+int vacfileflush(VacFile*, int);
VacFile *vacfileincref(VacFile*);
int vacfiledecref(VacFile*);
+int vacfilesetsize(VacFile *f, uvlong size);
+
+int vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me);
+int vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me);
void vdcleanup(VacDir *dir);
void vdcopy(VacDir *dst, VacDir *src);
@@ -134,3 +133,4 @@ VacDirEnum *vdeopen(VacFile*);
int vderead(VacDirEnum*, VacDir *);
void vdeclose(VacDirEnum*);
int vdeunread(VacDirEnum*);
+
diff --git a/src/cmd/vac/vacfs.c b/src/cmd/vac/vacfs.c
index 92349ed2..8c9649a7 100644
--- a/src/cmd/vac/vacfs.c
+++ b/src/cmd/vac/vacfs.c
@@ -445,7 +445,7 @@ rcreate(Fid* fid)
}
mode |= ModeDir;
}
- vf = vacfilecreate(vf, rhdr.name, mode, "none");
+ vf = vacfilecreate(vf, rhdr.name, mode);
if(vf == nil) {
char err[80];
rerrstr(err, sizeof err);
@@ -541,7 +541,7 @@ rremove(Fid *f)
goto Exit;
}
- if(!vacfileremove(vf, "none")) {
+ if(!vacfileremove(vf)) {
rerrstr(errbuf, sizeof errbuf);
err = errbuf;
}