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; }
VtFile * vtfileroot(VtCache *c, u32int 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; }
/* * 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; } }