static int dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size) { Block *b; ulong bn; Entry e; int epb; epb = s->dsize/VtEntrySize; bn = elem/epb; elem -= bn*epb; b = sourceBlock(s, bn, OReadOnly); if(b == nil) goto Err; if(!entryUnpack(&e, b->data, elem)) goto Err; /* hanging entries are returned as zero size */ if(!(e.flags & VtEntryActive) || e.gen != gen) *size = 0; else *size = e.size; blockPut(b); return 1; Err: blockPut(b); return 0; }
/* assume file is locked, assume f->msource is locked */ static int fileCheckEmpty(File *f) { u32int i, n; Block *b; MetaBlock mb; Source *r; r = f->msource; n = (sourceGetSize(r)+r->dsize-1)/r->dsize; for(i=0; i<n; i++){ b = sourceBlock(r, i, OReadOnly); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, r->dsize)) goto Err; if(mb.nindex > 0){ vtSetError(ENotEmpty); goto Err; } blockPut(b); } return 1; Err: blockPut(b); return 0; }
Tnode* initxsuper(void) { Block *b; Tnode *t; b = readBlock(PartSuper, 0); if(b == nil) return stringnode("reading super: %r"); if(!superUnpack(&super, b->data)) { blockPut(b); return stringnode("unpacking super: %r"); } blockPut(b); t = stringnode("super " "version=%#ux " "epoch=[%#ux,%#ux) " "qid=%#llux " "active=%#x " "next=%#x " "current=%#x " "last=%V " "name=%s", super.version, super.epochLow, super.epochHigh, super.qid, super.active, super.next, super.current, super.last, super.name); t->expand = xsuperexpand; return t; }
static int getEntry(Source *r, Entry *e, int checkepoch) { u32int epoch; Block *b; if(r == nil){ memset(&e, 0, sizeof e); return 1; } b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly); if(b == nil) return 0; if(!entryUnpack(e, b->data, r->offset % r->epb)){ blockPut(b); return 0; } epoch = b->l.epoch; blockPut(b); if(checkepoch){ b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly); if(b){ if(b->l.epoch >= epoch) fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n", r, b->addr, b->l.epoch, r->score, epoch); blockPut(b); } } return 1; }
static int sourceKill(Source *r, int doremove) { Entry e; Block *b; uint32_t addr; uint32_t tag; int type; assert(sourceIsLocked(r)); b = sourceLoad(r, &e); if(b == nil) return 0; assert(b->l.epoch == r->fs->ehi); if(doremove==0 && e.size == 0) { /* already truncated */ blockPut(b); return 1; } /* remember info on link we are removing */ addr = globalToLocal(e.score); type = entryType(&e); tag = e.tag; if(doremove) { if(e.gen != ~0) e.gen++; e.dsize = 0; e.psize = 0; e.flags = 0; } else { e.flags &= ~VtEntryLocal; } e.depth = 0; e.size = 0; e.tag = 0; memmove(e.score, vtZeroScore, VtScoreSize); entryPack(&e, b->data, r->offset % r->epb); blockDirty(b); if(addr != NilBlock) blockRemoveLink(b, addr, type, tag, 1); blockPut(b); if(doremove) { sourceUnlock(r); sourceClose(r); } return 1; }
static void checkEpoch(Fsck *chk, u32int epoch) { u32int a; Block *b; Entry e; Label l; chk->print("checking epoch %ud...\n", epoch); for(a=0; a<chk->nblocks; a++){ if(!readLabel(chk->cache, &l, (a+chk->hint)%chk->nblocks)){ error(chk, "could not read label for addr 0x%.8#ux", a); continue; } if(l.tag == RootTag && l.epoch == epoch) break; } if(a == chk->nblocks){ chk->print("could not find root block for epoch %ud", epoch); return; } a = (a+chk->hint)%chk->nblocks; b = cacheLocalData(chk->cache, a, BtDir, RootTag, OReadOnly, 0); if(b == nil){ error(chk, "could not read root block 0x%.8#ux: %R", a); return; } /* no one should point at root blocks */ setBit(chk->amap, a); setBit(chk->emap, a); setBit(chk->xmap, a); /* * First entry is the rest of the file system. * Second entry is link to previous epoch root, * just a convenience to help the search. */ if(!entryUnpack(&e, b->data, 0)){ error(chk, "could not unpack root block 0x%.8#ux: %R", a); blockPut(b); return; } walkEpoch(chk, b, e.score, BtDir, e.tag, epoch); if(entryUnpack(&e, b->data, 1)) chk->hint = globalToLocal(e.score); blockPut(b); }
/* * Change the depth of the source r. * The entry e for r is contained in block p. */ static int sourceGrowDepth(Source *r, Block *p, Entry *e, int depth) { Block *b, *bb; uint32_t tag; int type; Entry oe; assert(sourceIsLocked(r)); assert(depth <= VtPointerDepth); type = entryType(e); b = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite); if(b == nil) return 0; tag = e->tag; if(tag == 0) tag = tagGen(); oe = *e; /* * Keep adding layers until we get to the right depth * or an error occurs. */ while(e->depth < depth) { bb = cacheAllocBlock(r->fs->cache, type+1, tag, r->fs->ehi, r->fs->elo); if(bb == nil) break; //fprint(2, "alloc %lux grow %V\n", bb->addr, b->score); memmove(bb->data, b->score, VtScoreSize); memmove(e->score, bb->score, VtScoreSize); e->depth++; type++; e->tag = tag; e->flags |= VtEntryLocal; blockDependency(bb, b, 0, vtZeroScore, nil); blockPut(b); b = bb; blockDirty(b); } entryPack(e, p->data, r->offset % r->epb); blockDependency(p, b, r->offset % r->epb, nil, &oe); blockPut(b); blockDirty(p); return e->depth == depth; }
int sourceSetSize(Source *r, uint64_t size) { int depth; Entry e; Block *b; assert(sourceIsLocked(r)); if(size == 0) return sourceTruncate(r); if(size > VtMaxFileSize || size > ((uint64_t)MaxBlock)*r->dsize) { vtSetError(ETooBig); return 0; } b = sourceLoad(r, &e); if(b == nil) return 0; /* quick out */ if(e.size == size) { blockPut(b); return 1; } depth = sizeToDepth(size, e.psize, e.dsize); if(depth < e.depth) { if(!sourceShrinkDepth(r, b, &e, depth)) { blockPut(b); return 0; } } else if(depth > e.depth) { if(!sourceGrowDepth(r, b, &e, depth)) { blockPut(b); return 0; } } if(size < e.size) sourceShrinkSize(r, &e, size); e.size = size; entryPack(&e, b->data, r->offset % r->epb); blockDirty(b); blockPut(b); return 1; }
static int fileMetaRemove(File *f, char *uid) { Block *b; MetaBlock mb; MetaEntry me; int i; File *up; up = f->up; fileWAccess(up, uid); fileMetaLock(f); sourceLock(up->msource, OReadWrite); b = sourceBlock(up->msource, f->boff, OReadWrite); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, up->msource->dsize)) { fprint(2, "U\n"); goto Err; } if(!mbSearch(&mb, f->dir.elem, &i, &me)) { fprint(2, "S\n"); goto Err; } mbDelete(&mb, i); mbPack(&mb); sourceUnlock(up->msource); blockDirty(b); blockPut(b); f->removed = 1; f->boff = NilBlock; f->dirty = 0; fileMetaUnlock(f); return 1; Err: sourceUnlock(up->msource); blockPut(b); fileMetaUnlock(f); return 0; }
static int deeFill(DirEntryEnum *dee) { int i, n; Source *meta, *source; MetaBlock mb; MetaEntry me; File *f; Block *b; DirEntry *de; /* clean up first */ for(i=dee->i; i<dee->n; i++) deCleanup(dee->buf+i); vtMemFree(dee->buf); dee->buf = nil; dee->i = 0; dee->n = 0; f = dee->file; source = f->source; meta = f->msource; b = sourceBlock(meta, dee->boff, OReadOnly); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, meta->dsize)) goto Err; n = mb.nindex; dee->buf = vtMemAlloc(n * sizeof(DirEntry)); for(i=0; i<n; i++){ de = dee->buf + i; meUnpack(&me, &mb, i); if(!deUnpack(de, &me)) goto Err; dee->n++; if(!(de->mode & ModeDir)) if(!dirEntrySize(source, de->entry, de->gen, &de->size)) goto Err; } dee->boff++; blockPut(b); return 1; Err: blockPut(b); return 0; }
static void scanSource(Fsck *chk, char *name, Source *r) { u32int a, nb, o; Block *b; Entry e; if(!chk->useventi && globalToLocal(r->score)==NilBlock) return; if(!sourceGetEntry(r, &e)){ error(chk, "could not get entry for %s", name); return; } a = globalToLocal(e.score); if(!chk->useventi && a==NilBlock) return; if(getBit(chk->smap, a)) return; setBit(chk->smap, a); nb = (sourceGetSize(r) + r->dsize-1) / r->dsize; for(o = 0; o < nb; o++){ b = sourceBlock(r, o, OReadOnly); if(b == nil){ error(chk, "could not read block in data file %s", name); continue; } if(b->addr != NilBlock && getBit(chk->errmap, b->addr)){ warn(chk, "previously reported error in block %ux is in file %s", b->addr, name); } blockPut(b); } }
int fsEpochLow(Fs *fs, uint32_t low) { Block *bs; Super super; vtLock(fs->elk); if(low > fs->ehi){ vtSetError("bad low epoch (must be <= %ud)", fs->ehi); vtUnlock(fs->elk); return 0; } if((bs = superGet(fs->cache, &super)) == nil){ vtUnlock(fs->elk); return 0; } super.epochLow = low; fs->elo = low; superWrite(bs, &super, 1); blockPut(bs); vtUnlock(fs->elk); return 1; }
/* * Lock two (usually sibling) sources. This needs special care * because the Entries for both sources might be in the same block. * We also try to lock blocks in left-to-right order within the tree. */ int sourceLock2(Source *r, Source *rr, int mode) { Block *b, *bb; if(rr == nil) return sourceLock(r, mode); if(mode == -1) mode = r->mode; if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->epb) { b = sourceLoadBlock(r, mode); if(b == nil) return 0; if(memcmp(r->score, rr->score, VtScoreSize) != 0) { memmove(rr->score, b->score, VtScoreSize); rr->scoreEpoch = b->l.epoch; rr->tag = b->l.tag; rr->epoch = rr->fs->ehi; } blockDupLock(b); bb = b; } else if(r->parent==rr->parent || r->offset > rr->offset) { bb = sourceLoadBlock(rr, mode); b = sourceLoadBlock(r, mode); } else { b = sourceLoadBlock(r, mode); bb = sourceLoadBlock(rr, mode); } if(b == nil || bb == nil) { if(b) blockPut(b); if(bb) blockPut(bb); return 0; } /* * 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; return 1; }
/* * the file is locked already * f->msource is unlocked */ static File * dirLookup(File *f, char *elem) { int i; MetaBlock mb; MetaEntry me; Block *b; Source *meta; File *ff; u32int bo, nb; meta = f->msource; b = nil; if(!sourceLock(meta, -1)) return nil; nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize; for(bo=0; bo<nb; bo++){ b = sourceBlock(meta, bo, OReadOnly); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, meta->dsize)) goto Err; if(mbSearch(&mb, elem, &i, &me)){ ff = fileAlloc(f->fs); if(!deUnpack(&ff->dir, &me)){ fileFree(ff); goto Err; } sourceUnlock(meta); blockPut(b); ff->boff = bo; ff->mode = f->mode; ff->issnapshot = f->issnapshot; return ff; } blockPut(b); b = nil; } vtSetError(ENoFile); /* fall through */ Err: sourceUnlock(meta); blockPut(b); return nil; }
void sourceUnlock(Source *r) { Block *b; if(r->b == nil) { fprint(2, "sourceUnlock: already unlocked\n"); abort(); } b = r->b; r->b = nil; blockPut(b); }
int sourceGetEntry(Source *r, Entry *e) { Block *b; assert(sourceIsLocked(r)); b = sourceLoad(r, e); if(b == nil) return 0; blockPut(b); return 1; }
Source * sourceRoot(Fs *fs, uint32_t addr, int mode) { Source *r; Block *b; b = cacheLocalData(fs->cache, addr, BtDir, RootTag, mode, 0); if(b == nil) return nil; if(mode == OReadWrite && b->l.epoch != fs->ehi) { consPrint("sourceRoot: fs->ehi = %ud, b->l = %L\n", fs->ehi, &b->l); blockPut(b); vtSetError(EBadRoot); return nil; } r = sourceAlloc(fs, b, nil, 0, mode, 0); blockPut(b); return r; }
/* * Changes the file block bn to be the given block score. * Very sneaky. Only used by flfmt. */ int fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag) { Block *b; Entry e; Source *s; if(!fileLock(f)) return 0; s = nil; if(f->dir.mode & ModeDir){ vtSetError(ENotFile); goto Err; } if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err; } if(!sourceLock(f->source, -1)) goto Err; s = f->source; b = _sourceBlock(s, bn, OReadWrite, 1, tag); if(b == nil) goto Err; if(!sourceGetEntry(s, &e)) goto Err; if(b->l.type == BtDir){ memmove(e.score, score, VtScoreSize); assert(e.tag == tag || e.tag == 0); e.tag = tag; e.flags |= VtEntryLocal; entryPack(&e, b->data, f->source->offset % f->source->epb); }else memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize); blockDirty(b); blockPut(b); sourceUnlock(s); fileUnlock(f); return 1; Err: if(s) sourceUnlock(s); fileUnlock(f); return 0; }
Block* readBlock(int part, u32int addr) { u32int start, end; u64int offset; int n, nn; Block *b; uchar *buf; start = partStart(part); end = partEnd(part); if(addr >= end-start) { werrstr("bad addr 0x%.8ux; wanted 0x%.8ux - 0x%.8ux", addr, start, end); return nil; } b = allocBlock(); b->addr = addr; buf = b->data; offset = ((u64int)(addr+start))*h.blockSize; n = h.blockSize; while(n > 0) { nn = pread(fd, buf, n, offset); if(nn < 0) { blockPut(b); return nil; } if(nn == 0) { werrstr("short read"); blockPut(b); return nil; } n -= nn; offset += nn; buf += nn; } return b; }
Block* dataBlock(uchar score[VtScoreSize], uint type, uint tag) { Block *b, *bl; int lpb; Label l; u32int addr; addr = globalToLocal(score); if(addr == NilBlock) return ventiBlock(score, type); lpb = h.blockSize/LabelSize; bl = readBlock(PartLabel, addr/lpb); if(bl == nil) return nil; if(!labelUnpack(&l, bl->data, addr%lpb)) { werrstr("%r"); blockPut(bl); return nil; } blockPut(bl); if(l.type != type) { werrstr("type mismatch; got %d (%s) wanted %d (%s)", l.type, btStr(l.type), type, btStr(type)); return nil; } if(tag && l.tag != tag) { werrstr("tag mismatch; got 0x%.8ux wanted 0x%.8ux", l.tag, tag); return nil; } b = readBlock(PartData, addr); if(b == nil) return nil; b->l = l; return b; }
static int setEntry(Source *r, Entry *e) { Block *b; Entry oe; b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite); if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score); if(b == nil) return 0; if(!entryUnpack(&oe, b->data, r->offset % r->epb)){ blockPut(b); return 0; } e->gen = oe.gen; entryPack(e, b->data, r->offset % r->epb); /* BUG b should depend on the entry pointer */ blockDirty(b); blockPut(b); return 1; }
uint64_t sourceGetSize(Source *r) { Entry e; Block *b; assert(sourceIsLocked(r)); b = sourceLoad(r, &e); if(b == nil) return 0; blockPut(b); return e.size; }
/* * Must be careful with this. Doesn't record * dependencies, so don't introduce any! */ int sourceSetEntry(Source *r, Entry *e) { Block *b; Entry oe; assert(sourceIsLocked(r)); b = sourceLoad(r, &oe); if(b == nil) return 0; entryPack(e, b->data, r->offset%r->epb); blockDirty(b); blockPut(b); return 1; }
/* * We've just walked the whole write buffer. Notice blocks that * aren't marked available but that we didn't visit. They are lost. */ static void checkLeak(Fsck *chk) { u32int a, nfree, nlost; Block *b; Label l; nfree = 0; nlost = 0; for(a = 0; a < chk->nblocks; a++){ if(!readLabel(chk->cache, &l, a)){ error(chk, "could not read label: addr 0x%ux %d %d: %R", a, l.type, l.state); continue; } if(getBit(chk->amap, a)) continue; if(l.state == BsFree || l.epochClose <= chk->fs->elo || l.epochClose == l.epoch){ nfree++; setBit(chk->amap, a); continue; } if(l.state&BsClosed) continue; nlost++; // warn(chk, "unreachable block: addr 0x%ux type %d tag 0x%ux " // "state %s epoch %ud close %ud", a, l.type, l.tag, // bsStr(l.state), l.epoch, l.epochClose); b = cacheLocal(chk->cache, PartData, a, OReadOnly); if(b == nil){ error(chk, "could not read block 0x%#.8ux", a); continue; } chk->close(chk, b, 0); chk->nclose++; setBit(chk->amap, a); blockPut(b); } chk->print("fsys blocks: total=%ud used=%ud(%.1f%%) free=%ud(%.1f%%) lost=%ud(%.1f%%)\n", chk->nblocks, chk->nblocks - nfree-nlost, 100.*(chk->nblocks - nfree - nlost)/chk->nblocks, nfree, 100.*nfree/chk->nblocks, nlost, 100.*nlost/chk->nblocks); }
int saveQid(Fs *fs) { Block *b; Super super; uint64_t qidMax; if((b = superGet(fs->cache, &super)) == nil) return 0; qidMax = super.qid; blockPut(b); if(!fileSetQidSpace(fs->file, 0, qidMax)) return 0; return 1; }
Block* superGet(Cache *c, Super* super) { Block *b; if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){ fprint(2, "%s: superGet: cacheLocal failed: %R\n", argv0); return nil; } if(!superUnpack(super, b->data)){ fprint(2, "%s: superGet: superUnpack failed: %R\n", argv0); blockPut(b); return nil; } return b; }
int fsNextQid(Fs *fs, uint64_t *qid) { Block *b; Super super; if((b = superGet(fs->cache, &super)) == nil) return 0; *qid = super.qid++; /* * It's okay if the super block doesn't go to disk immediately, * since fileMetaAlloc will record a dependency between the * block holding this qid and the super block. See file.c:/^fileMetaAlloc. */ superWrite(b, &super, 0); blockPut(b); return 1; }
void fsCheck(Fsck *chk) { Block *b; Super super; checkInit(chk); b = superGet(chk->cache, &super); if(b == nil){ chk->print("could not load super block: %R"); return; } blockPut(b); chk->hint = super.active; checkEpochs(chk); chk->smap = vtMemAllocZ(chk->nblocks/8+1); checkDirs(chk); vtMemFree(chk->smap); }
Source * sourceOpen(Source *r, uint32_t offset, int mode, int issnapshot) { uint32_t bn; Block *b; assert(sourceIsLocked(r)); if(r->mode == OReadWrite) assert(r->epoch == r->b->l.epoch); if(!r->dir) { vtSetError(ENotDir); return nil; } bn = offset/(r->dsize/VtEntrySize); b = sourceBlock(r, bn, mode); if(b == nil) return nil; r = sourceAlloc(r->fs, b, r, offset, mode, issnapshot); blockPut(b); return r; }
Block* ventiBlock(uchar score[VtScoreSize], uint type) { int n; Block *b; b = allocBlock(); memmove(b->score, score, VtScoreSize); b->addr = NilBlock; n = vtread(z, b->score, vtType[type], b->data, h.blockSize); if(n < 0) { fprint(2, "vtread returns %d: %r\n", n); blockPut(b); return nil; } vtzeroextend(vtType[type], b->data, n, h.blockSize); b->l.type = type; b->l.state = 0; b->l.tag = 0; b->l.epoch = 0; return b; }