123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052 |
- #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 */
- fprint(2, "getid %s %lld+%lld = %lld\n", f->dir.elem, f->qidoffset, f->dir.qid, f->qidoffset+f->dir.qid);
- return f->qidoffset + f->dir.qid;
- }
- 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;
- }
- fprint(2, "offset %s %lld\n", ff->dir.elem, ff->dir.qidoffset);
- ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
- vtfileunlock(meta);
- vtblockput(b);
- ff->boff = bo;
- ff->mode = f->mode;
- return ff;
- }
- vtblockput(b);
- b = nil;
- }
- werrstr(ENoFile);
- /* fall through */
- Err:
- vtfileunlock(meta);
- vtblockput(b);
- return nil;
- }
- /*
- * 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)
- goto Err;
- if(ff->dir.mode & ModeSnapshot)
- ff->mode = VtOREAD;
- 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 Err1;
- }else{
- ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
- if(ff->source == nil)
- goto Err1;
- }
- vtfileunlock(f->source);
- /* link in and up parent ref count */
- ff->next = f->down;
- f->down = ff;
- ff->up = f;
- vacfileincref(f);
- Exit:
- fileunlock(f);
- return ff;
- Err1:
- vtfileunlock(f->source);
- Err:
- fileunlock(f);
- if(ff != nil)
- vacfiledecref(ff);
- return nil;
- }
- /*
- * Open a path in the vac file system:
- * just walk each element one at a time.
- */
- VacFile*
- vacfileopen(VacFs *fs, char *path)
- {
- VacFile *f, *ff;
- char *p, elem[VtMaxStringSize], *opath;
- int n;
- f = fs->root;
- vacfileincref(f);
- opath = path;
- while(*path != 0){
- for(p = path; *p && *p != '/'; p++)
- ;
- n = p - path;
- if(n > 0){
- if(n > VtMaxStringSize){
- werrstr("%s: element too long", EBadPath);
- goto Err;
- }
- memmove(elem, path, n);
- elem[n] = 0;
- ff = vacfilewalk(f, elem);
- if(ff == nil){
- werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
- goto Err;
- }
- vacfiledecref(f);
- f = ff;
- }
- if(*p == '/')
- p++;
- path = p;
- }
- return f;
- Err:
- vacfiledecref(f);
- return nil;
- }
- /*
- * Extract the score for the bn'th block in f.
- */
- int
- vacfileblockscore(VacFile *f, u32int bn, u8int *score)
- {
- 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;
- }
- /*
- * Read data from f.
- */
- int
- vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
- {
- 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;
- int epb;
- epb = s->dsize/VtEntrySize;
- bn = offset/epb;
- offset -= bn*epb;
- b = vtfileblock(s, bn, VtOREAD);
- if(b == nil)
- goto Err;
- if(vtentryunpack(&e, b->data, offset) < 0)
- goto Err;
- /* 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;
- }
- }
- 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
- start = 0;
- }
-
- if(start > nb)
- start = nb;
- for(bo=start; bo<nb; bo++){
- if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
- goto Err;
- if(mbunpack(&mb, b->data, ms->dsize) < 0)
- goto Err;
- nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
- if(n <= nn && mb.nindex < mb.maxindex){
- /* reopen for writing */
- vtblockput(b);
- if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
- goto Err;
- goto Found;
- }
- vtblockput(b);
- }
- /* No block found, extend the file by one metablock. */
- vtfileflushbefore(ms, nb*(uvlong)ms->dsize);
- if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
- goto Err;
- vtfilesetsize(ms, (nb+1)*ms->dsize);
- mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
- Found:
- /* Now we have a block; allocate space to write the entry. */
- p = mballoc(&mb, n);
- if(p == nil){
- /* mballoc might have changed block */
- mbpack(&mb);
- werrstr(EBadMeta);
- goto Err;
- }
- /* Figure out where to put the index entry, and write it. */
- mbsearch(&mb, dir->elem, &i, &me);
- assert(me.p == nil); /* not already there */
- me.p = p;
- me.size = n;
- vdpack(dir, &me, VacDirVersion);
- vdunpack(dir, &me);
- mbinsert(&mb, i, &me);
- mbpack(&mb);
- vtblockput(b);
- return bo;
- Err:
- vtblockput(b);
- return NilBlock;
- }
- /*
- * 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);
- vdunpack(&f->dir, &me);
- mbinsert(&mb, i, &me);
- mbpack(&mb);
-
- /* Done */
- vtblockput(b);
- vtfileunlock(fp->msource);
- f->dirty = 0;
- return 0;
- }
-
- /*
- * The entry must be moved to another block.
- * This can only really happen on renames that
- * make the name very long.
- */
-
- /* Allocate a spot in a new block. */
- if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){
- /* mbresize above might have modified block */
- mbpack(&mb);
- goto Err;
- }
- f->boff = bo;
- /* Now we're committed. Delete entry in old block. */
- mbdelete(&mb, i, &me);
- mbpack(&mb);
- vtblockput(b);
- vtfileunlock(fp->msource);
- f->dirty = 0;
- return 0;
- Err:
- vtblockput(b);
- Err1:
- vtfileunlock(fp->msource);
- return -1;
- }
- /*
- * Remove the directory entry for f.
- */
- static int
- 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;
- 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 *fp, char *elem, ulong mode)
- {
- VacFile *ff;
- VacDir *dir;
- VtFile *pr, *r, *mr;
- int type;
- u32int bo;
- if(filelock(fp) < 0)
- return nil;
- /*
- * First, look to see that there's not a file in memory
- * with the same name.
- */
- for(ff = fp->down; ff; ff=ff->next){
- if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
- ff = nil;
- werrstr(EExists);
- goto Err1;
- }
- }
- /*
- * Next check the venti blocks.
- */
- ff = dirlookup(fp, elem);
- if(ff != nil){
- werrstr(EExists);
- goto Err1;
- }
- /*
- * By the way, you can't create in a read-only file system.
- */
- pr = fp->source;
- if(pr->mode != VtORDWR){
- werrstr(EReadOnly);
- goto Err1;
- }
- /*
- * Okay, time to actually create something. Lock the two
- * halves of the directory and create a file.
- */
- if(vtfilelock2(fp->source, fp->msource, -1) < 0)
- goto Err1;
- ff = filealloc(fp->fs);
- ff->qidoffset = fp->qidoffset; /* hopefully fp->qidoffset == 0 */
- type = VtDataType;
- if(mode & ModeDir)
- type = VtDirType;
- mr = nil;
- if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
- goto Err;
- if(mode & ModeDir)
- if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
- goto Err;
- /*
- * Fill in the directory entry and write it to disk.
- */
- dir = &ff->dir;
- dir->elem = vtstrdup(elem);
- dir->entry = r->offset;
- dir->gen = r->gen;
- if(mode & ModeDir){
- dir->mentry = mr->offset;
- dir->mgen = mr->gen;
- }
- dir->size = 0;
- if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
- goto Err;
- dir->uid = vtstrdup(fp->dir.uid);
- dir->gid = vtstrdup(fp->dir.gid);
- dir->mid = vtstrdup("");
- dir->mtime = time(0L);
- dir->mcount = 0;
- dir->ctime = dir->mtime;
- dir->atime = dir->mtime;
- dir->mode = mode;
- if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
- goto Err;
- /*
- * Now we're committed.
- */
- vtfileunlock(fp->source);
- vtfileunlock(fp->msource);
- ff->source = r;
- ff->msource = mr;
- ff->boff = bo;
- /* Link into tree. */
- ff->next = fp->down;
- fp->down = ff;
- ff->up = fp;
- vacfileincref(fp);
- fileunlock(fp);
- return ff;
- Err:
- vtfileunlock(fp->source);
- vtfileunlock(fp->msource);
- if(r){
- vtfilelock(r, -1);
- vtfileremove(r);
- }
- if(mr){
- vtfilelock(mr, -1);
- vtfileremove(mr);
- }
- Err1:
- if(ff)
- vacfiledecref(ff);
- fileunlock(fp);
- return nil;
- }
- /*
- * Change the size of the file f.
- */
- int
- vacfilesetsize(VacFile *f, uvlong size)
- {
- if(vacfileisdir(f)){
- werrstr(ENotFile);
- return -1;
- }
-
- if(filelock(f) < 0)
- return -1;
- if(f->source->mode != VtORDWR){
- werrstr(EReadOnly);
- goto Err;
- }
- if(vtfilelock(f->source, -1) < 0)
- goto Err;
- if(vtfilesetsize(f->source, size) < 0){
- vtfileunlock(f->source);
- goto Err;
- }
- vtfileunlock(f->source);
- fileunlock(f);
- return 0;
- Err:
- fileunlock(f);
- return -1;
- }
- /*
- * Write data to f.
- */
- int
- vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
- {
- 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;
- }
- 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
- vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
- {
- int ret;
- vacfileflush(f, 0); /* flush blocks to venti, since we won't see them again */
- if(!(e->flags&VtEntryActive)){
- werrstr("missing entry for source");
- return -1;
- }
- if(me && !(me->flags&VtEntryActive))
- me = nil;
- if(f->msource && !me){
- werrstr("missing entry for msource");
- return -1;
- }
- if(me && !f->msource){
- werrstr("no msource to set");
- return -1;
- }
- if(filelock(f) < 0)
- return -1;
- if(f->source->mode != VtORDWR
- || (f->msource && f->msource->mode != VtORDWR)){
- werrstr(EReadOnly);
- fileunlock(f);
- return -1;
- }
- if(vtfilelock2(f->source, f->msource, -1) < 0){
- fileunlock(f);
- return -1;
- }
- ret = 0;
- if(vtfilesetentry(f->source, e) < 0)
- ret = -1;
- else if(me && vtfilesetentry(f->msource, me) < 0)
- ret = -1;
- vtfileunlock(f->source);
- if(f->msource)
- vtfileunlock(f->msource);
- fileunlock(f);
- return ret;
- }
- /*
- * Get the directory entry for f.
- */
- int
- vacfilegetdir(VacFile *f, VacDir *dir)
- {
- if(filerlock(f) < 0)
- return -1;
- filemetalock(f);
- vdcopy(dir, &f->dir);
- filemetaunlock(f);
- if(!vacfileisdir(f)){
- if(vtfilelock(f->source, VtOREAD) < 0){
- filerunlock(f);
- return -1;
- }
- dir->size = vtfilegetsize(f->source);
- vtfileunlock(f->source);
- }
- filerunlock(f);
- return 0;
- }
- /*
- * Set the directory entry for f.
- */
- int
- vacfilesetdir(VacFile *f, VacDir *dir)
- {
- VacFile *ff;
- char *oelem;
- u32int mask;
- u64int size;
- /* can not set permissions for the root */
- if(vacfileisroot(f)){
- werrstr(ERoot);
- return -1;
- }
- if(filelock(f) < 0)
- return -1;
- filemetalock(f);
-
- if(f->source->mode != VtORDWR){
- werrstr(EReadOnly);
- goto Err;
- }
- /* 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 */
- }
- /* Get ready... */
- if(vtfilelock2(f->source, f->msource, -1) < 0)
- goto Err;
- if(!vacfileisdir(f)){
- size = vtfilegetsize(f->source);
- if(size != dir->size){
- if(vtfilesetsize(f->source, dir->size) < 0){
- vtfileunlock(f->source);
- if(f->msource)
- vtfileunlock(f->msource);
- goto Err;
- }
- }
- }
- /* ... now commited to changing it. */
- vtfileunlock(f->source);
- if(f->msource)
- vtfileunlock(f->msource);
- oelem = nil;
- if(strcmp(f->dir.elem, dir->elem) != 0){
- oelem = f->dir.elem;
- f->dir.elem = vtstrdup(dir->elem);
- }
- if(strcmp(f->dir.uid, dir->uid) != 0){
- vtfree(f->dir.uid);
- f->dir.uid = vtstrdup(dir->uid);
- }
- if(strcmp(f->dir.gid, dir->gid) != 0){
- vtfree(f->dir.gid);
- f->dir.gid = vtstrdup(dir->gid);
- }
- 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:
- 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;
- if(f->source->mode != VtORDWR){
- fileunlock(f);
- werrstr(EReadOnly);
- return -1;
- }
- filemetalock(f);
- f->dir.qidspace = 1;
- f->dir.qidoffset = offset;
- f->dir.qidmax = max;
- f->dirty = 1;
- ret = filemetaflush(f, nil);
- filemetaunlock(f);
- fileunlock(f);
- return ret;
- }
- /*
- * 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)
- {
- u32int i, n;
- VtBlock *b;
- MetaBlock mb;
- VtFile *r;
- r = f->msource;
- n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
- for(i=0; i<n; i++){
- b = vtfileblock(r, i, VtOREAD);
- if(b == nil)
- return -1;
- if(mbunpack(&mb, b->data, r->dsize) < 0)
- goto Err;
- if(mb.nindex > 0){
- werrstr(ENotEmpty);
- goto Err;
- }
- vtblockput(b);
- }
- return 0;
- Err:
- vtblockput(b);
- return -1;
- }
- /*
- * Remove the vac file f.
- */
- int
- vacfileremove(VacFile *f)
- {
- VacFile *ff;
- /* Cannot remove the root */
- if(vacfileisroot(f)){
- werrstr(ERoot);
- return -1;
- }
- 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;
- for(ff=f->down; ff; ff=ff->next)
- assert(ff->removed);
- vtfileremove(f->source);
- f->source = nil;
- if(f->msource){
- vtfileremove(f->msource);
- f->msource = nil;
- }
- fileunlock(f);
- if(filemetaremove(f) < 0)
- return -1;
- return 0;
- Err:
- vtfileunlock(f->source);
- if(f->msource)
- vtfileunlock(f->msource);
- Err1:
- fileunlock(f);
- return -1;
- }
- /*
- * 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;
- }
- 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;
- }
- 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;
- }
- void
- vacfsjumpqid(VacFs *fs, uvlong step)
- {
- fs->qid += step;
- }
- /*
- * Set *maxqid to the maximum qid expected in this file system.
- * In newer vac archives, the maximum qid is stored in the
- * qidspace VacDir annotation. In older vac archives, the root
- * got created last, so it had the maximum qid.
- */
- int
- vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
- {
- VacDir vd;
-
- if(vacfilegetdir(fs->root, &vd) < 0)
- return -1;
- if(vd.qidspace)
- *maxqid = vd.qidmax;
- else
- *maxqid = vd.qid;
- vdcleanup(&vd);
- return 0;
- }
- void
- vacfsclose(VacFs *fs)
- {
- if(fs->root)
- vacfiledecref(fs->root);
- fs->root = nil;
- vtcachefree(fs->cache);
- vtfree(fs);
- }
- /*
- * Create a fresh vac fs.
- */
- VacFs *
- vacfscreate(VtConn *z, int bsize, int ncache)
- {
- VacFs *fs;
- VtFile *f;
- uchar buf[VtEntrySize], metascore[VtScoreSize];
- VtEntry e;
- VtBlock *b;
- MetaBlock mb;
- VacDir vd;
- MetaEntry me;
- int psize;
-
- if((fs = vacfsalloc(z, bsize, ncache, VtORDWR)) == nil)
- return nil;
-
- /*
- * Fake up an empty vac fs.
- */
- psize = bsize/VtEntrySize*VtEntrySize;
- f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
- vtfilelock(f, VtORDWR);
-
- /* Write metablock containing root directory VacDir. */
- b = vtcacheallocblock(fs->cache, VtDataType);
- mbinit(&mb, b->data, bsize, bsize/BytesPerEntry);
- memset(&vd, 0, sizeof vd);
- vd.elem = "/";
- vd.mode = 0777|ModeDir;
- vd.uid = "vac";
- vd.gid = "vac";
- vd.mid = "";
- me.size = vdsize(&vd, VacDirVersion);
- me.p = mballoc(&mb, me.size);
- vdpack(&vd, &me, VacDirVersion);
- mbinsert(&mb, 0, &me);
- mbpack(&mb);
- vtblockwrite(b);
- memmove(metascore, b->score, VtScoreSize);
- vtblockput(b);
-
- /* First entry: empty venti directory stream. */
- memset(&e, 0, sizeof e);
- e.flags = VtEntryActive;
- e.psize = psize;
- e.dsize = bsize;
- e.type = VtDirType;
- memmove(e.score, vtzeroscore, VtScoreSize);
- vtentrypack(&e, buf, 0);
- vtfilewrite(f, buf, VtEntrySize, 0);
-
- /* Second entry: empty metadata stream. */
- e.type = VtDataType;
- vtentrypack(&e, buf, 0);
- vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
- /* Third entry: metadata stream with root directory. */
- memmove(e.score, metascore, VtScoreSize);
- e.size = bsize;
- vtentrypack(&e, buf, 0);
- vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
- vtfileflush(f);
- vtfileunlock(f);
-
- /* Now open it as a vac fs. */
- fs->root = _vacfileroot(fs, f);
- if(fs->root == nil){
- werrstr("vacfileroot: %r");
- vacfsclose(fs);
- return nil;
- }
- return fs;
- }
- int
- vacfssync(VacFs *fs)
- {
- uchar buf[1024];
- VtEntry e;
- VtFile *f;
- VtRoot root;
- /* Sync the entire vacfs to disk. */
- if(vacfileflush(fs->root, 1) < 0)
- return -1;
- if(vtfilelock(fs->root->up->msource, -1) < 0)
- return -1;
- if(vtfileflush(fs->root->up->msource) < 0){
- vtfileunlock(fs->root->up->msource);
- return -1;
- }
- vtfileunlock(fs->root->up->msource);
- /* Prepare the dir stream for the root block. */
- if(getentry(fs->root->source, &e) < 0)
- return -1;
- vtentrypack(&e, buf, 0);
- if(getentry(fs->root->msource, &e) < 0)
- return -1;
- vtentrypack(&e, buf, 1);
- if(getentry(fs->root->up->msource, &e) < 0)
- return -1;
- vtentrypack(&e, buf, 2);
- f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
- vtfilelock(f, VtORDWR);
- if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
- || vtfileflush(f) < 0){
- vtfileunlock(f);
- vtfileclose(f);
- return -1;
- }
- vtfileunlock(f);
- if(getentry(f, &e) < 0){
- vtfileclose(f);
- return -1;
- }
- vtfileclose(f);
-
- /* Build a root block. */
- memset(&root, 0, sizeof root);
- strcpy(root.type, "vac");
- strcpy(root.name, fs->name);
- memmove(root.score, e.score, VtScoreSize);
- root.blocksize = fs->bsize;
- memmove(root.prev, fs->score, VtScoreSize);
- vtrootpack(&root, buf);
- if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
- werrstr("writing root: %r");
- return -1;
- }
- if(vtsync(fs->z) < 0)
- return -1;
- return 0;
- }
|