1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104 |
- #include "stdinc.h"
- #include "vac.h"
- #include "dat.h"
- #include "fns.h"
- #include "error.h"
- #define debug 0
- /*
- * Vac file system. This is a simplified version of the same code in Fossil.
- *
- * The locking order in the tree is upward: a thread can hold the lock
- * for a VacFile and then acquire the lock of f->up (the parent),
- * but not vice-versa.
- *
- * A vac file is one or two venti files. Plain data files are one venti file,
- * while directores are two: a venti data file containing traditional
- * directory entries, and a venti directory file containing venti
- * directory entries. The traditional directory entries in the data file
- * contain integers indexing into the venti directory entry file.
- * It's a little complicated, but it makes the data usable by standard
- * tools like venti/copy.
- *
- */
-
- static int filemetaflush(VacFile*, char*);
- struct VacFile
- {
- VacFs *fs; /* immutable */
- /* meta data for file: protected by the lk in the parent */
- int ref; /* holds this data structure up */
- int partial; /* file was never really open */
- int removed; /* file has been removed */
- int dirty; /* dir is dirty with respect to meta data in block */
- u32int boff; /* block offset within msource for this file's metadata */
- VacDir dir; /* metadata for this file */
- VacFile *up; /* parent file */
- VacFile *next; /* sibling */
- RWLock lk; /* lock for the following */
- VtFile *source; /* actual data */
- VtFile *msource; /* metadata for children in a directory */
- VacFile *down; /* children */
- int mode;
-
- uvlong qidoffset; /* qid offset */
- };
- static VacFile*
- filealloc(VacFs *fs)
- {
- VacFile *f;
- f = vtmallocz(sizeof(VacFile));
- f->ref = 1;
- f->fs = fs;
- f->boff = NilBlock;
- f->mode = fs->mode;
- return f;
- }
- static void
- filefree(VacFile *f)
- {
- vtfileclose(f->source);
- vtfileclose(f->msource);
- vdcleanup(&f->dir);
- memset(f, ~0, sizeof *f); /* paranoia */
- vtfree(f);
- }
- static int
- chksource(VacFile *f)
- {
- if(f->partial)
- return 0;
- if(f->source == nil
- || ((f->dir.mode & ModeDir) && f->msource == nil)){
- werrstr(ERemoved);
- return -1;
- }
- return 0;
- }
- static int
- filelock(VacFile *f)
- {
- wlock(&f->lk);
- if(chksource(f) < 0){
- wunlock(&f->lk);
- return -1;
- }
- return 0;
- }
- static void
- fileunlock(VacFile *f)
- {
- wunlock(&f->lk);
- }
- static int
- filerlock(VacFile *f)
- {
- rlock(&f->lk);
- if(chksource(f) < 0){
- runlock(&f->lk);
- return -1;
- }
- return 0;
- }
- static void
- filerunlock(VacFile *f)
- {
- runlock(&f->lk);
- }
- /*
- * The file metadata, like f->dir and f->ref,
- * are synchronized via the parent's lock.
- * This is why locking order goes up.
- */
- static void
- filemetalock(VacFile *f)
- {
- assert(f->up != nil);
- wlock(&f->up->lk);
- }
- static void
- filemetaunlock(VacFile *f)
- {
- wunlock(&f->up->lk);
- }
- uvlong
- vacfilegetid(VacFile *f)
- {
- /* immutable */
- return f->qidoffset + f->dir.qid;
- }
- uvlong
- vacfilegetqidoffset(VacFile *f)
- {
- return f->qidoffset;
- }
- ulong
- vacfilegetmcount(VacFile *f)
- {
- ulong mcount;
- filemetalock(f);
- mcount = f->dir.mcount;
- filemetaunlock(f);
- return mcount;
- }
- ulong
- vacfilegetmode(VacFile *f)
- {
- ulong mode;
- filemetalock(f);
- mode = f->dir.mode;
- filemetaunlock(f);
- return mode;
- }
- int
- vacfileisdir(VacFile *f)
- {
- /* immutable */
- return (f->dir.mode & ModeDir) != 0;
- }
- int
- vacfileisroot(VacFile *f)
- {
- return f == f->fs->root;
- }
- /*
- * The files are reference counted, and while the reference
- * is bigger than zero, each file can be found in its parent's
- * f->down list (chains via f->next), so that multiple threads
- * end up sharing a VacFile* when referring to the same file.
- *
- * Each VacFile holds a reference to its parent.
- */
- VacFile*
- vacfileincref(VacFile *vf)
- {
- filemetalock(vf);
- assert(vf->ref > 0);
- vf->ref++;
- filemetaunlock(vf);
- return vf;
- }
- int
- vacfiledecref(VacFile *f)
- {
- VacFile *p, *q, **qq;
- if(f->up == nil){
- /* never linked in */
- assert(f->ref == 1);
- filefree(f);
- return 0;
- }
-
- filemetalock(f);
- f->ref--;
- if(f->ref > 0){
- filemetaunlock(f);
- return -1;
- }
- assert(f->ref == 0);
- assert(f->down == nil);
- if(f->source && vtfilelock(f->source, -1) >= 0){
- vtfileflush(f->source);
- vtfileunlock(f->source);
- }
- if(f->msource && vtfilelock(f->msource, -1) >= 0){
- vtfileflush(f->msource);
- vtfileunlock(f->msource);
- }
- /*
- * Flush f's directory information to the cache.
- */
- filemetaflush(f, nil);
- p = f->up;
- qq = &p->down;
- for(q = *qq; q; q = *qq){
- if(q == f)
- break;
- qq = &q->next;
- }
- assert(q != nil);
- *qq = f->next;
- filemetaunlock(f);
- filefree(f);
- vacfiledecref(p);
- return 0;
- }
- /*
- * Construct a vacfile for the root of a vac tree, given the
- * venti file for the root information. That venti file is a
- * directory file containing VtEntries for three more venti files:
- * the two venti files making up the root directory, and a
- * third venti file that would be the metadata half of the
- * "root's parent".
- *
- * Fossil generates slightly different vac files, due to a now
- * impossible-to-change bug, which contain a VtEntry
- * for just one venti file, that itself contains the expected
- * three directory entries. Sigh.
- */
- VacFile*
- _vacfileroot(VacFs *fs, VtFile *r)
- {
- int redirected;
- char err[ERRMAX];
- VtBlock *b;
- VtFile *r0, *r1, *r2;
- MetaBlock mb;
- MetaEntry me;
- VacFile *root, *mr;
- redirected = 0;
- Top:
- b = nil;
- root = nil;
- mr = nil;
- r1 = nil;
- r2 = nil;
- if(vtfilelock(r, -1) < 0)
- return nil;
- r0 = vtfileopen(r, 0, fs->mode);
- if(debug)
- fprint(2, "r0 %p\n", r0);
- if(r0 == nil)
- goto Err;
- r2 = vtfileopen(r, 2, fs->mode);
- if(debug)
- fprint(2, "r2 %p\n", r2);
- if(r2 == nil){
- /*
- * some vac files (e.g., from fossil)
- * have an extra layer of indirection.
- */
- rerrstr(err, sizeof err);
- if(!redirected && strstr(err, "not active")){
- redirected = 1;
- vtfileunlock(r);
- r = r0;
- goto Top;
- }
- goto Err;
- }
- r1 = vtfileopen(r, 1, fs->mode);
- if(debug)
- fprint(2, "r1 %p\n", r1);
- if(r1 == nil)
- goto Err;
- mr = filealloc(fs);
- mr->msource = r2;
- r2 = nil;
- root = filealloc(fs);
- root->boff = 0;
- root->up = mr;
- root->source = r0;
- r0 = nil;
- root->msource = r1;
- r1 = nil;
- mr->down = root;
- vtfileunlock(r);
- if(vtfilelock(mr->msource, VtOREAD) < 0)
- goto Err1;
- b = vtfileblock(mr->msource, 0, VtOREAD);
- vtfileunlock(mr->msource);
- if(b == nil)
- goto Err1;
- if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
- goto Err1;
- meunpack(&me, &mb, 0);
- if(vdunpack(&root->dir, &me) < 0)
- goto Err1;
- vtblockput(b);
- return root;
- Err:
- vtfileunlock(r);
- Err1:
- vtblockput(b);
- if(r0)
- vtfileclose(r0);
- if(r1)
- vtfileclose(r1);
- if(r2)
- vtfileclose(r2);
- if(mr)
- filefree(mr);
- if(root)
- filefree(root);
- return nil;
- }
- /*
- * Vac directories are a sequence of metablocks, each of which
- * contains a bunch of metaentries sorted by file name.
- * The whole sequence isn't sorted, though, so you still have
- * to look at every block to find a given name.
- * Dirlookup looks in f for an element name elem.
- * It returns a new VacFile with the dir, boff, and mode
- * filled in, but the sources (venti files) are not, and f is
- * not yet linked into the tree. These details must be taken
- * care of by the caller.
- *
- * f must be locked, f->msource must not.
- */
- static VacFile*
- dirlookup(VacFile *f, char *elem)
- {
- int i;
- MetaBlock mb;
- MetaEntry me;
- VtBlock *b;
- VtFile *meta;
- VacFile *ff;
- u32int bo, nb;
- meta = f->msource;
- b = nil;
- if(vtfilelock(meta, -1) < 0)
- return nil;
- nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
- for(bo=0; bo<nb; bo++){
- b = vtfileblock(meta, bo, VtOREAD);
- if(b == nil)
- goto Err;
- if(mbunpack(&mb, b->data, meta->dsize) < 0)
- goto Err;
- if(mbsearch(&mb, elem, &i, &me) >= 0){
- ff = filealloc(f->fs);
- if(vdunpack(&ff->dir, &me) < 0){
- filefree(ff);
- goto Err;
- }
- ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
- vtfileunlock(meta);
- vtblockput(b);
- ff->boff = bo;
- ff->mode = f->mode;
- return ff;
- }
- vtblockput(b);
- b = nil;
- }
- werrstr(ENoFile);
- /* fall through */
- Err:
- vtfileunlock(meta);
- vtblockput(b);
- return nil;
- }
- /*
- * Open the venti file at offset in the directory f->source.
- * f is locked.
- */
- static VtFile *
- fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
- {
- VtFile *r;
- if((r = vtfileopen(f->source, offset, mode)) == nil)
- return nil;
- if(r == nil)
- return nil;
- if(r->gen != gen){
- werrstr(ERemoved);
- vtfileclose(r);
- return nil;
- }
- if(r->dir != dir && r->mode != -1){
- werrstr(EBadMeta);
- vtfileclose(r);
- return nil;
- }
- return r;
- }
- VacFile*
- vacfilegetparent(VacFile *f)
- {
- if(vacfileisroot(f))
- return vacfileincref(f);
- return vacfileincref(f->up);
- }
- /*
- * Given an unlocked vacfile (directory) f,
- * return the vacfile named elem in f.
- * Interprets . and .. as a convenience to callers.
- */
- VacFile*
- vacfilewalk(VacFile *f, char *elem)
- {
- VacFile *ff;
- if(elem[0] == 0){
- werrstr(EBadPath);
- return nil;
- }
- if(!vacfileisdir(f)){
- werrstr(ENotDir);
- return nil;
- }
- if(strcmp(elem, ".") == 0)
- return vacfileincref(f);
- if(strcmp(elem, "..") == 0)
- return vacfilegetparent(f);
- if(filelock(f) < 0)
- return nil;
- for(ff = f->down; ff; ff=ff->next){
- if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
- ff->ref++;
- goto Exit;
- }
- }
- ff = dirlookup(f, elem);
- if(ff == nil)
- 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 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, 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);
- else 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;
- mbunpack(&mb, b->data, ms->dsize);
- 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);
- 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.
- * 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, 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);
-
- 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, 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);
- }
- 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, 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(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, 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;
- 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;
- 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)
- {
- 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;
- }
- 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, ulong b, uchar *buf, int n)
- {
- uchar fscore[VtScoreSize];
- uchar 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;
- }
|