#include "stdinc.h"#include "vac.h"#include "dat.h"#include "fns.h"#include "error.h" #define debug 0 /* * 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 */ /* meta data for file: protected by the lk in the parent */ int ref; /* holds this data structure up */ int partial; /* file was never really open */ int removed; /* file has been removed */ int dirty; /* dir is dirty with respect to meta data in block */ u32int boff; /* block offset within msource for this file's metadata */ VacDir dir; /* metadata for this file */ VacFile *up; /* parent file */ VacFile *next; /* sibling */ RWLock lk; /* lock for the following */ VtFile *source; /* actual data */ VtFile *msource; /* metadata for children in a directory */ VacFile *down; /* children */ int mode; uvlong qidoffset; /* qid offset */}; static VacFile*filealloc(VacFs *fs){ VacFile *f; f = vtmallocz(sizeof(VacFile)); f->ref = 1; f->fs = fs; f->boff = NilBlock; f->mode = fs->mode; return f;} static voidfilefree(VacFile *f){ vtfileclose(f->source); vtfileclose(f->msource); vdcleanup(&f->dir); memset(f, ~0, sizeof *f); /* paranoia */ vtfree(f);} static intchksource(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 intfilelock(VacFile *f){ wlock(&f->lk); if(chksource(f) < 0){ wunlock(&f->lk); return -1; } return 0;} static voidfileunlock(VacFile *f){ wunlock(&f->lk);} static intfilerlock(VacFile *f){ rlock(&f->lk); if(chksource(f) < 0){ runlock(&f->lk); return -1; } return 0;} static voidfilerunlock(VacFile *f){ runlock(&f->lk);} /* * The file metadata, like f->dir and f->ref, * are synchronized via the parent's lock. * This is why locking order goes up. */static voidfilemetalock(VacFile *f){ assert(f->up != nil); wlock(&f->up->lk);} static voidfilemetaunlock(VacFile *f){ wunlock(&f->up->lk);} uvlongvacfilegetid(VacFile *f){ /* immutable */ return f->qidoffset + f->dir.qid;} uvlongvacfilegetqidoffset(VacFile *f){ return f->qidoffset;} ulongvacfilegetmcount(VacFile *f){ ulong mcount; filemetalock(f); mcount = f->dir.mcount; filemetaunlock(f); return mcount;} ulongvacfilegetmode(VacFile *f){ ulong mode; filemetalock(f); mode = f->dir.mode; filemetaunlock(f); return mode;} intvacfileisdir(VacFile *f){ /* immutable */ return (f->dir.mode & ModeDir) != 0;} intvacfileisroot(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;} intvacfiledecref(VacFile *f){ VacFile *p, *q, **qq; if(f->up == nil){ /* never linked in */ assert(f->ref == 1); filefree(f); return 0; } filemetalock(f); f->ref--; if(f->ref > 0){ filemetaunlock(f); return -1; } assert(f->ref == 0); assert(f->down == nil); 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){ int redirected; char err[ERRMAX]; VtBlock *b; VtFile *r0, *r1, *r2; MetaBlock mb; MetaEntry me; VacFile *root, *mr; redirected = 0;Top: b = nil; root = nil; mr = nil; r1 = nil; r2 = nil; if(vtfilelock(r, -1) < 0) return nil; r0 = vtfileopen(r, 0, fs->mode); if(debug) fprint(2, "r0 %p\n", r0); if(r0 == nil) goto Err; r2 = vtfileopen(r, 2, fs->mode); if(debug) fprint(2, "r2 %p\n", r2); if(r2 == nil){ /* * some vac files (e.g., from fossil) * have an extra layer of indirection. */ rerrstr(err, sizeof err); if(!redirected && strstr(err, "not active")){ redirected = 1; vtfileunlock(r); r = r0; goto Top; } goto Err; } r1 = vtfileopen(r, 1, fs->mode); if(debug) fprint(2, "r1 %p\n", r1); if(r1 == nil) goto Err; mr = filealloc(fs); mr->msource = r2; r2 = nil; root = filealloc(fs); root->boff = 0; root->up = mr; root->source = r0; r0 = nil; root->msource = r1; r1 = nil; mr->down = root; vtfileunlock(r); if(vtfilelock(mr->msource, VtOREAD) < 0) goto Err1; b = vtfileblock(mr->msource, 0, VtOREAD); vtfileunlock(mr->msource); if(b == nil) goto Err1; if(mbunpack(&mb, b->data, mr->msource->dsize) < 0) goto Err1; meunpack(&me, &mb, 0); if(vdunpack(&root->dir, &me) < 0) goto Err1; vtblockput(b); return root;Err: vtfileunlock(r);Err1: vtblockput(b); if(r0) vtfileclose(r0); if(r1) vtfileclose(r1); if(r2) vtfileclose(r2); if(mr) filefree(mr); if(root) filefree(root); 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; } ff->qidoffset = f->qidoffset + ff->dir.qidoffset; vtfileunlock(meta); vtblockput(b); ff->boff = bo; ff->mode = f->mode; return ff; } vtblockput(b); b = nil; } werrstr(ENoFile); /* fall through */Err: vtfileunlock(meta); vtblockput(b); return nil;} /* * 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((r = vtfileopen(f->source, offset, mode)) == nil) return nil; if(r == nil) return nil; if(r->gen != gen){ werrstr(ERemoved); vtfileclose(r); return nil; } if(r->dir != dir && r->mode != -1){ werrstr(EBadMeta); vtfileclose(r); return nil; } return r;} VacFile*vacfilegetparent(VacFile *f){ if(vacfileisroot(f)) return vacfileincref(f); return vacfileincref(f->up);} /* * Given an unlocked vacfile (directory) f, * return the vacfile named elem in f. * Interprets . and .. as a convenience to callers. */VacFile*vacfilewalk(VacFile *f, char *elem){ VacFile *ff; if(elem[0] == 0){ werrstr(EBadPath); return nil; } if(!vacfileisdir(f)){ werrstr(ENotDir); return nil; } if(strcmp(elem, ".") == 0) return vacfileincref(f); if(strcmp(elem, "..") == 0) return vacfilegetparent(f); if(filelock(f) < 0) return nil; for(ff = f->down; ff; ff=ff->next){ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ ff->ref++; goto Exit; } } ff = dirlookup(f, elem); if(ff == nil)Lines 1–500 of 2104. Load more
#include "stdinc.h" #include "vac.h" #include "dat.h" #include "fns.h" #include "error.h" #define debug 0 /* * 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 */ /* meta data for file: protected by the lk in the parent */ int ref; /* holds this data structure up */ int partial; /* file was never really open */ int removed; /* file has been removed */ int dirty; /* dir is dirty with respect to meta data in block */ u32int boff; /* block offset within msource for this file's metadata */ VacDir dir; /* metadata for this file */ VacFile *up; /* parent file */ VacFile *next; /* sibling */ RWLock lk; /* lock for the following */ VtFile *source; /* actual data */ VtFile *msource; /* metadata for children in a directory */ VacFile *down; /* children */ int mode; uvlong qidoffset; /* qid offset */ }; static VacFile* filealloc(VacFs *fs) { VacFile *f; f = vtmallocz(sizeof(VacFile)); f->ref = 1; f->fs = fs; f->boff = NilBlock; f->mode = fs->mode; return f; } static void filefree(VacFile *f) { vtfileclose(f->source); vtfileclose(f->msource); vdcleanup(&f->dir); memset(f, ~0, sizeof *f); /* paranoia */ 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 metadata, like f->dir and f->ref, * are synchronized via the parent's lock. * This is why locking order goes up. */ static void filemetalock(VacFile *f) { assert(f->up != nil); wlock(&f->up->lk); } static void filemetaunlock(VacFile *f) { wunlock(&f->up->lk); } uvlong vacfilegetid(VacFile *f) { /* immutable */ return f->qidoffset + f->dir.qid; } uvlong vacfilegetqidoffset(VacFile *f) { return f->qidoffset; } 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; } 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) { int redirected; char err[ERRMAX]; VtBlock *b; VtFile *r0, *r1, *r2; MetaBlock mb; MetaEntry me; VacFile *root, *mr; redirected = 0; Top: b = nil; root = nil; mr = nil; r1 = nil; r2 = nil; if(vtfilelock(r, -1) < 0) return nil; r0 = vtfileopen(r, 0, fs->mode); if(debug) fprint(2, "r0 %p\n", r0); if(r0 == nil) goto Err; r2 = vtfileopen(r, 2, fs->mode); if(debug) fprint(2, "r2 %p\n", r2); if(r2 == nil){ /* * some vac files (e.g., from fossil) * have an extra layer of indirection. */ rerrstr(err, sizeof err); if(!redirected && strstr(err, "not active")){ redirected = 1; vtfileunlock(r); r = r0; goto Top; } goto Err; } r1 = vtfileopen(r, 1, fs->mode); if(debug) fprint(2, "r1 %p\n", r1); if(r1 == nil) goto Err; mr = filealloc(fs); mr->msource = r2; r2 = nil; root = filealloc(fs); root->boff = 0; root->up = mr; root->source = r0; r0 = nil; root->msource = r1; r1 = nil; mr->down = root; vtfileunlock(r); if(vtfilelock(mr->msource, VtOREAD) < 0) goto Err1; b = vtfileblock(mr->msource, 0, VtOREAD); vtfileunlock(mr->msource); if(b == nil) goto Err1; if(mbunpack(&mb, b->data, mr->msource->dsize) < 0) goto Err1; meunpack(&me, &mb, 0); if(vdunpack(&root->dir, &me) < 0) goto Err1; vtblockput(b); return root; Err: vtfileunlock(r); Err1: vtblockput(b); if(r0) vtfileclose(r0); if(r1) vtfileclose(r1); if(r2) vtfileclose(r2); if(mr) filefree(mr); if(root) filefree(root); 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; } ff->qidoffset = f->qidoffset + ff->dir.qidoffset; vtfileunlock(meta); vtblockput(b); ff->boff = bo; ff->mode = f->mode; return ff; } vtblockput(b); b = nil; } werrstr(ENoFile); /* fall through */ Err: vtfileunlock(meta); vtblockput(b); return nil; } /* * 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((r = vtfileopen(f->source, offset, mode)) == nil) return nil; if(r == nil) return nil; if(r->gen != gen){ werrstr(ERemoved); vtfileclose(r); return nil; } if(r->dir != dir && r->mode != -1){ werrstr(EBadMeta); vtfileclose(r); return nil; } return r; } VacFile* vacfilegetparent(VacFile *f) { if(vacfileisroot(f)) return vacfileincref(f); return vacfileincref(f->up); } /* * Given an unlocked vacfile (directory) f, * return the vacfile named elem in f. * Interprets . and .. as a convenience to callers. */ VacFile* vacfilewalk(VacFile *f, char *elem) { VacFile *ff; if(elem[0] == 0){ werrstr(EBadPath); return nil; } if(!vacfileisdir(f)){ werrstr(ENotDir); return nil; } if(strcmp(elem, ".") == 0) return vacfileincref(f); if(strcmp(elem, "..") == 0) return vacfilegetparent(f); if(filelock(f) < 0) return nil; for(ff = f->down; ff; ff=ff->next){ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ ff->ref++; goto Exit; } } ff = dirlookup(f, elem); if(ff == nil)