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 (23784B)
       ---
            1 /*
            2  * Manage tree of VtFiles stored in the block cache.
            3  *
            4  * The single point of truth for the info about the VtFiles themselves
            5  * is the block data.  Because of this, there is no explicit locking of
            6  * VtFile structures, and indeed there may be more than one VtFile
            7  * structure for a given Venti file.  They synchronize through the
            8  * block cache.
            9  *
           10  * This is a bit simpler than fossil because there are no epochs
           11  * or tags or anything else.  Just mutable local blocks and immutable
           12  * Venti blocks.
           13  */
           14 
           15 #include <u.h>
           16 #include <libc.h>
           17 #include <venti.h>
           18 
           19 #define MaxBlock (1UL<<31)
           20 
           21 static char ENotDir[] = "walk in non-directory";
           22 static char ETooBig[] = "file too big";
           23 /* static char EBadAddr[] = "bad address"; */
           24 static char ELabelMismatch[] = "label mismatch";
           25 
           26 static int        sizetodepth(uvlong s, int psize, int dsize);
           27 static VtBlock         *fileload(VtFile *r, VtEntry *e);
           28 static int        shrinkdepth(VtFile*, VtBlock*, VtEntry*, int);
           29 static int        shrinksize(VtFile*, VtEntry*, uvlong);
           30 static int        growdepth(VtFile*, VtBlock*, VtEntry*, int);
           31 
           32 #define ISLOCKED(r)        ((r)->b != nil)
           33 #define DEPTH(t)        ((t)&VtTypeDepthMask)
           34 
           35 static VtFile *
           36 vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, u32int offset, int mode)
           37 {
           38         int epb;
           39         VtEntry e;
           40         VtFile *r;
           41 
           42         assert(p==nil || ISLOCKED(p));
           43 
           44         if(p == nil){
           45                 assert(offset == 0);
           46                 epb = 1;
           47         }else
           48                 epb = p->dsize / VtEntrySize;
           49 
           50         if(b->type != VtDirType){
           51                 werrstr("bad block type %#uo", b->type);
           52                 return nil;
           53         }
           54 
           55         /*
           56          * a non-active entry is the only thing that
           57          * can legitimately happen here. all the others
           58          * get prints.
           59          */
           60         if(vtentryunpack(&e, b->data, offset % epb) < 0){
           61                 fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySize, b->data+VtEntrySize*(offset%epb));
           62                 return nil;
           63         }
           64         if(!(e.flags & VtEntryActive)){
           65                 werrstr("entry not active");
           66                 return nil;
           67         }
           68 
           69         if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){
           70                 fprint(2, "depth %ud size %llud psize %lud dsize %lud\n",
           71                         DEPTH(e.type), e.size, e.psize, e.dsize);
           72                 werrstr("bad depth");
           73                 return nil;
           74         }
           75 
           76         r = vtmallocz(sizeof(VtFile));
           77         r->c = c;
           78         r->bsize = b->size;
           79         r->mode = mode;
           80         r->dsize = e.dsize;
           81         r->psize = e.psize;
           82         r->gen = e.gen;
           83         r->dir = (e.type & VtTypeBaseMask) == VtDirType;
           84         r->ref = 1;
           85         r->parent = p;
           86         if(p){
           87                 qlock(&p->lk);
           88                 assert(mode == VtOREAD || p->mode == VtORDWR);
           89                 p->ref++;
           90                 qunlock(&p->lk);
           91         }else{
           92                 assert(b->addr != NilBlock);
           93                 r->local = 1;
           94         }
           95         memmove(r->score, b->score, VtScoreSize);
           96         r->offset = offset;
           97         r->epb = epb;
           98 
           99         return r;
          100 }
          101 
          102 VtFile *
          103 vtfileroot(VtCache *c, u32int addr, int mode)
          104 {
          105         VtFile *r;
          106         VtBlock *b;
          107 
          108         b = vtcachelocal(c, addr, VtDirType);
          109         if(b == nil)
          110                 return nil;
          111         r = vtfilealloc(c, b, nil, 0, mode);
          112         vtblockput(b);
          113         return r;
          114 }
          115 
          116 VtFile*
          117 vtfileopenroot(VtCache *c, VtEntry *e)
          118 {
          119         VtBlock *b;
          120         VtFile *f;
          121 
          122         b = vtcacheallocblock(c, VtDirType, VtEntrySize);
          123         if(b == nil)
          124                 return nil;
          125 
          126         vtentrypack(e, b->data, 0);
          127         f = vtfilealloc(c, b, nil, 0, VtORDWR);
          128         vtblockput(b);
          129         return f;
          130 }
          131 
          132 VtFile *
          133 vtfilecreateroot(VtCache *c, int psize, int dsize, int type)
          134 {
          135         VtEntry e;
          136 
          137         memset(&e, 0, sizeof e);
          138         e.flags = VtEntryActive;
          139         e.psize = psize;
          140         e.dsize = dsize;
          141         e.type = type;
          142         memmove(e.score, vtzeroscore, VtScoreSize);
          143 
          144         return vtfileopenroot(c, &e);
          145 }
          146 
          147 VtFile *
          148 vtfileopen(VtFile *r, u32int offset, int mode)
          149 {
          150         ulong bn;
          151         VtBlock *b;
          152 
          153         assert(ISLOCKED(r));
          154         if(!r->dir){
          155                 werrstr(ENotDir);
          156                 return nil;
          157         }
          158 
          159         bn = offset/(r->dsize/VtEntrySize);
          160 
          161         b = vtfileblock(r, bn, mode);
          162         if(b == nil)
          163                 return nil;
          164         r = vtfilealloc(r->c, b, r, offset, mode);
          165         vtblockput(b);
          166         return r;
          167 }
          168 
          169 VtFile*
          170 vtfilecreate(VtFile *r, int psize, int dsize, int type)
          171 {
          172         return _vtfilecreate(r, -1, psize, dsize, type);
          173 }
          174 
          175 VtFile*
          176 _vtfilecreate(VtFile *r, int o, int psize, int dsize, int type)
          177 {
          178         int i;
          179         VtBlock *b;
          180         u32int bn, size;
          181         VtEntry e;
          182         int epb;
          183         VtFile *rr;
          184         u32int offset;
          185 
          186         assert(ISLOCKED(r));
          187         assert(type == VtDirType || type == VtDataType);
          188 
          189         if(!r->dir){
          190                 werrstr(ENotDir);
          191                 return nil;
          192         }
          193 
          194         epb = r->dsize/VtEntrySize;
          195 
          196         size = vtfilegetdirsize(r);
          197         /*
          198          * look at a random block to see if we can find an empty entry
          199          */
          200         if(o == -1){
          201                 offset = lnrand(size+1);
          202                 offset -= offset % epb;
          203         }else
          204                 offset = o;
          205 
          206         /* try the given block and then try the last block */
          207         for(;;){
          208                 bn = offset/epb;
          209                 b = vtfileblock(r, bn, VtORDWR);
          210                 if(b == nil)
          211                         return nil;
          212                 for(i=offset%r->epb; i<epb; i++){
          213                         if(vtentryunpack(&e, b->data, i) < 0)
          214                                 continue;
          215                         if((e.flags&VtEntryActive) == 0 && e.gen != ~0)
          216                                 goto Found;
          217                 }
          218                 vtblockput(b);
          219                 if(offset == size){
          220                         fprint(2, "vtfilecreate: cannot happen\n");
          221                         werrstr("vtfilecreate: cannot happen");
          222                         return nil;
          223                 }
          224                 offset = size;
          225         }
          226 
          227 Found:
          228         /* found an entry - gen already set */
          229         e.psize = psize;
          230         e.dsize = dsize;
          231         e.flags = VtEntryActive;
          232         e.type = type;
          233         e.size = 0;
          234         memmove(e.score, vtzeroscore, VtScoreSize);
          235         vtentrypack(&e, b->data, i);
          236 
          237         offset = bn*epb + i;
          238         if(offset+1 > size){
          239                 if(vtfilesetdirsize(r, offset+1) < 0){
          240                         vtblockput(b);
          241                         return nil;
          242                 }
          243         }
          244 
          245         rr = vtfilealloc(r->c, b, r, offset, VtORDWR);
          246         vtblockput(b);
          247         return rr;
          248 }
          249 
          250 static int
          251 vtfilekill(VtFile *r, int doremove)
          252 {
          253         VtEntry e;
          254         VtBlock *b;
          255 
          256         assert(ISLOCKED(r));
          257         b = fileload(r, &e);
          258         if(b == nil)
          259                 return -1;
          260 
          261         if(doremove==0 && e.size == 0){
          262                 /* already truncated */
          263                 vtblockput(b);
          264                 return 0;
          265         }
          266 
          267         if(doremove){
          268                 if(e.gen != ~0)
          269                         e.gen++;
          270                 e.dsize = 0;
          271                 e.psize = 0;
          272                 e.flags = 0;
          273         }else
          274                 e.flags &= ~VtEntryLocal;
          275         e.type = 0;
          276         e.size = 0;
          277         memmove(e.score, vtzeroscore, VtScoreSize);
          278         vtentrypack(&e, b->data, r->offset % r->epb);
          279         vtblockput(b);
          280 
          281         if(doremove){
          282                 vtfileunlock(r);
          283                 vtfileclose(r);
          284         }
          285 
          286         return 0;
          287 }
          288 
          289 int
          290 vtfileremove(VtFile *r)
          291 {
          292         return vtfilekill(r, 1);
          293 }
          294 
          295 int
          296 vtfiletruncate(VtFile *r)
          297 {
          298         return vtfilekill(r, 0);
          299 }
          300 
          301 uvlong
          302 vtfilegetsize(VtFile *r)
          303 {
          304         VtEntry e;
          305         VtBlock *b;
          306 
          307         assert(ISLOCKED(r));
          308         b = fileload(r, &e);
          309         if(b == nil)
          310                 return ~(uvlong)0;
          311         vtblockput(b);
          312 
          313         return e.size;
          314 }
          315 
          316 static int
          317 shrinksize(VtFile *r, VtEntry *e, uvlong size)
          318 {
          319         int i, depth, bsiz, type, isdir, ppb;
          320         uvlong ptrsz;
          321         uchar score[VtScoreSize];
          322         VtBlock *b;
          323 
          324         b = vtcacheglobal(r->c, e->score, e->type, r->dsize);
          325         if(b == nil)
          326                 return -1;
          327 
          328         ptrsz = e->dsize;
          329         ppb = e->psize/VtScoreSize;
          330         type = e->type;
          331         depth = DEPTH(type);
          332         for(i=0; i+1<depth; i++)
          333                 ptrsz *= ppb;
          334 
          335         isdir = r->dir;
          336         while(DEPTH(type) > 0){
          337                 if(b->addr == NilBlock){
          338                         /* not worth copying the block just so we can zero some of it */
          339                         vtblockput(b);
          340                         return -1;
          341                 }
          342 
          343                 /*
          344                  * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes
          345                  */
          346 
          347                 /* zero the pointers to unnecessary blocks */
          348                 i = (size+ptrsz-1)/ptrsz;
          349                 for(; i<ppb; i++)
          350                         memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSize);
          351 
          352                 /* recurse (go around again) on the partially necessary block */
          353                 i = size/ptrsz;
          354                 size = size%ptrsz;
          355                 if(size == 0){
          356                         vtblockput(b);
          357                         return 0;
          358                 }
          359                 ptrsz /= ppb;
          360                 type--;
          361                 memmove(score, b->data+i*VtScoreSize, VtScoreSize);
          362                 vtblockput(b);
          363                 if(type == VtDataType || type == VtDirType)
          364                         bsiz = r->dsize;
          365                 else
          366                         bsiz = r->psize;
          367                 b = vtcacheglobal(r->c, score, type, bsiz);
          368                 if(b == nil)
          369                         return -1;
          370         }
          371 
          372         if(b->addr == NilBlock){
          373                 vtblockput(b);
          374                 return -1;
          375         }
          376 
          377         /*
          378          * No one ever truncates BtDir blocks.
          379          */
          380         if(depth==0 && !isdir && e->dsize > size)
          381                 memset(b->data+size, 0, e->dsize-size);
          382         vtblockput(b);
          383         return 0;
          384 }
          385 
          386 int
          387 vtfilesetsize(VtFile *r, u64int size)
          388 {
          389         int depth, edepth;
          390         VtEntry e;
          391         VtBlock *b;
          392 
          393         assert(ISLOCKED(r));
          394         if(size == 0)
          395                 return vtfiletruncate(r);
          396 
          397         if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){
          398                 werrstr(ETooBig);
          399                 return -1;
          400         }
          401 
          402         b = fileload(r, &e);
          403         if(b == nil)
          404                 return -1;
          405 
          406         /* quick out */
          407         if(e.size == size){
          408                 vtblockput(b);
          409                 return 0;
          410         }
          411 
          412         depth = sizetodepth(size, e.psize, e.dsize);
          413         edepth = DEPTH(e.type);
          414         if(depth < edepth){
          415                 if(shrinkdepth(r, b, &e, depth) < 0){
          416                         vtblockput(b);
          417                         return -1;
          418                 }
          419         }else if(depth > edepth){
          420                 if(growdepth(r, b, &e, depth) < 0){
          421                         vtblockput(b);
          422                         return -1;
          423                 }
          424         }
          425 
          426         if(size < e.size)
          427                 shrinksize(r, &e, size);
          428 
          429         e.size = size;
          430         vtentrypack(&e, b->data, r->offset % r->epb);
          431         vtblockput(b);
          432 
          433         return 0;
          434 }
          435 
          436 int
          437 vtfilesetdirsize(VtFile *r, u32int ds)
          438 {
          439         uvlong size;
          440         int epb;
          441 
          442         assert(ISLOCKED(r));
          443         epb = r->dsize/VtEntrySize;
          444 
          445         size = (uvlong)r->dsize*(ds/epb);
          446         size += VtEntrySize*(ds%epb);
          447         return vtfilesetsize(r, size);
          448 }
          449 
          450 u32int
          451 vtfilegetdirsize(VtFile *r)
          452 {
          453         ulong ds;
          454         uvlong size;
          455         int epb;
          456 
          457         assert(ISLOCKED(r));
          458         epb = r->dsize/VtEntrySize;
          459 
          460         size = vtfilegetsize(r);
          461         ds = epb*(size/r->dsize);
          462         ds += (size%r->dsize)/VtEntrySize;
          463         return ds;
          464 }
          465 
          466 int
          467 vtfilegetentry(VtFile *r, VtEntry *e)
          468 {
          469         VtBlock *b;
          470 
          471         assert(ISLOCKED(r));
          472         b = fileload(r, e);
          473         if(b == nil)
          474                 return -1;
          475         vtblockput(b);
          476 
          477         return 0;
          478 }
          479 
          480 int
          481 vtfilesetentry(VtFile *r, VtEntry *e)
          482 {
          483         VtBlock *b;
          484         VtEntry ee;
          485 
          486         assert(ISLOCKED(r));
          487         b = fileload(r, &ee);
          488         if(b == nil)
          489                 return -1;
          490         vtentrypack(e, b->data, r->offset % r->epb);
          491         vtblockput(b);
          492         return 0;
          493 }
          494 
          495 static VtBlock *
          496 blockwalk(VtFile *r, VtBlock *p, int index, VtCache *c, int mode, VtEntry *e)
          497 {
          498         VtBlock *b;
          499         int type, size;
          500         uchar *score;
          501 
          502         switch(p->type){
          503         case VtDataType:
          504                 assert(0);
          505         case VtDirType:
          506                 type = e->type;
          507                 score = e->score;
          508                 break;
          509         default:
          510                 type = p->type - 1;
          511                 score = p->data+index*VtScoreSize;
          512                 break;
          513         }
          514 /*print("walk from %V/%d ty %d to %V ty %d\n", p->score, index, p->type, score, type); */
          515 
          516         if(type == VtDirType || type == VtDataType)
          517                 size = r->dsize;
          518         else
          519                 size = r->psize;
          520         if(mode == VtOWRITE && vtglobaltolocal(score) == NilBlock){
          521                 b = vtcacheallocblock(c, type, size);
          522                 if(b)
          523                         goto HaveCopy;
          524         }else
          525                 b = vtcacheglobal(c, score, type, size);
          526 
          527         if(b == nil || mode == VtOREAD)
          528                 return b;
          529 
          530         if(vtglobaltolocal(b->score) != NilBlock)
          531                 return b;
          532 
          533         /*
          534          * Copy on write.
          535          */
          536         e->flags |= VtEntryLocal;
          537 
          538         b = vtblockcopy(b/*, e->tag, fs->ehi, fs->elo*/);
          539         if(b == nil)
          540                 return nil;
          541 
          542 HaveCopy:
          543         if(p->type == VtDirType){
          544                 memmove(e->score, b->score, VtScoreSize);
          545                 vtentrypack(e, p->data, index);
          546         }else{
          547                 memmove(p->data+index*VtScoreSize, b->score, VtScoreSize);
          548         }
          549         return b;
          550 }
          551 
          552 /*
          553  * Change the depth of the VtFile r.
          554  * The entry e for r is contained in block p.
          555  */
          556 static int
          557 growdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth)
          558 {
          559         VtBlock *b, *bb;
          560 
          561         assert(ISLOCKED(r));
          562         assert(depth <= VtPointerDepth);
          563 
          564         b = vtcacheglobal(r->c, e->score, e->type, r->dsize);
          565         if(b == nil)
          566                 return -1;
          567 
          568         /*
          569          * Keep adding layers until we get to the right depth
          570          * or an error occurs.
          571          */
          572         while(DEPTH(e->type) < depth){
          573                 bb = vtcacheallocblock(r->c, e->type+1, r->psize);
          574                 if(bb == nil)
          575                         break;
          576                 memmove(bb->data, b->score, VtScoreSize);
          577                 memmove(e->score, bb->score, VtScoreSize);
          578                 e->type++;
          579                 e->flags |= VtEntryLocal;
          580                 vtblockput(b);
          581                 b = bb;
          582         }
          583 
          584         vtentrypack(e, p->data, r->offset % r->epb);
          585         vtblockput(b);
          586 
          587         if(DEPTH(e->type) == depth)
          588                 return 0;
          589         return -1;
          590 }
          591 
          592 static int
          593 shrinkdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth)
          594 {
          595         VtBlock *b, *nb, *ob, *rb;
          596 
          597         assert(ISLOCKED(r));
          598         assert(depth <= VtPointerDepth);
          599 
          600         rb = vtcacheglobal(r->c, e->score, e->type, r->psize);
          601         if(rb == nil)
          602                 return -1;
          603 
          604         /*
          605          * Walk down to the new root block.
          606          * We may stop early, but something is better than nothing.
          607          */
          608 
          609         ob = nil;
          610         b = rb;
          611         for(; DEPTH(e->type) > depth; e->type--){
          612                 nb = vtcacheglobal(r->c, b->data, e->type-1, r->psize);
          613                 if(nb == nil)
          614                         break;
          615                 if(ob!=nil && ob!=rb)
          616                         vtblockput(ob);
          617                 ob = b;
          618                 b = nb;
          619         }
          620 
          621         if(b == rb){
          622                 vtblockput(rb);
          623                 return 0;
          624         }
          625 
          626         /*
          627          * Right now, e points at the root block rb, b is the new root block,
          628          * and ob points at b.  To update:
          629          *
          630          *        (i) change e to point at b
          631          *        (ii) zero the pointer ob -> b
          632          *        (iii) free the root block
          633          *
          634          * p (the block containing e) must be written before
          635          * anything else.
          636           */
          637 
          638         /* (i) */
          639         memmove(e->score, b->score, VtScoreSize);
          640         vtentrypack(e, p->data, r->offset % r->epb);
          641 
          642         /* (ii) */
          643         memmove(ob->data, vtzeroscore, VtScoreSize);
          644 
          645         /* (iii) */
          646         vtblockput(rb);
          647         if(ob!=nil && ob!=rb)
          648                 vtblockput(ob);
          649         vtblockput(b);
          650 
          651         if(DEPTH(e->type) == depth)
          652                 return 0;
          653         return -1;
          654 }
          655 
          656 static int
          657 mkindices(VtEntry *e, u32int bn, int *index)
          658 {
          659         int i, np;
          660 
          661         memset(index, 0, (VtPointerDepth+1)*sizeof(int));
          662 
          663         np = e->psize/VtScoreSize;
          664         for(i=0; bn > 0; i++){
          665                 if(i >= VtPointerDepth){
          666                         werrstr("bad address 0x%lux", (ulong)bn);
          667                         return -1;
          668                 }
          669                 index[i] = bn % np;
          670                 bn /= np;
          671         }
          672         return i;
          673 }
          674 
          675 VtBlock *
          676 vtfileblock(VtFile *r, u32int bn, int mode)
          677 {
          678         VtBlock *b, *bb;
          679         int index[VtPointerDepth+1];
          680         VtEntry e;
          681         int i;
          682         int m;
          683 
          684         assert(ISLOCKED(r));
          685         assert(bn != NilBlock);
          686 
          687         b = fileload(r, &e);
          688         if(b == nil)
          689                 return nil;
          690 
          691         i = mkindices(&e, bn, index);
          692         if(i < 0)
          693                 goto Err;
          694         if(i > DEPTH(e.type)){
          695                 if(mode == VtOREAD){
          696                         werrstr("bad address 0x%lux", (ulong)bn);
          697                         goto Err;
          698                 }
          699                 index[i] = 0;
          700                 if(growdepth(r, b, &e, i) < 0)
          701                         goto Err;
          702         }
          703 
          704 assert(b->type == VtDirType);
          705 
          706         index[DEPTH(e.type)] = r->offset % r->epb;
          707 
          708         /* mode for intermediate block */
          709         m = mode;
          710         if(m == VtOWRITE)
          711                 m = VtORDWR;
          712 
          713         for(i=DEPTH(e.type); i>=0; i--){
          714                 bb = blockwalk(r, b, index[i], r->c, i==0 ? mode : m, &e);
          715                 if(bb == nil)
          716                         goto Err;
          717                 vtblockput(b);
          718                 b = bb;
          719         }
          720         b->pc = getcallerpc(&r);
          721         return b;
          722 Err:
          723         vtblockput(b);
          724         return nil;
          725 }
          726 
          727 int
          728 vtfileblockscore(VtFile *r, u32int bn, uchar score[VtScoreSize])
          729 {
          730         VtBlock *b, *bb;
          731         int index[VtPointerDepth+1];
          732         VtEntry e;
          733         int i;
          734 
          735         assert(ISLOCKED(r));
          736         assert(bn != NilBlock);
          737 
          738         b = fileload(r, &e);
          739         if(b == nil)
          740                 return -1;
          741 
          742         if(DEPTH(e.type) == 0){
          743                 memmove(score, e.score, VtScoreSize);
          744                 vtblockput(b);
          745                 return 0;
          746         }
          747 
          748         i = mkindices(&e, bn, index);
          749         if(i < 0){
          750                 vtblockput(b);
          751                 return -1;
          752         }
          753         if(i > DEPTH(e.type)){
          754                 memmove(score, vtzeroscore, VtScoreSize);
          755                 vtblockput(b);
          756                 return 0;
          757         }
          758 
          759         index[DEPTH(e.type)] = r->offset % r->epb;
          760 
          761         for(i=DEPTH(e.type); i>=1; i--){
          762                 bb = blockwalk(r, b, index[i], r->c, VtOREAD, &e);
          763                 if(bb == nil)
          764                         goto Err;
          765                 vtblockput(b);
          766                 b = bb;
          767                 if(memcmp(b->score, vtzeroscore, VtScoreSize) == 0)
          768                         break;
          769         }
          770 
          771         memmove(score, b->data+index[0]*VtScoreSize, VtScoreSize);
          772         vtblockput(b);
          773         return 0;
          774 
          775 Err:
          776         vtblockput(b);
          777         return -1;
          778 }
          779 
          780 void
          781 vtfileincref(VtFile *r)
          782 {
          783         qlock(&r->lk);
          784         r->ref++;
          785         qunlock(&r->lk);
          786 }
          787 
          788 void
          789 vtfileclose(VtFile *r)
          790 {
          791         if(r == nil)
          792                 return;
          793         qlock(&r->lk);
          794         r->ref--;
          795         if(r->ref){
          796                 qunlock(&r->lk);
          797                 return;
          798         }
          799         assert(r->ref == 0);
          800         qunlock(&r->lk);
          801         if(r->parent)
          802                 vtfileclose(r->parent);
          803         memset(r, ~0, sizeof(*r));
          804         vtfree(r);
          805 }
          806 
          807 /*
          808  * Retrieve the block containing the entry for r.
          809  * If a snapshot has happened, we might need
          810  * to get a new copy of the block.  We avoid this
          811  * in the common case by caching the score for
          812  * the block and the last epoch in which it was valid.
          813  *
          814  * We use r->mode to tell the difference between active
          815  * file system VtFiles (VtORDWR) and VtFiles for the
          816  * snapshot file system (VtOREAD).
          817  */
          818 static VtBlock*
          819 fileloadblock(VtFile *r, int mode)
          820 {
          821         char e[ERRMAX];
          822         u32int addr;
          823         VtBlock *b;
          824 
          825         switch(r->mode){
          826         default:
          827                 assert(0);
          828         case VtORDWR:
          829                 assert(r->mode == VtORDWR);
          830                 if(r->local == 1){
          831                         b = vtcacheglobal(r->c, r->score, VtDirType, r->bsize);
          832                         if(b == nil)
          833                                 return nil;
          834                         b->pc = getcallerpc(&r);
          835                         return b;
          836                 }
          837                 assert(r->parent != nil);
          838                 if(vtfilelock(r->parent, VtORDWR) < 0)
          839                         return nil;
          840                 b = vtfileblock(r->parent, r->offset/r->epb, VtORDWR);
          841                 vtfileunlock(r->parent);
          842                 if(b == nil)
          843                         return nil;
          844                 memmove(r->score, b->score, VtScoreSize);
          845                 r->local = 1;
          846                 return b;
          847 
          848         case VtOREAD:
          849                 if(mode == VtORDWR){
          850                         werrstr("read/write lock of read-only file");
          851                         return nil;
          852                 }
          853                 addr = vtglobaltolocal(r->score);
          854                 if(addr == NilBlock)
          855                         return vtcacheglobal(r->c, r->score, VtDirType, r->bsize);
          856 
          857                 b = vtcachelocal(r->c, addr, VtDirType);
          858                 if(b)
          859                         return b;
          860 
          861                 /*
          862                  * If it failed because the epochs don't match, the block has been
          863                  * archived and reclaimed.  Rewalk from the parent and get the
          864                  * new pointer.  This can't happen in the VtORDWR case
          865                  * above because blocks in the current epoch don't get
          866                  * reclaimed.  The fact that we're VtOREAD means we're
          867                  * a snapshot.  (Or else the file system is read-only, but then
          868                  * the archiver isn't going around deleting blocks.)
          869                  */
          870                 rerrstr(e, sizeof e);
          871                 if(strcmp(e, ELabelMismatch) == 0){
          872                         if(vtfilelock(r->parent, VtOREAD) < 0)
          873                                 return nil;
          874                         b = vtfileblock(r->parent, r->offset/r->epb, VtOREAD);
          875                         vtfileunlock(r->parent);
          876                         if(b){
          877                                 fprint(2, "vtfilealloc: lost %V found %V\n",
          878                                         r->score, b->score);
          879                                 memmove(r->score, b->score, VtScoreSize);
          880                                 return b;
          881                         }
          882                 }
          883                 return nil;
          884         }
          885 }
          886 
          887 int
          888 vtfilelock(VtFile *r, int mode)
          889 {
          890         VtBlock *b;
          891 
          892         if(mode == -1)
          893                 mode = r->mode;
          894 
          895         b = fileloadblock(r, mode);
          896         if(b == nil)
          897                 return -1;
          898         /*
          899          * The fact that we are holding b serves as the
          900          * lock entitling us to write to r->b.
          901          */
          902         assert(r->b == nil);
          903         r->b = b;
          904         b->pc = getcallerpc(&r);
          905         return 0;
          906 }
          907 
          908 /*
          909  * Lock two (usually sibling) VtFiles.  This needs special care
          910  * because the Entries for both vtFiles might be in the same block.
          911  * We also try to lock blocks in left-to-right order within the tree.
          912  */
          913 int
          914 vtfilelock2(VtFile *r, VtFile *rr, int mode)
          915 {
          916         VtBlock *b, *bb;
          917 
          918         if(rr == nil)
          919                 return vtfilelock(r, mode);
          920 
          921         if(mode == -1)
          922                 mode = r->mode;
          923 
          924         if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->epb){
          925                 b = fileloadblock(r, mode);
          926                 if(b == nil)
          927                         return -1;
          928                 vtblockduplock(b);
          929                 bb = b;
          930         }else if(r->parent==rr->parent || r->offset > rr->offset){
          931                 bb = fileloadblock(rr, mode);
          932                 b = fileloadblock(r, mode);
          933         }else{
          934                 b = fileloadblock(r, mode);
          935                 bb = fileloadblock(rr, mode);
          936         }
          937         if(b == nil || bb == nil){
          938                 if(b)
          939                         vtblockput(b);
          940                 if(bb)
          941                         vtblockput(bb);
          942                 return -1;
          943         }
          944 
          945         /*
          946          * The fact that we are holding b and bb serves
          947          * as the lock entitling us to write to r->b and rr->b.
          948          */
          949         r->b = b;
          950         rr->b = bb;
          951         b->pc = getcallerpc(&r);
          952         bb->pc = getcallerpc(&r);
          953         return 0;
          954 }
          955 
          956 void
          957 vtfileunlock(VtFile *r)
          958 {
          959         VtBlock *b;
          960 
          961         if(r->b == nil){
          962                 fprint(2, "vtfileunlock: already unlocked\n");
          963                 abort();
          964         }
          965         b = r->b;
          966         r->b = nil;
          967         vtblockput(b);
          968 }
          969 
          970 static VtBlock*
          971 fileload(VtFile *r, VtEntry *e)
          972 {
          973         VtBlock *b;
          974 
          975         assert(ISLOCKED(r));
          976         b = r->b;
          977         if(vtentryunpack(e, b->data, r->offset % r->epb) < 0)
          978                 return nil;
          979         vtblockduplock(b);
          980         return b;
          981 }
          982 
          983 static int
          984 sizetodepth(uvlong s, int psize, int dsize)
          985 {
          986         int np;
          987         int d;
          988 
          989         /* determine pointer depth */
          990         np = psize/VtScoreSize;
          991         s = (s + dsize - 1)/dsize;
          992         for(d = 0; s > 1; d++)
          993                 s = (s + np - 1)/np;
          994         return d;
          995 }
          996 
          997 long
          998 vtfileread(VtFile *f, void *data, long count, vlong offset)
          999 {
         1000         int frag;
         1001         VtBlock *b;
         1002         VtEntry e;
         1003 
         1004         assert(ISLOCKED(f));
         1005 
         1006         vtfilegetentry(f, &e);
         1007         if(count == 0)
         1008                 return 0;
         1009         if(count < 0 || offset < 0){
         1010                 werrstr("vtfileread: bad offset or count");
         1011                 return -1;
         1012         }
         1013         if(offset >= e.size)
         1014                 return 0;
         1015 
         1016         if(offset+count > e.size)
         1017                 count = e.size - offset;
         1018 
         1019         frag = offset % e.dsize;
         1020         if(frag+count > e.dsize)
         1021                 count = e.dsize - frag;
         1022 
         1023         b = vtfileblock(f, offset/e.dsize, VtOREAD);
         1024         if(b == nil)
         1025                 return -1;
         1026 
         1027         memmove(data, b->data+frag, count);
         1028         vtblockput(b);
         1029         return count;
         1030 }
         1031 
         1032 static long
         1033 filewrite1(VtFile *f, void *data, long count, vlong offset)
         1034 {
         1035         int frag, m;
         1036         VtBlock *b;
         1037         VtEntry e;
         1038 
         1039         vtfilegetentry(f, &e);
         1040         if(count < 0 || offset < 0){
         1041                 werrstr("vtfilewrite: bad offset or count");
         1042                 return -1;
         1043         }
         1044 
         1045         frag = offset % e.dsize;
         1046         if(frag+count > e.dsize)
         1047                 count = e.dsize - frag;
         1048 
         1049         m = VtORDWR;
         1050         if(frag == 0 && count == e.dsize)
         1051                 m = VtOWRITE;
         1052 
         1053         b = vtfileblock(f, offset/e.dsize, m);
         1054         if(b == nil)
         1055                 return -1;
         1056 
         1057         memmove(b->data+frag, data, count);
         1058         if(m == VtOWRITE && frag+count < e.dsize)
         1059                 memset(b->data+frag+count, 0, e.dsize-frag-count);
         1060 
         1061         if(offset+count > e.size){
         1062                 vtfilegetentry(f, &e);
         1063                 e.size = offset+count;
         1064                 vtfilesetentry(f, &e);
         1065         }
         1066 
         1067         vtblockput(b);
         1068         return count;
         1069 }
         1070 
         1071 long
         1072 vtfilewrite(VtFile *f, void *data, long count, vlong offset)
         1073 {
         1074         long tot, m;
         1075 
         1076         assert(ISLOCKED(f));
         1077 
         1078         tot = 0;
         1079         m = 0;
         1080         while(tot < count){
         1081                 m = filewrite1(f, (char*)data+tot, count-tot, offset+tot);
         1082                 if(m <= 0)
         1083                         break;
         1084                 tot += m;
         1085         }
         1086         if(tot==0)
         1087                 return m;
         1088         return tot;
         1089 }
         1090 
         1091 static int
         1092 flushblock(VtCache *c, VtBlock *bb, uchar score[VtScoreSize], int ppb, int epb,
         1093         int type)
         1094 {
         1095         u32int addr;
         1096         VtBlock *b;
         1097         VtEntry e;
         1098         int i;
         1099 
         1100         addr = vtglobaltolocal(score);
         1101         if(addr == NilBlock)
         1102                 return 0;
         1103 
         1104         if(bb){
         1105                 b = bb;
         1106                 if(memcmp(b->score, score, VtScoreSize) != 0)
         1107                         abort();
         1108         }else
         1109                 if((b = vtcachelocal(c, addr, type)) == nil)
         1110                         return -1;
         1111 
         1112         switch(type){
         1113         case VtDataType:
         1114                 break;
         1115 
         1116         case VtDirType:
         1117                 for(i=0; i<epb; i++){
         1118                         if(vtentryunpack(&e, b->data, i) < 0)
         1119                                 goto Err;
         1120                         if(!(e.flags&VtEntryActive))
         1121                                 continue;
         1122                         if(flushblock(c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize,
         1123                                 e.type) < 0)
         1124                                 goto Err;
         1125                         vtentrypack(&e, b->data, i);
         1126                 }
         1127                 break;
         1128 
         1129         default:        /* VtPointerTypeX */
         1130                 for(i=0; i<ppb; i++){
         1131                         if(flushblock(c, nil, b->data+VtScoreSize*i, ppb, epb, type-1) < 0)
         1132                                 goto Err;
         1133                 }
         1134                 break;
         1135         }
         1136 
         1137         if(vtblockwrite(b) < 0)
         1138                 goto Err;
         1139         memmove(score, b->score, VtScoreSize);
         1140         if(b != bb)
         1141                 vtblockput(b);
         1142         return 0;
         1143 
         1144 Err:
         1145         if(b != bb)
         1146                 vtblockput(b);
         1147         return -1;
         1148 }
         1149 
         1150 int
         1151 vtfileflush(VtFile *f)
         1152 {
         1153         int ret;
         1154         VtBlock *b;
         1155         VtEntry e;
         1156 
         1157         assert(ISLOCKED(f));
         1158         b = fileload(f, &e);
         1159         if(!(e.flags&VtEntryLocal)){
         1160                 vtblockput(b);
         1161                 return 0;
         1162         }
         1163 
         1164         ret = flushblock(f->c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize,
         1165                 e.type);
         1166         if(ret < 0){
         1167                 vtblockput(b);
         1168                 return -1;
         1169         }
         1170 
         1171         vtentrypack(&e, b->data, f->offset % f->epb);
         1172         vtblockput(b);
         1173         return 0;
         1174 }
         1175 
         1176 int
         1177 vtfileflushbefore(VtFile *r, u64int offset)
         1178 {
         1179         VtBlock *b, *bb;
         1180         VtEntry e;
         1181         int i, base, depth, ppb, epb, doflush;
         1182         int index[VtPointerDepth+1], j, ret;
         1183         VtBlock *bi[VtPointerDepth+2];
         1184         uchar *score;
         1185 
         1186         assert(ISLOCKED(r));
         1187         if(offset == 0)
         1188                 return 0;
         1189 
         1190         b = fileload(r, &e);
         1191         if(b == nil)
         1192                 return -1;
         1193 
         1194         /*
         1195          * compute path through tree for the last written byte and the next one.
         1196          */
         1197         ret = -1;
         1198         memset(bi, 0, sizeof bi);
         1199         depth = DEPTH(e.type);
         1200         bi[depth+1] = b;
         1201         i = mkindices(&e, (offset-1)/e.dsize, index);
         1202         if(i < 0)
         1203                 goto Err;
         1204         if(i > depth)
         1205                 goto Err;
         1206         ppb = e.psize / VtScoreSize;
         1207         epb = e.dsize / VtEntrySize;
         1208 
         1209         /*
         1210          * load the blocks along the last written byte
         1211          */
         1212         index[depth] = r->offset % r->epb;
         1213         for(i=depth; i>=0; i--){
         1214                 bb = blockwalk(r, b, index[i], r->c, VtORDWR, &e);
         1215                 if(bb == nil)
         1216                         goto Err;
         1217                 bi[i] = bb;
         1218                 b = bb;
         1219         }
         1220         ret = 0;
         1221 
         1222         /*
         1223          * walk up the path from leaf to root, flushing anything that
         1224          * has been finished.
         1225          */
         1226         base = e.type&~VtTypeDepthMask;
         1227         for(i=0; i<=depth; i++){
         1228                 doflush = 0;
         1229                 if(i == 0){
         1230                         /* leaf: data or dir block */
         1231                         if(offset%e.dsize == 0)
         1232                                 doflush = 1;
         1233                 }else{
         1234                         /*
         1235                          * interior node: pointer blocks.
         1236                          * specifically, b = bi[i] is a block whose index[i-1]'th entry
         1237                          * points at bi[i-1].
         1238                          */
         1239                         b = bi[i];
         1240 
         1241                         /*
         1242                          * the index entries up to but not including index[i-1] point at
         1243                          * finished blocks, so flush them for sure.
         1244                          */
         1245                         for(j=0; j<index[i-1]; j++)
         1246                                 if(flushblock(r->c, nil, b->data+j*VtScoreSize, ppb, epb, base+i-1) < 0)
         1247                                         goto Err;
         1248 
         1249                         /*
         1250                          * if index[i-1] is the last entry in the block and is global
         1251                          * (i.e. the kid is flushed), then we can flush this block.
         1252                          */
         1253                         if(j==ppb-1 && vtglobaltolocal(b->data+j*VtScoreSize)==NilBlock)
         1254                                 doflush = 1;
         1255                 }
         1256                 if(doflush){
         1257                         if(i == depth)
         1258                                 score = e.score;
         1259                         else
         1260                                 score = bi[i+1]->data+index[i]*VtScoreSize;
         1261                         if(flushblock(r->c, bi[i], score, ppb, epb, base+i) < 0)
         1262                                 goto Err;
         1263                 }
         1264         }
         1265 
         1266 Err:
         1267         /* top: entry.  do this always so that the score is up-to-date */
         1268         vtentrypack(&e, bi[depth+1]->data, index[depth]);
         1269         for(i=0; i<nelem(bi); i++)
         1270                 if(bi[i])
         1271                         vtblockput(bi[i]);
         1272         return ret;
         1273 }