URI: 
       tfile.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfile.c (40202B)
       ---
            1 #include "stdinc.h"
            2 #include "vac.h"
            3 #include "dat.h"
            4 #include "fns.h"
            5 #include "error.h"
            6 
            7 #define debug 0
            8 
            9 /*
           10  * Vac file system.  This is a simplified version of the same code in Fossil.
           11  *
           12  * The locking order in the tree is upward: a thread can hold the lock
           13  * for a VacFile and then acquire the lock of f->up (the parent),
           14  * but not vice-versa.
           15  *
           16  * A vac file is one or two venti files.  Plain data files are one venti file,
           17  * while directores are two: a venti data file containing traditional
           18  * directory entries, and a venti directory file containing venti
           19  * directory entries.  The traditional directory entries in the data file
           20  * contain integers indexing into the venti directory entry file.
           21  * It's a little complicated, but it makes the data usable by standard
           22  * tools like venti/copy.
           23  *
           24  */
           25 
           26 static int filemetaflush(VacFile*, char*);
           27 
           28 struct VacFile
           29 {
           30         VacFs        *fs;        /* immutable */
           31 
           32         /* meta data for file: protected by the lk in the parent */
           33         int                ref;        /* holds this data structure up */
           34 
           35         int                partial;        /* file was never really open */
           36         int                removed;        /* file has been removed */
           37         int                dirty;        /* dir is dirty with respect to meta data in block */
           38         u32int        boff;                /* block offset within msource for this file's metadata */
           39         VacDir        dir;                /* metadata for this file */
           40         VacFile        *up;                /* parent file */
           41         VacFile        *next;        /* sibling */
           42 
           43         RWLock        lk;                /* lock for the following */
           44         VtFile        *source;        /* actual data */
           45         VtFile        *msource;        /* metadata for children in a directory */
           46         VacFile        *down;        /* children */
           47         int                mode;
           48 
           49         uvlong        qidoffset;        /* qid offset */
           50 };
           51 
           52 static VacFile*
           53 filealloc(VacFs *fs)
           54 {
           55         VacFile *f;
           56 
           57         f = vtmallocz(sizeof(VacFile));
           58         f->ref = 1;
           59         f->fs = fs;
           60         f->boff = NilBlock;
           61         f->mode = fs->mode;
           62         return f;
           63 }
           64 
           65 static void
           66 filefree(VacFile *f)
           67 {
           68         vtfileclose(f->source);
           69         vtfileclose(f->msource);
           70         vdcleanup(&f->dir);
           71         memset(f, ~0, sizeof *f);        /* paranoia */
           72         vtfree(f);
           73 }
           74 
           75 static int
           76 chksource(VacFile *f)
           77 {
           78         if(f->partial)
           79                 return 0;
           80 
           81         if(f->source == nil
           82         || ((f->dir.mode & ModeDir) && f->msource == nil)){
           83                 werrstr(ERemoved);
           84                 return -1;
           85         }
           86         return 0;
           87 }
           88 
           89 static int
           90 filelock(VacFile *f)
           91 {
           92         wlock(&f->lk);
           93         if(chksource(f) < 0){
           94                 wunlock(&f->lk);
           95                 return -1;
           96         }
           97         return 0;
           98 }
           99 
          100 static void
          101 fileunlock(VacFile *f)
          102 {
          103         wunlock(&f->lk);
          104 }
          105 
          106 static int
          107 filerlock(VacFile *f)
          108 {
          109         rlock(&f->lk);
          110         if(chksource(f) < 0){
          111                 runlock(&f->lk);
          112                 return -1;
          113         }
          114         return 0;
          115 }
          116 
          117 static void
          118 filerunlock(VacFile *f)
          119 {
          120         runlock(&f->lk);
          121 }
          122 
          123 /*
          124  * The file metadata, like f->dir and f->ref,
          125  * are synchronized via the parent's lock.
          126  * This is why locking order goes up.
          127  */
          128 static void
          129 filemetalock(VacFile *f)
          130 {
          131         assert(f->up != nil);
          132         wlock(&f->up->lk);
          133 }
          134 
          135 static void
          136 filemetaunlock(VacFile *f)
          137 {
          138         wunlock(&f->up->lk);
          139 }
          140 
          141 uvlong
          142 vacfilegetid(VacFile *f)
          143 {
          144         /* immutable */
          145         return f->qidoffset + f->dir.qid;
          146 }
          147 
          148 uvlong
          149 vacfilegetqidoffset(VacFile *f)
          150 {
          151         return f->qidoffset;
          152 }
          153 
          154 ulong
          155 vacfilegetmcount(VacFile *f)
          156 {
          157         ulong mcount;
          158 
          159         filemetalock(f);
          160         mcount = f->dir.mcount;
          161         filemetaunlock(f);
          162         return mcount;
          163 }
          164 
          165 ulong
          166 vacfilegetmode(VacFile *f)
          167 {
          168         ulong mode;
          169 
          170         filemetalock(f);
          171         mode = f->dir.mode;
          172         filemetaunlock(f);
          173         return mode;
          174 }
          175 
          176 int
          177 vacfileisdir(VacFile *f)
          178 {
          179         /* immutable */
          180         return (f->dir.mode & ModeDir) != 0;
          181 }
          182 
          183 int
          184 vacfileisroot(VacFile *f)
          185 {
          186         return f == f->fs->root;
          187 }
          188 
          189 /*
          190  * The files are reference counted, and while the reference
          191  * is bigger than zero, each file can be found in its parent's
          192  * f->down list (chains via f->next), so that multiple threads
          193  * end up sharing a VacFile* when referring to the same file.
          194  *
          195  * Each VacFile holds a reference to its parent.
          196  */
          197 VacFile*
          198 vacfileincref(VacFile *vf)
          199 {
          200         filemetalock(vf);
          201         assert(vf->ref > 0);
          202         vf->ref++;
          203         filemetaunlock(vf);
          204         return vf;
          205 }
          206 
          207 int
          208 vacfiledecref(VacFile *f)
          209 {
          210         VacFile *p, *q, **qq;
          211 
          212         if(f->up == nil){
          213                 /* never linked in */
          214                 assert(f->ref == 1);
          215                 filefree(f);
          216                 return 0;
          217         }
          218 
          219         filemetalock(f);
          220         f->ref--;
          221         if(f->ref > 0){
          222                 filemetaunlock(f);
          223                 return -1;
          224         }
          225         assert(f->ref == 0);
          226         assert(f->down == nil);
          227 
          228         if(f->source && vtfilelock(f->source, -1) >= 0){
          229                 vtfileflush(f->source);
          230                 vtfileunlock(f->source);
          231         }
          232         if(f->msource && vtfilelock(f->msource, -1) >= 0){
          233                 vtfileflush(f->msource);
          234                 vtfileunlock(f->msource);
          235         }
          236 
          237         /*
          238          * Flush f's directory information to the cache.
          239          */
          240         filemetaflush(f, nil);
          241 
          242         p = f->up;
          243         qq = &p->down;
          244         for(q = *qq; q; q = *qq){
          245                 if(q == f)
          246                         break;
          247                 qq = &q->next;
          248         }
          249         assert(q != nil);
          250         *qq = f->next;
          251 
          252         filemetaunlock(f);
          253         filefree(f);
          254         vacfiledecref(p);
          255         return 0;
          256 }
          257 
          258 
          259 /*
          260  * Construct a vacfile for the root of a vac tree, given the
          261  * venti file for the root information.  That venti file is a
          262  * directory file containing VtEntries for three more venti files:
          263  * the two venti files making up the root directory, and a
          264  * third venti file that would be the metadata half of the
          265  * "root's parent".
          266  *
          267  * Fossil generates slightly different vac files, due to a now
          268  * impossible-to-change bug, which contain a VtEntry
          269  * for just one venti file, that itself contains the expected
          270  * three directory entries.  Sigh.
          271  */
          272 VacFile*
          273 _vacfileroot(VacFs *fs, VtFile *r)
          274 {
          275         int redirected;
          276         char err[ERRMAX];
          277         VtBlock *b;
          278         VtFile *r0, *r1, *r2;
          279         MetaBlock mb;
          280         MetaEntry me;
          281         VacFile *root, *mr;
          282 
          283         redirected = 0;
          284 Top:
          285         b = nil;
          286         root = nil;
          287         mr = nil;
          288         r1 = nil;
          289         r2 = nil;
          290 
          291         if(vtfilelock(r, -1) < 0)
          292                 return nil;
          293         r0 = vtfileopen(r, 0, fs->mode);
          294         if(debug)
          295                 fprint(2, "r0 %p\n", r0);
          296         if(r0 == nil)
          297                 goto Err;
          298         r2 = vtfileopen(r, 2, fs->mode);
          299         if(debug)
          300                 fprint(2, "r2 %p\n", r2);
          301         if(r2 == nil){
          302                 /*
          303                  * some vac files (e.g., from fossil)
          304                  * have an extra layer of indirection.
          305                  */
          306                 rerrstr(err, sizeof err);
          307                 if(!redirected && strstr(err, "not active")){
          308                         redirected = 1;
          309                         vtfileunlock(r);
          310                         r = r0;
          311                         goto Top;
          312                 }
          313                 goto Err;
          314         }
          315         r1 = vtfileopen(r, 1, fs->mode);
          316         if(debug)
          317                 fprint(2, "r1 %p\n", r1);
          318         if(r1 == nil)
          319                 goto Err;
          320 
          321         mr = filealloc(fs);
          322         mr->msource = r2;
          323         r2 = nil;
          324 
          325         root = filealloc(fs);
          326         root->boff = 0;
          327         root->up = mr;
          328         root->source = r0;
          329         r0 = nil;
          330         root->msource = r1;
          331         r1 = nil;
          332 
          333         mr->down = root;
          334         vtfileunlock(r);
          335 
          336         if(vtfilelock(mr->msource, VtOREAD) < 0)
          337                 goto Err1;
          338         b = vtfileblock(mr->msource, 0, VtOREAD);
          339         vtfileunlock(mr->msource);
          340         if(b == nil)
          341                 goto Err1;
          342 
          343         if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
          344                 goto Err1;
          345 
          346         meunpack(&me, &mb, 0);
          347         if(vdunpack(&root->dir, &me) < 0)
          348                 goto Err1;
          349         vtblockput(b);
          350 
          351         return root;
          352 Err:
          353         vtfileunlock(r);
          354 Err1:
          355         vtblockput(b);
          356         if(r0)
          357                 vtfileclose(r0);
          358         if(r1)
          359                 vtfileclose(r1);
          360         if(r2)
          361                 vtfileclose(r2);
          362         if(mr)
          363                 filefree(mr);
          364         if(root)
          365                 filefree(root);
          366 
          367         return nil;
          368 }
          369 
          370 /*
          371  * Vac directories are a sequence of metablocks, each of which
          372  * contains a bunch of metaentries sorted by file name.
          373  * The whole sequence isn't sorted, though, so you still have
          374  * to look at every block to find a given name.
          375  * Dirlookup looks in f for an element name elem.
          376  * It returns a new VacFile with the dir, boff, and mode
          377  * filled in, but the sources (venti files) are not, and f is
          378  * not yet linked into the tree.  These details must be taken
          379  * care of by the caller.
          380  *
          381  * f must be locked, f->msource must not.
          382  */
          383 static VacFile*
          384 dirlookup(VacFile *f, char *elem)
          385 {
          386         int i;
          387         MetaBlock mb;
          388         MetaEntry me;
          389         VtBlock *b;
          390         VtFile *meta;
          391         VacFile *ff;
          392         u32int bo, nb;
          393 
          394         meta = f->msource;
          395         b = nil;
          396         if(vtfilelock(meta, -1) < 0)
          397                 return nil;
          398         nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
          399         for(bo=0; bo<nb; bo++){
          400                 b = vtfileblock(meta, bo, VtOREAD);
          401                 if(b == nil)
          402                         goto Err;
          403                 if(mbunpack(&mb, b->data, meta->dsize) < 0)
          404                         goto Err;
          405                 if(mbsearch(&mb, elem, &i, &me) >= 0){
          406                         ff = filealloc(f->fs);
          407                         if(vdunpack(&ff->dir, &me) < 0){
          408                                 filefree(ff);
          409                                 goto Err;
          410                         }
          411                         ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
          412                         vtfileunlock(meta);
          413                         vtblockput(b);
          414                         ff->boff = bo;
          415                         ff->mode = f->mode;
          416                         return ff;
          417                 }
          418                 vtblockput(b);
          419                 b = nil;
          420         }
          421         werrstr(ENoFile);
          422         /* fall through */
          423 Err:
          424         vtfileunlock(meta);
          425         vtblockput(b);
          426         return nil;
          427 }
          428 
          429 /*
          430  * Open the venti file at offset in the directory f->source.
          431  * f is locked.
          432  */
          433 static VtFile *
          434 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
          435 {
          436         VtFile *r;
          437 
          438         if((r = vtfileopen(f->source, offset, mode)) == nil)
          439                 return nil;
          440         if(r == nil)
          441                 return nil;
          442         if(r->gen != gen){
          443                 werrstr(ERemoved);
          444                 vtfileclose(r);
          445                 return nil;
          446         }
          447         if(r->dir != dir && r->mode != -1){
          448                 werrstr(EBadMeta);
          449                 vtfileclose(r);
          450                 return nil;
          451         }
          452         return r;
          453 }
          454 
          455 VacFile*
          456 vacfilegetparent(VacFile *f)
          457 {
          458         if(vacfileisroot(f))
          459                 return vacfileincref(f);
          460         return vacfileincref(f->up);
          461 }
          462 
          463 /*
          464  * Given an unlocked vacfile (directory) f,
          465  * return the vacfile named elem in f.
          466  * Interprets . and .. as a convenience to callers.
          467  */
          468 VacFile*
          469 vacfilewalk(VacFile *f, char *elem)
          470 {
          471         VacFile *ff;
          472 
          473         if(elem[0] == 0){
          474                 werrstr(EBadPath);
          475                 return nil;
          476         }
          477 
          478         if(!vacfileisdir(f)){
          479                 werrstr(ENotDir);
          480                 return nil;
          481         }
          482 
          483         if(strcmp(elem, ".") == 0)
          484                 return vacfileincref(f);
          485 
          486         if(strcmp(elem, "..") == 0)
          487                 return vacfilegetparent(f);
          488 
          489         if(filelock(f) < 0)
          490                 return nil;
          491 
          492         for(ff = f->down; ff; ff=ff->next){
          493                 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
          494                         ff->ref++;
          495                         goto Exit;
          496                 }
          497         }
          498 
          499         ff = dirlookup(f, elem);
          500         if(ff == nil)
          501                 goto Err;
          502 
          503         if(ff->dir.mode & ModeSnapshot)
          504                 ff->mode = VtOREAD;
          505 
          506         if(vtfilelock(f->source, f->mode) < 0)
          507                 goto Err;
          508         if(ff->dir.mode & ModeDir){
          509                 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
          510                 ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
          511                 if(ff->source == nil || ff->msource == nil)
          512                         goto Err1;
          513         }else{
          514                 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
          515                 if(ff->source == nil)
          516                         goto Err1;
          517         }
          518         vtfileunlock(f->source);
          519 
          520         /* link in and up parent ref count */
          521         ff->next = f->down;
          522         f->down = ff;
          523         ff->up = f;
          524         vacfileincref(f);
          525 Exit:
          526         fileunlock(f);
          527         return ff;
          528 
          529 Err1:
          530         vtfileunlock(f->source);
          531 Err:
          532         fileunlock(f);
          533         if(ff != nil)
          534                 vacfiledecref(ff);
          535         return nil;
          536 }
          537 
          538 /*
          539  * Open a path in the vac file system:
          540  * just walk each element one at a time.
          541  */
          542 VacFile*
          543 vacfileopen(VacFs *fs, char *path)
          544 {
          545         VacFile *f, *ff;
          546         char *p, elem[VtMaxStringSize], *opath;
          547         int n;
          548 
          549         f = fs->root;
          550         vacfileincref(f);
          551         opath = path;
          552         while(*path != 0){
          553                 for(p = path; *p && *p != '/'; p++)
          554                         ;
          555                 n = p - path;
          556                 if(n > 0){
          557                         if(n > VtMaxStringSize){
          558                                 werrstr("%s: element too long", EBadPath);
          559                                 goto Err;
          560                         }
          561                         memmove(elem, path, n);
          562                         elem[n] = 0;
          563                         ff = vacfilewalk(f, elem);
          564                         if(ff == nil){
          565                                 werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
          566                                 goto Err;
          567                         }
          568                         vacfiledecref(f);
          569                         f = ff;
          570                 }
          571                 if(*p == '/')
          572                         p++;
          573                 path = p;
          574         }
          575         return f;
          576 Err:
          577         vacfiledecref(f);
          578         return nil;
          579 }
          580 
          581 /*
          582  * Extract the score for the bn'th block in f.
          583  */
          584 int
          585 vacfileblockscore(VacFile *f, u32int bn, u8int *score)
          586 {
          587         VtFile *s;
          588         uvlong size;
          589         int dsize, ret;
          590 
          591         ret = -1;
          592         if(filerlock(f) < 0)
          593                 return -1;
          594         if(vtfilelock(f->source, VtOREAD) < 0)
          595                 goto out;
          596 
          597         s = f->source;
          598         dsize = s->dsize;
          599         size = vtfilegetsize(s);
          600         if((uvlong)bn*dsize >= size)
          601                 goto out1;
          602         ret = vtfileblockscore(f->source, bn, score);
          603 
          604 out1:
          605         vtfileunlock(f->source);
          606 out:
          607         filerunlock(f);
          608         return ret;
          609 }
          610 
          611 /*
          612  * Read data from f.
          613  */
          614 int
          615 vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
          616 {
          617         int n;
          618 
          619         if(offset < 0){
          620                 werrstr(EBadOffset);
          621                 return -1;
          622         }
          623         if(filerlock(f) < 0)
          624                 return -1;
          625         if(vtfilelock(f->source, VtOREAD) < 0){
          626                 filerunlock(f);
          627                 return -1;
          628         }
          629         n = vtfileread(f->source, buf, cnt, offset);
          630         vtfileunlock(f->source);
          631         filerunlock(f);
          632         return n;
          633 }
          634 
          635 static int
          636 getentry(VtFile *f, VtEntry *e)
          637 {
          638         if(vtfilelock(f, VtOREAD) < 0)
          639                 return -1;
          640         if(vtfilegetentry(f, e) < 0){
          641                 vtfileunlock(f);
          642                 return -1;
          643         }
          644         vtfileunlock(f);
          645         if(vtglobaltolocal(e->score) != NilBlock){
          646                 werrstr("internal error - data not on venti");
          647                 return -1;
          648         }
          649         return 0;
          650 }
          651 
          652 /*
          653  * Get the VtEntries for the data contained in f.
          654  */
          655 int
          656 vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
          657 {
          658         if(filerlock(f) < 0)
          659                 return -1;
          660         if(e && getentry(f->source, e) < 0){
          661                 filerunlock(f);
          662                 return -1;
          663         }
          664         if(me){
          665                 if(f->msource == nil)
          666                         memset(me, 0, sizeof *me);
          667                 else if(getentry(f->msource, me) < 0){
          668                         filerunlock(f);
          669                         return -1;
          670                 }
          671         }
          672         filerunlock(f);
          673         return 0;
          674 }
          675 
          676 /*
          677  * Get the file's size.
          678  */
          679 int
          680 vacfilegetsize(VacFile *f, uvlong *size)
          681 {
          682         if(filerlock(f) < 0)
          683                 return -1;
          684         if(vtfilelock(f->source, VtOREAD) < 0){
          685                 filerunlock(f);
          686                 return -1;
          687         }
          688         *size = vtfilegetsize(f->source);
          689         vtfileunlock(f->source);
          690         filerunlock(f);
          691 
          692         return 0;
          693 }
          694 
          695 /*
          696  * Directory reading.
          697  *
          698  * A VacDirEnum is a buffer containing directory entries.
          699  * Directory entries contain malloced strings and need to
          700  * be cleaned up with vdcleanup.  The invariant in the
          701  * VacDirEnum is that the directory entries between
          702  * vde->i and vde->n are owned by the vde and need to
          703  * be cleaned up if it is closed.  Those from 0 up to vde->i
          704  * have been handed to the reader, and the reader must
          705  * take care of calling vdcleanup as appropriate.
          706  */
          707 VacDirEnum*
          708 vdeopen(VacFile *f)
          709 {
          710         VacDirEnum *vde;
          711         VacFile *p;
          712 
          713         if(!vacfileisdir(f)){
          714                 werrstr(ENotDir);
          715                 return nil;
          716         }
          717 
          718         /*
          719          * There might be changes to this directory's children
          720          * that have not been flushed out into the cache yet.
          721          * Those changes are only available if we look at the
          722          * VacFile structures directory.  But the directory reader
          723          * is going to read the cache blocks directly, so update them.
          724          */
          725         if(filelock(f) < 0)
          726                 return nil;
          727         for(p=f->down; p; p=p->next)
          728                 filemetaflush(p, nil);
          729         fileunlock(f);
          730 
          731         vde = vtmallocz(sizeof(VacDirEnum));
          732         vde->file = vacfileincref(f);
          733 
          734         return vde;
          735 }
          736 
          737 /*
          738  * Figure out the size of the directory entry at offset.
          739  * The rest of the metadata is kept in the data half,
          740  * but since venti has to track the data size anyway,
          741  * we just use that one and avoid updating the directory
          742  * each time the file size changes.
          743  */
          744 static int
          745 direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size)
          746 {
          747         VtBlock *b;
          748         ulong bn;
          749         VtEntry e;
          750         int epb;
          751 
          752         epb = s->dsize/VtEntrySize;
          753         bn = offset/epb;
          754         offset -= bn*epb;
          755 
          756         b = vtfileblock(s, bn, VtOREAD);
          757         if(b == nil)
          758                 goto Err;
          759         if(vtentryunpack(&e, b->data, offset) < 0)
          760                 goto Err;
          761 
          762         /* dangling entries are returned as zero size */
          763         if(!(e.flags & VtEntryActive) || e.gen != gen)
          764                 *size = 0;
          765         else
          766                 *size = e.size;
          767         vtblockput(b);
          768         return 0;
          769 
          770 Err:
          771         vtblockput(b);
          772         return -1;
          773 }
          774 
          775 /*
          776  * Fill in vde with a new batch of directory entries.
          777  */
          778 static int
          779 vdefill(VacDirEnum *vde)
          780 {
          781         int i, n;
          782         VtFile *meta, *source;
          783         MetaBlock mb;
          784         MetaEntry me;
          785         VacFile *f;
          786         VtBlock *b;
          787         VacDir *de;
          788 
          789         /* clean up first */
          790         for(i=vde->i; i<vde->n; i++)
          791                 vdcleanup(vde->buf+i);
          792         vtfree(vde->buf);
          793         vde->buf = nil;
          794         vde->i = 0;
          795         vde->n = 0;
          796 
          797         f = vde->file;
          798 
          799         source = f->source;
          800         meta = f->msource;
          801 
          802         b = vtfileblock(meta, vde->boff, VtOREAD);
          803         if(b == nil)
          804                 goto Err;
          805         if(mbunpack(&mb, b->data, meta->dsize) < 0)
          806                 goto Err;
          807 
          808         n = mb.nindex;
          809         vde->buf = vtmalloc(n * sizeof(VacDir));
          810 
          811         for(i=0; i<n; i++){
          812                 de = vde->buf + i;
          813                 meunpack(&me, &mb, i);
          814                 if(vdunpack(de, &me) < 0)
          815                         goto Err;
          816                 vde->n++;
          817                 if(!(de->mode & ModeDir))
          818                 if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
          819                         goto Err;
          820         }
          821         vde->boff++;
          822         vtblockput(b);
          823         return 0;
          824 Err:
          825         vtblockput(b);
          826         return -1;
          827 }
          828 
          829 /*
          830  * Read a single directory entry from vde into de.
          831  * Returns -1 on error, 0 on EOF, and 1 on success.
          832  * When it returns 1, it becomes the caller's responsibility
          833  * to call vdcleanup(de) to free the strings contained
          834  * inside, or else to call vdunread to give it back.
          835  */
          836 int
          837 vderead(VacDirEnum *vde, VacDir *de)
          838 {
          839         int ret;
          840         VacFile *f;
          841         u32int nb;
          842 
          843         f = vde->file;
          844         if(filerlock(f) < 0)
          845                 return -1;
          846 
          847         if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
          848                 filerunlock(f);
          849                 return -1;
          850         }
          851 
          852         nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
          853 
          854         while(vde->i >= vde->n){
          855                 if(vde->boff >= nb){
          856                         ret = 0;
          857                         goto Return;
          858                 }
          859                 if(vdefill(vde) < 0){
          860                         ret = -1;
          861                         goto Return;
          862                 }
          863         }
          864 
          865         memmove(de, vde->buf + vde->i, sizeof(VacDir));
          866         vde->i++;
          867         ret = 1;
          868 
          869 Return:
          870         vtfileunlock(f->source);
          871         vtfileunlock(f->msource);
          872         filerunlock(f);
          873 
          874         return ret;
          875 }
          876 
          877 /*
          878  * "Unread" the last directory entry that was read,
          879  * so that the next vderead will return the same one.
          880  * If the caller calls vdeunread(vde) it should not call
          881  * vdcleanup on the entry being "unread".
          882  */
          883 int
          884 vdeunread(VacDirEnum *vde)
          885 {
          886         if(vde->i > 0){
          887                 vde->i--;
          888                 return 0;
          889         }
          890         return -1;
          891 }
          892 
          893 /*
          894  * Close the enumerator.
          895  */
          896 void
          897 vdeclose(VacDirEnum *vde)
          898 {
          899         int i;
          900         if(vde == nil)
          901                 return;
          902         /* free the strings */
          903         for(i=vde->i; i<vde->n; i++)
          904                 vdcleanup(vde->buf+i);
          905         vtfree(vde->buf);
          906         vacfiledecref(vde->file);
          907         vtfree(vde);
          908 }
          909 
          910 
          911 /*
          912  * On to mutation.  If the vac file system has been opened
          913  * read-write, then the files and directories can all be edited.
          914  * Changes are kept in the in-memory cache until flushed out
          915  * to venti, so we must be careful to explicitly flush data
          916  * that we're not likely to modify again.
          917  *
          918  * Each VacFile has its own copy of its VacDir directory entry
          919  * in f->dir, but otherwise the cache is the authoratative source
          920  * for data.  Thus, for the most part, it suffices if we just
          921  * call vtfileflushbefore and vtfileflush when we modify things.
          922  * There are a few places where we have to remember to write
          923  * changed VacDirs back into the cache.  If f->dir *is* out of sync,
          924  * then f->dirty should be set.
          925  *
          926  * The metadata in a directory is, to venti, a plain data file,
          927  * but as mentioned above it is actually a sequence of
          928  * MetaBlocks that contain sorted lists of VacDir entries.
          929  * The filemetaxxx routines manipulate that stream.
          930  */
          931 
          932 /*
          933  * Find space in fp for the directory entry dir (not yet written to disk)
          934  * and write it to disk, returning NilBlock on failure,
          935  * or the block number on success.
          936  *
          937  * Start is a suggested block number to try.
          938  * The caller must have filemetalock'ed f and have
          939  * vtfilelock'ed f->up->msource.
          940  */
          941 static u32int
          942 filemetaalloc(VacFile *fp, VacDir *dir, u32int start)
          943 {
          944         u32int nb, bo;
          945         VtBlock *b;
          946         MetaBlock mb;
          947         int nn;
          948         uchar *p;
          949         int i, n;
          950         MetaEntry me;
          951         VtFile *ms;
          952 
          953         ms = fp->msource;
          954         n = vdsize(dir, VacDirVersion);
          955 
          956         /* Look for a block with room for a new entry of size n. */
          957         nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
          958         if(start == NilBlock){
          959                 if(nb > 0)
          960                         start = nb - 1;
          961                 else
          962                         start = 0;
          963         }
          964 
          965         if(start > nb)
          966                 start = nb;
          967         for(bo=start; bo<nb; bo++){
          968                 if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
          969                         goto Err;
          970                 if(mbunpack(&mb, b->data, ms->dsize) < 0)
          971                         goto Err;
          972                 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
          973                 if(n <= nn && mb.nindex < mb.maxindex){
          974                         /* reopen for writing */
          975                         vtblockput(b);
          976                         if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
          977                                 goto Err;
          978                         mbunpack(&mb, b->data, ms->dsize);
          979                         goto Found;
          980                 }
          981                 vtblockput(b);
          982         }
          983 
          984         /* No block found, extend the file by one metablock. */
          985         vtfileflushbefore(ms, nb*(uvlong)ms->dsize);
          986         if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
          987                 goto Err;
          988         vtfilesetsize(ms, (nb+1)*ms->dsize);
          989         mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
          990 
          991 Found:
          992         /* Now we have a block; allocate space to write the entry. */
          993         p = mballoc(&mb, n);
          994         if(p == nil){
          995                 /* mballoc might have changed block */
          996                 mbpack(&mb);
          997                 werrstr(EBadMeta);
          998                 goto Err;
          999         }
         1000 
         1001         /* Figure out where to put the index entry, and write it. */
         1002         mbsearch(&mb, dir->elem, &i, &me);
         1003         assert(me.p == nil);        /* not already there */
         1004         me.p = p;
         1005         me.size = n;
         1006         vdpack(dir, &me, VacDirVersion);
         1007         mbinsert(&mb, i, &me);
         1008         mbpack(&mb);
         1009         vtblockput(b);
         1010         return bo;
         1011 
         1012 Err:
         1013         vtblockput(b);
         1014         return NilBlock;
         1015 }
         1016 
         1017 /*
         1018  * Update f's directory entry in the block cache.
         1019  * We look for the directory entry by name;
         1020  * if we're trying to rename the file, oelem is the old name.
         1021  *
         1022  * Assumes caller has filemetalock'ed f.
         1023  */
         1024 static int
         1025 filemetaflush(VacFile *f, char *oelem)
         1026 {
         1027         int i, n;
         1028         MetaBlock mb;
         1029         MetaEntry me, me2;
         1030         VacFile *fp;
         1031         VtBlock *b;
         1032         u32int bo;
         1033 
         1034         if(!f->dirty)
         1035                 return 0;
         1036 
         1037         if(oelem == nil)
         1038                 oelem = f->dir.elem;
         1039 
         1040         /*
         1041          * Locate f's old metadata in the parent's metadata file.
         1042          * We know which block it was in, but not exactly where
         1043          * in the block.
         1044          */
         1045         fp = f->up;
         1046         if(vtfilelock(fp->msource, -1) < 0)
         1047                 return -1;
         1048         /* can happen if source is clri'ed out from under us */
         1049         if(f->boff == NilBlock)
         1050                 goto Err1;
         1051         b = vtfileblock(fp->msource, f->boff, VtORDWR);
         1052         if(b == nil)
         1053                 goto Err1;
         1054         if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
         1055                 goto Err;
         1056         if(mbsearch(&mb, oelem, &i, &me) < 0)
         1057                 goto Err;
         1058 
         1059         /*
         1060          * Check whether we can resize the entry and keep it
         1061          * in this block.
         1062          */
         1063         n = vdsize(&f->dir, VacDirVersion);
         1064         if(mbresize(&mb, &me, n) >= 0){
         1065                 /* Okay, can be done without moving to another block. */
         1066 
         1067                 /* Remove old data */
         1068                 mbdelete(&mb, i, &me);
         1069 
         1070                 /* Find new location if renaming */
         1071                 if(strcmp(f->dir.elem, oelem) != 0)
         1072                         mbsearch(&mb, f->dir.elem, &i, &me2);
         1073 
         1074                 /* Pack new data into new location. */
         1075                 vdpack(&f->dir, &me, VacDirVersion);
         1076 vdunpack(&f->dir, &me);
         1077                 mbinsert(&mb, i, &me);
         1078                 mbpack(&mb);
         1079 
         1080                 /* Done */
         1081                 vtblockput(b);
         1082                 vtfileunlock(fp->msource);
         1083                 f->dirty = 0;
         1084                 return 0;
         1085         }
         1086 
         1087         /*
         1088          * The entry must be moved to another block.
         1089          * This can only really happen on renames that
         1090          * make the name very long.
         1091          */
         1092 
         1093         /* Allocate a spot in a new block. */
         1094         if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){
         1095                 /* mbresize above might have modified block */
         1096                 mbpack(&mb);
         1097                 goto Err;
         1098         }
         1099         f->boff = bo;
         1100 
         1101         /* Now we're committed.  Delete entry in old block. */
         1102         mbdelete(&mb, i, &me);
         1103         mbpack(&mb);
         1104         vtblockput(b);
         1105         vtfileunlock(fp->msource);
         1106 
         1107         f->dirty = 0;
         1108         return 0;
         1109 
         1110 Err:
         1111         vtblockput(b);
         1112 Err1:
         1113         vtfileunlock(fp->msource);
         1114         return -1;
         1115 }
         1116 
         1117 /*
         1118  * Remove the directory entry for f.
         1119  */
         1120 static int
         1121 filemetaremove(VacFile *f)
         1122 {
         1123         VtBlock *b;
         1124         MetaBlock mb;
         1125         MetaEntry me;
         1126         int i;
         1127         VacFile *fp;
         1128 
         1129         b = nil;
         1130         fp = f->up;
         1131         filemetalock(f);
         1132 
         1133         if(vtfilelock(fp->msource, VtORDWR) < 0)
         1134                 goto Err;
         1135         b = vtfileblock(fp->msource, f->boff, VtORDWR);
         1136         if(b == nil)
         1137                 goto Err;
         1138 
         1139         if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
         1140                 goto Err;
         1141         if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
         1142                 goto Err;
         1143         mbdelete(&mb, i, &me);
         1144         mbpack(&mb);
         1145         vtblockput(b);
         1146         vtfileunlock(fp->msource);
         1147 
         1148         f->removed = 1;
         1149         f->boff = NilBlock;
         1150         f->dirty = 0;
         1151 
         1152         filemetaunlock(f);
         1153         return 0;
         1154 
         1155 Err:
         1156         vtfileunlock(fp->msource);
         1157         vtblockput(b);
         1158         filemetaunlock(f);
         1159         return -1;
         1160 }
         1161 
         1162 /*
         1163  * That was far too much effort for directory entries.
         1164  * Now we can write code that *does* things.
         1165  */
         1166 
         1167 /*
         1168  * Flush all data associated with f out of the cache and onto venti.
         1169  * If recursive is set, flush f's children too.
         1170  * Vacfiledecref knows how to flush source and msource too.
         1171  */
         1172 int
         1173 vacfileflush(VacFile *f, int recursive)
         1174 {
         1175         int ret;
         1176         VacFile **kids, *p;
         1177         int i, nkids;
         1178 
         1179         if(f->mode == VtOREAD)
         1180                 return 0;
         1181 
         1182         ret = 0;
         1183         filemetalock(f);
         1184         if(filemetaflush(f, nil) < 0)
         1185                 ret = -1;
         1186         filemetaunlock(f);
         1187 
         1188         if(filelock(f) < 0)
         1189                 return -1;
         1190 
         1191         /*
         1192          * Lock order prevents us from flushing kids while holding
         1193          * lock, so make a list and then flush without the lock.
         1194          */
         1195         nkids = 0;
         1196         kids = nil;
         1197         if(recursive){
         1198                 nkids = 0;
         1199                 for(p=f->down; p; p=p->next)
         1200                         nkids++;
         1201                 kids = vtmalloc(nkids*sizeof(VacFile*));
         1202                 i = 0;
         1203                 for(p=f->down; p; p=p->next){
         1204                         kids[i++] = p;
         1205                         p->ref++;
         1206                 }
         1207         }
         1208         if(nkids > 0){
         1209                 fileunlock(f);
         1210                 for(i=0; i<nkids; i++){
         1211                         if(vacfileflush(kids[i], 1) < 0)
         1212                                 ret = -1;
         1213                         vacfiledecref(kids[i]);
         1214                 }
         1215                 filelock(f);
         1216         }
         1217         free(kids);
         1218 
         1219         /*
         1220          * Now we can flush our own data.
         1221          */
         1222         vtfilelock(f->source, -1);
         1223         if(vtfileflush(f->source) < 0)
         1224                 ret = -1;
         1225         vtfileunlock(f->source);
         1226         if(f->msource){
         1227                 vtfilelock(f->msource, -1);
         1228                 if(vtfileflush(f->msource) < 0)
         1229                         ret = -1;
         1230                 vtfileunlock(f->msource);
         1231         }
         1232         fileunlock(f);
         1233 
         1234         return ret;
         1235 }
         1236 
         1237 /*
         1238  * Create a new file named elem in fp with the given mode.
         1239  * The mode can be changed later except for the ModeDir bit.
         1240  */
         1241 VacFile*
         1242 vacfilecreate(VacFile *fp, char *elem, ulong mode)
         1243 {
         1244         VacFile *ff;
         1245         VacDir *dir;
         1246         VtFile *pr, *r, *mr;
         1247         int type;
         1248         u32int bo;
         1249 
         1250         if(filelock(fp) < 0)
         1251                 return nil;
         1252 
         1253         /*
         1254          * First, look to see that there's not a file in memory
         1255          * with the same name.
         1256          */
         1257         for(ff = fp->down; ff; ff=ff->next){
         1258                 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
         1259                         ff = nil;
         1260                         werrstr(EExists);
         1261                         goto Err1;
         1262                 }
         1263         }
         1264 
         1265         /*
         1266          * Next check the venti blocks.
         1267          */
         1268         ff = dirlookup(fp, elem);
         1269         if(ff != nil){
         1270                 werrstr(EExists);
         1271                 goto Err1;
         1272         }
         1273 
         1274         /*
         1275          * By the way, you can't create in a read-only file system.
         1276          */
         1277         pr = fp->source;
         1278         if(pr->mode != VtORDWR){
         1279                 werrstr(EReadOnly);
         1280                 goto Err1;
         1281         }
         1282 
         1283         /*
         1284          * Okay, time to actually create something.  Lock the two
         1285          * halves of the directory and create a file.
         1286          */
         1287         if(vtfilelock2(fp->source, fp->msource, -1) < 0)
         1288                 goto Err1;
         1289         ff = filealloc(fp->fs);
         1290         ff->qidoffset = fp->qidoffset;        /* hopefully fp->qidoffset == 0 */
         1291         type = VtDataType;
         1292         if(mode & ModeDir)
         1293                 type = VtDirType;
         1294         mr = nil;
         1295         if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
         1296                 goto Err;
         1297         if(mode & ModeDir)
         1298         if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
         1299                 goto Err;
         1300 
         1301         /*
         1302          * Fill in the directory entry and write it to disk.
         1303          */
         1304         dir = &ff->dir;
         1305         dir->elem = vtstrdup(elem);
         1306         dir->entry = r->offset;
         1307         dir->gen = r->gen;
         1308         if(mode & ModeDir){
         1309                 dir->mentry = mr->offset;
         1310                 dir->mgen = mr->gen;
         1311         }
         1312         dir->size = 0;
         1313         if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
         1314                 goto Err;
         1315         dir->uid = vtstrdup(fp->dir.uid);
         1316         dir->gid = vtstrdup(fp->dir.gid);
         1317         dir->mid = vtstrdup("");
         1318         dir->mtime = time(0L);
         1319         dir->mcount = 0;
         1320         dir->ctime = dir->mtime;
         1321         dir->atime = dir->mtime;
         1322         dir->mode = mode;
         1323         if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
         1324                 goto Err;
         1325 
         1326         /*
         1327          * Now we're committed.
         1328          */
         1329         vtfileunlock(fp->source);
         1330         vtfileunlock(fp->msource);
         1331         ff->source = r;
         1332         ff->msource = mr;
         1333         ff->boff = bo;
         1334 
         1335         /* Link into tree. */
         1336         ff->next = fp->down;
         1337         fp->down = ff;
         1338         ff->up = fp;
         1339         vacfileincref(fp);
         1340 
         1341         fileunlock(fp);
         1342 
         1343         filelock(ff);
         1344         vtfilelock(ff->source, -1);
         1345         vtfileunlock(ff->source);
         1346         fileunlock(ff);
         1347 
         1348         return ff;
         1349 
         1350 Err:
         1351         vtfileunlock(fp->source);
         1352         vtfileunlock(fp->msource);
         1353         if(r){
         1354                 vtfilelock(r, -1);
         1355                 vtfileremove(r);
         1356         }
         1357         if(mr){
         1358                 vtfilelock(mr, -1);
         1359                 vtfileremove(mr);
         1360         }
         1361 Err1:
         1362         if(ff)
         1363                 vacfiledecref(ff);
         1364         fileunlock(fp);
         1365         return nil;
         1366 }
         1367 
         1368 /*
         1369  * Change the size of the file f.
         1370  */
         1371 int
         1372 vacfilesetsize(VacFile *f, uvlong size)
         1373 {
         1374         if(vacfileisdir(f)){
         1375                 werrstr(ENotFile);
         1376                 return -1;
         1377         }
         1378 
         1379         if(filelock(f) < 0)
         1380                 return -1;
         1381 
         1382         if(f->source->mode != VtORDWR){
         1383                 werrstr(EReadOnly);
         1384                 goto Err;
         1385         }
         1386         if(vtfilelock(f->source, -1) < 0)
         1387                 goto Err;
         1388         if(vtfilesetsize(f->source, size) < 0){
         1389                 vtfileunlock(f->source);
         1390                 goto Err;
         1391         }
         1392         vtfileunlock(f->source);
         1393         fileunlock(f);
         1394         return 0;
         1395 
         1396 Err:
         1397         fileunlock(f);
         1398         return -1;
         1399 }
         1400 
         1401 /*
         1402  * Write data to f.
         1403  */
         1404 int
         1405 vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
         1406 {
         1407         if(vacfileisdir(f)){
         1408                 werrstr(ENotFile);
         1409                 return -1;
         1410         }
         1411         if(filelock(f) < 0)
         1412                 return -1;
         1413         if(f->source->mode != VtORDWR){
         1414                 werrstr(EReadOnly);
         1415                 goto Err;
         1416         }
         1417         if(offset < 0){
         1418                 werrstr(EBadOffset);
         1419                 goto Err;
         1420         }
         1421 
         1422         if(vtfilelock(f->source, -1) < 0)
         1423                 goto Err;
         1424         if(f->dir.mode & ModeAppend)
         1425                 offset = vtfilegetsize(f->source);
         1426         if(vtfilewrite(f->source, buf, cnt, offset) != cnt
         1427         || vtfileflushbefore(f->source, offset) < 0){
         1428                 vtfileunlock(f->source);
         1429                 goto Err;
         1430         }
         1431         vtfileunlock(f->source);
         1432         fileunlock(f);
         1433         return cnt;
         1434 
         1435 Err:
         1436         fileunlock(f);
         1437         return -1;
         1438 }
         1439 
         1440 /*
         1441  * Set (!) the VtEntry for the data contained in f.
         1442  * This let's us efficiently copy data from one file to another.
         1443  */
         1444 int
         1445 vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
         1446 {
         1447         int ret;
         1448 
         1449         vacfileflush(f, 0);        /* flush blocks to venti, since we won't see them again */
         1450 
         1451         if(!(e->flags&VtEntryActive)){
         1452                 werrstr("missing entry for source");
         1453                 return -1;
         1454         }
         1455         if(me && !(me->flags&VtEntryActive))
         1456                 me = nil;
         1457         if(f->msource && !me){
         1458                 werrstr("missing entry for msource");
         1459                 return -1;
         1460         }
         1461         if(me && !f->msource){
         1462                 werrstr("no msource to set");
         1463                 return -1;
         1464         }
         1465 
         1466         if(filelock(f) < 0)
         1467                 return -1;
         1468         if(f->source->mode != VtORDWR
         1469         || (f->msource && f->msource->mode != VtORDWR)){
         1470                 werrstr(EReadOnly);
         1471                 fileunlock(f);
         1472                 return -1;
         1473         }
         1474         if(vtfilelock2(f->source, f->msource, -1) < 0){
         1475                 fileunlock(f);
         1476                 return -1;
         1477         }
         1478         ret = 0;
         1479         if(vtfilesetentry(f->source, e) < 0)
         1480                 ret = -1;
         1481         else if(me && vtfilesetentry(f->msource, me) < 0)
         1482                 ret = -1;
         1483 
         1484         vtfileunlock(f->source);
         1485         if(f->msource)
         1486                 vtfileunlock(f->msource);
         1487         fileunlock(f);
         1488         return ret;
         1489 }
         1490 
         1491 /*
         1492  * Get the directory entry for f.
         1493  */
         1494 int
         1495 vacfilegetdir(VacFile *f, VacDir *dir)
         1496 {
         1497         if(filerlock(f) < 0)
         1498                 return -1;
         1499 
         1500         filemetalock(f);
         1501         vdcopy(dir, &f->dir);
         1502         filemetaunlock(f);
         1503 
         1504         if(!vacfileisdir(f)){
         1505                 if(vtfilelock(f->source, VtOREAD) < 0){
         1506                         filerunlock(f);
         1507                         return -1;
         1508                 }
         1509                 dir->size = vtfilegetsize(f->source);
         1510                 vtfileunlock(f->source);
         1511         }
         1512         filerunlock(f);
         1513 
         1514         return 0;
         1515 }
         1516 
         1517 /*
         1518  * Set the directory entry for f.
         1519  */
         1520 int
         1521 vacfilesetdir(VacFile *f, VacDir *dir)
         1522 {
         1523         VacFile *ff;
         1524         char *oelem;
         1525         u32int mask;
         1526         u64int size;
         1527 
         1528         /* can not set permissions for the root */
         1529         if(vacfileisroot(f)){
         1530                 werrstr(ERoot);
         1531                 return -1;
         1532         }
         1533 
         1534         if(filelock(f) < 0)
         1535                 return -1;
         1536         filemetalock(f);
         1537 
         1538         if(f->source->mode != VtORDWR){
         1539                 werrstr(EReadOnly);
         1540                 goto Err;
         1541         }
         1542 
         1543         /* On rename, check new name does not already exist */
         1544         if(strcmp(f->dir.elem, dir->elem) != 0){
         1545                 for(ff = f->up->down; ff; ff=ff->next){
         1546                         if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
         1547                                 werrstr(EExists);
         1548                                 goto Err;
         1549                         }
         1550                 }
         1551                 ff = dirlookup(f->up, dir->elem);
         1552                 if(ff != nil){
         1553                         vacfiledecref(ff);
         1554                         werrstr(EExists);
         1555                         goto Err;
         1556                 }
         1557                 werrstr("");        /* "failed" dirlookup poisoned it */
         1558         }
         1559 
         1560         /* Get ready... */
         1561         if(vtfilelock2(f->source, f->msource, -1) < 0)
         1562                 goto Err;
         1563         if(!vacfileisdir(f)){
         1564                 size = vtfilegetsize(f->source);
         1565                 if(size != dir->size){
         1566                         if(vtfilesetsize(f->source, dir->size) < 0){
         1567                                 vtfileunlock(f->source);
         1568                                 if(f->msource)
         1569                                         vtfileunlock(f->msource);
         1570                                 goto Err;
         1571                         }
         1572                 }
         1573         }
         1574         /* ... now commited to changing it. */
         1575         vtfileunlock(f->source);
         1576         if(f->msource)
         1577                 vtfileunlock(f->msource);
         1578 
         1579         oelem = nil;
         1580         if(strcmp(f->dir.elem, dir->elem) != 0){
         1581                 oelem = f->dir.elem;
         1582                 f->dir.elem = vtstrdup(dir->elem);
         1583         }
         1584 
         1585         if(strcmp(f->dir.uid, dir->uid) != 0){
         1586                 vtfree(f->dir.uid);
         1587                 f->dir.uid = vtstrdup(dir->uid);
         1588         }
         1589 
         1590         if(strcmp(f->dir.gid, dir->gid) != 0){
         1591                 vtfree(f->dir.gid);
         1592                 f->dir.gid = vtstrdup(dir->gid);
         1593         }
         1594 
         1595         if(strcmp(f->dir.mid, dir->mid) != 0){
         1596                 vtfree(f->dir.mid);
         1597                 f->dir.mid = vtstrdup(dir->mid);
         1598         }
         1599 
         1600         f->dir.mtime = dir->mtime;
         1601         f->dir.atime = dir->atime;
         1602 
         1603         mask = ~(ModeDir|ModeSnapshot);
         1604         f->dir.mode &= ~mask;
         1605         f->dir.mode |= mask & dir->mode;
         1606         f->dirty = 1;
         1607 
         1608         if(filemetaflush(f, oelem) < 0){
         1609                 vtfree(oelem);
         1610                 goto Err;        /* that sucks */
         1611         }
         1612         vtfree(oelem);
         1613 
         1614         filemetaunlock(f);
         1615         fileunlock(f);
         1616         return 0;
         1617 
         1618 Err:
         1619         filemetaunlock(f);
         1620         fileunlock(f);
         1621         return -1;
         1622 }
         1623 
         1624 /*
         1625  * Set the qid space.
         1626  */
         1627 int
         1628 vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
         1629 {
         1630         int ret;
         1631 
         1632         if(filelock(f) < 0)
         1633                 return -1;
         1634         if(f->source->mode != VtORDWR){
         1635                 fileunlock(f);
         1636                 werrstr(EReadOnly);
         1637                 return -1;
         1638         }
         1639         filemetalock(f);
         1640         f->dir.qidspace = 1;
         1641         f->dir.qidoffset = offset;
         1642         f->dir.qidmax = max;
         1643         f->dirty = 1;
         1644         ret = filemetaflush(f, nil);
         1645         filemetaunlock(f);
         1646         fileunlock(f);
         1647         return ret;
         1648 }
         1649 
         1650 /*
         1651  * Check that the file is empty, returning 0 if it is.
         1652  * Returns -1 on error (and not being empty is an error).
         1653  */
         1654 static int
         1655 filecheckempty(VacFile *f)
         1656 {
         1657         u32int i, n;
         1658         VtBlock *b;
         1659         MetaBlock mb;
         1660         VtFile *r;
         1661 
         1662         r = f->msource;
         1663         n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
         1664         for(i=0; i<n; i++){
         1665                 b = vtfileblock(r, i, VtOREAD);
         1666                 if(b == nil)
         1667                         return -1;
         1668                 if(mbunpack(&mb, b->data, r->dsize) < 0)
         1669                         goto Err;
         1670                 if(mb.nindex > 0){
         1671                         werrstr(ENotEmpty);
         1672                         goto Err;
         1673                 }
         1674                 vtblockput(b);
         1675         }
         1676         return 0;
         1677 
         1678 Err:
         1679         vtblockput(b);
         1680         return -1;
         1681 }
         1682 
         1683 /*
         1684  * Remove the vac file f.
         1685  */
         1686 int
         1687 vacfileremove(VacFile *f)
         1688 {
         1689         VacFile *ff;
         1690 
         1691         /* Cannot remove the root */
         1692         if(vacfileisroot(f)){
         1693                 werrstr(ERoot);
         1694                 return -1;
         1695         }
         1696 
         1697         if(filelock(f) < 0)
         1698                 return -1;
         1699         if(f->source->mode != VtORDWR){
         1700                 werrstr(EReadOnly);
         1701                 goto Err1;
         1702         }
         1703         if(vtfilelock2(f->source, f->msource, -1) < 0)
         1704                 goto Err1;
         1705         if(vacfileisdir(f) && filecheckempty(f)<0)
         1706                 goto Err;
         1707 
         1708         for(ff=f->down; ff; ff=ff->next)
         1709                 assert(ff->removed);
         1710 
         1711         vtfileremove(f->source);
         1712         f->source = nil;
         1713         if(f->msource){
         1714                 vtfileremove(f->msource);
         1715                 f->msource = nil;
         1716         }
         1717         fileunlock(f);
         1718 
         1719         if(filemetaremove(f) < 0)
         1720                 return -1;
         1721         return 0;
         1722 
         1723 Err:
         1724         vtfileunlock(f->source);
         1725         if(f->msource)
         1726                 vtfileunlock(f->msource);
         1727 Err1:
         1728         fileunlock(f);
         1729         return -1;
         1730 }
         1731 
         1732 /*
         1733  * Vac file system format.
         1734  */
         1735 static char EBadVacFormat[] = "bad format for vac file";
         1736 
         1737 static VacFs *
         1738 vacfsalloc(VtConn *z, int bsize, ulong cachemem, int mode)
         1739 {
         1740         VacFs *fs;
         1741 
         1742         fs = vtmallocz(sizeof(VacFs));
         1743         fs->z = z;
         1744         fs->bsize = bsize;
         1745         fs->mode = mode;
         1746         fs->cache = vtcachealloc(z, cachemem);
         1747         return fs;
         1748 }
         1749 
         1750 static int
         1751 readscore(int fd, uchar score[VtScoreSize])
         1752 {
         1753         char buf[45], *pref;
         1754         int n;
         1755 
         1756         n = readn(fd, buf, sizeof(buf)-1);
         1757         if(n < sizeof(buf)-1) {
         1758                 werrstr("short read");
         1759                 return -1;
         1760         }
         1761         buf[n] = 0;
         1762 
         1763         if(vtparsescore(buf, &pref, score) < 0){
         1764                 werrstr(EBadVacFormat);
         1765                 return -1;
         1766         }
         1767         if(pref==nil || strcmp(pref, "vac") != 0) {
         1768                 werrstr("not a vac file");
         1769                 return -1;
         1770         }
         1771         return 0;
         1772 }
         1773 
         1774 VacFs*
         1775 vacfsopen(VtConn *z, char *file, int mode, ulong cachemem)
         1776 {
         1777         int fd;
         1778         uchar score[VtScoreSize];
         1779         char *prefix;
         1780 
         1781         if(vtparsescore(file, &prefix, score) >= 0){
         1782                 if(prefix == nil || strcmp(prefix, "vac") != 0){
         1783                         werrstr("not a vac file");
         1784                         return nil;
         1785                 }
         1786         }else{
         1787                 fd = open(file, OREAD);
         1788                 if(fd < 0)
         1789                         return nil;
         1790                 if(readscore(fd, score) < 0){
         1791                         close(fd);
         1792                         return nil;
         1793                 }
         1794                 close(fd);
         1795         }
         1796 if(debug) fprint(2, "vacfsopen %V\n", score);
         1797         return vacfsopenscore(z, score, mode, cachemem);
         1798 }
         1799 
         1800 VacFs*
         1801 vacfsopenscore(VtConn *z, u8int *score, int mode, ulong cachemem)
         1802 {
         1803         VacFs *fs;
         1804         int n;
         1805         VtRoot rt;
         1806         uchar buf[VtRootSize];
         1807         VacFile *root;
         1808         VtFile *r;
         1809         VtEntry e;
         1810 
         1811         n = vtread(z, score, VtRootType, buf, VtRootSize);
         1812         if(n < 0) {
         1813 if(debug) fprint(2, "read %r\n");
         1814                 return nil;
         1815         }
         1816         if(n != VtRootSize){
         1817                 werrstr("vtread on root too short");
         1818 if(debug) fprint(2, "size %d\n", n);
         1819                 return nil;
         1820         }
         1821 
         1822         if(vtrootunpack(&rt, buf) < 0) {
         1823 if(debug) fprint(2, "unpack: %r\n");
         1824                 return nil;
         1825         }
         1826 
         1827         if(strcmp(rt.type, "vac") != 0) {
         1828 if(debug) fprint(2, "bad type %s\n", rt.type);
         1829                 werrstr("not a vac root");
         1830                 return nil;
         1831         }
         1832 
         1833         fs = vacfsalloc(z, rt.blocksize, cachemem, mode);
         1834         memmove(fs->score, score, VtScoreSize);
         1835         fs->mode = mode;
         1836 
         1837         memmove(e.score, rt.score, VtScoreSize);
         1838         e.gen = 0;
         1839 
         1840         // Don't waste cache memory on pointer blocks
         1841         // when rt.blocksize is large.
         1842         e.psize = (rt.blocksize/VtEntrySize)*VtEntrySize;
         1843         if(e.psize > 60000)
         1844                 e.psize = (60000/VtEntrySize)*VtEntrySize;
         1845 
         1846         e.dsize = rt.blocksize;
         1847 if(debug) fprint(2, "openscore %d psize %d dsize %d\n", (int)rt.blocksize, (int)e.psize, (int)e.dsize);
         1848         e.type = VtDirType;
         1849         e.flags = VtEntryActive;
         1850         e.size = 3*VtEntrySize;
         1851 
         1852         root = nil;
         1853         if((r = vtfileopenroot(fs->cache, &e)) == nil)
         1854                 goto Err;
         1855         if(debug)
         1856                 fprint(2, "r %p\n", r);
         1857         root = _vacfileroot(fs, r);
         1858         if(debug)
         1859                 fprint(2, "root %p\n", root);
         1860         vtfileclose(r);
         1861         if(root == nil)
         1862                 goto Err;
         1863         fs->root = root;
         1864         return fs;
         1865 Err:
         1866         if(root)
         1867                 vacfiledecref(root);
         1868         vacfsclose(fs);
         1869         return nil;
         1870 }
         1871 
         1872 int
         1873 vacfsmode(VacFs *fs)
         1874 {
         1875         return fs->mode;
         1876 }
         1877 
         1878 VacFile*
         1879 vacfsgetroot(VacFs *fs)
         1880 {
         1881         return vacfileincref(fs->root);
         1882 }
         1883 
         1884 int
         1885 vacfsgetblocksize(VacFs *fs)
         1886 {
         1887         return fs->bsize;
         1888 }
         1889 
         1890 int
         1891 vacfsgetscore(VacFs *fs, u8int *score)
         1892 {
         1893         memmove(score, fs->score, VtScoreSize);
         1894         return 0;
         1895 }
         1896 
         1897 int
         1898 _vacfsnextqid(VacFs *fs, uvlong *qid)
         1899 {
         1900         ++fs->qid;
         1901         *qid = fs->qid;
         1902         return 0;
         1903 }
         1904 
         1905 void
         1906 vacfsjumpqid(VacFs *fs, uvlong step)
         1907 {
         1908         fs->qid += step;
         1909 }
         1910 
         1911 /*
         1912  * Set *maxqid to the maximum qid expected in this file system.
         1913  * In newer vac archives, the maximum qid is stored in the
         1914  * qidspace VacDir annotation.  In older vac archives, the root
         1915  * got created last, so it had the maximum qid.
         1916  */
         1917 int
         1918 vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
         1919 {
         1920         VacDir vd;
         1921 
         1922         if(vacfilegetdir(fs->root, &vd) < 0)
         1923                 return -1;
         1924         if(vd.qidspace)
         1925                 *maxqid = vd.qidmax;
         1926         else
         1927                 *maxqid = vd.qid;
         1928         vdcleanup(&vd);
         1929         return 0;
         1930 }
         1931 
         1932 
         1933 void
         1934 vacfsclose(VacFs *fs)
         1935 {
         1936         if(fs->root)
         1937                 vacfiledecref(fs->root);
         1938         fs->root = nil;
         1939         vtcachefree(fs->cache);
         1940         vtfree(fs);
         1941 }
         1942 
         1943 /*
         1944  * Create a fresh vac fs.
         1945  */
         1946 VacFs *
         1947 vacfscreate(VtConn *z, int bsize, ulong cachemem)
         1948 {
         1949         VacFs *fs;
         1950         VtFile *f;
         1951         uchar buf[VtEntrySize], metascore[VtScoreSize];
         1952         VtEntry e;
         1953         VtBlock *b;
         1954         MetaBlock mb;
         1955         VacDir vd;
         1956         MetaEntry me;
         1957         int psize;
         1958 
         1959         if((fs = vacfsalloc(z, bsize, cachemem, VtORDWR)) == nil)
         1960                 return nil;
         1961 
         1962         /*
         1963          * Fake up an empty vac fs.
         1964          */
         1965         psize = bsize/VtScoreSize*VtScoreSize;
         1966         if(psize > 60000)
         1967                 psize = 60000/VtScoreSize*VtScoreSize;
         1968 if(debug) fprint(2, "create bsize %d psize %d\n", bsize, psize);
         1969 
         1970         f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
         1971         if(f == nil)
         1972                 sysfatal("vtfilecreateroot: %r");
         1973         vtfilelock(f, VtORDWR);
         1974 
         1975         /* Write metablock containing root directory VacDir. */
         1976         b = vtcacheallocblock(fs->cache, VtDataType, bsize);
         1977         mbinit(&mb, b->data, bsize, bsize/BytesPerEntry);
         1978         memset(&vd, 0, sizeof vd);
         1979         vd.elem = "/";
         1980         vd.mode = 0777|ModeDir;
         1981         vd.uid = "vac";
         1982         vd.gid = "vac";
         1983         vd.mid = "";
         1984         me.size = vdsize(&vd, VacDirVersion);
         1985         me.p = mballoc(&mb, me.size);
         1986         vdpack(&vd, &me, VacDirVersion);
         1987         mbinsert(&mb, 0, &me);
         1988         mbpack(&mb);
         1989         vtblockwrite(b);
         1990         memmove(metascore, b->score, VtScoreSize);
         1991         vtblockput(b);
         1992 
         1993         /* First entry: empty venti directory stream. */
         1994         memset(&e, 0, sizeof e);
         1995         e.flags = VtEntryActive;
         1996         e.psize = psize;
         1997         e.dsize = bsize;
         1998         e.type = VtDirType;
         1999         memmove(e.score, vtzeroscore, VtScoreSize);
         2000         vtentrypack(&e, buf, 0);
         2001         vtfilewrite(f, buf, VtEntrySize, 0);
         2002 
         2003         /* Second entry: empty metadata stream. */
         2004         e.type = VtDataType;
         2005         vtentrypack(&e, buf, 0);
         2006         vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
         2007 
         2008         /* Third entry: metadata stream with root directory. */
         2009         memmove(e.score, metascore, VtScoreSize);
         2010         e.size = bsize;
         2011         vtentrypack(&e, buf, 0);
         2012         vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
         2013 
         2014         vtfileflush(f);
         2015         vtfileunlock(f);
         2016 
         2017         /* Now open it as a vac fs. */
         2018         fs->root = _vacfileroot(fs, f);
         2019         if(fs->root == nil){
         2020                 werrstr("vacfileroot: %r");
         2021                 vacfsclose(fs);
         2022                 return nil;
         2023         }
         2024 
         2025         return fs;
         2026 }
         2027 
         2028 int
         2029 vacfssync(VacFs *fs)
         2030 {
         2031         uchar buf[1024];
         2032         VtEntry e;
         2033         VtFile *f;
         2034         VtRoot root;
         2035 
         2036         /* Sync the entire vacfs to disk. */
         2037         if(vacfileflush(fs->root, 1) < 0)
         2038                 return -1;
         2039         if(vtfilelock(fs->root->up->msource, -1) < 0)
         2040                 return -1;
         2041         if(vtfileflush(fs->root->up->msource) < 0){
         2042                 vtfileunlock(fs->root->up->msource);
         2043                 return -1;
         2044         }
         2045         vtfileunlock(fs->root->up->msource);
         2046 
         2047         /* Prepare the dir stream for the root block. */
         2048         if(getentry(fs->root->source, &e) < 0)
         2049                 return -1;
         2050         vtentrypack(&e, buf, 0);
         2051         if(getentry(fs->root->msource, &e) < 0)
         2052                 return -1;
         2053         vtentrypack(&e, buf, 1);
         2054         if(getentry(fs->root->up->msource, &e) < 0)
         2055                 return -1;
         2056         vtentrypack(&e, buf, 2);
         2057 
         2058         f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
         2059         vtfilelock(f, VtORDWR);
         2060         if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
         2061         || vtfileflush(f) < 0){
         2062                 vtfileunlock(f);
         2063                 vtfileclose(f);
         2064                 return -1;
         2065         }
         2066         vtfileunlock(f);
         2067         if(getentry(f, &e) < 0){
         2068                 vtfileclose(f);
         2069                 return -1;
         2070         }
         2071         vtfileclose(f);
         2072 
         2073         /* Build a root block. */
         2074         memset(&root, 0, sizeof root);
         2075         strcpy(root.type, "vac");
         2076         strcpy(root.name, fs->name);
         2077         memmove(root.score, e.score, VtScoreSize);
         2078         root.blocksize = fs->bsize;
         2079         memmove(root.prev, fs->score, VtScoreSize);
         2080         vtrootpack(&root, buf);
         2081         if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
         2082                 werrstr("writing root: %r");
         2083                 return -1;
         2084         }
         2085         if(vtsync(fs->z) < 0)
         2086                 return -1;
         2087         return 0;
         2088 }
         2089 
         2090 int
         2091 vacfiledsize(VacFile *f)
         2092 {
         2093         VtEntry e;
         2094 
         2095         if(vacfilegetentries(f,&e,nil) < 0)
         2096                 return -1;
         2097         return e.dsize;
         2098 }
         2099 
         2100 /*
         2101  * Does block b of f have the same SHA1 hash as the n bytes at buf?
         2102  */
         2103 int
         2104 sha1matches(VacFile *f, ulong b, uchar *buf, int n)
         2105 {
         2106         uchar fscore[VtScoreSize];
         2107         uchar bufscore[VtScoreSize];
         2108 
         2109         if(vacfileblockscore(f, b, fscore) < 0)
         2110                 return 0;
         2111         n = vtzerotruncate(VtDataType, buf, n);
         2112         sha1(buf, n, bufscore, nil);
         2113         if(memcmp(bufscore, fscore, VtScoreSize) == 0)
         2114                 return 1;
         2115         return 0;
         2116 }