/* * 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. */ #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){ int fd, m; uchar oscore[VtScoreSize]; Block *b, *bs; Disk *disk; Fs *fs; Super super; 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->name = vtStrDup(file); 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, "%s: fs->ehi %d fs->elo %d active=%d\n", argv0, 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, "%s: got fs source\n", argv0); vtRLock(fs->elk); fs->file = fileRoot(fs->source); fs->source->file = fs->file; /* point back */ vtRUnlock(fs->elk); if(fs->file == nil){ vtSetError("fileRoot: %R"); goto Err; } //fprint(2, "%s: got file root\n", argv0); if(mode == OReadWrite){ fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000); fs->snap = snapInit(fs); } return fs; Err:fprint(2, "%s: fsOpen error\n", argv0); 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); vtMemFree(fs->name); 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, "%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;} voidsuperWrite(Block* b, Super* super, int forceWrite){ superPack(super, b->data); blockDirty(b); if(forceWrite){ while(!blockWrite(b, Waitlock)){ /* this should no longer happen */ fprint(2, "%s: could not write super block; " "waiting 10 seconds\n", argv0); 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, "%s: bumpEpoch: blockCopy: %R\n", argv0); return 0; } if(0) fprint(2, "%s: snapshot root from %d to %d\n", argv0, 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;Lines 1–500 of 1108. Load more
/* * 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. */ #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) { int fd, m; uchar oscore[VtScoreSize]; Block *b, *bs; Disk *disk; Fs *fs; Super super; 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->name = vtStrDup(file); 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, "%s: fs->ehi %d fs->elo %d active=%d\n", argv0, 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, "%s: got fs source\n", argv0); vtRLock(fs->elk); fs->file = fileRoot(fs->source); fs->source->file = fs->file; /* point back */ vtRUnlock(fs->elk); if(fs->file == nil){ vtSetError("fileRoot: %R"); goto Err; } //fprint(2, "%s: got file root\n", argv0); if(mode == OReadWrite){ fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000); fs->snap = snapInit(fs); } return fs; Err: fprint(2, "%s: fsOpen error\n", argv0); fsClose(fs); return nil; } void fsClose(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); vtMemFree(fs->name); vtRUnlock(fs->elk); vtLockFree(fs->elk); memset(fs, ~0, sizeof(Fs)); vtMemFree(fs); } int fsRedial(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); } int fsGetBlockSize(Fs *fs) { return fs->blockSize; } 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; } void superWrite(Block* b, Super* super, int forceWrite) { superPack(super, b->data); blockDirty(b); if(forceWrite){ while(!blockWrite(b, Waitlock)){ /* this should no longer happen */ fprint(2, "%s: could not write super block; " "waiting 10 seconds\n", argv0); 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 int fsNeedArch(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; } int fsEpochLow(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 int bumpEpoch(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, "%s: bumpEpoch: blockCopy: %R\n", argv0); return 0; } if(0) fprint(2, "%s: snapshot root from %d to %d\n", argv0, 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; } int saveQid(Fs *fs) { Block *b; Super super; u64int qidMax; if((b = superGet(fs->cache, &super)) == nil) return 0; qidMax = super.qid;