static VtBlock * blockwalk(VtBlock *p, int index, VtCache *c, int mode, VtEntry *e) { VtBlock *b; int type; uchar *score; VtEntry oe; 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){ print("VtOWRITE && NilBlock\n"); b = vtcacheallocblock(c, type); print("vtcachealloblcok returns %p\n", b); if(b) goto HaveCopy; }else b = vtcacheglobal(c, score, type); if(b == nil || mode == VtOREAD) return b; if(vtglobaltolocal(b->score) != NilBlock) return b; oe = *e; /* * 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; }
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; }
/* * 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]; u32int addr; VtBlock *b; switch(r->mode){ default: assert(0); case VtOWRITE: 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); if(b == nil) b = vtfileblock(r->parent, r->offset/r->epb, VtOWRITE); vtfileunlock(r->parent); if(b == nil) return nil; memmove(r->score, b->score, VtScoreSize); r->local = 1; return b; 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 vtfileflushbefore(VtFile *r, u64int offset) { VtBlock *b, *bb; VtEntry e; int i, base, depth, ppb, epb, doflush; int index[VtPointerDepth+1], j, ret; VtBlock *bi[VtPointerDepth+2]; uchar *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; }