📄 fs.c
字号:
#include "stdinc.h"#include "dat.h"#include "fns.h"#include "error.h"static void fsMetaFlush(void *a);static Snap *snapInit(Fs*);static void snapClose(Snap*);Fs *fsOpen(char *file, VtSession *z, long ncache, int mode){ Fs *fs; Disk *disk; int fd; Block *b, *bs; Super super; int m; uchar oscore[VtScoreSize]; switch(mode){ default: vtSetError(EBadMode); return nil; case OReadOnly: m = OREAD; break; case OReadWrite: m = ORDWR; break; } fd = open(file, m); if(fd < 0){ vtSetError("open %s: %r", file); return nil; } bwatchInit(); disk = diskAlloc(fd); if(disk == nil){ vtSetError("diskAlloc: %R"); close(fd); return nil; } fs = vtMemAllocZ(sizeof(Fs)); fs->mode = mode; fs->blockSize = diskBlockSize(disk); fs->elk = vtLockAlloc(); fs->cache = cacheAlloc(disk, z, ncache, mode); if(mode == OReadWrite && z) fs->arch = archInit(fs->cache, disk, fs, z); fs->z = z; b = cacheLocal(fs->cache, PartSuper, 0, mode); if(b == nil) goto Err; if(!superUnpack(&super, b->data)){ blockPut(b); vtSetError("bad super block"); goto Err; } blockPut(b); fs->ehi = super.epochHigh; fs->elo = super.epochLow;//fprint(2, "fs->ehi %d fs->elo %d active=%d\n", fs->ehi, fs->elo, super.active); fs->source = sourceRoot(fs, super.active, mode); if(fs->source == nil){ /* * Perhaps it failed because the block is copy-on-write. * Do the copy and try again. */ if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0) goto Err; b = cacheLocalData(fs->cache, super.active, BtDir, RootTag, OReadWrite, 0); if(b == nil){ vtSetError("cacheLocalData: %R"); goto Err; } if(b->l.epoch == fs->ehi){ blockPut(b); vtSetError("bad root source block"); goto Err; } b = blockCopy(b, RootTag, fs->ehi, fs->elo); if(b == nil) goto Err; localToGlobal(super.active, oscore); super.active = b->addr; bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite); if(bs == nil){ blockPut(b); vtSetError("cacheLocal: %R"); goto Err; } superPack(&super, bs->data); blockDependency(bs, b, 0, oscore, nil); blockPut(b); blockDirty(bs); blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0); blockPut(bs); fs->source = sourceRoot(fs, super.active, mode); if(fs->source == nil){ vtSetError("sourceRoot: %R"); goto Err; } }//fprint(2, "got fs source\n"); vtRLock(fs->elk); fs->file = fileRoot(fs->source); vtRUnlock(fs->elk); if(fs->file == nil){ vtSetError("fileRoot: %R"); goto Err; }//fprint(2, "got file root\n"); if(mode == OReadWrite){ fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000); fs->snap = snapInit(fs); } return fs;Err:fprint(2, "fsOpen error\n"); fsClose(fs); return nil;}voidfsClose(Fs *fs){ vtRLock(fs->elk); periodicKill(fs->metaFlush); snapClose(fs->snap); if(fs->file){ fileMetaFlush(fs->file, 0); if(!fileDecRef(fs->file)) vtFatal("fsClose: files still in use: %r\n"); } fs->file = nil; sourceClose(fs->source); cacheFree(fs->cache); if(fs->arch) archFree(fs->arch); vtRUnlock(fs->elk); vtLockFree(fs->elk); memset(fs, ~0, sizeof(Fs)); vtMemFree(fs);}intfsRedial(Fs *fs, char *host){ if(!vtRedial(fs->z, host)) return 0; if(!vtConnect(fs->z, 0)) return 0; return 1;}File *fsGetRoot(Fs *fs){ return fileIncRef(fs->file);}intfsGetBlockSize(Fs *fs){ return fs->blockSize;}Block*superGet(Cache *c, Super* super){ Block *b; if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){ fprint(2, "superGet: cacheLocal failed: %R"); return nil; } if(!superUnpack(super, b->data)){ fprint(2, "superGet: superUnpack failed: %R"); blockPut(b); return nil; } return b;}voidsuperWrite(Block* b, Super* super, int forceWrite){ superPack(super, b->data); blockDirty(b); if(forceWrite){ while(!blockWrite(b)){ /* BUG: what should really happen here? */ fprint(2, "could not write super block; waiting 10 seconds\n"); sleep(10*1000); } while(b->iostate != BioClean && b->iostate != BioDirty){ assert(b->iostate == BioWriting); vtSleep(b->ioready); } /* * it's okay that b might still be dirty. * that means it got written out but with an old root pointer, * but the other fields went out, and those are the ones * we really care about. (specifically, epochHigh; see fsSnapshot). */ }}/* * Prepare the directory to store a snapshot. * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#] * Archival snapshots go into /archive/yyyy/mmdd[.#]. * * TODO This should be rewritten to eliminate most of the duplication. */static File*fileOpenSnapshot(Fs *fs, char *dstpath, int doarchive){ int n; char buf[30], *s, *p, *elem; File *dir, *f; Tm now; if(dstpath){ if((p = strrchr(dstpath, '/')) != nil){ *p++ = '\0'; elem = p; p = dstpath; if(*p == '\0') p = "/"; }else{ p = "/"; elem = dstpath; } if((dir = fileOpen(fs, p)) == nil) return nil; f = fileCreate(dir, elem, ModeDir|ModeSnapshot|0555, "adm"); fileDecRef(dir); return f; }else if(doarchive){ /* * a snapshot intended to be archived to venti. */ dir = fileOpen(fs, "/archive"); if(dir == nil) return nil; now = *localtime(time(0)); /* yyyy */ snprint(buf, sizeof(buf), "%d", now.year+1900); f = fileWalk(dir, buf); if(f == nil) f = fileCreate(dir, buf, ModeDir|0555, "adm"); fileDecRef(dir); if(f == nil) return nil; dir = f; /* mmdd[#] */ snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); s = buf+strlen(buf); for(n=0;; n++){ if(n) seprint(s, buf+sizeof(buf), ".%d", n); f = fileWalk(dir, buf); if(f != nil){ fileDecRef(f); continue; } f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); break; } fileDecRef(dir); return f; }else{ /* * Just a temporary snapshot * We'll use /snapshot/yyyy/mmdd/hhmm. * There may well be a better naming scheme. * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.) */ dir = fileOpen(fs, "/snapshot"); if(dir == nil) return nil; now = *localtime(time(0)); /* yyyy */ snprint(buf, sizeof(buf), "%d", now.year+1900); f = fileWalk(dir, buf); if(f == nil) f = fileCreate(dir, buf, ModeDir|0555, "adm"); fileDecRef(dir); if(f == nil) return nil; dir = f; /* mmdd */ snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); f = fileWalk(dir, buf); if(f == nil) f = fileCreate(dir, buf, ModeDir|0555, "adm"); fileDecRef(dir); if(f == nil) return nil; dir = f; /* hhmm[.#] */ snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min); s = buf+strlen(buf); for(n=0;; n++){ if(n) seprint(s, buf+sizeof(buf), ".%d", n); f = fileWalk(dir, buf); if(f != nil){ fileDecRef(f); continue; } f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); break; } fileDecRef(dir); return f; }}static intfsNeedArch(Fs *fs, uint archMinute){ int need; File *f; char buf[100]; Tm now; ulong then; then = time(0); now = *localtime(then); /* back up to yesterday if necessary */ if(now.hour < archMinute/60 || now.hour == archMinute/60 && now.min < archMinute%60) now = *localtime(then-86400); snprint(buf, sizeof buf, "/archive/%d/%02d%02d", now.year+1900, now.mon+1, now.mday); need = 1; vtRLock(fs->elk); f = fileOpen(fs, buf); if(f){ need = 0; fileDecRef(f); } vtRUnlock(fs->elk); return need;}intfsEpochLow(Fs *fs, u32int 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;}static intbumpEpoch(Fs *fs, int doarchive){ uchar oscore[VtScoreSize]; u32int oldaddr; Block *b, *bs; Entry e; Source *r; Super super; /* * Duplicate the root block. * * As a hint to flchk, the garbage collector, * and any (human) debuggers, store a pointer * to the old root block in entry 1 of the new root block. */ r = fs->source; b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly); if(b == nil) return 0; memset(&e, 0, sizeof e); e.flags = VtEntryActive | VtEntryLocal | VtEntryDir; memmove(e.score, b->score, VtScoreSize); e.tag = RootTag; e.snap = b->l.epoch; b = blockCopy(b, RootTag, fs->ehi+1, fs->elo); if(b == nil){ fprint(2, "bumpEpoch: blockCopy: %R\n"); return 0; } if(0) fprint(2, "snapshot root from %d to %d\n", oldaddr, b->addr); entryPack(&e, b->data, 1); blockDirty(b); /* * Update the superblock with the new root and epoch. */ if((bs = superGet(fs->cache, &super)) == nil) return 0; fs->ehi++; memmove(r->score, b->score, VtScoreSize); r->epoch = fs->ehi; super.epochHigh = fs->ehi; oldaddr = super.active; super.active = b->addr; if(doarchive) super.next = oldaddr; /* * Record that the new super.active can't get written out until * the new b gets written out. Until then, use the old value. */ localToGlobal(oldaddr, oscore); blockDependency(bs, b, 0, oscore, nil); blockPut(b); /* * We force the super block to disk so that super.epochHigh gets updated. * Otherwise, if we crash and come back, we might incorrectly treat as active * some of the blocks that making up the snapshot we just created. * Basically every block in the active file system and all the blocks in * the recently-created snapshot depend on the super block now. * Rather than record all those dependencies, we just force the block to disk. * * Note that blockWrite might actually (will probably) send a slightly outdated * super.active to disk. It will be the address of the most recent root that has * gone to disk. */ superWrite(bs, &super, 1); blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0); blockPut(bs); return 1;}intsaveQid(Fs *fs){ Block *b; Super super; u64int 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;}intfsSnapshot(Fs *fs, char *srcpath, char *dstpath, int doarchive){ File *src, *dst; assert(fs->mode == OReadWrite); dst = nil; if(fs->halted){ vtSetError("file system is halted"); return 0; } /* * Freeze file system activity. */ vtLock(fs->elk); /* * Get the root of the directory we're going to save. */ if(srcpath == nil) srcpath = "/active"; src = fileOpen(fs, srcpath); if(src == nil) goto Err; /* * It is important that we maintain the invariant that: * if both b and bb are marked as Active with start epoch e * and b points at bb, then no other pointers to bb exist. * * When bb is unlinked from b, its close epoch is set to b's epoch. * A block with epoch == close epoch is * treated as free by cacheAllocBlock; this aggressively * reclaims blocks after they have been stored to Venti. * * Let's say src->source is block sb, and src->msource is block * mb. Let's also say that block b holds the Entry structures for * both src->source and src->msource (their Entry structures might * be in different blocks, but the argument is the same). * That is, right now we have: * * b Active w/ epoch e, holds ptrs to sb and mb. * sb Active w/ epoch e. * mb Active w/ epoch e. * * With things as they are now, the invariant requires that * b holds the only pointers to sb and mb. We want to record * pointers to sb and mb in new Entries corresponding to dst, * which breaks the invariant. Thus we need to do something
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -