123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * Manage tree of VtFiles stored in the block cache.
- *
- * The single point of truth for the info about the VtFiles themselves
- * is the block data. Because of this, there is no explicit locking of
- * VtFile structures, and indeed there may be more than one VtFile
- * structure for a given Venti file. They synchronize through the
- * block cache.
- *
- * This is a bit simpler than fossil because there are no epochs
- * or tags or anything else. Just mutable local blocks and immutable
- * Venti blocks.
- */
- #include <u.h>
- #include <libc.h>
- #include <venti.h>
- #define MaxBlock (1UL<<31)
- static char ENotDir[] = "walk in non-directory";
- static char ETooBig[] = "file too big";
- /* static char EBadAddr[] = "bad address"; */
- static char ELabelMismatch[] = "label mismatch";
- static int sizetodepth(uint64_t s, int psize, int dsize);
- static VtBlock *fileload(VtFile *r, VtEntry *e);
- static int shrinkdepth(VtFile*, VtBlock*, VtEntry*, int);
- static int shrinksize(VtFile*, VtEntry*, uint64_t);
- static int growdepth(VtFile*, VtBlock*, VtEntry*, int);
- #define ISLOCKED(r) ((r)->b != nil)
- #define DEPTH(t) ((t)&VtTypeDepthMask)
- static VtFile *
- vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, uint32_t offset, int mode)
- {
- int epb;
- uint32_t size;
- VtEntry e;
- VtFile *r;
- assert(p==nil || ISLOCKED(p));
- if(p == nil){
- assert(offset == 0);
- epb = 1;
- }else
- epb = p->dsize / VtEntrySize;
- if(b->type != VtDirType){
- werrstr("bad block type %#uo", b->type);
- return nil;
- }
- /*
- * a non-active entry is the only thing that
- * can legitimately happen here. all the others
- * get prints.
- */
- if(vtentryunpack(&e, b->data, offset % epb) < 0){
- fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySize, b->data+VtEntrySize*(offset%epb));
- return nil;
- }
- if(!(e.flags & VtEntryActive)){
- werrstr("entry not active");
- return nil;
- }
- if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){
- fprint(2, "depth %ud size %llud psize %ud dsize %ud\n",
- DEPTH(e.type), e.size, e.psize, e.dsize);
- werrstr("bad depth");
- return nil;
- }
- size = vtcacheblocksize(c);
- if(e.dsize > size || e.psize > size){
- werrstr("block sizes %ud, %ud bigger than cache block size %ud",
- e.psize, e.dsize, size);
- return nil;
- }
- r = vtmallocz(sizeof(VtFile));
- r->c = c;
- r->mode = mode;
- r->dsize = e.dsize;
- r->psize = e.psize;
- r->gen = e.gen;
- r->dir = (e.type & VtTypeBaseMask) == VtDirType;
- r->ref = 1;
- r->parent = p;
- if(p){
- qlock(&p->lk);
- assert(mode == VtOREAD || p->mode == VtORDWR);
- p->ref++;
- qunlock(&p->lk);
- }else{
- assert(b->addr != NilBlock);
- r->local = 1;
- }
- memmove(r->score, b->score, VtScoreSize);
- r->offset = offset;
- r->epb = epb;
- return r;
- }
- VtFile *
- vtfileroot(VtCache *c, uint32_t addr, int mode)
- {
- VtFile *r;
- VtBlock *b;
- b = vtcachelocal(c, addr, VtDirType);
- if(b == nil)
- return nil;
- r = vtfilealloc(c, b, nil, 0, mode);
- vtblockput(b);
- return r;
- }
- VtFile*
- vtfileopenroot(VtCache *c, VtEntry *e)
- {
- VtBlock *b;
- VtFile *f;
- b = vtcacheallocblock(c, VtDirType);
- if(b == nil)
- return nil;
- vtentrypack(e, b->data, 0);
- f = vtfilealloc(c, b, nil, 0, VtORDWR);
- vtblockput(b);
- return f;
- }
- VtFile *
- vtfilecreateroot(VtCache *c, int psize, int dsize, int type)
- {
- VtEntry e;
- memset(&e, 0, sizeof e);
- e.flags = VtEntryActive;
- e.psize = psize;
- e.dsize = dsize;
- e.type = type;
- memmove(e.score, vtzeroscore, VtScoreSize);
- return vtfileopenroot(c, &e);
- }
- VtFile *
- vtfileopen(VtFile *r, uint32_t offset, int mode)
- {
- uint32_t bn;
- VtBlock *b;
- assert(ISLOCKED(r));
- if(!r->dir){
- werrstr(ENotDir);
- return nil;
- }
- bn = offset/(r->dsize/VtEntrySize);
- b = vtfileblock(r, bn, mode);
- if(b == nil)
- return nil;
- r = vtfilealloc(r->c, b, r, offset, mode);
- vtblockput(b);
- return r;
- }
- VtFile*
- vtfilecreate(VtFile *r, int psize, int dsize, int type)
- {
- return _vtfilecreate(r, -1, psize, dsize, type);
- }
- VtFile*
- _vtfilecreate(VtFile *r, int o, int psize, int dsize, int type)
- {
- int i;
- VtBlock *b;
- uint32_t bn, size;
- VtEntry e;
- int epb;
- VtFile *rr;
- uint32_t offset;
-
- assert(ISLOCKED(r));
- assert(psize <= VtMaxLumpSize);
- assert(dsize <= VtMaxLumpSize);
- assert(type == VtDirType || type == VtDataType);
- if(!r->dir){
- werrstr(ENotDir);
- return nil;
- }
- epb = r->dsize/VtEntrySize;
- size = vtfilegetdirsize(r);
- /*
- * look at a random block to see if we can find an empty entry
- */
- if(o == -1){
- offset = lnrand(size+1);
- offset -= offset % epb;
- }else
- offset = o;
- /* try the given block and then try the last block */
- for(;;){
- bn = offset/epb;
- b = vtfileblock(r, bn, VtORDWR);
- if(b == nil)
- return nil;
- for(i=offset%r->epb; i<epb; i++){
- if(vtentryunpack(&e, b->data, i) < 0)
- continue;
- if((e.flags&VtEntryActive) == 0 && e.gen != ~0)
- goto Found;
- }
- vtblockput(b);
- if(offset == size){
- fprint(2, "vtfilecreate: cannot happen\n");
- werrstr("vtfilecreate: cannot happen");
- return nil;
- }
- offset = size;
- }
- Found:
- /* found an entry - gen already set */
- e.psize = psize;
- e.dsize = dsize;
- e.flags = VtEntryActive;
- e.type = type;
- e.size = 0;
- memmove(e.score, vtzeroscore, VtScoreSize);
- vtentrypack(&e, b->data, i);
- offset = bn*epb + i;
- if(offset+1 > size){
- if(vtfilesetdirsize(r, offset+1) < 0){
- vtblockput(b);
- return nil;
- }
- }
- rr = vtfilealloc(r->c, b, r, offset, VtORDWR);
- vtblockput(b);
- return rr;
- }
- static int
- vtfilekill(VtFile *r, int doremove)
- {
- VtEntry e;
- VtBlock *b;
- assert(ISLOCKED(r));
- b = fileload(r, &e);
- if(b == nil)
- return -1;
- if(doremove==0 && e.size == 0){
- /* already truncated */
- vtblockput(b);
- return 0;
- }
- if(doremove){
- if(e.gen != ~0)
- e.gen++;
- e.dsize = 0;
- e.psize = 0;
- e.flags = 0;
- }else
- e.flags &= ~VtEntryLocal;
- e.type = 0;
- e.size = 0;
- memmove(e.score, vtzeroscore, VtScoreSize);
- vtentrypack(&e, b->data, r->offset % r->epb);
- vtblockput(b);
- if(doremove){
- vtfileunlock(r);
- vtfileclose(r);
- }
- return 0;
- }
- int
- vtfileremove(VtFile *r)
- {
- return vtfilekill(r, 1);
- }
- int
- vtfiletruncate(VtFile *r)
- {
- return vtfilekill(r, 0);
- }
- uint64_t
- vtfilegetsize(VtFile *r)
- {
- VtEntry e;
- VtBlock *b;
- assert(ISLOCKED(r));
- b = fileload(r, &e);
- if(b == nil)
- return ~(uint64_t)0;
- vtblockput(b);
- return e.size;
- }
- static int
- shrinksize(VtFile *r, VtEntry *e, uint64_t size)
- {
- int i, depth, type, isdir, ppb;
- uint64_t ptrsz;
- uint8_t score[VtScoreSize];
- VtBlock *b;
- b = vtcacheglobal(r->c, e->score, e->type);
- if(b == nil)
- return -1;
- ptrsz = e->dsize;
- ppb = e->psize/VtScoreSize;
- type = e->type;
- depth = DEPTH(type);
- for(i=0; i+1<depth; i++)
- ptrsz *= ppb;
- isdir = r->dir;
- while(depth > 0){
- if(b->addr == NilBlock){
- /* not worth copying the block just so we can zero some of it */
- vtblockput(b);
- return -1;
- }
- /*
- * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes
- */
- /* zero the pointers to unnecessary blocks */
- i = (size+ptrsz-1)/ptrsz;
- for(; i<ppb; i++)
- memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSize);
- /* recurse (go around again) on the partially necessary block */
- i = size/ptrsz;
- size = size%ptrsz;
- if(size == 0){
- vtblockput(b);
- return 0;
- }
- ptrsz /= ppb;
- type--;
- memmove(score, b->data+i*VtScoreSize, VtScoreSize);
- vtblockput(b);
- b = vtcacheglobal(r->c, score, type);
- if(b == nil)
- return -1;
- }
- if(b->addr == NilBlock){
- vtblockput(b);
- return -1;
- }
- /*
- * No one ever truncates BtDir blocks.
- */
- if(depth==0 && !isdir && e->dsize > size)
- memset(b->data+size, 0, e->dsize-size);
- vtblockput(b);
- return 0;
- }
- int
- vtfilesetsize(VtFile *r, uint64_t size)
- {
- int depth, edepth;
- VtEntry e;
- VtBlock *b;
- assert(ISLOCKED(r));
- if(size == 0)
- return vtfiletruncate(r);
- if(size > VtMaxFileSize || size > ((uint64_t)MaxBlock)*r->dsize){
- werrstr(ETooBig);
- return -1;
- }
- b = fileload(r, &e);
- if(b == nil)
- return -1;
- /* quick out */
- if(e.size == size){
- vtblockput(b);
- return 0;
- }
- depth = sizetodepth(size, e.psize, e.dsize);
- edepth = DEPTH(e.type);
- if(depth < edepth){
- if(shrinkdepth(r, b, &e, depth) < 0){
- vtblockput(b);
- return -1;
- }
- }else if(depth > edepth){
- if(growdepth(r, b, &e, depth) < 0){
- vtblockput(b);
- return -1;
- }
- }
- if(size < e.size)
- shrinksize(r, &e, size);
- e.size = size;
- vtentrypack(&e, b->data, r->offset % r->epb);
- vtblockput(b);
- return 0;
- }
- int
- vtfilesetdirsize(VtFile *r, uint32_t ds)
- {
- uint64_t size;
- int epb;
- assert(ISLOCKED(r));
- epb = r->dsize/VtEntrySize;
- size = (uint64_t)r->dsize*(ds/epb);
- size += VtEntrySize*(ds%epb);
- return vtfilesetsize(r, size);
- }
- uint32_t
- vtfilegetdirsize(VtFile *r)
- {
- uint32_t ds;
- uint64_t size;
- int epb;
- assert(ISLOCKED(r));
- epb = r->dsize/VtEntrySize;
- size = vtfilegetsize(r);
- ds = epb*(size/r->dsize);
- ds += (size%r->dsize)/VtEntrySize;
- return ds;
- }
- int
- vtfilegetentry(VtFile *r, VtEntry *e)
- {
- VtBlock *b;
- assert(ISLOCKED(r));
- b = fileload(r, e);
- if(b == nil)
- return -1;
- vtblockput(b);
- return 0;
- }
- int
- vtfilesetentry(VtFile *r, VtEntry *e)
- {
- VtBlock *b;
- VtEntry ee;
- assert(ISLOCKED(r));
- b = fileload(r, &ee);
- if(b == nil)
- return -1;
- vtentrypack(e, b->data, r->offset % r->epb);
- vtblockput(b);
- return 0;
- }
- static VtBlock *
- blockwalk(VtBlock *p, int index, VtCache *c, int mode, VtEntry *e)
- {
- VtBlock *b;
- int type;
- uint8_t *score;
- switch(p->type){
- case VtDataType:
- assert(0);
- case VtDirType:
- type = e->type;
- score = e->score;
- break;
- default:
- type = p->type - 1;
- score = p->data+index*VtScoreSize;
- break;
- }
- /*print("walk from %V/%d ty %d to %V ty %d\n", p->score, index, p->type, score, type); */
- if(mode == VtOWRITE && vtglobaltolocal(score) == NilBlock){
- b = vtcacheallocblock(c, type);
- if(b)
- goto HaveCopy;
- }else
- b = vtcacheglobal(c, score, type);
- if(b == nil || mode == VtOREAD)
- return b;
- if(vtglobaltolocal(b->score) != NilBlock)
- return b;
- /*
- * Copy on write.
- */
- e->flags |= VtEntryLocal;
- b = vtblockcopy(b/*, e->tag, fs->ehi, fs->elo*/);
- if(b == nil)
- return nil;
- HaveCopy:
- if(p->type == VtDirType){
- memmove(e->score, b->score, VtScoreSize);
- vtentrypack(e, p->data, index);
- }else{
- memmove(p->data+index*VtScoreSize, b->score, VtScoreSize);
- }
- return b;
- }
- /*
- * Change the depth of the VtFile r.
- * The entry e for r is contained in block p.
- */
- static int
- growdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth)
- {
- VtBlock *b, *bb;
- assert(ISLOCKED(r));
- assert(depth <= VtPointerDepth);
- b = vtcacheglobal(r->c, e->score, e->type);
- if(b == nil)
- return -1;
- /*
- * Keep adding layers until we get to the right depth
- * or an error occurs.
- */
- while(DEPTH(e->type) < depth){
- bb = vtcacheallocblock(r->c, e->type+1);
- if(bb == nil)
- break;
- memmove(bb->data, b->score, VtScoreSize);
- memmove(e->score, bb->score, VtScoreSize);
- e->type++;
- e->flags |= VtEntryLocal;
- vtblockput(b);
- b = bb;
- }
- vtentrypack(e, p->data, r->offset % r->epb);
- vtblockput(b);
- if(DEPTH(e->type) == depth)
- return 0;
- return -1;
- }
- static int
- shrinkdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth)
- {
- VtBlock *b, *nb, *ob, *rb;
- assert(ISLOCKED(r));
- assert(depth <= VtPointerDepth);
- rb = vtcacheglobal(r->c, e->score, e->type);
- if(rb == nil)
- return -1;
- /*
- * Walk down to the new root block.
- * We may stop early, but something is better than nothing.
- */
- ob = nil;
- b = rb;
- for(; DEPTH(e->type) > depth; e->type--){
- nb = vtcacheglobal(r->c, b->data, e->type-1);
- if(nb == nil)
- break;
- if(ob!=nil && ob!=rb)
- vtblockput(ob);
- ob = b;
- b = nb;
- }
- if(b == rb){
- vtblockput(rb);
- return 0;
- }
- /*
- * Right now, e points at the root block rb, b is the new root block,
- * and ob points at b. To update:
- *
- * (i) change e to point at b
- * (ii) zero the pointer ob -> b
- * (iii) free the root block
- *
- * p (the block containing e) must be written before
- * anything else.
- */
- /* (i) */
- memmove(e->score, b->score, VtScoreSize);
- vtentrypack(e, p->data, r->offset % r->epb);
- /* (ii) */
- memmove(ob->data, vtzeroscore, VtScoreSize);
- /* (iii) */
- vtblockput(rb);
- if(ob!=nil && ob!=rb)
- vtblockput(ob);
- vtblockput(b);
- if(DEPTH(e->type) == depth)
- return 0;
- return -1;
- }
- static int
- mkindices(VtEntry *e, uint32_t bn, int *index)
- {
- int i, np;
- memset(index, 0, (VtPointerDepth+1)*sizeof(int));
- np = e->psize/VtScoreSize;
- for(i=0; bn > 0; i++){
- if(i >= VtPointerDepth){
- werrstr("bad address 0x%lux", (uint32_t)bn);
- return -1;
- }
- index[i] = bn % np;
- bn /= np;
- }
- return i;
- }
- VtBlock *
- vtfileblock(VtFile *r, uint32_t bn, int mode)
- {
- VtBlock *b, *bb;
- int index[VtPointerDepth+1];
- VtEntry e;
- int i;
- int m;
- assert(ISLOCKED(r));
- assert(bn != NilBlock);
- b = fileload(r, &e);
- if(b == nil)
- return nil;
- i = mkindices(&e, bn, index);
- if(i < 0)
- goto Err;
- if(i > DEPTH(e.type)){
- if(mode == VtOREAD){
- werrstr("bad address 0x%lux", (uint32_t)bn);
- goto Err;
- }
- index[i] = 0;
- if(growdepth(r, b, &e, i) < 0)
- goto Err;
- }
- assert(b->type == VtDirType);
- index[DEPTH(e.type)] = r->offset % r->epb;
- /* mode for intermediate block */
- m = mode;
- if(m == VtOWRITE)
- m = VtORDWR;
- for(i=DEPTH(e.type); i>=0; i--){
- bb = blockwalk(b, index[i], r->c, i==0 ? mode : m, &e);
- if(bb == nil)
- goto Err;
- vtblockput(b);
- b = bb;
- }
- b->pc = getcallerpc(&r);
- return b;
- Err:
- vtblockput(b);
- return nil;
- }
- int
- vtfileblockscore(VtFile *r, uint32_t bn, uint8_t score[VtScoreSize])
- {
- VtBlock *b, *bb;
- int index[VtPointerDepth+1];
- VtEntry e;
- int i;
- assert(ISLOCKED(r));
- assert(bn != NilBlock);
- b = fileload(r, &e);
- if(b == nil)
- return -1;
- if(DEPTH(e.type) == 0){
- memmove(score, e.score, VtScoreSize);
- vtblockput(b);
- return 0;
- }
- i = mkindices(&e, bn, index);
- if(i < 0){
- vtblockput(b);
- return -1;
- }
- if(i > DEPTH(e.type)){
- memmove(score, vtzeroscore, VtScoreSize);
- vtblockput(b);
- return 0;
- }
- index[DEPTH(e.type)] = r->offset % r->epb;
- for(i=DEPTH(e.type); i>=1; i--){
- bb = blockwalk(b, index[i], r->c, VtOREAD, &e);
- if(bb == nil)
- goto Err;
- vtblockput(b);
- b = bb;
- if(memcmp(b->score, vtzeroscore, VtScoreSize) == 0)
- break;
- }
- memmove(score, b->data+index[0]*VtScoreSize, VtScoreSize);
- vtblockput(b);
- return 0;
- Err:
- vtblockput(b);
- return -1;
- }
- void
- vtfileincref(VtFile *r)
- {
- qlock(&r->lk);
- r->ref++;
- qunlock(&r->lk);
- }
- void
- vtfileclose(VtFile *r)
- {
- if(r == nil)
- return;
- qlock(&r->lk);
- r->ref--;
- if(r->ref){
- qunlock(&r->lk);
- return;
- }
- assert(r->ref == 0);
- qunlock(&r->lk);
- if(r->parent)
- vtfileclose(r->parent);
- memset(r, ~0, sizeof(*r));
- vtfree(r);
- }
- /*
- * Retrieve the block containing the entry for r.
- * If a snapshot has happened, we might need
- * to get a new copy of the block. We avoid this
- * in the common case by caching the score for
- * the block and the last epoch in which it was valid.
- *
- * We use r->mode to tell the difference between active
- * file system VtFiles (VtORDWR) and VtFiles for the
- * snapshot file system (VtOREAD).
- */
- static VtBlock*
- fileloadblock(VtFile *r, int mode)
- {
- char e[ERRMAX];
- uint32_t addr;
- VtBlock *b;
- switch(r->mode){
- default:
- assert(0);
- case VtORDWR:
- assert(r->mode == VtORDWR);
- if(r->local == 1){
- b = vtcacheglobal(r->c, r->score, VtDirType);
- if(b == nil)
- return nil;
- b->pc = getcallerpc(&r);
- return b;
- }
- assert(r->parent != nil);
- if(vtfilelock(r->parent, VtORDWR) < 0)
- return nil;
- b = vtfileblock(r->parent, r->offset/r->epb, VtORDWR);
- vtfileunlock(r->parent);
- if(b == nil)
- return nil;
- memmove(r->score, b->score, VtScoreSize);
- r->local = 1;
- return b;
- case VtOREAD:
- if(mode == VtORDWR){
- werrstr("read/write lock of read-only file");
- return nil;
- }
- addr = vtglobaltolocal(r->score);
- if(addr == NilBlock)
- return vtcacheglobal(r->c, r->score, VtDirType);
- b = vtcachelocal(r->c, addr, VtDirType);
- if(b)
- return b;
- /*
- * If it failed because the epochs don't match, the block has been
- * archived and reclaimed. Rewalk from the parent and get the
- * new pointer. This can't happen in the VtORDWR case
- * above because blocks in the current epoch don't get
- * reclaimed. The fact that we're VtOREAD means we're
- * a snapshot. (Or else the file system is read-only, but then
- * the archiver isn't going around deleting blocks.)
- */
- rerrstr(e, sizeof e);
- if(strcmp(e, ELabelMismatch) == 0){
- if(vtfilelock(r->parent, VtOREAD) < 0)
- return nil;
- b = vtfileblock(r->parent, r->offset/r->epb, VtOREAD);
- vtfileunlock(r->parent);
- if(b){
- fprint(2, "vtfilealloc: lost %V found %V\n",
- r->score, b->score);
- memmove(r->score, b->score, VtScoreSize);
- return b;
- }
- }
- return nil;
- }
- }
- int
- vtfilelock(VtFile *r, int mode)
- {
- VtBlock *b;
- if(mode == -1)
- mode = r->mode;
- b = fileloadblock(r, mode);
- if(b == nil)
- return -1;
- /*
- * The fact that we are holding b serves as the
- * lock entitling us to write to r->b.
- */
- assert(r->b == nil);
- r->b = b;
- b->pc = getcallerpc(&r);
- return 0;
- }
- /*
- * Lock two (usually sibling) VtFiles. This needs special care
- * because the Entries for both vtFiles might be in the same block.
- * We also try to lock blocks in left-to-right order within the tree.
- */
- int
- vtfilelock2(VtFile *r, VtFile *rr, int mode)
- {
- VtBlock *b, *bb;
- if(rr == nil)
- return vtfilelock(r, mode);
- if(mode == -1)
- mode = r->mode;
- if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->epb){
- b = fileloadblock(r, mode);
- if(b == nil)
- return -1;
- vtblockduplock(b);
- bb = b;
- }else if(r->parent==rr->parent || r->offset > rr->offset){
- bb = fileloadblock(rr, mode);
- b = fileloadblock(r, mode);
- }else{
- b = fileloadblock(r, mode);
- bb = fileloadblock(rr, mode);
- }
- if(b == nil || bb == nil){
- if(b)
- vtblockput(b);
- if(bb)
- vtblockput(bb);
- return -1;
- }
- /*
- * The fact that we are holding b and bb serves
- * as the lock entitling us to write to r->b and rr->b.
- */
- r->b = b;
- rr->b = bb;
- b->pc = getcallerpc(&r);
- bb->pc = getcallerpc(&r);
- return 0;
- }
- void
- vtfileunlock(VtFile *r)
- {
- VtBlock *b;
- if(r->b == nil){
- fprint(2, "vtfileunlock: already unlocked\n");
- abort();
- }
- b = r->b;
- r->b = nil;
- vtblockput(b);
- }
- static VtBlock*
- fileload(VtFile *r, VtEntry *e)
- {
- VtBlock *b;
- assert(ISLOCKED(r));
- b = r->b;
- if(vtentryunpack(e, b->data, r->offset % r->epb) < 0)
- return nil;
- vtblockduplock(b);
- return b;
- }
- static int
- sizetodepth(uint64_t s, int psize, int dsize)
- {
- int np;
- int d;
-
- /* determine pointer depth */
- np = psize/VtScoreSize;
- s = (s + dsize - 1)/dsize;
- for(d = 0; s > 1; d++)
- s = (s + np - 1)/np;
- return d;
- }
- int32_t
- vtfileread(VtFile *f, void *data, int32_t count, int64_t offset)
- {
- int frag;
- VtBlock *b;
- VtEntry e;
- assert(ISLOCKED(f));
- vtfilegetentry(f, &e);
- if(count == 0)
- return 0;
- if(count < 0 || offset < 0){
- werrstr("vtfileread: bad offset or count");
- return -1;
- }
- if(offset >= e.size)
- return 0;
- if(offset+count > e.size)
- count = e.size - offset;
- frag = offset % e.dsize;
- if(frag+count > e.dsize)
- count = e.dsize - frag;
- b = vtfileblock(f, offset/e.dsize, VtOREAD);
- if(b == nil)
- return -1;
- memmove(data, b->data+frag, count);
- vtblockput(b);
- return count;
- }
- static int32_t
- filewrite1(VtFile *f, void *data, int32_t count, int64_t offset)
- {
- int frag, m;
- VtBlock *b;
- VtEntry e;
- vtfilegetentry(f, &e);
- if(count < 0 || offset < 0){
- werrstr("vtfilewrite: bad offset or count");
- return -1;
- }
- frag = offset % e.dsize;
- if(frag+count > e.dsize)
- count = e.dsize - frag;
- m = VtORDWR;
- if(frag == 0 && count == e.dsize)
- m = VtOWRITE;
- b = vtfileblock(f, offset/e.dsize, m);
- if(b == nil)
- return -1;
- memmove(b->data+frag, data, count);
- if(m == VtOWRITE && frag+count < e.dsize)
- memset(b->data+frag+count, 0, e.dsize-frag-count);
- if(offset+count > e.size){
- vtfilegetentry(f, &e);
- e.size = offset+count;
- vtfilesetentry(f, &e);
- }
- vtblockput(b);
- return count;
- }
- int32_t
- vtfilewrite(VtFile *f, void *data, int32_t count, int64_t offset)
- {
- int32_t tot, m;
- assert(ISLOCKED(f));
- tot = 0;
- m = 0;
- while(tot < count){
- m = filewrite1(f, (char*)data+tot, count-tot, offset+tot);
- if(m <= 0)
- break;
- tot += m;
- }
- if(tot==0)
- return m;
- return tot;
- }
- static int
- flushblock(VtCache *c, VtBlock *bb, uint8_t score[VtScoreSize], int ppb,
- int epb,
- int type)
- {
- uint32_t addr;
- VtBlock *b;
- VtEntry e;
- int i;
- addr = vtglobaltolocal(score);
- if(addr == NilBlock)
- return 0;
- if(bb){
- b = bb;
- if(memcmp(b->score, score, VtScoreSize) != 0)
- abort();
- }else
- if((b = vtcachelocal(c, addr, type)) == nil)
- return -1;
- switch(type){
- case VtDataType:
- break;
- case VtDirType:
- for(i=0; i<epb; i++){
- if(vtentryunpack(&e, b->data, i) < 0)
- goto Err;
- if(!(e.flags&VtEntryActive))
- continue;
- if(flushblock(c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize,
- e.type) < 0)
- goto Err;
- vtentrypack(&e, b->data, i);
- }
- break;
-
- default: /* VtPointerTypeX */
- for(i=0; i<ppb; i++){
- if(flushblock(c, nil, b->data+VtScoreSize*i, ppb, epb, type-1) < 0)
- goto Err;
- }
- break;
- }
- if(vtblockwrite(b) < 0)
- goto Err;
- memmove(score, b->score, VtScoreSize);
- if(b != bb)
- vtblockput(b);
- return 0;
- Err:
- if(b != bb)
- vtblockput(b);
- return -1;
- }
- int
- vtfileflush(VtFile *f)
- {
- int ret;
- VtBlock *b;
- VtEntry e;
- assert(ISLOCKED(f));
- b = fileload(f, &e);
- if(!(e.flags&VtEntryLocal)){
- vtblockput(b);
- return 0;
- }
- ret = flushblock(f->c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize,
- e.type);
- if(ret < 0){
- vtblockput(b);
- return -1;
- }
- vtentrypack(&e, b->data, f->offset % f->epb);
- vtblockput(b);
- return 0;
- }
- int
- vtfileflushbefore(VtFile *r, uint64_t offset)
- {
- VtBlock *b, *bb;
- VtEntry e;
- int i, base, depth, ppb, epb, doflush;
- int index[VtPointerDepth+1], j, ret;
- VtBlock *bi[VtPointerDepth+2];
- uint8_t *score;
- assert(ISLOCKED(r));
- if(offset == 0)
- return 0;
- b = fileload(r, &e);
- if(b == nil)
- return -1;
- /*
- * compute path through tree for the last written byte and the next one.
- */
- ret = -1;
- memset(bi, 0, sizeof bi);
- depth = DEPTH(e.type);
- bi[depth+1] = b;
- i = mkindices(&e, (offset-1)/e.dsize, index);
- if(i < 0)
- goto Err;
- if(i > depth)
- goto Err;
- ppb = e.psize / VtScoreSize;
- epb = e.dsize / VtEntrySize;
- /*
- * load the blocks along the last written byte
- */
- index[depth] = r->offset % r->epb;
- for(i=depth; i>=0; i--){
- bb = blockwalk(b, index[i], r->c, VtORDWR, &e);
- if(bb == nil)
- goto Err;
- bi[i] = bb;
- b = bb;
- }
- ret = 0;
- /*
- * walk up the path from leaf to root, flushing anything that
- * has been finished.
- */
- base = e.type&~VtTypeDepthMask;
- for(i=0; i<=depth; i++){
- doflush = 0;
- if(i == 0){
- /* leaf: data or dir block */
- if(offset%e.dsize == 0)
- doflush = 1;
- }else{
- /*
- * interior node: pointer blocks.
- * specifically, b = bi[i] is a block whose index[i-1]'th entry
- * points at bi[i-1].
- */
- b = bi[i];
- /*
- * the index entries up to but not including index[i-1] point at
- * finished blocks, so flush them for sure.
- */
- for(j=0; j<index[i-1]; j++)
- if(flushblock(r->c, nil, b->data+j*VtScoreSize, ppb, epb, base+i-1) < 0)
- goto Err;
- /*
- * if index[i-1] is the last entry in the block and is global
- * (i.e. the kid is flushed), then we can flush this block.
- */
- if(j==ppb-1 && vtglobaltolocal(b->data+j*VtScoreSize)==NilBlock)
- doflush = 1;
- }
- if(doflush){
- if(i == depth)
- score = e.score;
- else
- score = bi[i+1]->data+index[i]*VtScoreSize;
- if(flushblock(r->c, bi[i], score, ppb, epb, base+i) < 0)
- goto Err;
- }
- }
- Err:
- /* top: entry. do this always so that the score is up-to-date */
- vtentrypack(&e, bi[depth+1]->data, index[depth]);
- for(i=0; i<nelem(bi); i++)
- if(bi[i])
- vtblockput(bi[i]);
- return ret;
- }
|