commit - e9b70a5f4ca38a98b64c9e0cea528693f5297376
commit + ecc0a1b0e77605dee8d9d33d2bc0d3d5ce7ca951
blob - 1967c43614fd3f81cbbc599f00db92da77f29821
blob + 000a762a8de80c9fb0dfe3eec6a6971a6b225182
--- src/cmd/vac/dat.h
+++ src/cmd/vac/dat.h
VacDir *buf;
};
-void _mbinit(MetaBlock*, u8int*, uint, uint);
-int _mbsearch(MetaBlock*, char*, int*, MetaEntry*);
blob - b7384b7fb65962f118904e9d7b0c4aba6a68f10b
blob + 035b27774ce3660735c5af110b3d8d941524f553
--- src/cmd/vac/file.c
+++ src/cmd/vac/file.c
#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 */
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)
{
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)
{
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);
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)
{
+ if(vacfileisroot(f))
+ return vacfileincref(f);
+ return vacfileincref(f->up);
+}
+
+/*
+ * 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;
- fileraccess(f);
-
if(elem[0] == 0){
werrstr(EBadPath);
return nil;
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;
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;
Exit:
fileunlock(f);
return ff;
+
+Err1:
+ vtfileunlock(f->source);
Err:
fileunlock(f);
if(ff != nil)
return nil;
}
+/*
+ * Open a path in the vac file system:
+ * just walk each element one at a time.
+ */
VacFile*
-vacfilewalk(VacFile *f, char *elem)
+vacfileopen(VacFs *fs, char *path)
{
- return _filewalk(f, elem, 0);
-}
-
-VacFile*
-_fileopen(VacFs *fs, char *path, int partial)
-{
VacFile *f, *ff;
char *p, elem[VtMaxStringSize], *opath;
int n;
}
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;
return nil;
}
-VacFile*
-vacfileopen(VacFs *fs, char *path)
-{
- return _fileopen(fs, path, 0);
-}
-
-#if 0
-static void
-filesettmp(VacFile *f, int istmp)
-{
- int i;
- VtEntry e;
- VtFile *r;
-
- 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;
- }
- if(istmp)
- e.flags |= VtEntryNoArchive;
- else
- e.flags &= ~VtEntryNoArchive;
- if(vtfilesetentry(r, &e) < 0){
- fprint(2, "vtfilesetentry failed (cannot happen): %r\n");
- continue;
- }
- }
-}
-#endif
-
-VacFile*
-vacfilecreate(VacFile *f, char *elem, ulong mode, char *uid)
-{
- VacFile *ff;
- VacDir *dir;
- VtFile *pr, *r, *mr;
- int isdir;
-
- if(filelock(f) < 0)
- return nil;
-
- r = nil;
- mr = nil;
- for(ff = f->down; ff; ff=ff->next){
- if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
- ff = nil;
- werrstr(EExists);
- goto Err1;
- }
- }
-
- ff = dirlookup(f, elem);
- if(ff != nil){
- werrstr(EExists);
- goto Err1;
- }
-
- pr = f->source;
- if(pr->mode != VtORDWR){
- werrstr(EReadOnly);
- goto Err1;
- }
-
- if(vtfilelock2(f->source, f->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)
- goto Err;
- if(isdir){
- mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType);
- if(mr == nil)
- goto Err;
- }
-
- dir = &ff->dir;
- dir->elem = vtstrdup(elem);
- dir->entry = r->offset;
- dir->gen = r->gen;
- if(isdir){
- dir->mentry = mr->offset;
- dir->mgen = mr->gen;
- }
- dir->size = 0;
- if(_vacfsnextqid(f->fs, &dir->qid) < 0)
- goto Err;
- dir->uid = vtstrdup(uid);
- dir->gid = vtstrdup(f->dir.gid);
- dir->mid = vtstrdup(uid);
- 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)
- goto Err;
-
- vtfileunlock(f->source);
- vtfileunlock(f->msource);
-
- ff->source = r;
- ff->msource = mr;
-
-#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 in and up parent ref count */
- ff->next = f->down;
- f->down = ff;
- ff->up = f;
- vacfileincref(f);
-
- filewaccess(f, uid);
-
- fileunlock(f);
- return ff;
-
-Err:
- vtfileunlock(f->source);
- vtfileunlock(f->msource);
-Err1:
- if(r){
- vtfilelock(r, -1);
- vtfileremove(r);
- }
- if(mr){
- vtfilelock(mr, -1);
- vtfileremove(mr);
- }
- if(ff)
- vacfiledecref(ff);
- fileunlock(f);
- return nil;
-}
-
+/*
+ * Extract the score for the bn'th block in f.
+ */
int
vacfileblockscore(VacFile *f, u32int bn, u8int *score)
{
ret = -1;
if(filerlock(f) < 0)
return -1;
- fileraccess(f);
if(vtfilelock(f->source, VtOREAD) < 0)
goto out;
return ret;
}
+/*
+ * Read data from f.
+ */
int
vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
{
- VtFile *s;
- uvlong size;
- u32int bn;
- int off, dsize, n, nn;
- VtBlock *b;
- uchar *p;
+ int n;
- if(filerlock(f) < 0)
- return -1;
-
if(offset < 0){
werrstr(EBadOffset);
- goto Err1;
+ return -1;
}
-
- 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);
+ if(filerlock(f) < 0)
+ return -1;
+ if(vtfilelock(f->source, VtOREAD) < 0){
+ filerunlock(f);
+ return -1;
}
- vtfileunlock(s);
+ n = vtfileread(f->source, buf, cnt, offset);
+ vtfileunlock(f->source);
filerunlock(f);
- return p-(uchar*)buf;
-
-Err:
- vtfileunlock(s);
-Err1:
- filerunlock(f);
- return -1;
+ return n;
}
-int
-vacfilesetsize(VacFile *f, uvlong size)
+static int
+getentry(VtFile *f, VtEntry *e)
{
- int r;
-
- if(filelock(f) < 0)
+ if(vtfilelock(f, VtOREAD) < 0)
return -1;
- r = 0;
- if(f->dir.mode & ModeDir){
- werrstr(ENotFile);
- goto Err;
+ if(vtfilegetentry(f, e) < 0){
+ vtfileunlock(f);
+ return -1;
}
- if(f->source->mode != VtORDWR){
- werrstr(EReadOnly);
- goto Err;
- }
- if(vtfilelock(f->source, -1) < 0)
- goto Err;
- r = vtfilesetsize(f->source, size);
- vtfileunlock(f->source);
-Err:
- fileunlock(f);
- return r;
-}
-
-int
-filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
-{
- VtFile *s;
- ulong bn;
- int off, dsize, n;
- VtBlock *b;
- uchar *p;
- vlong eof;
-
- if(filelock(f) < 0)
+ vtfileunlock(f);
+ if(vtglobaltolocal(e->score) != NilBlock){
+ werrstr("internal error - data not on venti");
return -1;
-
- s = nil;
- if(f->dir.mode & ModeDir){
- werrstr(ENotFile);
- goto Err;
}
-
- if(f->source->mode != VtORDWR){
- werrstr(EReadOnly);
- goto Err;
- }
- if(offset < 0){
- werrstr(EBadOffset);
- 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)
- goto Err;
- vtfileunlock(s);
- fileunlock(f);
- return p-(uchar*)buf;
-Err:
- if(s)
- vtfileunlock(s);
- fileunlock(f);
- return -1;
+ return 0;
}
+/*
+ * Get the VtEntries for the data contained in f.
+ */
int
-vacfilegetdir(VacFile *f, VacDir *dir)
+vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
{
if(filerlock(f) < 0)
return -1;
-
- filemetalock(f);
- vdcopy(dir, &f->dir);
- filemetaunlock(f);
-
- if(!vacfileisdir(f)){
- if(vtfilelock(f->source, VtOREAD) < 0){
+ 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;
}
- dir->size = vtfilegetsize(f->source);
- vtfileunlock(f->source);
}
filerunlock(f);
-
return 0;
}
+/*
+ * Get the file's size.
+ */
int
-vacfiletruncate(VacFile *f, char *uid)
+vacfilegetsize(VacFile *f, uvlong *size)
{
- if(vacfileisdir(f)){
- werrstr(ENotFile);
+ if(filerlock(f) < 0)
return -1;
- }
-
- if(filelock(f) < 0)
+ if(vtfilelock(f->source, VtOREAD) < 0){
+ filerunlock(f);
return -1;
-
- if(f->source->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);
- fileunlock(f);
- return -1;
- }
+ *size = vtfilegetsize(f->source);
vtfileunlock(f->source);
- fileunlock(f);
+ filerunlock(f);
- filewaccess(f, uid);
-
return 0;
}
-int
-vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
-{
- VacFile *ff;
- char *oelem;
- u32int mask;
- u64int size;
+/*
+ * 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;
- /* can not set permissions for the root */
- if(vacfileisroot(f)){
- werrstr(ERoot);
- return -1;
+ 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 -1;
+ return nil;
+ for(p=f->down; p; p=p->next)
+ filemetaflush(p, nil);
+ fileunlock(f);
- if(f->source->mode != VtORDWR){
- werrstr(EReadOnly);
- fileunlock(f);
- return -1;
- }
+ vde = vtmallocz(sizeof(VacDirEnum));
+ vde->file = vacfileincref(f);
- filemetalock(f);
+ return vde;
+}
- /* 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;
- }
- }
+/*
+ * 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;
- ff = dirlookup(f->up, dir->elem);
- if(ff != nil){
- vacfiledecref(ff);
- werrstr(EExists);
- goto Err;
- }
- }
+ epb = s->dsize/VtEntrySize;
+ bn = offset/epb;
+ offset -= bn*epb;
- if(vtfilelock2(f->source, f->msource, -1) < 0)
+ b = vtfileblock(s, bn, VtOREAD);
+ if(b == nil)
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;
- }
- /* 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
- vtfileunlock(f->source);
- if(f->msource)
- vtfileunlock(f->msource);
+ if(vtentryunpack(&e, b->data, offset) < 0)
+ goto Err;
- oelem = nil;
- if(strcmp(f->dir.elem, dir->elem) != 0){
- oelem = f->dir.elem;
- f->dir.elem = vtstrdup(dir->elem);
- }
+ /* 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(strcmp(f->dir.uid, dir->uid) != 0){
- vtfree(f->dir.uid);
- f->dir.uid = vtstrdup(dir->uid);
- }
+Err:
+ vtblockput(b);
+ return -1;
+}
- if(strcmp(f->dir.gid, dir->gid) != 0){
- vtfree(f->dir.gid);
- f->dir.gid = vtstrdup(dir->gid);
- }
+/*
+ * 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;
- f->dir.mtime = dir->mtime;
- f->dir.atime = dir->atime;
+ /* 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;
-/*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); */
+ f = vde->file;
- filemetaflush2(f, oelem);
- vtfree(oelem);
+ source = f->source;
+ meta = f->msource;
- filemetaunlock(f);
- fileunlock(f);
+ b = vtfileblock(meta, vde->boff, VtOREAD);
+ if(b == nil)
+ goto Err;
+ if(mbunpack(&mb, b->data, meta->dsize) < 0)
+ goto Err;
- filewaccess(f->up, uid);
+ 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:
- filemetaunlock(f);
- fileunlock(f);
+ 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
-vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
+vderead(VacDirEnum *vde, VacDir *de)
{
int ret;
+ VacFile *f;
+ u32int nb;
- if(filelock(f) < 0)
+ f = vde->file;
+ if(filerlock(f) < 0)
return -1;
- filemetalock(f);
- f->dir.qidspace = 1;
- f->dir.qidoffset = offset;
- f->dir.qidmax = max;
- ret = filemetaflush2(f, nil);
- filemetaunlock(f);
- fileunlock(f);
- return ret;
-}
-uvlong
-vacfilegetid(VacFile *f)
-{
- /* immutable */
- return f->dir.qid;
-}
+ if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
+ filerunlock(f);
+ return -1;
+ }
-ulong
-vacfilegetmcount(VacFile *f)
-{
- ulong mcount;
+ nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
- filemetalock(f);
- mcount = f->dir.mcount;
- filemetaunlock(f);
- return mcount;
-}
+ while(vde->i >= vde->n){
+ if(vde->boff >= nb){
+ ret = 0;
+ goto Return;
+ }
+ if(vdefill(vde) < 0){
+ ret = -1;
+ goto Return;
+ }
+ }
-ulong
-vacfilegetmode(VacFile *f)
-{
- ulong mode;
+ memmove(de, vde->buf + vde->i, sizeof(VacDir));
+ vde->i++;
+ ret = 1;
- 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);
+Return:
vtfileunlock(f->source);
+ vtfileunlock(f->msource);
filerunlock(f);
- return 0;
+ 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
-vacfilegetvtentry(VacFile *f, VtEntry *e)
+vdeunread(VacDirEnum *vde)
{
- if(filerlock(f) < 0)
+ if(vde->i > 0){
+ vde->i--;
return 0;
- if(vtfilelock(f->source, VtOREAD) < 0){
- filerunlock(f);
- return -1;
}
- vtfilegetentry(f->source, e);
- vtfileunlock(f->source);
- filerunlock(f);
-
- return 0;
+ return -1;
}
+/*
+ * Close the enumerator.
+ */
void
-vacfilemetaflush(VacFile *f, int rec)
+vdeclose(VacDirEnum *vde)
{
- VacFile **kids, *p;
- int nkids;
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);
+}
- filemetalock(f);
- filemetaflush2(f, nil);
- filemetaunlock(f);
- if(!rec || !vacfileisdir(f))
- return;
+/*
+ * 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.
+ */
- 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++;
+/*
+ * 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
+ start = 0;
}
- fileunlock(f);
+
+ 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;
+ }
- for(i=0; i<nkids; i++){
- vacfilemetaflush(kids[i], 1);
- vacfiledecref(kids[i]);
+ /* 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;
}
- vtfree(kids);
-}
-/* assumes metalock is held */
+ /* 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
-filemetaflush2(VacFile *f, char *oelem)
+filemetaflush(VacFile *f, char *oelem)
{
- VacFile *fp;
- VtBlock *b, *bb;
+ int i, n;
MetaBlock mb;
MetaEntry me, me2;
- int i, n;
- u32int boff;
+ 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 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){
- /* fits in the block */
+ /* 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);
- /* vtblockdirty(b); */
+
+ /* Done */
vtblockput(b);
vtfileunlock(fp->msource);
f->dirty = 0;
- return -1;
+ return 0;
}
-
+
/*
- * 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.
+ * The entry must be moved to another block.
+ * This can only really happen on renames that
+ * make the name very long.
*/
- boff = filemetaalloc(fp, &f->dir, f->boff+1);
- if(boff == NilBlock){
- /* mbresize might have modified block */
+
+ /* 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);
- /* vtblockdirty(b); */
goto Err;
}
- f->boff = boff;
+ f->boff = bo;
- /* make sure deletion goes to disk after new entry */
- bb = vtfileblock(fp->msource, f->boff, VtORDWR);
+ /* Now we're committed. Delete entry in old block. */
mbdelete(&mb, i, &me);
mbpack(&mb);
- vtblockput(bb);
- /* vtblockdirty(b); */
vtblockput(b);
vtfileunlock(fp->msource);
f->dirty = 0;
-
return 0;
Err:
return -1;
}
+/*
+ * Remove the directory entry for f.
+ */
static int
-filemetaremove(VacFile *f, char *uid)
+filemetaremove(VacFile *f)
{
VtBlock *b;
MetaBlock mb;
MetaEntry me;
int i;
- VacFile *up;
+ VacFile *fp;
b = nil;
- up = f->up;
- filewaccess(up, uid);
+ fp = f->up;
filemetalock(f);
- if(vtfilelock(up->msource, VtORDWR) < 0)
+ if(vtfilelock(fp->msource, VtORDWR) < 0)
goto Err;
- b = vtfileblock(up->msource, f->boff, VtORDWR);
+ b = vtfileblock(fp->msource, f->boff, VtORDWR);
if(b == nil)
goto Err;
- if(mbunpack(&mb, b->data, up->msource->dsize) < 0)
+ 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);
- vtfileunlock(up->msource);
-
- /* vtblockdirty(b); */
vtblockput(b);
+ vtfileunlock(fp->msource);
f->removed = 1;
f->boff = NilBlock;
return 0;
Err:
- vtfileunlock(up->msource);
+ vtfileunlock(fp->msource);
vtblockput(b);
filemetaunlock(f);
return -1;
}
-/* assume file is locked, assume f->msource is locked */
-static int
-filecheckempty(VacFile *f)
+/*
+ * 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)
{
- u32int i, n;
- VtBlock *b;
- MetaBlock mb;
- VtFile *r;
+ int ret;
+ VacFile **kids, *p;
+ int i, nkids;
+
+ if(f->mode == VtOREAD)
+ return 0;
- r = f->msource;
- n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
- for(i=0; i<n; i++){
- b = vtfileblock(r, i, VtORDWR);
- if(b == nil)
- goto Err;
- if(mbunpack(&mb, b->data, r->dsize) < 0)
- goto Err;
- if(mb.nindex > 0){
- werrstr(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;
+ 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++;
}
- vtblockput(b);
}
- return 0;
-Err:
- vtblockput(b);
- return -1;
+ fileunlock(f);
+
+ for(i=0; i<nkids; i++){
+ if(vacfileflush(kids[i], 1) < 0)
+ ret = -1;
+ vacfiledecref(kids[i]);
+ }
+ free(kids);
+ return ret;
}
-
-int
-vacfileremove(VacFile *f, char *muid)
+
+/*
+ * 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)
{
VacFile *ff;
+ VacDir *dir;
+ VtFile *pr, *r, *mr;
+ int type;
+ u32int bo;
- /* can not remove the root */
- if(vacfileisroot(f)){
- werrstr(ERoot);
- return -1;
+ 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(filelock(f) < 0)
- return -1;
+ /*
+ * Next check the venti blocks.
+ */
+ ff = dirlookup(fp, elem);
+ if(ff != nil){
+ werrstr(EExists);
+ goto Err1;
+ }
- if(f->source->mode != VtORDWR){
+ /*
+ * 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;
- if(vacfileisdir(f) && filecheckempty(f)<0)
+ 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;
- 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;
+ /*
+ * 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)) < 0)
+ goto Err;
- fileunlock(f);
+ /*
+ * Now we're committed.
+ */
+ vtfileunlock(fp->source);
+ vtfileunlock(fp->msource);
+ ff->source = r;
+ ff->msource = mr;
+ ff->boff = bo;
- if(filemetaremove(f, muid) < 0)
- return -1;
+ /* Link into tree. */
+ ff->next = fp->down;
+ fp->down = ff;
+ ff->up = fp;
+ vacfileincref(fp);
- return 0;
+ fileunlock(fp);
+ return ff;
Err:
- vtfileunlock(f->source);
- if(f->msource)
- vtfileunlock(f->msource);
+ vtfileunlock(fp->source);
+ vtfileunlock(fp->msource);
+ if(r){
+ vtfilelock(r, -1);
+ vtfileremove(r);
+ }
+ if(mr){
+ vtfilelock(mr, -1);
+ vtfileremove(mr);
+ }
Err1:
- fileunlock(f);
- return -1;
+ if(ff)
+ vacfiledecref(ff);
+ fileunlock(fp);
+ return nil;
}
-static int
-clri(VacFile *f, char *uid)
+/*
+ * Change the size of the file f.
+ */
+int
+vacfilesetsize(VacFile *f, uvlong size)
{
- int r;
-
- if(f == nil)
+ if(vacfileisdir(f)){
+ werrstr(ENotFile);
return -1;
- if(f->up->source->mode != VtORDWR){
- werrstr(EReadOnly);
- vacfiledecref(f);
+ }
+
+ if(filelock(f) < 0)
return -1;
+
+ if(f->source->mode != VtORDWR){
+ werrstr(EReadOnly);
+ goto Err;
}
- r = filemetaremove(f, uid);
- vacfiledecref(f);
- return r;
-}
+ 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;
-int
-vacfileclripath(VacFs *fs, char *path, char *uid)
-{
- return clri(_fileopen(fs, path, 1), uid);
+Err:
+ fileunlock(f);
+ return -1;
}
+/*
+ * Write data to f.
+ */
int
-vacfileclri(VacFile *dir, char *elem, char *uid)
+vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
{
- return clri(_filewalk(dir, elem, 1), uid);
-}
+ 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;
+ }
-VacFile*
-vacfileincref(VacFile *vf)
-{
- filemetalock(vf);
- assert(vf->ref > 0);
- vf->ref++;
- filemetaunlock(vf);
- return vf;
+ 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;
+ }
+ vtfileunlock(f->source);
+ fileunlock(f);
+ return cnt;
+
+Err:
+ 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
-vacfiledecref(VacFile *f)
+vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
{
- VacFile *p, *q, **qq;
+ int ret;
- if(f->up == nil){
- /* never linked in */
- assert(f->ref == 1);
- filefree(f);
- return 0;
+ 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;
+ }
- filemetalock(f);
- f->ref--;
- if(f->ref > 0){
- filemetaunlock(f);
+ if(filelock(f) < 0)
return -1;
+ if(f->source->mode != VtORDWR
+ || (f->msource && f->msource->mode != VtORDWR)){
+ werrstr(EReadOnly);
+ fileunlock(f);
+ return -1;
}
- assert(f->ref == 0);
- assert(f->down == nil);
+ 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;
- filemetaflush2(f, nil);
+ vtfileunlock(f->source);
+ if(f->msource)
+ vtfileunlock(f->msource);
+ fileunlock(f);
+ return ret;
+}
- 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;
+/*
+ * 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);
- filefree(f);
- vacfiledecref(p);
+
+ 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;
}
-VacFile*
-vacfilegetparent(VacFile *f)
-{
- if(vacfileisroot(f))
- return vacfileincref(f);
- return vacfileincref(f->up);
-}
-
+/*
+ * Set the directory entry for f.
+ */
int
-vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid)
+vacfilesetdir(VacFile *f, VacDir *dir)
{
- werrstr("read only file system");
- return -1;
-}
+ VacFile *ff;
+ char *oelem;
+ u32int mask;
+ u64int size;
-VacDirEnum*
-vdeopen(VacFile *f)
-{
- VacDirEnum *vde;
- VacFile *p;
-
- if(!vacfileisdir(f)){
- werrstr(ENotDir);
- return nil;
+ /* can not set permissions for the root */
+ if(vacfileisroot(f)){
+ werrstr(ERoot);
+ return -1;
}
- /* flush out meta data */
if(filelock(f) < 0)
- return nil;
- for(p=f->down; p; p=p->next)
- filemetaflush2(p, nil);
- fileunlock(f);
+ return -1;
+ filemetalock(f);
+
+ if(f->source->mode != VtORDWR){
+ werrstr(EReadOnly);
+ goto Err;
+ }
- vde = vtmallocz(sizeof(VacDirEnum));
- vde->file = vacfileincref(f);
+ /* 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 */
+ }
- return vde;
-}
+ /* 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);
-static int
-direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
-{
- VtBlock *b;
- ulong bn;
- VtEntry e;
- int epb;
+ oelem = nil;
+ if(strcmp(f->dir.elem, dir->elem) != 0){
+ oelem = f->dir.elem;
+ f->dir.elem = vtstrdup(dir->elem);
+ }
- epb = s->dsize/VtEntrySize;
- bn = elem/epb;
- elem -= bn*epb;
+ if(strcmp(f->dir.uid, dir->uid) != 0){
+ vtfree(f->dir.uid);
+ f->dir.uid = vtstrdup(dir->uid);
+ }
- b = vtfileblock(s, bn, VtOREAD);
- if(b == nil)
- goto Err;
- if(vtentryunpack(&e, b->data, elem) < 0)
- goto Err;
+ if(strcmp(f->dir.gid, dir->gid) != 0){
+ vtfree(f->dir.gid);
+ f->dir.gid = vtstrdup(dir->gid);
+ }
- /* hanging entries are returned as zero size */
- if(!(e.flags & VtEntryActive) || e.gen != gen)
- *size = 0;
- else
- *size = e.size;
- vtblockput(b);
+ f->dir.mtime = dir->mtime;
+ f->dir.atime = dir->atime;
+
+ mask = ~(ModeDir|ModeSnapshot);
+ f->dir.mode &= ~mask;
+ f->dir.mode |= mask & dir->mode;
+ f->dirty = 1;
+
+ if(filemetaflush(f, oelem) < 0){
+ vtfree(oelem);
+ goto Err; /* that sucks */
+ }
+ vtfree(oelem);
+
+ filemetaunlock(f);
+ fileunlock(f);
return 0;
Err:
- vtblockput(b);
+ filemetaunlock(f);
+ fileunlock(f);
return -1;
}
+/*
+ * Set the qid space.
+ */
+int
+vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
+{
+ int ret;
+
+ if(filelock(f) < 0)
+ return -1;
+ filemetalock(f);
+ f->dir.qidspace = 1;
+ f->dir.qidoffset = offset;
+ f->dir.qidmax = max;
+ ret = filemetaflush(f, nil);
+ filemetaunlock(f);
+ fileunlock(f);
+ return ret;
+}
+
+/*
+ * Check that the file is empty, returning 0 if it is.
+ * Returns -1 on error (and not being empty is an error).
+ */
static int
-vdefill(VacDirEnum *vde)
+filecheckempty(VacFile *f)
{
- int i, n;
- VtFile *meta, *source;
- MetaBlock mb;
- MetaEntry me;
- VacFile *f;
+ u32int i, n;
VtBlock *b;
- VacDir *de;
+ MetaBlock mb;
+ VtFile *r;
- /* 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));
-
+ r = f->msource;
+ n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
for(i=0; i<n; i++){
- de = vde->buf + i;
- meunpack(&me, &mb, i);
- if(vdunpack(de, &me) < 0)
+ b = vtfileblock(r, i, VtOREAD);
+ if(b == nil)
+ return -1;
+ if(mbunpack(&mb, b->data, r->dsize) < 0)
goto Err;
- vde->n++;
- if(!(de->mode & ModeDir))
- if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
+ if(mb.nindex > 0){
+ werrstr(ENotEmpty);
goto Err;
+ }
+ vtblockput(b);
}
- vde->boff++;
- vtblockput(b);
return 0;
+
Err:
vtblockput(b);
return -1;
}
+/*
+ * Remove the vac file f.
+ */
int
-vderead(VacDirEnum *vde, VacDir *de)
+vacfileremove(VacFile *f)
{
- int ret, didread;
- VacFile *f;
- u32int nb;
+ VacFile *ff;
- f = vde->file;
- if(filerlock(f) < 0)
+ /* Cannot remove the root */
+ if(vacfileisroot(f)){
+ werrstr(ERoot);
return -1;
+ }
- if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
- filerunlock(f);
+ if(filelock(f) < 0)
return -1;
+ if(f->source->mode != VtORDWR){
+ werrstr(EReadOnly);
+ goto Err1;
}
+ if(vtfilelock2(f->source, f->msource, -1) < 0)
+ goto Err1;
+ if(vacfileisdir(f) && filecheckempty(f)<0)
+ goto Err;
- nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
+ for(ff=f->down; ff; ff=ff->next)
+ assert(ff->removed);
- 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;
- }
+ vtfileremove(f->source);
+ f->source = nil;
+ if(f->msource){
+ vtfileremove(f->msource);
+ f->msource = nil;
}
+ fileunlock(f);
- memmove(de, vde->buf + vde->i, sizeof(VacDir));
- vde->i++;
- ret = 1;
+ if(filemetaremove(f) < 0)
+ return -1;
+ return 0;
-Return:
+Err:
vtfileunlock(f->source);
- vtfileunlock(f->msource);
- filerunlock(f);
+ if(f->msource)
+ vtfileunlock(f->msource);
+Err1:
+ fileunlock(f);
+ return -1;
+}
- if(didread)
- fileraccess(f);
- return ret;
+/*
+ * Vac file system format.
+ */
+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->z = z;
+ fs->bsize = bsize;
+ fs->mode = mode;
+ fs->cache = vtcachealloc(z, bsize, ncache);
+ return fs;
}
-int
-vdeunread(VacDirEnum *vde)
+static int
+readscore(int fd, uchar score[VtScoreSize])
{
- if(vde->i > 0){
- vde->i--;
- return 0;
+ char buf[45], *pref;
+ int n;
+
+ n = readn(fd, buf, sizeof(buf)-1);
+ if(n < sizeof(buf)-1) {
+ werrstr("short read");
+ return -1;
}
- 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;
}
-void
-vdeclose(VacDirEnum *vde)
+VacFs*
+vacfsopen(VtConn *z, char *file, int mode, int ncache)
{
- 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);
+ 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);
}
-/*
- * caller must lock f->source and f->msource
- * caller must NOT lock the source and msource
- * referenced by dir.
- */
-static u32int
-filemetaalloc(VacFile *f, VacDir *dir, u32int start)
+VacFs*
+vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
{
- u32int nb, bo;
- VtBlock *b;
- MetaBlock mb;
- int nn;
- uchar *p;
- int i, n;
- MetaEntry me;
- VtFile *s, *ms;
+ VacFs *fs;
+ int n;
+ VtRoot rt;
+ uchar buf[VtRootSize];
+ VacFile *root;
+ VtFile *r;
+ VtEntry e;
- 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;
+ n = vtread(z, score, VtRootType, buf, VtRootSize);
+ if(n < 0)
+ return nil;
+ if(n != VtRootSize){
+ werrstr("vtread on root too short");
+ return 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);
- }
+ if(vtrootunpack(&rt, buf) < 0)
+ return nil;
- p = mballoc(&mb, n);
- if(p == nil){
- /* mballoc might have changed block */
- mbpack(&mb);
- werrstr(EBadMeta);
- goto Err;
+ if(strcmp(rt.type, "vac") != 0) {
+ werrstr("not a vac root");
+ return nil;
}
- mbsearch(&mb, dir->elem, &i, &me);
- assert(me.p == nil);
- me.p = p;
- me.size = n;
- vdpack(dir, &me, VacDirVersion);
- mbinsert(&mb, i, &me);
- mbpack(&mb);
+ fs = vacfsalloc(z, rt.blocksize, ncache, mode);
+ memmove(fs->score, score, VtScoreSize);
+ fs->mode = mode;
- vtblockput(b);
- return bo;
+ 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:
- vtblockput(b);
- return NilBlock;
+ if(root)
+ vacfiledecref(root);
+ vacfsclose(fs);
+ return nil;
}
-static int
-chksource(VacFile *f)
+int
+vacfsmode(VacFs *fs)
{
- if(f->partial)
- return 0;
-
- if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
- werrstr(ERemoved);
- return -1;
- }
- return 0;
+ return fs->mode;
}
-static int
-filerlock(VacFile *f)
+VacFile*
+vacfsgetroot(VacFs *fs)
{
-/* assert(!canwlock(&f->fs->elk)); */
- rlock(&f->lk);
- if(chksource(f) < 0){
- runlock(&f->lk);
- return -1;
- }
- return 0;
+ return vacfileincref(fs->root);
}
-static void
-filerunlock(VacFile *f)
+int
+vacfsgetblocksize(VacFs *fs)
{
- runlock(&f->lk);
+ return fs->bsize;
}
-static int
-filelock(VacFile *f)
+int
+vacfsgetscore(VacFs *fs, u8int *score)
{
-/* assert(!canwlock(&f->fs->elk)); */
- wlock(&f->lk);
- if(chksource(f) < 0){
- wunlock(&f->lk);
- return -1;
- }
+ memmove(score, fs->score, VtScoreSize);
return 0;
}
-static void
-fileunlock(VacFile *f)
+int
+_vacfsnextqid(VacFs *fs, uvlong *qid)
{
- wunlock(&f->lk);
+ ++fs->qid;
+ *qid = fs->qid;
+ return 0;
}
-/*
- * 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)
+void
+vacfsclose(VacFs *fs)
{
- assert(f->up != nil);
-/* assert(!canwlock(&f->fs->elk)); */
- wlock(&f->up->lk);
+ if(fs->root)
+ vacfiledecref(fs->root);
+ fs->root = nil;
+ vtcachefree(fs->cache);
+ vtfree(fs);
}
-static void
-filemetaunlock(VacFile *f)
-{
- wunlock(&f->up->lk);
-}
-
/*
- * f->source and f->msource must NOT be locked.
- * see filemetalock.
+ * Create a fresh vac fs.
*/
-static void
-fileraccess(VacFile* f)
+VacFs *
+vacfscreate(VtConn *z, int bsize, int ncache)
{
- if(f->mode == VtOREAD)
- return;
+ 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);
- filemetalock(f);
- f->dir.atime = time(0L);
- f->dirty = 1;
- filemetaunlock(f);
+ /* 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;
}
-/*
- * f->source and f->msource must NOT be locked.
- * see filemetalock.
- */
-static void
-filewaccess(VacFile* f, char *mid)
+int
+vacfssync(VacFs *fs)
{
- if(f->mode == VtOREAD)
- return;
+ uchar buf[1024];
+ VtEntry e;
+ VtFile *f;
+ VtRoot root;
- 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);
- }
- f->dir.mcount++;
- f->dirty = 1;
- filemetaunlock(f);
+ /* Sync the entire vacfs to disk. */
+ if(vacfileflush(fs->root, 1) < 0)
+ return -1;
-/*RSC: let's try this */
-/*presotto - lets not
- if(f->up)
- filewaccess(f->up, mid);
-*/
-}
+ /* 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;
+}
blob - afcb7f7b9e2eb4aa7618617b55d674996cddb274
blob + 27cd479f5d7122ae1578003a3b15671711a0a4cf
--- src/cmd/vac/fns.h
+++ src/cmd/vac/fns.h
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);
blob - 9d3bb349cbc03e21b685a4d789648a9e9a586cc8
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
--- src/cmd/vac/fs.c
+++ src/cmd/vac/fs.c
-#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);
-}
-
blob - 79811dfe60ba9cc6763851b96758f4174bbc05b2
blob + cb92fad8d7d3915fb78f926bf511ff5ca8bdef7a
--- src/cmd/vac/pack.c
+++ src/cmd/vac/pack.c
}
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;
}
blob - 7637e03431ca51072ae35eef03a5cf179e1ba5b6
blob + 71244f43a9694d81d1c8a1ce3f11b316e0c7b701
--- src/cmd/vac/vac.c
+++ src/cmd/vac/vac.c
{
VtEntry dir;
- if(vacfilegetvtentry(vf, &dir) < 0)
+ if(vacfilegetentries(vf, &dir, nil) < 0)
return -1;
dirsinkwrite(k, &dir);
return 0;
blob - a0c4a91fb49027583ddd0eaae957bcf2604d4c45
blob + 5ce69734e7a06635adafad7c0a533bb7b81cc55f
--- src/cmd/vac/vac.h
+++ src/cmd/vac/vac.h
uvlong qidmax; /* qid maximum */
};
-
struct VacFs
{
- int ref;
+ char name[128];
uchar score[VtScoreSize];
VacFile *root;
VtConn *z;
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);
int vderead(VacDirEnum*, VacDir *);
void vdeclose(VacDirEnum*);
int vdeunread(VacDirEnum*);
+
blob - 92349ed266e21e235e1d79a9e98eebe0683f85a6
blob + 8c9649a73492e0ca0ef7c82d2c100270060e407f
--- src/cmd/vac/vacfs.c
+++ src/cmd/vac/vacfs.c
}
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);
goto Exit;
}
- if(!vacfileremove(vf, "none")) {
+ if(!vacfileremove(vf)) {
rerrstr(errbuf, sizeof errbuf);
err = errbuf;
}