12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #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 */
- uint32_t 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;
-
- uint64_t 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);
- }
- uint64_t
- vacfilegetid(VacFile *f)
- {
- /* immutable */
- return f->qidoffset + f->dir.qid;
- }
- uint64_t
- vacfilegetqidoffset(VacFile *f)
- {
- return f->qidoffset;
- }
- uint32_t
- vacfilegetmcount(VacFile *f)
- {
- uint32_t mcount;
- filemetalock(f);
- mcount = f->dir.mcount;
- filemetaunlock(f);
- return mcount;
- }
- uint32_t
- vacfilegetmode(VacFile *f)
- {
- uint32_t 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;
- uint32_t 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, uint32_t offset, uint32_t 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, uint32_t bn, uint8_t *score)
- {
- VtFile *s;
- uint64_t 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((uint64_t)bn*dsize >= size)
- goto out1;
- ret = vtfileblockscore(f->source, bn, score);
- out1:
- vtfileunlock(f->source);
- out:
- filerunlock(f);
- return ret;
- }
- /*
- * Read data from f.
- */
- int
- vacfileread(VacFile *f, void *buf, int cnt, int64_t 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);
- else if(getentry(f->msource, me) < 0){
- filerunlock(f);
- return -1;
- }
- }
- filerunlock(f);
- return 0;
- }
- /*
- * Get the file's size.
- */
- int
- vacfilegetsize(VacFile *f, uint64_t *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, uint32_t offset, uint32_t gen, uint64_t *size)
- {
- VtBlock *b;
- uint32_t 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;
- uint32_t 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 uint32_t
- filemetaalloc(VacFile *fp, VacDir *dir, uint32_t start)
- {
- uint32_t nb, bo;
- VtBlock *b;
- MetaBlock mb;
- int nn;
- uint8_t *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;
- mbunpack(&mb, b->data, ms->dsize);
- goto Found;
- }
- vtblockput(b);
- }
- /* No block found, extend the file by one metablock. */
- vtfileflushbefore(ms, nb*(uint64_t)ms->dsize);
- if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
- goto Err;
- vtfilesetsize(ms, (nb+1)*ms->dsize);
- mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
- Found:
- /* Now we have a block; allocate space to write the entry. */
- p = mballoc(&mb, n);
- if(p == nil){
- /* mballoc might have changed block */
- mbpack(&mb);
- werrstr(EBadMeta);
- goto Err;
- }
- /* Figure out where to put the index entry, and write it. */
- mbsearch(&mb, dir->elem, &i, &me);
- assert(me.p == nil); /* not already there */
- me.p = p;
- me.size = n;
- vdpack(dir, &me, VacDirVersion);
- mbinsert(&mb, i, &me);
- mbpack(&mb);
- vtblockput(b);
- return bo;
- Err:
- vtblockput(b);
- return NilBlock;
- }
- /*
- * Update f's directory entry in the block cache.
- * We look for the directory entry by name;
- * if we're trying to rename the file, oelem is the old name.
- *
- * Assumes caller has filemetalock'ed f.
- */
- static int
- filemetaflush(VacFile *f, char *oelem)
- {
- int i, n;
- MetaBlock mb;
- MetaEntry me, me2;
- VacFile *fp;
- VtBlock *b;
- uint32_t 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.
- * Vacfiledecref knows how to flush source and msource 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);
- if(filelock(f) < 0)
- return -1;
- /*
- * Lock order prevents us from flushing kids while holding
- * lock, so make a list and then flush without the lock.
- */
- 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++;
- }
- }
- if(nkids > 0){
- fileunlock(f);
- for(i=0; i<nkids; i++){
- if(vacfileflush(kids[i], 1) < 0)
- ret = -1;
- vacfiledecref(kids[i]);
- }
- filelock(f);
- }
- free(kids);
- /*
- * Now we can flush our own data.
- */
- 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);
- }
- fileunlock(f);
- 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, uint32_t mode)
- {
- VacFile *ff;
- VacDir *dir;
- VtFile *pr, *r, *mr;
- int type;
- uint32_t 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);
-
- filelock(ff);
- vtfilelock(ff->source, -1);
- vtfileunlock(ff->source);
- fileunlock(ff);
- 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, uint64_t 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, int64_t 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;
- uint32_t mask;
- uint64_t 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);
- }
- if(strcmp(f->dir.mid, dir->mid) != 0){
- vtfree(f->dir.mid);
- f->dir.mid = vtstrdup(dir->mid);
- }
- 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, uint64_t offset, uint64_t 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)
- {
- uint32_t 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, uint8_t 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;
- uint8_t score[VtScoreSize];
- char *prefix;
-
- if(vtparsescore(file, &prefix, score) >= 0){
- if(prefix == nil || 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, uint8_t *score, int mode, int ncache)
- {
- VacFs *fs;
- int n;
- VtRoot rt;
- uint8_t 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;
- 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, uint8_t *score)
- {
- memmove(score, fs->score, VtScoreSize);
- return 0;
- }
- int
- _vacfsnextqid(VacFs *fs, uint64_t *qid)
- {
- ++fs->qid;
- *qid = fs->qid;
- return 0;
- }
- void
- vacfsjumpqid(VacFs *fs, uint64_t 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, uint64_t *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;
- uint8_t buf[VtEntrySize], metascore[VtScoreSize];
- VtEntry e;
- VtBlock *b;
- MetaBlock mb;
- VacDir vd;
- MetaEntry me;
- int psize;
- int mbsize;
-
- if((fs = vacfsalloc(z, bsize, ncache, VtORDWR)) == nil)
- return nil;
-
- /*
- * Fake up an empty vac fs.
- */
- psize = bsize;
- f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
- vtfilelock(f, VtORDWR);
-
- /* Metablocks can't be too big -- they have 16-bit offsets in them. */
- mbsize = bsize;
- if(mbsize >= 56*1024)
- mbsize = 56*1024;
- /* Write metablock containing root directory VacDir. */
- b = vtcacheallocblock(fs->cache, VtDataType);
- mbinit(&mb, b->data, mbsize, mbsize/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;
- e.dsize = mbsize;
- vtentrypack(&e, buf, 0);
- vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
- /* Third entry: metadata stream with root directory. */
- memmove(e.score, metascore, VtScoreSize);
- e.size = mbsize;
- 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)
- {
- uint8_t 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;
- }
- int
- vacfiledsize(VacFile *f)
- {
- VtEntry e;
- if(vacfilegetentries(f,&e,nil) < 0)
- return -1;
- return e.dsize;
- }
- /*
- * Does block b of f have the same SHA1 hash as the n bytes at buf?
- */
- int
- sha1matches(VacFile *f, uint32_t b, uint8_t *buf, int n)
- {
- uint8_t fscore[VtScoreSize];
- uint8_t bufscore[VtScoreSize];
-
- if(vacfileblockscore(f, b, fscore) < 0)
- return 0;
- n = vtzerotruncate(VtDataType, buf, n);
- sha1(buf, n, bufscore, nil);
- if(memcmp(bufscore, fscore, VtScoreSize) == 0)
- return 1;
- return 0;
- }
|