15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier #include "dat.h"
35e96a66cSDavid du Colombier #include "fns.h"
45e96a66cSDavid du Colombier #include "error.h"
55e96a66cSDavid du Colombier
65e96a66cSDavid du Colombier static void fsMetaFlush(void *a);
75e96a66cSDavid du Colombier static Snap *snapInit(Fs*);
85e96a66cSDavid du Colombier static void snapClose(Snap*);
95e96a66cSDavid du Colombier
105e96a66cSDavid du Colombier Fs *
fsOpen(char * file,VtSession * z,long ncache,int mode)115e96a66cSDavid du Colombier fsOpen(char *file, VtSession *z, long ncache, int mode)
125e96a66cSDavid du Colombier {
133b86f2f8SDavid du Colombier int fd, m;
1461201b97SDavid du Colombier uchar oscore[VtScoreSize];
153b86f2f8SDavid du Colombier Block *b, *bs;
163b86f2f8SDavid du Colombier Disk *disk;
173b86f2f8SDavid du Colombier Fs *fs;
183b86f2f8SDavid du Colombier Super super;
195e96a66cSDavid du Colombier
205e96a66cSDavid du Colombier switch(mode){
215e96a66cSDavid du Colombier default:
225e96a66cSDavid du Colombier vtSetError(EBadMode);
235e96a66cSDavid du Colombier return nil;
245e96a66cSDavid du Colombier case OReadOnly:
255e96a66cSDavid du Colombier m = OREAD;
265e96a66cSDavid du Colombier break;
275e96a66cSDavid du Colombier case OReadWrite:
285e96a66cSDavid du Colombier m = ORDWR;
295e96a66cSDavid du Colombier break;
305e96a66cSDavid du Colombier }
315e96a66cSDavid du Colombier fd = open(file, m);
325e96a66cSDavid du Colombier if(fd < 0){
33dc5a79c1SDavid du Colombier vtSetError("open %s: %r", file);
345e96a66cSDavid du Colombier return nil;
355e96a66cSDavid du Colombier }
365e96a66cSDavid du Colombier
375e96a66cSDavid du Colombier bwatchInit();
385e96a66cSDavid du Colombier disk = diskAlloc(fd);
395e96a66cSDavid du Colombier if(disk == nil){
40dc5a79c1SDavid du Colombier vtSetError("diskAlloc: %R");
415e96a66cSDavid du Colombier close(fd);
425e96a66cSDavid du Colombier return nil;
435e96a66cSDavid du Colombier }
445e96a66cSDavid du Colombier
455e96a66cSDavid du Colombier fs = vtMemAllocZ(sizeof(Fs));
465e96a66cSDavid du Colombier fs->mode = mode;
473b86f2f8SDavid du Colombier fs->name = vtStrDup(file);
485e96a66cSDavid du Colombier fs->blockSize = diskBlockSize(disk);
495e96a66cSDavid du Colombier fs->elk = vtLockAlloc();
505e96a66cSDavid du Colombier fs->cache = cacheAlloc(disk, z, ncache, mode);
516042bf6dSDavid du Colombier if(mode == OReadWrite && z)
525e96a66cSDavid du Colombier fs->arch = archInit(fs->cache, disk, fs, z);
535e96a66cSDavid du Colombier fs->z = z;
545e96a66cSDavid du Colombier
555e96a66cSDavid du Colombier b = cacheLocal(fs->cache, PartSuper, 0, mode);
565e96a66cSDavid du Colombier if(b == nil)
575e96a66cSDavid du Colombier goto Err;
585e96a66cSDavid du Colombier if(!superUnpack(&super, b->data)){
595e96a66cSDavid du Colombier blockPut(b);
60dc5a79c1SDavid du Colombier vtSetError("bad super block");
615e96a66cSDavid du Colombier goto Err;
625e96a66cSDavid du Colombier }
635e96a66cSDavid du Colombier blockPut(b);
645e96a66cSDavid du Colombier
655e96a66cSDavid du Colombier fs->ehi = super.epochHigh;
665e96a66cSDavid du Colombier fs->elo = super.epochLow;
675e96a66cSDavid du Colombier
683b86f2f8SDavid du Colombier //fprint(2, "%s: fs->ehi %d fs->elo %d active=%d\n", argv0, fs->ehi, fs->elo, super.active);
695e96a66cSDavid du Colombier
705e96a66cSDavid du Colombier fs->source = sourceRoot(fs, super.active, mode);
715e96a66cSDavid du Colombier if(fs->source == nil){
725e96a66cSDavid du Colombier /*
735e96a66cSDavid du Colombier * Perhaps it failed because the block is copy-on-write.
745e96a66cSDavid du Colombier * Do the copy and try again.
755e96a66cSDavid du Colombier */
765e96a66cSDavid du Colombier if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0)
775e96a66cSDavid du Colombier goto Err;
783b86f2f8SDavid du Colombier b = cacheLocalData(fs->cache, super.active, BtDir, RootTag,
793b86f2f8SDavid du Colombier OReadWrite, 0);
80dc5a79c1SDavid du Colombier if(b == nil){
81dc5a79c1SDavid du Colombier vtSetError("cacheLocalData: %R");
825e96a66cSDavid du Colombier goto Err;
83dc5a79c1SDavid du Colombier }
84e569ccb5SDavid du Colombier if(b->l.epoch == fs->ehi){
855e96a66cSDavid du Colombier blockPut(b);
86dc5a79c1SDavid du Colombier vtSetError("bad root source block");
875e96a66cSDavid du Colombier goto Err;
885e96a66cSDavid du Colombier }
895e96a66cSDavid du Colombier b = blockCopy(b, RootTag, fs->ehi, fs->elo);
905e96a66cSDavid du Colombier if(b == nil)
915e96a66cSDavid du Colombier goto Err;
9261201b97SDavid du Colombier localToGlobal(super.active, oscore);
935e96a66cSDavid du Colombier super.active = b->addr;
945e96a66cSDavid du Colombier bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
955e96a66cSDavid du Colombier if(bs == nil){
965e96a66cSDavid du Colombier blockPut(b);
97dc5a79c1SDavid du Colombier vtSetError("cacheLocal: %R");
985e96a66cSDavid du Colombier goto Err;
995e96a66cSDavid du Colombier }
1005e96a66cSDavid du Colombier superPack(&super, bs->data);
10161201b97SDavid du Colombier blockDependency(bs, b, 0, oscore, nil);
102fe853e23SDavid du Colombier blockPut(b);
1035e96a66cSDavid du Colombier blockDirty(bs);
104e569ccb5SDavid du Colombier blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
1055e96a66cSDavid du Colombier blockPut(bs);
1065e96a66cSDavid du Colombier fs->source = sourceRoot(fs, super.active, mode);
107dc5a79c1SDavid du Colombier if(fs->source == nil){
108dc5a79c1SDavid du Colombier vtSetError("sourceRoot: %R");
1095e96a66cSDavid du Colombier goto Err;
1105e96a66cSDavid du Colombier }
111dc5a79c1SDavid du Colombier }
1125e96a66cSDavid du Colombier
1133b86f2f8SDavid du Colombier //fprint(2, "%s: got fs source\n", argv0);
1145e96a66cSDavid du Colombier
1155e96a66cSDavid du Colombier vtRLock(fs->elk);
1165e96a66cSDavid du Colombier fs->file = fileRoot(fs->source);
1173b86f2f8SDavid du Colombier fs->source->file = fs->file; /* point back */
1185e96a66cSDavid du Colombier vtRUnlock(fs->elk);
119dc5a79c1SDavid du Colombier if(fs->file == nil){
120dc5a79c1SDavid du Colombier vtSetError("fileRoot: %R");
1215e96a66cSDavid du Colombier goto Err;
122dc5a79c1SDavid du Colombier }
1235e96a66cSDavid du Colombier
1243b86f2f8SDavid du Colombier //fprint(2, "%s: got file root\n", argv0);
1255e96a66cSDavid du Colombier
1265e96a66cSDavid du Colombier if(mode == OReadWrite){
1275e96a66cSDavid du Colombier fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
1285e96a66cSDavid du Colombier fs->snap = snapInit(fs);
1295e96a66cSDavid du Colombier }
1305e96a66cSDavid du Colombier return fs;
1315e96a66cSDavid du Colombier
1325e96a66cSDavid du Colombier Err:
1333b86f2f8SDavid du Colombier fprint(2, "%s: fsOpen error\n", argv0);
1345e96a66cSDavid du Colombier fsClose(fs);
1355e96a66cSDavid du Colombier return nil;
1365e96a66cSDavid du Colombier }
1375e96a66cSDavid du Colombier
1385e96a66cSDavid du Colombier void
fsClose(Fs * fs)1395e96a66cSDavid du Colombier fsClose(Fs *fs)
1405e96a66cSDavid du Colombier {
1415e96a66cSDavid du Colombier vtRLock(fs->elk);
1425e96a66cSDavid du Colombier periodicKill(fs->metaFlush);
1435e96a66cSDavid du Colombier snapClose(fs->snap);
1445e96a66cSDavid du Colombier if(fs->file){
1455e96a66cSDavid du Colombier fileMetaFlush(fs->file, 0);
1465e96a66cSDavid du Colombier if(!fileDecRef(fs->file))
1475e96a66cSDavid du Colombier vtFatal("fsClose: files still in use: %r\n");
1485e96a66cSDavid du Colombier }
1495e96a66cSDavid du Colombier fs->file = nil;
1505e96a66cSDavid du Colombier sourceClose(fs->source);
1515e96a66cSDavid du Colombier cacheFree(fs->cache);
1525e96a66cSDavid du Colombier if(fs->arch)
1535e96a66cSDavid du Colombier archFree(fs->arch);
1543b86f2f8SDavid du Colombier vtMemFree(fs->name);
1555e96a66cSDavid du Colombier vtRUnlock(fs->elk);
1565e96a66cSDavid du Colombier vtLockFree(fs->elk);
1575e96a66cSDavid du Colombier memset(fs, ~0, sizeof(Fs));
1585e96a66cSDavid du Colombier vtMemFree(fs);
1595e96a66cSDavid du Colombier }
1605e96a66cSDavid du Colombier
1615e96a66cSDavid du Colombier int
fsRedial(Fs * fs,char * host)1625e96a66cSDavid du Colombier fsRedial(Fs *fs, char *host)
1635e96a66cSDavid du Colombier {
1645e96a66cSDavid du Colombier if(!vtRedial(fs->z, host))
1655e96a66cSDavid du Colombier return 0;
1665e96a66cSDavid du Colombier if(!vtConnect(fs->z, 0))
1675e96a66cSDavid du Colombier return 0;
1685e96a66cSDavid du Colombier return 1;
1695e96a66cSDavid du Colombier }
1705e96a66cSDavid du Colombier
1715e96a66cSDavid du Colombier File *
fsGetRoot(Fs * fs)1725e96a66cSDavid du Colombier fsGetRoot(Fs *fs)
1735e96a66cSDavid du Colombier {
1745e96a66cSDavid du Colombier return fileIncRef(fs->file);
1755e96a66cSDavid du Colombier }
1765e96a66cSDavid du Colombier
1775e96a66cSDavid du Colombier int
fsGetBlockSize(Fs * fs)1785e96a66cSDavid du Colombier fsGetBlockSize(Fs *fs)
1795e96a66cSDavid du Colombier {
1805e96a66cSDavid du Colombier return fs->blockSize;
1815e96a66cSDavid du Colombier }
1825e96a66cSDavid du Colombier
1835e96a66cSDavid du Colombier Block*
superGet(Cache * c,Super * super)1845e96a66cSDavid du Colombier superGet(Cache *c, Super* super)
1855e96a66cSDavid du Colombier {
1865e96a66cSDavid du Colombier Block *b;
1875e96a66cSDavid du Colombier
1885e96a66cSDavid du Colombier if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){
1893b86f2f8SDavid du Colombier fprint(2, "%s: superGet: cacheLocal failed: %R\n", argv0);
1905e96a66cSDavid du Colombier return nil;
1915e96a66cSDavid du Colombier }
1925e96a66cSDavid du Colombier if(!superUnpack(super, b->data)){
1933b86f2f8SDavid du Colombier fprint(2, "%s: superGet: superUnpack failed: %R\n", argv0);
1945e96a66cSDavid du Colombier blockPut(b);
1955e96a66cSDavid du Colombier return nil;
1965e96a66cSDavid du Colombier }
1975e96a66cSDavid du Colombier
1985e96a66cSDavid du Colombier return b;
1995e96a66cSDavid du Colombier }
2005e96a66cSDavid du Colombier
2015e96a66cSDavid du Colombier void
superWrite(Block * b,Super * super,int forceWrite)202e569ccb5SDavid du Colombier superWrite(Block* b, Super* super, int forceWrite)
2035e96a66cSDavid du Colombier {
2045e96a66cSDavid du Colombier superPack(super, b->data);
2055e96a66cSDavid du Colombier blockDirty(b);
2065e96a66cSDavid du Colombier if(forceWrite){
207*e12a9870SDavid du Colombier while(!blockWrite(b, Waitlock)){
208*e12a9870SDavid du Colombier /* this should no longer happen */
2093b86f2f8SDavid du Colombier fprint(2, "%s: could not write super block; "
2103b86f2f8SDavid du Colombier "waiting 10 seconds\n", argv0);
2113541ceb4SDavid du Colombier sleep(10*1000);
2125e96a66cSDavid du Colombier }
2135e96a66cSDavid du Colombier while(b->iostate != BioClean && b->iostate != BioDirty){
2145e96a66cSDavid du Colombier assert(b->iostate == BioWriting);
2155e96a66cSDavid du Colombier vtSleep(b->ioready);
2165e96a66cSDavid du Colombier }
2175e96a66cSDavid du Colombier /*
2185e96a66cSDavid du Colombier * it's okay that b might still be dirty.
2195e96a66cSDavid du Colombier * that means it got written out but with an old root pointer,
2205e96a66cSDavid du Colombier * but the other fields went out, and those are the ones
2215e96a66cSDavid du Colombier * we really care about. (specifically, epochHigh; see fsSnapshot).
2225e96a66cSDavid du Colombier */
2235e96a66cSDavid du Colombier }
2245e96a66cSDavid du Colombier }
2255e96a66cSDavid du Colombier
2265e96a66cSDavid du Colombier /*
2275e96a66cSDavid du Colombier * Prepare the directory to store a snapshot.
22861201b97SDavid du Colombier * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#]
2295e96a66cSDavid du Colombier * Archival snapshots go into /archive/yyyy/mmdd[.#].
2305e96a66cSDavid du Colombier *
2315e96a66cSDavid du Colombier * TODO This should be rewritten to eliminate most of the duplication.
2325e96a66cSDavid du Colombier */
2335e96a66cSDavid du Colombier static File*
fileOpenSnapshot(Fs * fs,char * dstpath,int doarchive)234c3c4501eSDavid du Colombier fileOpenSnapshot(Fs *fs, char *dstpath, int doarchive)
2355e96a66cSDavid du Colombier {
2365e96a66cSDavid du Colombier int n;
237c3c4501eSDavid du Colombier char buf[30], *s, *p, *elem;
2385e96a66cSDavid du Colombier File *dir, *f;
2395e96a66cSDavid du Colombier Tm now;
2405e96a66cSDavid du Colombier
241c3c4501eSDavid du Colombier if(dstpath){
242c3c4501eSDavid du Colombier if((p = strrchr(dstpath, '/')) != nil){
243c3c4501eSDavid du Colombier *p++ = '\0';
244c3c4501eSDavid du Colombier elem = p;
245c3c4501eSDavid du Colombier p = dstpath;
246c3c4501eSDavid du Colombier if(*p == '\0')
247c3c4501eSDavid du Colombier p = "/";
248c3c4501eSDavid du Colombier }else{
249c3c4501eSDavid du Colombier p = "/";
250c3c4501eSDavid du Colombier elem = dstpath;
251c3c4501eSDavid du Colombier }
252c3c4501eSDavid du Colombier if((dir = fileOpen(fs, p)) == nil)
253c3c4501eSDavid du Colombier return nil;
254c3c4501eSDavid du Colombier f = fileCreate(dir, elem, ModeDir|ModeSnapshot|0555, "adm");
255c3c4501eSDavid du Colombier fileDecRef(dir);
256c3c4501eSDavid du Colombier return f;
257c3c4501eSDavid du Colombier }else if(doarchive){
2585e96a66cSDavid du Colombier /*
2595e96a66cSDavid du Colombier * a snapshot intended to be archived to venti.
2605e96a66cSDavid du Colombier */
2615e96a66cSDavid du Colombier dir = fileOpen(fs, "/archive");
2625e96a66cSDavid du Colombier if(dir == nil)
2635e96a66cSDavid du Colombier return nil;
2645e96a66cSDavid du Colombier now = *localtime(time(0));
2655e96a66cSDavid du Colombier
2665e96a66cSDavid du Colombier /* yyyy */
2675e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%d", now.year+1900);
2685e96a66cSDavid du Colombier f = fileWalk(dir, buf);
2695e96a66cSDavid du Colombier if(f == nil)
2705e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm");
2715e96a66cSDavid du Colombier fileDecRef(dir);
2725e96a66cSDavid du Colombier if(f == nil)
2735e96a66cSDavid du Colombier return nil;
2745e96a66cSDavid du Colombier dir = f;
2755e96a66cSDavid du Colombier
2765e96a66cSDavid du Colombier /* mmdd[#] */
2775e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
2785e96a66cSDavid du Colombier s = buf+strlen(buf);
2795e96a66cSDavid du Colombier for(n=0;; n++){
2805e96a66cSDavid du Colombier if(n)
2815e96a66cSDavid du Colombier seprint(s, buf+sizeof(buf), ".%d", n);
2825e96a66cSDavid du Colombier f = fileWalk(dir, buf);
2835e96a66cSDavid du Colombier if(f != nil){
2845e96a66cSDavid du Colombier fileDecRef(f);
2855e96a66cSDavid du Colombier continue;
2865e96a66cSDavid du Colombier }
2875e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
2885e96a66cSDavid du Colombier break;
2895e96a66cSDavid du Colombier }
2905e96a66cSDavid du Colombier fileDecRef(dir);
2915e96a66cSDavid du Colombier return f;
2925e96a66cSDavid du Colombier }else{
2935e96a66cSDavid du Colombier /*
2945e96a66cSDavid du Colombier * Just a temporary snapshot
2955e96a66cSDavid du Colombier * We'll use /snapshot/yyyy/mmdd/hhmm.
2965e96a66cSDavid du Colombier * There may well be a better naming scheme.
2975e96a66cSDavid du Colombier * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.)
2985e96a66cSDavid du Colombier */
2995e96a66cSDavid du Colombier dir = fileOpen(fs, "/snapshot");
3005e96a66cSDavid du Colombier if(dir == nil)
3015e96a66cSDavid du Colombier return nil;
3025e96a66cSDavid du Colombier
3035e96a66cSDavid du Colombier now = *localtime(time(0));
3045e96a66cSDavid du Colombier
3055e96a66cSDavid du Colombier /* yyyy */
3065e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%d", now.year+1900);
3075e96a66cSDavid du Colombier f = fileWalk(dir, buf);
3085e96a66cSDavid du Colombier if(f == nil)
3095e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm");
3105e96a66cSDavid du Colombier fileDecRef(dir);
3115e96a66cSDavid du Colombier if(f == nil)
3125e96a66cSDavid du Colombier return nil;
3135e96a66cSDavid du Colombier dir = f;
3145e96a66cSDavid du Colombier
3155e96a66cSDavid du Colombier /* mmdd */
3165e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
3175e96a66cSDavid du Colombier f = fileWalk(dir, buf);
3185e96a66cSDavid du Colombier if(f == nil)
3195e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm");
3205e96a66cSDavid du Colombier fileDecRef(dir);
3215e96a66cSDavid du Colombier if(f == nil)
3225e96a66cSDavid du Colombier return nil;
3235e96a66cSDavid du Colombier dir = f;
3245e96a66cSDavid du Colombier
32561201b97SDavid du Colombier /* hhmm[.#] */
3265e96a66cSDavid du Colombier snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min);
32761201b97SDavid du Colombier s = buf+strlen(buf);
32861201b97SDavid du Colombier for(n=0;; n++){
32961201b97SDavid du Colombier if(n)
33061201b97SDavid du Colombier seprint(s, buf+sizeof(buf), ".%d", n);
3315e96a66cSDavid du Colombier f = fileWalk(dir, buf);
3325e96a66cSDavid du Colombier if(f != nil){
3335e96a66cSDavid du Colombier fileDecRef(f);
33461201b97SDavid du Colombier continue;
3355e96a66cSDavid du Colombier }
3365e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
33761201b97SDavid du Colombier break;
33861201b97SDavid du Colombier }
3395e96a66cSDavid du Colombier fileDecRef(dir);
3405e96a66cSDavid du Colombier return f;
3415e96a66cSDavid du Colombier }
3425e96a66cSDavid du Colombier }
3435e96a66cSDavid du Colombier
344dc5a79c1SDavid du Colombier static int
fsNeedArch(Fs * fs,uint archMinute)345dc5a79c1SDavid du Colombier fsNeedArch(Fs *fs, uint archMinute)
346dc5a79c1SDavid du Colombier {
347dc5a79c1SDavid du Colombier int need;
348dc5a79c1SDavid du Colombier File *f;
349dc5a79c1SDavid du Colombier char buf[100];
350dc5a79c1SDavid du Colombier Tm now;
351dc5a79c1SDavid du Colombier ulong then;
352dc5a79c1SDavid du Colombier
353dc5a79c1SDavid du Colombier then = time(0);
354dc5a79c1SDavid du Colombier now = *localtime(then);
355dc5a79c1SDavid du Colombier
356dc5a79c1SDavid du Colombier /* back up to yesterday if necessary */
357dc5a79c1SDavid du Colombier if(now.hour < archMinute/60
358dc5a79c1SDavid du Colombier || now.hour == archMinute/60 && now.min < archMinute%60)
359dc5a79c1SDavid du Colombier now = *localtime(then-86400);
360dc5a79c1SDavid du Colombier
361dc5a79c1SDavid du Colombier snprint(buf, sizeof buf, "/archive/%d/%02d%02d",
362dc5a79c1SDavid du Colombier now.year+1900, now.mon+1, now.mday);
363dc5a79c1SDavid du Colombier need = 1;
364dc5a79c1SDavid du Colombier vtRLock(fs->elk);
365dc5a79c1SDavid du Colombier f = fileOpen(fs, buf);
366dc5a79c1SDavid du Colombier if(f){
367dc5a79c1SDavid du Colombier need = 0;
368dc5a79c1SDavid du Colombier fileDecRef(f);
369dc5a79c1SDavid du Colombier }
370dc5a79c1SDavid du Colombier vtRUnlock(fs->elk);
371dc5a79c1SDavid du Colombier return need;
372dc5a79c1SDavid du Colombier }
373dc5a79c1SDavid du Colombier
3745e96a66cSDavid du Colombier int
fsEpochLow(Fs * fs,u32int low)3755e96a66cSDavid du Colombier fsEpochLow(Fs *fs, u32int low)
3765e96a66cSDavid du Colombier {
3775e96a66cSDavid du Colombier Block *bs;
3785e96a66cSDavid du Colombier Super super;
3795e96a66cSDavid du Colombier
3805e96a66cSDavid du Colombier vtLock(fs->elk);
3815e96a66cSDavid du Colombier if(low > fs->ehi){
3825e96a66cSDavid du Colombier vtSetError("bad low epoch (must be <= %ud)", fs->ehi);
3835e96a66cSDavid du Colombier vtUnlock(fs->elk);
3845e96a66cSDavid du Colombier return 0;
3855e96a66cSDavid du Colombier }
3865e96a66cSDavid du Colombier
3875e96a66cSDavid du Colombier if((bs = superGet(fs->cache, &super)) == nil){
3885e96a66cSDavid du Colombier vtUnlock(fs->elk);
3895e96a66cSDavid du Colombier return 0;
3905e96a66cSDavid du Colombier }
3915e96a66cSDavid du Colombier
3925e96a66cSDavid du Colombier super.epochLow = low;
3935e96a66cSDavid du Colombier fs->elo = low;
394e569ccb5SDavid du Colombier superWrite(bs, &super, 1);
395e569ccb5SDavid du Colombier blockPut(bs);
3965e96a66cSDavid du Colombier vtUnlock(fs->elk);
3975e96a66cSDavid du Colombier
3985e96a66cSDavid du Colombier return 1;
3995e96a66cSDavid du Colombier }
4005e96a66cSDavid du Colombier
4015e96a66cSDavid du Colombier static int
bumpEpoch(Fs * fs,int doarchive)4025e96a66cSDavid du Colombier bumpEpoch(Fs *fs, int doarchive)
4035e96a66cSDavid du Colombier {
40461201b97SDavid du Colombier uchar oscore[VtScoreSize];
4055e96a66cSDavid du Colombier u32int oldaddr;
4065e96a66cSDavid du Colombier Block *b, *bs;
4075e96a66cSDavid du Colombier Entry e;
4085e96a66cSDavid du Colombier Source *r;
4095e96a66cSDavid du Colombier Super super;
4105e96a66cSDavid du Colombier
4115e96a66cSDavid du Colombier /*
4125e96a66cSDavid du Colombier * Duplicate the root block.
4135e96a66cSDavid du Colombier *
4145e96a66cSDavid du Colombier * As a hint to flchk, the garbage collector,
4155e96a66cSDavid du Colombier * and any (human) debuggers, store a pointer
4165e96a66cSDavid du Colombier * to the old root block in entry 1 of the new root block.
4175e96a66cSDavid du Colombier */
4185e96a66cSDavid du Colombier r = fs->source;
4195e96a66cSDavid du Colombier b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly);
4205e96a66cSDavid du Colombier if(b == nil)
4215e96a66cSDavid du Colombier return 0;
4225e96a66cSDavid du Colombier
4235e96a66cSDavid du Colombier memset(&e, 0, sizeof e);
4245e96a66cSDavid du Colombier e.flags = VtEntryActive | VtEntryLocal | VtEntryDir;
4255e96a66cSDavid du Colombier memmove(e.score, b->score, VtScoreSize);
4265e96a66cSDavid du Colombier e.tag = RootTag;
4275e96a66cSDavid du Colombier e.snap = b->l.epoch;
4285e96a66cSDavid du Colombier
4295e96a66cSDavid du Colombier b = blockCopy(b, RootTag, fs->ehi+1, fs->elo);
4305e96a66cSDavid du Colombier if(b == nil){
4313b86f2f8SDavid du Colombier fprint(2, "%s: bumpEpoch: blockCopy: %R\n", argv0);
4325e96a66cSDavid du Colombier return 0;
4335e96a66cSDavid du Colombier }
4345e96a66cSDavid du Colombier
4353b86f2f8SDavid du Colombier if(0) fprint(2, "%s: snapshot root from %d to %d\n", argv0, oldaddr, b->addr);
4365e96a66cSDavid du Colombier entryPack(&e, b->data, 1);
4375e96a66cSDavid du Colombier blockDirty(b);
4385e96a66cSDavid du Colombier
4395e96a66cSDavid du Colombier /*
4405e96a66cSDavid du Colombier * Update the superblock with the new root and epoch.
4415e96a66cSDavid du Colombier */
4425e96a66cSDavid du Colombier if((bs = superGet(fs->cache, &super)) == nil)
4435e96a66cSDavid du Colombier return 0;
4445e96a66cSDavid du Colombier
4455e96a66cSDavid du Colombier fs->ehi++;
4465e96a66cSDavid du Colombier memmove(r->score, b->score, VtScoreSize);
4475e96a66cSDavid du Colombier r->epoch = fs->ehi;
4485e96a66cSDavid du Colombier
4495e96a66cSDavid du Colombier super.epochHigh = fs->ehi;
4505e96a66cSDavid du Colombier oldaddr = super.active;
4515e96a66cSDavid du Colombier super.active = b->addr;
4525e96a66cSDavid du Colombier if(doarchive)
4535e96a66cSDavid du Colombier super.next = oldaddr;
4545e96a66cSDavid du Colombier
4555e96a66cSDavid du Colombier /*
4565e96a66cSDavid du Colombier * Record that the new super.active can't get written out until
4575e96a66cSDavid du Colombier * the new b gets written out. Until then, use the old value.
4585e96a66cSDavid du Colombier */
45961201b97SDavid du Colombier localToGlobal(oldaddr, oscore);
46061201b97SDavid du Colombier blockDependency(bs, b, 0, oscore, nil);
4615e96a66cSDavid du Colombier blockPut(b);
4625e96a66cSDavid du Colombier
4635e96a66cSDavid du Colombier /*
4645e96a66cSDavid du Colombier * We force the super block to disk so that super.epochHigh gets updated.
4655e96a66cSDavid du Colombier * Otherwise, if we crash and come back, we might incorrectly treat as active
4665e96a66cSDavid du Colombier * some of the blocks that making up the snapshot we just created.
4675e96a66cSDavid du Colombier * Basically every block in the active file system and all the blocks in
4685e96a66cSDavid du Colombier * the recently-created snapshot depend on the super block now.
4695e96a66cSDavid du Colombier * Rather than record all those dependencies, we just force the block to disk.
4705e96a66cSDavid du Colombier *
4715e96a66cSDavid du Colombier * Note that blockWrite might actually (will probably) send a slightly outdated
4725e96a66cSDavid du Colombier * super.active to disk. It will be the address of the most recent root that has
4735e96a66cSDavid du Colombier * gone to disk.
4745e96a66cSDavid du Colombier */
475e569ccb5SDavid du Colombier superWrite(bs, &super, 1);
476e569ccb5SDavid du Colombier blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
477e569ccb5SDavid du Colombier blockPut(bs);
4785e96a66cSDavid du Colombier
4795e96a66cSDavid du Colombier return 1;
4805e96a66cSDavid du Colombier }
4815e96a66cSDavid du Colombier
4825e96a66cSDavid du Colombier int
saveQid(Fs * fs)4835e96a66cSDavid du Colombier saveQid(Fs *fs)
4845e96a66cSDavid du Colombier {
4855e96a66cSDavid du Colombier Block *b;
4865e96a66cSDavid du Colombier Super super;
4875e96a66cSDavid du Colombier u64int qidMax;
4885e96a66cSDavid du Colombier
4895e96a66cSDavid du Colombier if((b = superGet(fs->cache, &super)) == nil)
4905e96a66cSDavid du Colombier return 0;
4915e96a66cSDavid du Colombier qidMax = super.qid;
4925e96a66cSDavid du Colombier blockPut(b);
4935e96a66cSDavid du Colombier
4945e96a66cSDavid du Colombier if(!fileSetQidSpace(fs->file, 0, qidMax))
4955e96a66cSDavid du Colombier return 0;
4965e96a66cSDavid du Colombier
4975e96a66cSDavid du Colombier return 1;
4985e96a66cSDavid du Colombier }
4995e96a66cSDavid du Colombier
5005e96a66cSDavid du Colombier int
fsSnapshot(Fs * fs,char * srcpath,char * dstpath,int doarchive)501c3c4501eSDavid du Colombier fsSnapshot(Fs *fs, char *srcpath, char *dstpath, int doarchive)
5025e96a66cSDavid du Colombier {
5035e96a66cSDavid du Colombier File *src, *dst;
5045e96a66cSDavid du Colombier
5055e96a66cSDavid du Colombier assert(fs->mode == OReadWrite);
5065e96a66cSDavid du Colombier
5075e96a66cSDavid du Colombier dst = nil;
5085e96a66cSDavid du Colombier
50981cf8742SDavid du Colombier if(fs->halted){
51081cf8742SDavid du Colombier vtSetError("file system is halted");
51181cf8742SDavid du Colombier return 0;
51281cf8742SDavid du Colombier }
51381cf8742SDavid du Colombier
5145e96a66cSDavid du Colombier /*
5155e96a66cSDavid du Colombier * Freeze file system activity.
5165e96a66cSDavid du Colombier */
5175e96a66cSDavid du Colombier vtLock(fs->elk);
5185e96a66cSDavid du Colombier
5195e96a66cSDavid du Colombier /*
5205e96a66cSDavid du Colombier * Get the root of the directory we're going to save.
5215e96a66cSDavid du Colombier */
522c3c4501eSDavid du Colombier if(srcpath == nil)
523c3c4501eSDavid du Colombier srcpath = "/active";
524c3c4501eSDavid du Colombier src = fileOpen(fs, srcpath);
5255e96a66cSDavid du Colombier if(src == nil)
5265e96a66cSDavid du Colombier goto Err;
5275e96a66cSDavid du Colombier
5285e96a66cSDavid du Colombier /*
5295e96a66cSDavid du Colombier * It is important that we maintain the invariant that:
530e569ccb5SDavid du Colombier * if both b and bb are marked as Active with start epoch e
5315e96a66cSDavid du Colombier * and b points at bb, then no other pointers to bb exist.
5325e96a66cSDavid du Colombier *
533e569ccb5SDavid du Colombier * When bb is unlinked from b, its close epoch is set to b's epoch.
534e569ccb5SDavid du Colombier * A block with epoch == close epoch is
535e569ccb5SDavid du Colombier * treated as free by cacheAllocBlock; this aggressively
536e569ccb5SDavid du Colombier * reclaims blocks after they have been stored to Venti.
5375e96a66cSDavid du Colombier *
5385e96a66cSDavid du Colombier * Let's say src->source is block sb, and src->msource is block
5395e96a66cSDavid du Colombier * mb. Let's also say that block b holds the Entry structures for
5405e96a66cSDavid du Colombier * both src->source and src->msource (their Entry structures might
5415e96a66cSDavid du Colombier * be in different blocks, but the argument is the same).
5425e96a66cSDavid du Colombier * That is, right now we have:
5435e96a66cSDavid du Colombier *
5445e96a66cSDavid du Colombier * b Active w/ epoch e, holds ptrs to sb and mb.
5455e96a66cSDavid du Colombier * sb Active w/ epoch e.
5465e96a66cSDavid du Colombier * mb Active w/ epoch e.
5475e96a66cSDavid du Colombier *
5485e96a66cSDavid du Colombier * With things as they are now, the invariant requires that
5495e96a66cSDavid du Colombier * b holds the only pointers to sb and mb. We want to record
5505e96a66cSDavid du Colombier * pointers to sb and mb in new Entries corresponding to dst,
5515e96a66cSDavid du Colombier * which breaks the invariant. Thus we need to do something
5525e96a66cSDavid du Colombier * about b. Specifically, we bump the file system's epoch and
5535e96a66cSDavid du Colombier * then rewalk the path from the root down to and including b.
5545e96a66cSDavid du Colombier * This will copy-on-write as we walk, so now the state will be:
5555e96a66cSDavid du Colombier *
5565e96a66cSDavid du Colombier * b Snap w/ epoch e, holds ptrs to sb and mb.
5575e96a66cSDavid du Colombier * new-b Active w/ epoch e+1, holds ptrs to sb and mb.
5585e96a66cSDavid du Colombier * sb Active w/ epoch e.
5595e96a66cSDavid du Colombier * mb Active w/ epoch e.
5605e96a66cSDavid du Colombier *
561e569ccb5SDavid du Colombier * In this state, it's perfectly okay to make more pointers to sb and mb.
5625e96a66cSDavid du Colombier */
5635e96a66cSDavid du Colombier if(!bumpEpoch(fs, 0) || !fileWalkSources(src))
5645e96a66cSDavid du Colombier goto Err;
5655e96a66cSDavid du Colombier
5665e96a66cSDavid du Colombier /*
567e569ccb5SDavid du Colombier * Sync to disk. I'm not sure this is necessary, but better safe than sorry.
5685e96a66cSDavid du Colombier */
5695e96a66cSDavid du Colombier cacheFlush(fs->cache, 1);
5705e96a66cSDavid du Colombier
5715e96a66cSDavid du Colombier /*
5725e96a66cSDavid du Colombier * Create the directory where we will store the copy of src.
5735e96a66cSDavid du Colombier */
574c3c4501eSDavid du Colombier dst = fileOpenSnapshot(fs, dstpath, doarchive);
5755e96a66cSDavid du Colombier if(dst == nil)
5765e96a66cSDavid du Colombier goto Err;
5775e96a66cSDavid du Colombier
5785e96a66cSDavid du Colombier /*
5795e96a66cSDavid du Colombier * Actually make the copy by setting dst's source and msource
5805e96a66cSDavid du Colombier * to be src's.
5815e96a66cSDavid du Colombier */
5825e96a66cSDavid du Colombier if(!fileSnapshot(dst, src, fs->ehi-1, doarchive))
5835e96a66cSDavid du Colombier goto Err;
5845e96a66cSDavid du Colombier
5855e96a66cSDavid du Colombier fileDecRef(src);
5865e96a66cSDavid du Colombier fileDecRef(dst);
587fb7f0c93SDavid du Colombier src = nil;
588fb7f0c93SDavid du Colombier dst = nil;
589fb7f0c93SDavid du Colombier
5905e96a66cSDavid du Colombier /*
5915e96a66cSDavid du Colombier * Make another copy of the file system. This one is for the
5925e96a66cSDavid du Colombier * archiver, so that the file system we archive has the recently
5935e96a66cSDavid du Colombier * added snapshot both in /active and in /archive/yyyy/mmdd[.#].
5945e96a66cSDavid du Colombier */
5955e96a66cSDavid du Colombier if(doarchive){
5965e96a66cSDavid du Colombier if(!saveQid(fs))
5975e96a66cSDavid du Colombier goto Err;
5985e96a66cSDavid du Colombier if(!bumpEpoch(fs, 1))
5995e96a66cSDavid du Colombier goto Err;
6005e96a66cSDavid du Colombier }
6015e96a66cSDavid du Colombier
6025e96a66cSDavid du Colombier vtUnlock(fs->elk);
6035e96a66cSDavid du Colombier
6045e96a66cSDavid du Colombier /* BUG? can fs->arch fall out from under us here? */
6055e96a66cSDavid du Colombier if(doarchive && fs->arch)
6065e96a66cSDavid du Colombier archKick(fs->arch);
6075e96a66cSDavid du Colombier
6085e96a66cSDavid du Colombier return 1;
6095e96a66cSDavid du Colombier
6105e96a66cSDavid du Colombier Err:
6113b86f2f8SDavid du Colombier fprint(2, "%s: fsSnapshot: %R\n", argv0);
6125e96a66cSDavid du Colombier if(src)
6135e96a66cSDavid du Colombier fileDecRef(src);
6145e96a66cSDavid du Colombier if(dst)
6155e96a66cSDavid du Colombier fileDecRef(dst);
6165e96a66cSDavid du Colombier vtUnlock(fs->elk);
6175e96a66cSDavid du Colombier return 0;
6185e96a66cSDavid du Colombier }
6195e96a66cSDavid du Colombier
6205e96a66cSDavid du Colombier int
fsVac(Fs * fs,char * name,uchar score[VtScoreSize])6215e96a66cSDavid du Colombier fsVac(Fs *fs, char *name, uchar score[VtScoreSize])
6225e96a66cSDavid du Colombier {
6235e96a66cSDavid du Colombier int r;
6245e96a66cSDavid du Colombier DirEntry de;
6255e96a66cSDavid du Colombier Entry e, ee;
6265e96a66cSDavid du Colombier File *f;
6275e96a66cSDavid du Colombier
6285e96a66cSDavid du Colombier vtRLock(fs->elk);
6295e96a66cSDavid du Colombier f = fileOpen(fs, name);
6305e96a66cSDavid du Colombier if(f == nil){
6315e96a66cSDavid du Colombier vtRUnlock(fs->elk);
6325e96a66cSDavid du Colombier return 0;
6335e96a66cSDavid du Colombier }
6345e96a66cSDavid du Colombier
635e569ccb5SDavid du Colombier if(!fileGetSources(f, &e, &ee) || !fileGetDir(f, &de)){
6365e96a66cSDavid du Colombier fileDecRef(f);
6375e96a66cSDavid du Colombier vtRUnlock(fs->elk);
6385e96a66cSDavid du Colombier return 0;
6395e96a66cSDavid du Colombier }
6405e96a66cSDavid du Colombier fileDecRef(f);
6415e96a66cSDavid du Colombier
6425e96a66cSDavid du Colombier r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score);
6435e96a66cSDavid du Colombier vtRUnlock(fs->elk);
6445e96a66cSDavid du Colombier return r;
6455e96a66cSDavid du Colombier }
6465e96a66cSDavid du Colombier
6475e96a66cSDavid du Colombier static int
vtWriteBlock(VtSession * z,uchar * buf,uint n,uint type,uchar score[VtScoreSize])6485e96a66cSDavid du Colombier vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize])
6495e96a66cSDavid du Colombier {
6505e96a66cSDavid du Colombier if(!vtWrite(z, score, type, buf, n))
6515e96a66cSDavid du Colombier return 0;
6525e96a66cSDavid du Colombier if(!vtSha1Check(score, buf, n))
6535e96a66cSDavid du Colombier return 0;
6545e96a66cSDavid du Colombier return 1;
6555e96a66cSDavid du Colombier }
6565e96a66cSDavid du Colombier
6575e96a66cSDavid du Colombier int
mkVac(VtSession * z,uint blockSize,Entry * pe,Entry * pee,DirEntry * pde,uchar score[VtScoreSize])6585e96a66cSDavid du Colombier mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize])
6595e96a66cSDavid du Colombier {
6605e96a66cSDavid du Colombier uchar buf[8192];
6615e96a66cSDavid du Colombier int i;
6625e96a66cSDavid du Colombier uchar *p;
6635e96a66cSDavid du Colombier uint n;
6645e96a66cSDavid du Colombier DirEntry de;
6655e96a66cSDavid du Colombier Entry e, ee, eee;
6665e96a66cSDavid du Colombier MetaBlock mb;
6675e96a66cSDavid du Colombier MetaEntry me;
6685e96a66cSDavid du Colombier VtRoot root;
6695e96a66cSDavid du Colombier
6705e96a66cSDavid du Colombier e = *pe;
6715e96a66cSDavid du Colombier ee = *pee;
6725e96a66cSDavid du Colombier de = *pde;
6735e96a66cSDavid du Colombier
6745e96a66cSDavid du Colombier if(globalToLocal(e.score) != NilBlock
6755e96a66cSDavid du Colombier || (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){
6765e96a66cSDavid du Colombier vtSetError("can only vac paths already stored on venti");
6775e96a66cSDavid du Colombier return 0;
6785e96a66cSDavid du Colombier }
6795e96a66cSDavid du Colombier
6805e96a66cSDavid du Colombier /*
6815e96a66cSDavid du Colombier * Build metadata source for root.
6825e96a66cSDavid du Colombier */
6835e96a66cSDavid du Colombier n = deSize(&de);
6845e96a66cSDavid du Colombier if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){
6855e96a66cSDavid du Colombier vtSetError("DirEntry too big");
6865e96a66cSDavid du Colombier return 0;
6875e96a66cSDavid du Colombier }
6885e96a66cSDavid du Colombier memset(buf, 0, sizeof buf);
6895e96a66cSDavid du Colombier mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1);
6905e96a66cSDavid du Colombier p = mbAlloc(&mb, n);
6915e96a66cSDavid du Colombier if(p == nil)
6925e96a66cSDavid du Colombier abort();
6935e96a66cSDavid du Colombier mbSearch(&mb, de.elem, &i, &me);
6945e96a66cSDavid du Colombier assert(me.p == nil);
6955e96a66cSDavid du Colombier me.p = p;
6965e96a66cSDavid du Colombier me.size = n;
6975e96a66cSDavid du Colombier dePack(&de, &me);
6985e96a66cSDavid du Colombier mbInsert(&mb, i, &me);
6995e96a66cSDavid du Colombier mbPack(&mb);
7005e96a66cSDavid du Colombier
7015e96a66cSDavid du Colombier eee.size = n+MetaHeaderSize+MetaIndexSize;
7025e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score))
7035e96a66cSDavid du Colombier return 0;
7045e96a66cSDavid du Colombier eee.psize = 8192;
7055e96a66cSDavid du Colombier eee.dsize = 8192;
7065e96a66cSDavid du Colombier eee.depth = 0;
7075e96a66cSDavid du Colombier eee.flags = VtEntryActive;
7085e96a66cSDavid du Colombier
7095e96a66cSDavid du Colombier /*
7105e96a66cSDavid du Colombier * Build root source with three entries in it.
7115e96a66cSDavid du Colombier */
7125e96a66cSDavid du Colombier entryPack(&e, buf, 0);
7135e96a66cSDavid du Colombier entryPack(&ee, buf, 1);
7145e96a66cSDavid du Colombier entryPack(&eee, buf, 2);
7155e96a66cSDavid du Colombier
7165e96a66cSDavid du Colombier n = VtEntrySize*3;
7175e96a66cSDavid du Colombier memset(&root, 0, sizeof root);
7185e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, n, VtDirType, root.score))
7195e96a66cSDavid du Colombier return 0;
7205e96a66cSDavid du Colombier
7215e96a66cSDavid du Colombier /*
7225e96a66cSDavid du Colombier * Save root.
7235e96a66cSDavid du Colombier */
7245e96a66cSDavid du Colombier root.version = VtRootVersion;
72534e04225SDavid du Colombier strecpy(root.type, root.type+sizeof root.type, "vac");
7265e96a66cSDavid du Colombier strecpy(root.name, root.name+sizeof root.name, de.elem);
7275e96a66cSDavid du Colombier root.blockSize = blockSize;
7285e96a66cSDavid du Colombier vtRootPack(&root, buf);
7295e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
7305e96a66cSDavid du Colombier return 0;
7315e96a66cSDavid du Colombier
7325e96a66cSDavid du Colombier return 1;
7335e96a66cSDavid du Colombier }
7345e96a66cSDavid du Colombier
7355e96a66cSDavid du Colombier int
fsSync(Fs * fs)7365e96a66cSDavid du Colombier fsSync(Fs *fs)
7375e96a66cSDavid du Colombier {
7385e96a66cSDavid du Colombier vtLock(fs->elk);
73981cf8742SDavid du Colombier fileMetaFlush(fs->file, 1);
7405e96a66cSDavid du Colombier cacheFlush(fs->cache, 1);
7415e96a66cSDavid du Colombier vtUnlock(fs->elk);
7425e96a66cSDavid du Colombier return 1;
7435e96a66cSDavid du Colombier }
7445e96a66cSDavid du Colombier
7455e96a66cSDavid du Colombier int
fsHalt(Fs * fs)74681cf8742SDavid du Colombier fsHalt(Fs *fs)
74781cf8742SDavid du Colombier {
74881cf8742SDavid du Colombier vtLock(fs->elk);
74981cf8742SDavid du Colombier fs->halted = 1;
75081cf8742SDavid du Colombier fileMetaFlush(fs->file, 1);
75181cf8742SDavid du Colombier cacheFlush(fs->cache, 1);
75281cf8742SDavid du Colombier return 1;
75381cf8742SDavid du Colombier }
75481cf8742SDavid du Colombier
75581cf8742SDavid du Colombier int
fsUnhalt(Fs * fs)75681cf8742SDavid du Colombier fsUnhalt(Fs *fs)
75781cf8742SDavid du Colombier {
75881cf8742SDavid du Colombier if(!fs->halted)
75981cf8742SDavid du Colombier return 0;
76081cf8742SDavid du Colombier fs->halted = 0;
76181cf8742SDavid du Colombier vtUnlock(fs->elk);
76281cf8742SDavid du Colombier return 1;
76381cf8742SDavid du Colombier }
76481cf8742SDavid du Colombier
76581cf8742SDavid du Colombier int
fsNextQid(Fs * fs,u64int * qid)7665e96a66cSDavid du Colombier fsNextQid(Fs *fs, u64int *qid)
7675e96a66cSDavid du Colombier {
7685e96a66cSDavid du Colombier Block *b;
7695e96a66cSDavid du Colombier Super super;
7705e96a66cSDavid du Colombier
7715e96a66cSDavid du Colombier if((b = superGet(fs->cache, &super)) == nil)
7725e96a66cSDavid du Colombier return 0;
7735e96a66cSDavid du Colombier
7745e96a66cSDavid du Colombier *qid = super.qid++;
7755e96a66cSDavid du Colombier
7765e96a66cSDavid du Colombier /*
7775e96a66cSDavid du Colombier * It's okay if the super block doesn't go to disk immediately,
7785e96a66cSDavid du Colombier * since fileMetaAlloc will record a dependency between the
7795e96a66cSDavid du Colombier * block holding this qid and the super block. See file.c:/^fileMetaAlloc.
7805e96a66cSDavid du Colombier */
781e569ccb5SDavid du Colombier superWrite(b, &super, 0);
782e569ccb5SDavid du Colombier blockPut(b);
7835e96a66cSDavid du Colombier return 1;
7845e96a66cSDavid du Colombier }
7855e96a66cSDavid du Colombier
7865e96a66cSDavid du Colombier static void
fsMetaFlush(void * a)7875e96a66cSDavid du Colombier fsMetaFlush(void *a)
7885e96a66cSDavid du Colombier {
7890b9a5132SDavid du Colombier int rv;
7905e96a66cSDavid du Colombier Fs *fs = a;
7915e96a66cSDavid du Colombier
7925e96a66cSDavid du Colombier vtRLock(fs->elk);
7930b9a5132SDavid du Colombier rv = fileMetaFlush(fs->file, 1);
7945e96a66cSDavid du Colombier vtRUnlock(fs->elk);
7950b9a5132SDavid du Colombier if(rv > 0)
7965e96a66cSDavid du Colombier cacheFlush(fs->cache, 0);
7975e96a66cSDavid du Colombier }
7985e96a66cSDavid du Colombier
799dc5a79c1SDavid du Colombier static int
fsEsearch1(File * f,char * path,u32int savetime,u32int * plo)800dc5a79c1SDavid du Colombier fsEsearch1(File *f, char *path, u32int savetime, u32int *plo)
801dc5a79c1SDavid du Colombier {
802dc5a79c1SDavid du Colombier int n, r;
803dc5a79c1SDavid du Colombier DirEntry de;
804dc5a79c1SDavid du Colombier DirEntryEnum *dee;
805dc5a79c1SDavid du Colombier File *ff;
806dc5a79c1SDavid du Colombier Entry e, ee;
807dc5a79c1SDavid du Colombier char *t;
808dc5a79c1SDavid du Colombier
809dc5a79c1SDavid du Colombier dee = deeOpen(f);
810dc5a79c1SDavid du Colombier if(dee == nil)
811dc5a79c1SDavid du Colombier return 0;
812dc5a79c1SDavid du Colombier
813dc5a79c1SDavid du Colombier n = 0;
814dc5a79c1SDavid du Colombier for(;;){
815dc5a79c1SDavid du Colombier r = deeRead(dee, &de);
816dc5a79c1SDavid du Colombier if(r <= 0)
817dc5a79c1SDavid du Colombier break;
818dc5a79c1SDavid du Colombier if(de.mode & ModeSnapshot){
819dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil){
820e569ccb5SDavid du Colombier if(fileGetSources(ff, &e, &ee))
821dc5a79c1SDavid du Colombier if(de.mtime >= savetime && e.snap != 0)
822dc5a79c1SDavid du Colombier if(e.snap < *plo)
823dc5a79c1SDavid du Colombier *plo = e.snap;
824dc5a79c1SDavid du Colombier fileDecRef(ff);
825dc5a79c1SDavid du Colombier }
826dc5a79c1SDavid du Colombier }
827dc5a79c1SDavid du Colombier else if(de.mode & ModeDir){
828dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil){
829dc5a79c1SDavid du Colombier t = smprint("%s/%s", path, de.elem);
830dc5a79c1SDavid du Colombier n += fsEsearch1(ff, t, savetime, plo);
831dc5a79c1SDavid du Colombier vtMemFree(t);
832dc5a79c1SDavid du Colombier fileDecRef(ff);
833dc5a79c1SDavid du Colombier }
834dc5a79c1SDavid du Colombier }
835dc5a79c1SDavid du Colombier deCleanup(&de);
836dc5a79c1SDavid du Colombier if(r < 0)
837dc5a79c1SDavid du Colombier break;
838dc5a79c1SDavid du Colombier }
839dc5a79c1SDavid du Colombier deeClose(dee);
840dc5a79c1SDavid du Colombier
841dc5a79c1SDavid du Colombier return n;
842dc5a79c1SDavid du Colombier }
843dc5a79c1SDavid du Colombier
844dc5a79c1SDavid du Colombier static int
fsEsearch(Fs * fs,char * path,u32int savetime,u32int * plo)845dc5a79c1SDavid du Colombier fsEsearch(Fs *fs, char *path, u32int savetime, u32int *plo)
846dc5a79c1SDavid du Colombier {
847dc5a79c1SDavid du Colombier int n;
848dc5a79c1SDavid du Colombier File *f;
849dc5a79c1SDavid du Colombier DirEntry de;
850dc5a79c1SDavid du Colombier
851dc5a79c1SDavid du Colombier f = fileOpen(fs, path);
852dc5a79c1SDavid du Colombier if(f == nil)
853dc5a79c1SDavid du Colombier return 0;
854dc5a79c1SDavid du Colombier if(!fileGetDir(f, &de)){
855dc5a79c1SDavid du Colombier fileDecRef(f);
856dc5a79c1SDavid du Colombier return 0;
857dc5a79c1SDavid du Colombier }
858dc5a79c1SDavid du Colombier if((de.mode & ModeDir) == 0){
859dc5a79c1SDavid du Colombier fileDecRef(f);
860dc5a79c1SDavid du Colombier deCleanup(&de);
861dc5a79c1SDavid du Colombier return 0;
862dc5a79c1SDavid du Colombier }
863dc5a79c1SDavid du Colombier deCleanup(&de);
864dc5a79c1SDavid du Colombier n = fsEsearch1(f, path, savetime, plo);
865dc5a79c1SDavid du Colombier fileDecRef(f);
866dc5a79c1SDavid du Colombier return n;
867dc5a79c1SDavid du Colombier }
868dc5a79c1SDavid du Colombier
869dc5a79c1SDavid du Colombier void
fsSnapshotCleanup(Fs * fs,u32int age)870dc5a79c1SDavid du Colombier fsSnapshotCleanup(Fs *fs, u32int age)
871dc5a79c1SDavid du Colombier {
872dc5a79c1SDavid du Colombier u32int lo;
873dc5a79c1SDavid du Colombier
874dc5a79c1SDavid du Colombier /*
875dc5a79c1SDavid du Colombier * Find the best low epoch we can use,
876dc5a79c1SDavid du Colombier * given that we need to save all the unventied archives
877dc5a79c1SDavid du Colombier * and all the snapshots younger than age.
878dc5a79c1SDavid du Colombier */
879dc5a79c1SDavid du Colombier vtRLock(fs->elk);
880dc5a79c1SDavid du Colombier lo = fs->ehi;
881dc5a79c1SDavid du Colombier fsEsearch(fs, "/archive", 0, &lo);
882dc5a79c1SDavid du Colombier fsEsearch(fs, "/snapshot", time(0)-age*60, &lo);
883dc5a79c1SDavid du Colombier vtRUnlock(fs->elk);
884dc5a79c1SDavid du Colombier
885dc5a79c1SDavid du Colombier fsEpochLow(fs, lo);
886dc5a79c1SDavid du Colombier fsSnapshotRemove(fs);
887dc5a79c1SDavid du Colombier }
888dc5a79c1SDavid du Colombier
889dc5a79c1SDavid du Colombier /* remove all snapshots that have expired */
890dc5a79c1SDavid du Colombier /* return number of directory entries remaining */
891dc5a79c1SDavid du Colombier static int
fsRsearch1(File * f,char * s)892dc5a79c1SDavid du Colombier fsRsearch1(File *f, char *s)
893dc5a79c1SDavid du Colombier {
894dc5a79c1SDavid du Colombier int n, r;
895dc5a79c1SDavid du Colombier DirEntry de;
896dc5a79c1SDavid du Colombier DirEntryEnum *dee;
897dc5a79c1SDavid du Colombier File *ff;
898dc5a79c1SDavid du Colombier char *t;
899dc5a79c1SDavid du Colombier
900dc5a79c1SDavid du Colombier dee = deeOpen(f);
901dc5a79c1SDavid du Colombier if(dee == nil)
902dc5a79c1SDavid du Colombier return 0;
903dc5a79c1SDavid du Colombier
904dc5a79c1SDavid du Colombier n = 0;
905dc5a79c1SDavid du Colombier for(;;){
906dc5a79c1SDavid du Colombier r = deeRead(dee, &de);
907dc5a79c1SDavid du Colombier if(r <= 0)
908dc5a79c1SDavid du Colombier break;
909dc5a79c1SDavid du Colombier n++;
910dc5a79c1SDavid du Colombier if(de.mode & ModeSnapshot){
911dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil)
912dc5a79c1SDavid du Colombier fileDecRef(ff);
913dc5a79c1SDavid du Colombier else if(strcmp(vtGetError(), ESnapOld) == 0){
914dc5a79c1SDavid du Colombier if(fileClri(f, de.elem, "adm"))
915dc5a79c1SDavid du Colombier n--;
916dc5a79c1SDavid du Colombier }
917dc5a79c1SDavid du Colombier }
918dc5a79c1SDavid du Colombier else if(de.mode & ModeDir){
919dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil){
920dc5a79c1SDavid du Colombier t = smprint("%s/%s", s, de.elem);
921dc5a79c1SDavid du Colombier if(fsRsearch1(ff, t) == 0)
922dc5a79c1SDavid du Colombier if(fileRemove(ff, "adm"))
923dc5a79c1SDavid du Colombier n--;
924dc5a79c1SDavid du Colombier vtMemFree(t);
925dc5a79c1SDavid du Colombier fileDecRef(ff);
926dc5a79c1SDavid du Colombier }
927dc5a79c1SDavid du Colombier }
928dc5a79c1SDavid du Colombier deCleanup(&de);
929dc5a79c1SDavid du Colombier if(r < 0)
930dc5a79c1SDavid du Colombier break;
931dc5a79c1SDavid du Colombier }
932dc5a79c1SDavid du Colombier deeClose(dee);
933dc5a79c1SDavid du Colombier
934dc5a79c1SDavid du Colombier return n;
935dc5a79c1SDavid du Colombier }
936dc5a79c1SDavid du Colombier
937dc5a79c1SDavid du Colombier static int
fsRsearch(Fs * fs,char * path)938dc5a79c1SDavid du Colombier fsRsearch(Fs *fs, char *path)
939dc5a79c1SDavid du Colombier {
940dc5a79c1SDavid du Colombier File *f;
941dc5a79c1SDavid du Colombier DirEntry de;
942dc5a79c1SDavid du Colombier
943dc5a79c1SDavid du Colombier f = fileOpen(fs, path);
944dc5a79c1SDavid du Colombier if(f == nil)
945dc5a79c1SDavid du Colombier return 0;
946dc5a79c1SDavid du Colombier if(!fileGetDir(f, &de)){
947dc5a79c1SDavid du Colombier fileDecRef(f);
948dc5a79c1SDavid du Colombier return 0;
949dc5a79c1SDavid du Colombier }
950dc5a79c1SDavid du Colombier if((de.mode & ModeDir) == 0){
951dc5a79c1SDavid du Colombier fileDecRef(f);
952dc5a79c1SDavid du Colombier deCleanup(&de);
953dc5a79c1SDavid du Colombier return 0;
954dc5a79c1SDavid du Colombier }
955dc5a79c1SDavid du Colombier deCleanup(&de);
956dc5a79c1SDavid du Colombier fsRsearch1(f, path);
957dc5a79c1SDavid du Colombier fileDecRef(f);
958dc5a79c1SDavid du Colombier return 1;
959dc5a79c1SDavid du Colombier }
960dc5a79c1SDavid du Colombier
961dc5a79c1SDavid du Colombier void
fsSnapshotRemove(Fs * fs)962dc5a79c1SDavid du Colombier fsSnapshotRemove(Fs *fs)
963dc5a79c1SDavid du Colombier {
964dc5a79c1SDavid du Colombier vtRLock(fs->elk);
965dc5a79c1SDavid du Colombier fsRsearch(fs, "/snapshot");
966dc5a79c1SDavid du Colombier vtRUnlock(fs->elk);
967dc5a79c1SDavid du Colombier }
968dc5a79c1SDavid du Colombier
9695e96a66cSDavid du Colombier struct Snap
9705e96a66cSDavid du Colombier {
9715e96a66cSDavid du Colombier Fs *fs;
9725e96a66cSDavid du Colombier Periodic*tick;
9735e96a66cSDavid du Colombier VtLock *lk;
9745e96a66cSDavid du Colombier uint snapMinutes;
9755e96a66cSDavid du Colombier uint archMinute;
976dc5a79c1SDavid du Colombier uint snapLife;
9775e96a66cSDavid du Colombier u32int lastSnap;
9785e96a66cSDavid du Colombier u32int lastArch;
979dc5a79c1SDavid du Colombier u32int lastCleanup;
9805e96a66cSDavid du Colombier uint ignore;
9815e96a66cSDavid du Colombier };
9825e96a66cSDavid du Colombier
9835e96a66cSDavid du Colombier static void
snapEvent(void * v)9845e96a66cSDavid du Colombier snapEvent(void *v)
9855e96a66cSDavid du Colombier {
9865e96a66cSDavid du Colombier Snap *s;
9875e96a66cSDavid du Colombier u32int now, min;
9885e96a66cSDavid du Colombier Tm tm;
989dc5a79c1SDavid du Colombier int need;
99057195852SDavid du Colombier u32int snaplife;
9915e96a66cSDavid du Colombier
9925e96a66cSDavid du Colombier s = v;
9935e96a66cSDavid du Colombier
9945e96a66cSDavid du Colombier now = time(0)/60;
9955e96a66cSDavid du Colombier vtLock(s->lk);
9965e96a66cSDavid du Colombier
9975e96a66cSDavid du Colombier /*
9985e96a66cSDavid du Colombier * Snapshots happen every snapMinutes minutes.
9995e96a66cSDavid du Colombier * If we miss a snapshot (for example, because we
10005e96a66cSDavid du Colombier * were down), we wait for the next one.
10015e96a66cSDavid du Colombier */
10025e96a66cSDavid du Colombier if(s->snapMinutes != ~0 && s->snapMinutes != 0
10035e96a66cSDavid du Colombier && now%s->snapMinutes==0 && now != s->lastSnap){
1004c3c4501eSDavid du Colombier if(!fsSnapshot(s->fs, nil, nil, 0))
10053b86f2f8SDavid du Colombier fprint(2, "%s: fsSnapshot snap: %R\n", argv0);
10065e96a66cSDavid du Colombier s->lastSnap = now;
10075e96a66cSDavid du Colombier }
10085e96a66cSDavid du Colombier
10095e96a66cSDavid du Colombier /*
10105e96a66cSDavid du Colombier * Archival snapshots happen at archMinute.
1011dc5a79c1SDavid du Colombier * If we miss an archive (for example, because we
1012dc5a79c1SDavid du Colombier * were down), we do it as soon as possible.
10135e96a66cSDavid du Colombier */
10145e96a66cSDavid du Colombier tm = *localtime(now*60);
10155e96a66cSDavid du Colombier min = tm.hour*60+tm.min;
1016dc5a79c1SDavid du Colombier if(s->archMinute != ~0){
1017dc5a79c1SDavid du Colombier need = 0;
1018dc5a79c1SDavid du Colombier if(min == s->archMinute && now != s->lastArch)
1019dc5a79c1SDavid du Colombier need = 1;
1020dc5a79c1SDavid du Colombier if(s->lastArch == 0){
1021dc5a79c1SDavid du Colombier s->lastArch = 1;
1022dc5a79c1SDavid du Colombier if(fsNeedArch(s->fs, s->archMinute))
1023dc5a79c1SDavid du Colombier need = 1;
1024dc5a79c1SDavid du Colombier }
1025dc5a79c1SDavid du Colombier if(need){
1026c3c4501eSDavid du Colombier fsSnapshot(s->fs, nil, nil, 1);
10275e96a66cSDavid du Colombier s->lastArch = now;
10285e96a66cSDavid du Colombier }
1029dc5a79c1SDavid du Colombier }
1030dc5a79c1SDavid du Colombier
1031dc5a79c1SDavid du Colombier /*
1032dc5a79c1SDavid du Colombier * Snapshot cleanup happens every snaplife or every day.
1033dc5a79c1SDavid du Colombier */
103457195852SDavid du Colombier snaplife = s->snapLife;
103557195852SDavid du Colombier if(snaplife == ~0)
103657195852SDavid du Colombier snaplife = 24*60;
103757195852SDavid du Colombier if(s->lastCleanup+snaplife < now){
1038dc5a79c1SDavid du Colombier fsSnapshotCleanup(s->fs, s->snapLife);
1039dc5a79c1SDavid du Colombier s->lastCleanup = now;
1040dc5a79c1SDavid du Colombier }
10415e96a66cSDavid du Colombier vtUnlock(s->lk);
10425e96a66cSDavid du Colombier }
10435e96a66cSDavid du Colombier
10445e96a66cSDavid du Colombier static Snap*
snapInit(Fs * fs)10455e96a66cSDavid du Colombier snapInit(Fs *fs)
10465e96a66cSDavid du Colombier {
10475e96a66cSDavid du Colombier Snap *s;
10485e96a66cSDavid du Colombier
10495e96a66cSDavid du Colombier s = vtMemAllocZ(sizeof(Snap));
10505e96a66cSDavid du Colombier s->fs = fs;
10515e96a66cSDavid du Colombier s->tick = periodicAlloc(snapEvent, s, 10*1000);
10525e96a66cSDavid du Colombier s->lk = vtLockAlloc();
10535e96a66cSDavid du Colombier s->snapMinutes = -1;
10545e96a66cSDavid du Colombier s->archMinute = -1;
1055dc5a79c1SDavid du Colombier s->snapLife = -1;
10565e96a66cSDavid du Colombier s->ignore = 5*2; /* wait five minutes for clock to stabilize */
10575e96a66cSDavid du Colombier return s;
10585e96a66cSDavid du Colombier }
10595e96a66cSDavid du Colombier
10605e96a66cSDavid du Colombier void
snapGetTimes(Snap * s,u32int * arch,u32int * snap,u32int * snaplen)1061dc5a79c1SDavid du Colombier snapGetTimes(Snap *s, u32int *arch, u32int *snap, u32int *snaplen)
10625e96a66cSDavid du Colombier {
1063e569ccb5SDavid du Colombier if(s == nil){
1064e569ccb5SDavid du Colombier *snap = -1;
1065e569ccb5SDavid du Colombier *arch = -1;
1066e569ccb5SDavid du Colombier *snaplen = -1;
1067e569ccb5SDavid du Colombier return;
1068e569ccb5SDavid du Colombier }
1069e569ccb5SDavid du Colombier
10705e96a66cSDavid du Colombier vtLock(s->lk);
10715e96a66cSDavid du Colombier *snap = s->snapMinutes;
10725e96a66cSDavid du Colombier *arch = s->archMinute;
1073dc5a79c1SDavid du Colombier *snaplen = s->snapLife;
10745e96a66cSDavid du Colombier vtUnlock(s->lk);
10755e96a66cSDavid du Colombier }
10765e96a66cSDavid du Colombier
10775e96a66cSDavid du Colombier void
snapSetTimes(Snap * s,u32int arch,u32int snap,u32int snaplen)1078dc5a79c1SDavid du Colombier snapSetTimes(Snap *s, u32int arch, u32int snap, u32int snaplen)
10795e96a66cSDavid du Colombier {
1080e569ccb5SDavid du Colombier if(s == nil)
1081e569ccb5SDavid du Colombier return;
1082e569ccb5SDavid du Colombier
10835e96a66cSDavid du Colombier vtLock(s->lk);
10845e96a66cSDavid du Colombier s->snapMinutes = snap;
10855e96a66cSDavid du Colombier s->archMinute = arch;
1086dc5a79c1SDavid du Colombier s->snapLife = snaplen;
10875e96a66cSDavid du Colombier vtUnlock(s->lk);
10885e96a66cSDavid du Colombier }
10895e96a66cSDavid du Colombier
10905e96a66cSDavid du Colombier static void
snapClose(Snap * s)10915e96a66cSDavid du Colombier snapClose(Snap *s)
10925e96a66cSDavid du Colombier {
10935e96a66cSDavid du Colombier if(s == nil)
10945e96a66cSDavid du Colombier return;
10955e96a66cSDavid du Colombier
10965e96a66cSDavid du Colombier periodicKill(s->tick);
10975e96a66cSDavid du Colombier vtMemFree(s);
10985e96a66cSDavid du Colombier }
10995e96a66cSDavid du Colombier
1100