xref: /plan9/sys/src/cmd/fossil/fs.c (revision 81cf874224a4620dc66ce2d894718f8bb735f81d)
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 *
115e96a66cSDavid du Colombier fsOpen(char *file, VtSession *z, long ncache, int mode)
125e96a66cSDavid du Colombier {
135e96a66cSDavid du Colombier 	Fs *fs;
145e96a66cSDavid du Colombier 	Disk *disk;
155e96a66cSDavid du Colombier 	int fd;
165e96a66cSDavid du Colombier 	Block *b, *bs;
175e96a66cSDavid du Colombier 	Super super;
185e96a66cSDavid du Colombier 	int m;
1961201b97SDavid du Colombier 	uchar oscore[VtScoreSize];
205e96a66cSDavid du Colombier 
215e96a66cSDavid du Colombier 	switch(mode){
225e96a66cSDavid du Colombier 	default:
235e96a66cSDavid du Colombier 		vtSetError(EBadMode);
245e96a66cSDavid du Colombier 		return nil;
255e96a66cSDavid du Colombier 	case OReadOnly:
265e96a66cSDavid du Colombier 		m = OREAD;
275e96a66cSDavid du Colombier 		break;
285e96a66cSDavid du Colombier 	case OReadWrite:
295e96a66cSDavid du Colombier 		m = ORDWR;
305e96a66cSDavid du Colombier 		break;
315e96a66cSDavid du Colombier 	}
325e96a66cSDavid du Colombier 	fd = open(file, m);
335e96a66cSDavid du Colombier 	if(fd < 0){
345e96a66cSDavid du Colombier 		vtOSError();
355e96a66cSDavid du Colombier 		return nil;
365e96a66cSDavid du Colombier 	}
375e96a66cSDavid du Colombier 
385e96a66cSDavid du Colombier 	bwatchInit();
395e96a66cSDavid du Colombier 	disk = diskAlloc(fd);
405e96a66cSDavid du Colombier 	if(disk == nil){
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;
475e96a66cSDavid du Colombier 	fs->blockSize = diskBlockSize(disk);
485e96a66cSDavid du Colombier 	fs->elk = vtLockAlloc();
495e96a66cSDavid du Colombier 	fs->cache = cacheAlloc(disk, z, ncache, mode);
505e96a66cSDavid du Colombier 	if(mode == OReadWrite)
515e96a66cSDavid du Colombier 		fs->arch = archInit(fs->cache, disk, fs, z);
525e96a66cSDavid du Colombier 	fs->z = z;
535e96a66cSDavid du Colombier 
545e96a66cSDavid du Colombier 	b = cacheLocal(fs->cache, PartSuper, 0, mode);
555e96a66cSDavid du Colombier 	if(b == nil)
565e96a66cSDavid du Colombier 		goto Err;
575e96a66cSDavid du Colombier 	if(!superUnpack(&super, b->data)){
585e96a66cSDavid du Colombier 		blockPut(b);
595e96a66cSDavid du Colombier 		goto Err;
605e96a66cSDavid du Colombier 	}
615e96a66cSDavid du Colombier 	blockPut(b);
625e96a66cSDavid du Colombier 
635e96a66cSDavid du Colombier 	fs->ehi = super.epochHigh;
645e96a66cSDavid du Colombier 	fs->elo = super.epochLow;
655e96a66cSDavid du Colombier 
665e96a66cSDavid du Colombier fprint(2, "fs->ehi %d fs->elo %d active=%d\n", fs->ehi, fs->elo, super.active);
675e96a66cSDavid du Colombier 
685e96a66cSDavid du Colombier 	fs->source = sourceRoot(fs, super.active, mode);
695e96a66cSDavid du Colombier 	if(fs->source == nil){
705e96a66cSDavid du Colombier 		/*
715e96a66cSDavid du Colombier 		 * Perhaps it failed because the block is copy-on-write.
725e96a66cSDavid du Colombier 		 * Do the copy and try again.
735e96a66cSDavid du Colombier 		 */
745e96a66cSDavid du Colombier 		if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0)
755e96a66cSDavid du Colombier 			goto Err;
765e96a66cSDavid du Colombier 		b = cacheLocalData(fs->cache, super.active, BtDir, RootTag, OReadWrite, 0);
775e96a66cSDavid du Colombier 		if(b == nil)
785e96a66cSDavid du Colombier 			goto Err;
795e96a66cSDavid du Colombier 		if(!(b->l.state&BsClosed) && b->l.epoch == fs->ehi){
805e96a66cSDavid du Colombier 			blockPut(b);
815e96a66cSDavid du Colombier 			goto Err;
825e96a66cSDavid du Colombier 		}
835e96a66cSDavid du Colombier 		b = blockCopy(b, RootTag, fs->ehi, fs->elo);
845e96a66cSDavid du Colombier 		if(b == nil)
855e96a66cSDavid du Colombier 			goto Err;
8661201b97SDavid du Colombier 		localToGlobal(super.active, oscore);
875e96a66cSDavid du Colombier 		super.active = b->addr;
885e96a66cSDavid du Colombier 		bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
895e96a66cSDavid du Colombier 		if(bs == nil){
905e96a66cSDavid du Colombier 			blockPut(b);
915e96a66cSDavid du Colombier 			goto Err;
925e96a66cSDavid du Colombier 		}
935e96a66cSDavid du Colombier 		superPack(&super, bs->data);
9461201b97SDavid du Colombier 		blockDependency(bs, b, 0, oscore, nil);
955e96a66cSDavid du Colombier 		blockDirty(bs);
965e96a66cSDavid du Colombier 		blockPut(bs);
975e96a66cSDavid du Colombier 		blockPut(b);
985e96a66cSDavid du Colombier 		fs->source = sourceRoot(fs, super.active, mode);
995e96a66cSDavid du Colombier 		if(fs->source == nil)
1005e96a66cSDavid du Colombier 			goto Err;
1015e96a66cSDavid du Colombier 	}
1025e96a66cSDavid du Colombier 
1035e96a66cSDavid du Colombier fprint(2, "got fs source\n");
1045e96a66cSDavid du Colombier 
1055e96a66cSDavid du Colombier 	vtRLock(fs->elk);
1065e96a66cSDavid du Colombier 	fs->file = fileRoot(fs->source);
1075e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
1085e96a66cSDavid du Colombier 	if(fs->file == nil)
1095e96a66cSDavid du Colombier 		goto Err;
1105e96a66cSDavid du Colombier 
1115e96a66cSDavid du Colombier fprint(2, "got file root\n");
1125e96a66cSDavid du Colombier 
1135e96a66cSDavid du Colombier 	if(mode == OReadWrite){
1145e96a66cSDavid du Colombier 		fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
1155e96a66cSDavid du Colombier 		fs->snap = snapInit(fs);
1165e96a66cSDavid du Colombier 	}
1175e96a66cSDavid du Colombier 	return fs;
1185e96a66cSDavid du Colombier 
1195e96a66cSDavid du Colombier Err:
1205e96a66cSDavid du Colombier 	fsClose(fs);
1215e96a66cSDavid du Colombier 	return nil;
1225e96a66cSDavid du Colombier }
1235e96a66cSDavid du Colombier 
1245e96a66cSDavid du Colombier void
1255e96a66cSDavid du Colombier fsClose(Fs *fs)
1265e96a66cSDavid du Colombier {
1275e96a66cSDavid du Colombier 	vtRLock(fs->elk);
1285e96a66cSDavid du Colombier 	periodicKill(fs->metaFlush);
1295e96a66cSDavid du Colombier 	snapClose(fs->snap);
1305e96a66cSDavid du Colombier 	if(fs->file){
1315e96a66cSDavid du Colombier 		fileMetaFlush(fs->file, 0);
1325e96a66cSDavid du Colombier 		if(!fileDecRef(fs->file))
1335e96a66cSDavid du Colombier 			vtFatal("fsClose: files still in use: %r\n");
1345e96a66cSDavid du Colombier 	}
1355e96a66cSDavid du Colombier 	fs->file = nil;
1365e96a66cSDavid du Colombier 	sourceClose(fs->source);
1375e96a66cSDavid du Colombier 	cacheFree(fs->cache);
1385e96a66cSDavid du Colombier 	if(fs->arch)
1395e96a66cSDavid du Colombier 		archFree(fs->arch);
1405e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
1415e96a66cSDavid du Colombier 	vtLockFree(fs->elk);
1425e96a66cSDavid du Colombier 	memset(fs, ~0, sizeof(Fs));
1435e96a66cSDavid du Colombier 	vtMemFree(fs);
1445e96a66cSDavid du Colombier }
1455e96a66cSDavid du Colombier 
1465e96a66cSDavid du Colombier int
1475e96a66cSDavid du Colombier fsRedial(Fs *fs, char *host)
1485e96a66cSDavid du Colombier {
1495e96a66cSDavid du Colombier 	if(!vtRedial(fs->z, host))
1505e96a66cSDavid du Colombier 		return 0;
1515e96a66cSDavid du Colombier 	if(!vtConnect(fs->z, 0))
1525e96a66cSDavid du Colombier 		return 0;
1535e96a66cSDavid du Colombier 	return 1;
1545e96a66cSDavid du Colombier }
1555e96a66cSDavid du Colombier 
1565e96a66cSDavid du Colombier File *
1575e96a66cSDavid du Colombier fsGetRoot(Fs *fs)
1585e96a66cSDavid du Colombier {
1595e96a66cSDavid du Colombier 	return fileIncRef(fs->file);
1605e96a66cSDavid du Colombier }
1615e96a66cSDavid du Colombier 
1625e96a66cSDavid du Colombier int
1635e96a66cSDavid du Colombier fsGetBlockSize(Fs *fs)
1645e96a66cSDavid du Colombier {
1655e96a66cSDavid du Colombier 	return fs->blockSize;
1665e96a66cSDavid du Colombier }
1675e96a66cSDavid du Colombier 
1685e96a66cSDavid du Colombier Block*
1695e96a66cSDavid du Colombier superGet(Cache *c, Super* super)
1705e96a66cSDavid du Colombier {
1715e96a66cSDavid du Colombier 	Block *b;
1725e96a66cSDavid du Colombier 
1735e96a66cSDavid du Colombier 	if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){
1745e96a66cSDavid du Colombier 		fprint(2, "superGet: cacheLocal failed: %R");
1755e96a66cSDavid du Colombier 		return nil;
1765e96a66cSDavid du Colombier 	}
1775e96a66cSDavid du Colombier 	if(!superUnpack(super, b->data)){
1785e96a66cSDavid du Colombier 		fprint(2, "superGet: superUnpack failed: %R");
1795e96a66cSDavid du Colombier 		blockPut(b);
1805e96a66cSDavid du Colombier 		return nil;
1815e96a66cSDavid du Colombier 	}
1825e96a66cSDavid du Colombier 
1835e96a66cSDavid du Colombier 	return b;
1845e96a66cSDavid du Colombier }
1855e96a66cSDavid du Colombier 
1865e96a66cSDavid du Colombier void
1875e96a66cSDavid du Colombier superPut(Block* b, Super* super, int forceWrite)
1885e96a66cSDavid du Colombier {
1895e96a66cSDavid du Colombier 	superPack(super, b->data);
1905e96a66cSDavid du Colombier 	blockDirty(b);
1915e96a66cSDavid du Colombier 	if(forceWrite){
1925e96a66cSDavid du Colombier 		while(!blockWrite(b)){
1935e96a66cSDavid du Colombier 			/* BUG: what should really happen here? */
1945e96a66cSDavid du Colombier 			fprint(2, "could not write super block; waiting 10 seconds\n");
1955e96a66cSDavid du Colombier 			sleep(10*000);
1965e96a66cSDavid du Colombier 		}
1975e96a66cSDavid du Colombier 		while(b->iostate != BioClean && b->iostate != BioDirty){
1985e96a66cSDavid du Colombier 			assert(b->iostate == BioWriting);
1995e96a66cSDavid du Colombier 			vtSleep(b->ioready);
2005e96a66cSDavid du Colombier 		}
2015e96a66cSDavid du Colombier 		/*
2025e96a66cSDavid du Colombier 		 * it's okay that b might still be dirty.
2035e96a66cSDavid du Colombier 		 * that means it got written out but with an old root pointer,
2045e96a66cSDavid du Colombier 		 * but the other fields went out, and those are the ones
2055e96a66cSDavid du Colombier 		 * we really care about.  (specifically, epochHigh; see fsSnapshot).
2065e96a66cSDavid du Colombier 		 */
2075e96a66cSDavid du Colombier 	}
2085e96a66cSDavid du Colombier 	blockPut(b);
2095e96a66cSDavid du Colombier }
2105e96a66cSDavid du Colombier 
2115e96a66cSDavid du Colombier /*
2125e96a66cSDavid du Colombier  * Prepare the directory to store a snapshot.
21361201b97SDavid du Colombier  * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#]
2145e96a66cSDavid du Colombier  * Archival snapshots go into /archive/yyyy/mmdd[.#].
2155e96a66cSDavid du Colombier  *
2165e96a66cSDavid du Colombier  * TODO This should be rewritten to eliminate most of the duplication.
2175e96a66cSDavid du Colombier  */
2185e96a66cSDavid du Colombier static File*
2195e96a66cSDavid du Colombier fileOpenSnapshot(Fs *fs, int doarchive)
2205e96a66cSDavid du Colombier {
2215e96a66cSDavid du Colombier 	int n;
2225e96a66cSDavid du Colombier 	char buf[30], *s;
2235e96a66cSDavid du Colombier 	File *dir, *f;
2245e96a66cSDavid du Colombier 	Tm now;
2255e96a66cSDavid du Colombier 
2265e96a66cSDavid du Colombier 	if(doarchive){
2275e96a66cSDavid du Colombier 		/*
2285e96a66cSDavid du Colombier 		 * a snapshot intended to be archived to venti.
2295e96a66cSDavid du Colombier 		 */
2305e96a66cSDavid du Colombier 		dir = fileOpen(fs, "/archive");
2315e96a66cSDavid du Colombier 		if(dir == nil)
2325e96a66cSDavid du Colombier 			return nil;
2335e96a66cSDavid du Colombier 		now = *localtime(time(0));
2345e96a66cSDavid du Colombier 
2355e96a66cSDavid du Colombier 		/* yyyy */
2365e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%d", now.year+1900);
2375e96a66cSDavid du Colombier 		f = fileWalk(dir, buf);
2385e96a66cSDavid du Colombier 		if(f == nil)
2395e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
2405e96a66cSDavid du Colombier 		fileDecRef(dir);
2415e96a66cSDavid du Colombier 		if(f == nil)
2425e96a66cSDavid du Colombier 			return nil;
2435e96a66cSDavid du Colombier 		dir = f;
2445e96a66cSDavid du Colombier 
2455e96a66cSDavid du Colombier 		/* mmdd[#] */
2465e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
2475e96a66cSDavid du Colombier 		s = buf+strlen(buf);
2485e96a66cSDavid du Colombier 		for(n=0;; n++){
2495e96a66cSDavid du Colombier 			if(n)
2505e96a66cSDavid du Colombier 				seprint(s, buf+sizeof(buf), ".%d", n);
2515e96a66cSDavid du Colombier 			f = fileWalk(dir, buf);
2525e96a66cSDavid du Colombier 			if(f != nil){
2535e96a66cSDavid du Colombier 				fileDecRef(f);
2545e96a66cSDavid du Colombier 				continue;
2555e96a66cSDavid du Colombier 			}
2565e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
2575e96a66cSDavid du Colombier 			break;
2585e96a66cSDavid du Colombier 		}
2595e96a66cSDavid du Colombier 		fileDecRef(dir);
2605e96a66cSDavid du Colombier 		return f;
2615e96a66cSDavid du Colombier 	}else{
2625e96a66cSDavid du Colombier 		/*
2635e96a66cSDavid du Colombier 		 * Just a temporary snapshot
2645e96a66cSDavid du Colombier 		 * We'll use /snapshot/yyyy/mmdd/hhmm.
2655e96a66cSDavid du Colombier 		 * There may well be a better naming scheme.
2665e96a66cSDavid du Colombier 		 * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.)
2675e96a66cSDavid du Colombier 		 */
2685e96a66cSDavid du Colombier 		dir = fileOpen(fs, "/snapshot");
2695e96a66cSDavid du Colombier 		if(dir == nil)
2705e96a66cSDavid du Colombier 			return nil;
2715e96a66cSDavid du Colombier 
2725e96a66cSDavid du Colombier 		now = *localtime(time(0));
2735e96a66cSDavid du Colombier 
2745e96a66cSDavid du Colombier 		/* yyyy */
2755e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%d", now.year+1900);
2765e96a66cSDavid du Colombier 		f = fileWalk(dir, buf);
2775e96a66cSDavid du Colombier 		if(f == nil)
2785e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
2795e96a66cSDavid du Colombier 		fileDecRef(dir);
2805e96a66cSDavid du Colombier 		if(f == nil)
2815e96a66cSDavid du Colombier 			return nil;
2825e96a66cSDavid du Colombier 		dir = f;
2835e96a66cSDavid du Colombier 
2845e96a66cSDavid du Colombier 		/* mmdd */
2855e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
2865e96a66cSDavid du Colombier 		f = fileWalk(dir, buf);
2875e96a66cSDavid du Colombier 		if(f == nil)
2885e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
2895e96a66cSDavid du Colombier 		fileDecRef(dir);
2905e96a66cSDavid du Colombier 		if(f == nil)
2915e96a66cSDavid du Colombier 			return nil;
2925e96a66cSDavid du Colombier 		dir = f;
2935e96a66cSDavid du Colombier 
29461201b97SDavid du Colombier 		/* hhmm[.#] */
2955e96a66cSDavid du Colombier 		snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min);
29661201b97SDavid du Colombier 		s = buf+strlen(buf);
29761201b97SDavid du Colombier 		for(n=0;; n++){
29861201b97SDavid du Colombier 			if(n)
29961201b97SDavid du Colombier 				seprint(s, buf+sizeof(buf), ".%d", n);
3005e96a66cSDavid du Colombier 			f = fileWalk(dir, buf);
3015e96a66cSDavid du Colombier 			if(f != nil){
3025e96a66cSDavid du Colombier 				fileDecRef(f);
30361201b97SDavid du Colombier 				continue;
3045e96a66cSDavid du Colombier 			}
3055e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
30661201b97SDavid du Colombier 			break;
30761201b97SDavid du Colombier 		}
3085e96a66cSDavid du Colombier 		fileDecRef(dir);
3095e96a66cSDavid du Colombier 		return f;
3105e96a66cSDavid du Colombier 	}
3115e96a66cSDavid du Colombier }
3125e96a66cSDavid du Colombier 
3135e96a66cSDavid du Colombier int
3145e96a66cSDavid du Colombier fsEpochLow(Fs *fs, u32int low)
3155e96a66cSDavid du Colombier {
3165e96a66cSDavid du Colombier 	Block *bs;
3175e96a66cSDavid du Colombier 	Super super;
3185e96a66cSDavid du Colombier 
3195e96a66cSDavid du Colombier 	vtLock(fs->elk);
3205e96a66cSDavid du Colombier 	if(low > fs->ehi){
3215e96a66cSDavid du Colombier 		vtSetError("bad low epoch (must be <= %ud)", fs->ehi);
3225e96a66cSDavid du Colombier 		vtUnlock(fs->elk);
3235e96a66cSDavid du Colombier 		return 0;
3245e96a66cSDavid du Colombier 	}
3255e96a66cSDavid du Colombier 
3265e96a66cSDavid du Colombier 	if((bs = superGet(fs->cache, &super)) == nil){
3275e96a66cSDavid du Colombier 		vtUnlock(fs->elk);
3285e96a66cSDavid du Colombier 		return 0;
3295e96a66cSDavid du Colombier 	}
3305e96a66cSDavid du Colombier 
3315e96a66cSDavid du Colombier 	super.epochLow = low;
3325e96a66cSDavid du Colombier 	fs->elo = low;
3335e96a66cSDavid du Colombier 	superPut(bs, &super, 1);
3345e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
3355e96a66cSDavid du Colombier 
3365e96a66cSDavid du Colombier 	return 1;
3375e96a66cSDavid du Colombier }
3385e96a66cSDavid du Colombier 
3395e96a66cSDavid du Colombier static int
3405e96a66cSDavid du Colombier bumpEpoch(Fs *fs, int doarchive)
3415e96a66cSDavid du Colombier {
34261201b97SDavid du Colombier 	uchar oscore[VtScoreSize];
3435e96a66cSDavid du Colombier 	u32int oldaddr;
3445e96a66cSDavid du Colombier 	Block *b, *bs;
3455e96a66cSDavid du Colombier 	Entry e;
3465e96a66cSDavid du Colombier 	Source *r;
3475e96a66cSDavid du Colombier 	Super super;
3485e96a66cSDavid du Colombier 
3495e96a66cSDavid du Colombier 	/*
3505e96a66cSDavid du Colombier 	 * Duplicate the root block.
3515e96a66cSDavid du Colombier 	 *
3525e96a66cSDavid du Colombier 	 * As a hint to flchk, the garbage collector,
3535e96a66cSDavid du Colombier 	 * and any (human) debuggers, store a pointer
3545e96a66cSDavid du Colombier 	 * to the old root block in entry 1 of the new root block.
3555e96a66cSDavid du Colombier 	 */
3565e96a66cSDavid du Colombier 	r = fs->source;
3575e96a66cSDavid du Colombier 	b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly);
3585e96a66cSDavid du Colombier 	if(b == nil)
3595e96a66cSDavid du Colombier 		return 0;
3605e96a66cSDavid du Colombier 
3615e96a66cSDavid du Colombier 	memset(&e, 0, sizeof e);
3625e96a66cSDavid du Colombier 	e.flags = VtEntryActive | VtEntryLocal | VtEntryDir;
3635e96a66cSDavid du Colombier 	memmove(e.score, b->score, VtScoreSize);
3645e96a66cSDavid du Colombier 	e.tag = RootTag;
3655e96a66cSDavid du Colombier 	e.snap = b->l.epoch;
3665e96a66cSDavid du Colombier 
3675e96a66cSDavid du Colombier 	b = blockCopy(b, RootTag, fs->ehi+1, fs->elo);
3685e96a66cSDavid du Colombier 	if(b == nil){
3695e96a66cSDavid du Colombier 		fprint(2, "bumpEpoch: blockCopy: %R\n");
3705e96a66cSDavid du Colombier 		return 0;
3715e96a66cSDavid du Colombier 	}
3725e96a66cSDavid du Colombier 
3735e96a66cSDavid du Colombier 	if(0) fprint(2, "snapshot root from %d to %d\n", oldaddr, b->addr);
3745e96a66cSDavid du Colombier 	entryPack(&e, b->data, 1);
3755e96a66cSDavid du Colombier 	blockDirty(b);
3765e96a66cSDavid du Colombier 
3775e96a66cSDavid du Colombier 	/*
3785e96a66cSDavid du Colombier 	 * Update the superblock with the new root and epoch.
3795e96a66cSDavid du Colombier 	 */
3805e96a66cSDavid du Colombier 	if((bs = superGet(fs->cache, &super)) == nil)
3815e96a66cSDavid du Colombier 		return 0;
3825e96a66cSDavid du Colombier 
3835e96a66cSDavid du Colombier 	fs->ehi++;
3845e96a66cSDavid du Colombier 	memmove(r->score, b->score, VtScoreSize);
3855e96a66cSDavid du Colombier 	r->epoch = fs->ehi;
3865e96a66cSDavid du Colombier 
3875e96a66cSDavid du Colombier 	super.epochHigh = fs->ehi;
3885e96a66cSDavid du Colombier 	oldaddr = super.active;
3895e96a66cSDavid du Colombier 	super.active = b->addr;
3905e96a66cSDavid du Colombier 	if(doarchive)
3915e96a66cSDavid du Colombier 		super.next = oldaddr;
3925e96a66cSDavid du Colombier 
3935e96a66cSDavid du Colombier 	/*
3945e96a66cSDavid du Colombier 	 * Record that the new super.active can't get written out until
3955e96a66cSDavid du Colombier 	 * the new b gets written out.  Until then, use the old value.
3965e96a66cSDavid du Colombier 	 */
39761201b97SDavid du Colombier 	localToGlobal(oldaddr, oscore);
39861201b97SDavid du Colombier 	blockDependency(bs, b, 0, oscore, nil);
3995e96a66cSDavid du Colombier 	blockPut(b);
4005e96a66cSDavid du Colombier 
4015e96a66cSDavid du Colombier 	/*
4025e96a66cSDavid du Colombier 	 * We force the super block to disk so that super.epochHigh gets updated.
4035e96a66cSDavid du Colombier 	 * Otherwise, if we crash and come back, we might incorrectly treat as active
4045e96a66cSDavid du Colombier 	 * some of the blocks that making up the snapshot we just created.
4055e96a66cSDavid du Colombier 	 * Basically every block in the active file system and all the blocks in
4065e96a66cSDavid du Colombier 	 * the recently-created snapshot depend on the super block now.
4075e96a66cSDavid du Colombier 	 * Rather than record all those dependencies, we just force the block to disk.
4085e96a66cSDavid du Colombier 	 *
4095e96a66cSDavid du Colombier 	 * Note that blockWrite might actually (will probably) send a slightly outdated
4105e96a66cSDavid du Colombier 	 * super.active to disk.  It will be the address of the most recent root that has
4115e96a66cSDavid du Colombier 	 * gone to disk.
4125e96a66cSDavid du Colombier 	 */
4135e96a66cSDavid du Colombier 	superPut(bs, &super, 1);
4145e96a66cSDavid du Colombier 
4155e96a66cSDavid du Colombier 	return 1;
4165e96a66cSDavid du Colombier }
4175e96a66cSDavid du Colombier 
4185e96a66cSDavid du Colombier int
4195e96a66cSDavid du Colombier saveQid(Fs *fs)
4205e96a66cSDavid du Colombier {
4215e96a66cSDavid du Colombier 	Block *b;
4225e96a66cSDavid du Colombier 	Super super;
4235e96a66cSDavid du Colombier 	u64int qidMax;
4245e96a66cSDavid du Colombier 
4255e96a66cSDavid du Colombier 	if((b = superGet(fs->cache, &super)) == nil)
4265e96a66cSDavid du Colombier 		return 0;
4275e96a66cSDavid du Colombier 	qidMax = super.qid;
4285e96a66cSDavid du Colombier 	blockPut(b);
4295e96a66cSDavid du Colombier 
4305e96a66cSDavid du Colombier 	if(!fileSetQidSpace(fs->file, 0, qidMax))
4315e96a66cSDavid du Colombier 		return 0;
4325e96a66cSDavid du Colombier 
4335e96a66cSDavid du Colombier 	return 1;
4345e96a66cSDavid du Colombier }
4355e96a66cSDavid du Colombier 
4365e96a66cSDavid du Colombier int
4375e96a66cSDavid du Colombier fsSnapshot(Fs *fs, int doarchive)
4385e96a66cSDavid du Colombier {
4395e96a66cSDavid du Colombier 	File *src, *dst;
4405e96a66cSDavid du Colombier 
4415e96a66cSDavid du Colombier 	assert(fs->mode == OReadWrite);
4425e96a66cSDavid du Colombier 
4435e96a66cSDavid du Colombier 	dst = nil;
4445e96a66cSDavid du Colombier 
445*81cf8742SDavid du Colombier 	if(fs->halted){
446*81cf8742SDavid du Colombier 		vtSetError("file system is halted");
447*81cf8742SDavid du Colombier 		return 0;
448*81cf8742SDavid du Colombier 	}
449*81cf8742SDavid du Colombier 
4505e96a66cSDavid du Colombier 	/*
4515e96a66cSDavid du Colombier 	 * Freeze file system activity.
4525e96a66cSDavid du Colombier 	 */
4535e96a66cSDavid du Colombier 	vtLock(fs->elk);
4545e96a66cSDavid du Colombier 
4555e96a66cSDavid du Colombier 	/*
4565e96a66cSDavid du Colombier 	 * Get the root of the directory we're going to save.
4575e96a66cSDavid du Colombier 	 */
4585e96a66cSDavid du Colombier 	src = fileOpen(fs, "/active");
4595e96a66cSDavid du Colombier 	if(src == nil)
4605e96a66cSDavid du Colombier 		goto Err;
4615e96a66cSDavid du Colombier 
4625e96a66cSDavid du Colombier 	/*
4635e96a66cSDavid du Colombier 	 * It is important that we maintain the invariant that:
4645e96a66cSDavid du Colombier 	 *	if both b and bb are marked as Active with epoch e
4655e96a66cSDavid du Colombier 	 *	and b points at bb, then no other pointers to bb exist.
4665e96a66cSDavid du Colombier 	 *
4675e96a66cSDavid du Colombier 	 * The archiver uses this property to aggressively reclaim
4685e96a66cSDavid du Colombier 	 * such blocks once they have been stored on Venti, and
4695e96a66cSDavid du Colombier 	 * blockCleanup knows about this property as well.
4705e96a66cSDavid du Colombier 	 *
4715e96a66cSDavid du Colombier 	 * Let's say src->source is block sb, and src->msource is block
4725e96a66cSDavid du Colombier 	 * mb.  Let's also say that block b holds the Entry structures for
4735e96a66cSDavid du Colombier 	 * both src->source and src->msource (their Entry structures might
4745e96a66cSDavid du Colombier 	 * be in different blocks, but the argument is the same).
4755e96a66cSDavid du Colombier 	 * That is, right now we have:
4765e96a66cSDavid du Colombier 	 *
4775e96a66cSDavid du Colombier 	 *	b	Active w/ epoch e, holds ptrs to sb and mb.
4785e96a66cSDavid du Colombier 	 *	sb	Active w/ epoch e.
4795e96a66cSDavid du Colombier 	 *	mb	Active w/ epoch e.
4805e96a66cSDavid du Colombier 	 *
4815e96a66cSDavid du Colombier 	 * With things as they are now, the invariant requires that
4825e96a66cSDavid du Colombier 	 * b holds the only pointers to sb and mb.  We want to record
4835e96a66cSDavid du Colombier 	 * pointers to sb and mb in new Entries corresponding to dst,
4845e96a66cSDavid du Colombier 	 * which breaks the invariant.  Thus we need to do something
4855e96a66cSDavid du Colombier 	 * about b.  Specifically, we bump the file system's epoch and
4865e96a66cSDavid du Colombier 	 * then rewalk the path from the root down to and including b.
4875e96a66cSDavid du Colombier 	 * This will copy-on-write as we walk, so now the state will be:
4885e96a66cSDavid du Colombier 	 *
4895e96a66cSDavid du Colombier 	 *	b	Snap w/ epoch e, holds ptrs to sb and mb.
4905e96a66cSDavid du Colombier 	 *	new-b	Active w/ epoch e+1, holds ptrs to sb and mb.
4915e96a66cSDavid du Colombier 	 *	sb	Active w/ epoch e.
4925e96a66cSDavid du Colombier 	 *	mb	Active w/ epoch e.
4935e96a66cSDavid du Colombier 	 *
4945e96a66cSDavid du Colombier 	 * In this state, it's perfectly okay to add pointers to dst, which
4955e96a66cSDavid du Colombier 	 * will live in a block marked Active with epoch e+1.
4965e96a66cSDavid du Colombier 	 *
4975e96a66cSDavid du Colombier 	 * Of course, we need to make sure that the copied path makes
4985e96a66cSDavid du Colombier 	 * it out to disk before the new dst block; if the dst block goes out
4995e96a66cSDavid du Colombier 	 * first and then we crash, the invariant is violated.  Rather than
5005e96a66cSDavid du Colombier 	 * deal with the dependencies, we just sync the file system to disk
5015e96a66cSDavid du Colombier 	 * right now.
5025e96a66cSDavid du Colombier 	 */
5035e96a66cSDavid du Colombier 	if(!bumpEpoch(fs, 0) || !fileWalkSources(src))
5045e96a66cSDavid du Colombier 		goto Err;
5055e96a66cSDavid du Colombier 
5065e96a66cSDavid du Colombier 	/*
5075e96a66cSDavid du Colombier 	 * Sync to disk.
5085e96a66cSDavid du Colombier 	 */
5095e96a66cSDavid du Colombier 	cacheFlush(fs->cache, 1);
5105e96a66cSDavid du Colombier 
5115e96a66cSDavid du Colombier 	/*
5125e96a66cSDavid du Colombier 	 * Create the directory where we will store the copy of src.
5135e96a66cSDavid du Colombier 	 */
5145e96a66cSDavid du Colombier 	dst = fileOpenSnapshot(fs, doarchive);
5155e96a66cSDavid du Colombier 	if(dst == nil)
5165e96a66cSDavid du Colombier 		goto Err;
5175e96a66cSDavid du Colombier 
5185e96a66cSDavid du Colombier 	/*
5195e96a66cSDavid du Colombier 	 * Actually make the copy by setting dst's source and msource
5205e96a66cSDavid du Colombier 	 * to be src's.
5215e96a66cSDavid du Colombier 	 */
5225e96a66cSDavid du Colombier 	if(!fileSnapshot(dst, src, fs->ehi-1, doarchive))
5235e96a66cSDavid du Colombier 		goto Err;
5245e96a66cSDavid du Colombier 
5255e96a66cSDavid du Colombier 	fileDecRef(src);
5265e96a66cSDavid du Colombier 	fileDecRef(dst);
527fb7f0c93SDavid du Colombier 	src = nil;
528fb7f0c93SDavid du Colombier 	dst = nil;
529fb7f0c93SDavid du Colombier 
5305e96a66cSDavid du Colombier 	/*
5315e96a66cSDavid du Colombier 	 * Make another copy of the file system.  This one is for the
5325e96a66cSDavid du Colombier 	 * archiver, so that the file system we archive has the recently
5335e96a66cSDavid du Colombier 	 * added snapshot both in /active and in /archive/yyyy/mmdd[.#].
5345e96a66cSDavid du Colombier 	 */
5355e96a66cSDavid du Colombier 	if(doarchive){
5365e96a66cSDavid du Colombier 		if(!saveQid(fs))
5375e96a66cSDavid du Colombier 			goto Err;
5385e96a66cSDavid du Colombier 		if(!bumpEpoch(fs, 1))
5395e96a66cSDavid du Colombier 			goto Err;
5405e96a66cSDavid du Colombier 	}
5415e96a66cSDavid du Colombier 
5425e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
5435e96a66cSDavid du Colombier 
5445e96a66cSDavid du Colombier 	/* BUG? can fs->arch fall out from under us here? */
5455e96a66cSDavid du Colombier 	if(doarchive && fs->arch)
5465e96a66cSDavid du Colombier 		archKick(fs->arch);
5475e96a66cSDavid du Colombier 
5485e96a66cSDavid du Colombier 	return 1;
5495e96a66cSDavid du Colombier 
5505e96a66cSDavid du Colombier Err:
5515e96a66cSDavid du Colombier 	fprint(2, "fsSnapshot: %R\n");
5525e96a66cSDavid du Colombier 	if(src)
5535e96a66cSDavid du Colombier 		fileDecRef(src);
5545e96a66cSDavid du Colombier 	if(dst)
5555e96a66cSDavid du Colombier 		fileDecRef(dst);
5565e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
5575e96a66cSDavid du Colombier 	return 0;
5585e96a66cSDavid du Colombier }
5595e96a66cSDavid du Colombier 
5605e96a66cSDavid du Colombier int
5615e96a66cSDavid du Colombier fsVac(Fs *fs, char *name, uchar score[VtScoreSize])
5625e96a66cSDavid du Colombier {
5635e96a66cSDavid du Colombier 	int r;
5645e96a66cSDavid du Colombier 	DirEntry de;
5655e96a66cSDavid du Colombier 	Entry e, ee;
5665e96a66cSDavid du Colombier 	File *f;
5675e96a66cSDavid du Colombier 
5685e96a66cSDavid du Colombier 	vtRLock(fs->elk);
5695e96a66cSDavid du Colombier 	f = fileOpen(fs, name);
5705e96a66cSDavid du Colombier 	if(f == nil){
5715e96a66cSDavid du Colombier 		vtRUnlock(fs->elk);
5725e96a66cSDavid du Colombier 		return 0;
5735e96a66cSDavid du Colombier 	}
5745e96a66cSDavid du Colombier 
5755e96a66cSDavid du Colombier 	if(!fileGetSources(f, &e, &ee, 0) || !fileGetDir(f, &de)){
5765e96a66cSDavid du Colombier 		fileDecRef(f);
5775e96a66cSDavid du Colombier 		vtRUnlock(fs->elk);
5785e96a66cSDavid du Colombier 		return 0;
5795e96a66cSDavid du Colombier 	}
5805e96a66cSDavid du Colombier 	fileDecRef(f);
5815e96a66cSDavid du Colombier 
5825e96a66cSDavid du Colombier 	r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score);
5835e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
5845e96a66cSDavid du Colombier 	return r;
5855e96a66cSDavid du Colombier }
5865e96a66cSDavid du Colombier 
5875e96a66cSDavid du Colombier static int
5885e96a66cSDavid du Colombier vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize])
5895e96a66cSDavid du Colombier {
5905e96a66cSDavid du Colombier 	if(!vtWrite(z, score, type, buf, n))
5915e96a66cSDavid du Colombier 		return 0;
5925e96a66cSDavid du Colombier 	if(!vtSha1Check(score, buf, n))
5935e96a66cSDavid du Colombier 		return 0;
5945e96a66cSDavid du Colombier 	return 1;
5955e96a66cSDavid du Colombier }
5965e96a66cSDavid du Colombier 
5975e96a66cSDavid du Colombier int
5985e96a66cSDavid du Colombier mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize])
5995e96a66cSDavid du Colombier {
6005e96a66cSDavid du Colombier 	uchar buf[8192];
6015e96a66cSDavid du Colombier 	int i;
6025e96a66cSDavid du Colombier 	uchar *p;
6035e96a66cSDavid du Colombier 	uint n;
6045e96a66cSDavid du Colombier 	DirEntry de;
6055e96a66cSDavid du Colombier 	Entry e, ee, eee;
6065e96a66cSDavid du Colombier 	MetaBlock mb;
6075e96a66cSDavid du Colombier 	MetaEntry me;
6085e96a66cSDavid du Colombier 	VtRoot root;
6095e96a66cSDavid du Colombier 
6105e96a66cSDavid du Colombier 	e = *pe;
6115e96a66cSDavid du Colombier 	ee = *pee;
6125e96a66cSDavid du Colombier 	de = *pde;
6135e96a66cSDavid du Colombier 
6145e96a66cSDavid du Colombier 	if(globalToLocal(e.score) != NilBlock
6155e96a66cSDavid du Colombier 	|| (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){
6165e96a66cSDavid du Colombier 		vtSetError("can only vac paths already stored on venti");
6175e96a66cSDavid du Colombier 		return 0;
6185e96a66cSDavid du Colombier 	}
6195e96a66cSDavid du Colombier 
6205e96a66cSDavid du Colombier 	/*
6215e96a66cSDavid du Colombier 	 * Build metadata source for root.
6225e96a66cSDavid du Colombier 	 */
6235e96a66cSDavid du Colombier 	n = deSize(&de);
6245e96a66cSDavid du Colombier 	if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){
6255e96a66cSDavid du Colombier 		vtSetError("DirEntry too big");
6265e96a66cSDavid du Colombier 		return 0;
6275e96a66cSDavid du Colombier 	}
6285e96a66cSDavid du Colombier 	memset(buf, 0, sizeof buf);
6295e96a66cSDavid du Colombier 	mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1);
6305e96a66cSDavid du Colombier 	p = mbAlloc(&mb, n);
6315e96a66cSDavid du Colombier 	if(p == nil)
6325e96a66cSDavid du Colombier 		abort();
6335e96a66cSDavid du Colombier 	mbSearch(&mb, de.elem, &i, &me);
6345e96a66cSDavid du Colombier 	assert(me.p == nil);
6355e96a66cSDavid du Colombier 	me.p = p;
6365e96a66cSDavid du Colombier 	me.size = n;
6375e96a66cSDavid du Colombier 	dePack(&de, &me);
6385e96a66cSDavid du Colombier 	mbInsert(&mb, i, &me);
6395e96a66cSDavid du Colombier 	mbPack(&mb);
6405e96a66cSDavid du Colombier 
6415e96a66cSDavid du Colombier 	eee.size = n+MetaHeaderSize+MetaIndexSize;
6425e96a66cSDavid du Colombier 	if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score))
6435e96a66cSDavid du Colombier 		return 0;
6445e96a66cSDavid du Colombier 	eee.psize = 8192;
6455e96a66cSDavid du Colombier 	eee.dsize = 8192;
6465e96a66cSDavid du Colombier 	eee.depth = 0;
6475e96a66cSDavid du Colombier 	eee.flags = VtEntryActive;
6485e96a66cSDavid du Colombier 
6495e96a66cSDavid du Colombier 	/*
6505e96a66cSDavid du Colombier 	 * Build root source with three entries in it.
6515e96a66cSDavid du Colombier 	 */
6525e96a66cSDavid du Colombier 	entryPack(&e, buf, 0);
6535e96a66cSDavid du Colombier 	entryPack(&ee, buf, 1);
6545e96a66cSDavid du Colombier 	entryPack(&eee, buf, 2);
6555e96a66cSDavid du Colombier 
6565e96a66cSDavid du Colombier 	n = VtEntrySize*3;
6575e96a66cSDavid du Colombier 	memset(&root, 0, sizeof root);
6585e96a66cSDavid du Colombier 	if(!vtWriteBlock(z, buf, n, VtDirType, root.score))
6595e96a66cSDavid du Colombier 		return 0;
6605e96a66cSDavid du Colombier 
6615e96a66cSDavid du Colombier 	/*
6625e96a66cSDavid du Colombier 	 * Save root.
6635e96a66cSDavid du Colombier 	 */
6645e96a66cSDavid du Colombier 	root.version = VtRootVersion;
66534e04225SDavid du Colombier 	strecpy(root.type, root.type+sizeof root.type, "vac");
6665e96a66cSDavid du Colombier 	strecpy(root.name, root.name+sizeof root.name, de.elem);
6675e96a66cSDavid du Colombier 	root.blockSize = blockSize;
6685e96a66cSDavid du Colombier 	vtRootPack(&root, buf);
6695e96a66cSDavid du Colombier 	if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
6705e96a66cSDavid du Colombier 		return 0;
6715e96a66cSDavid du Colombier 
6725e96a66cSDavid du Colombier 	return 1;
6735e96a66cSDavid du Colombier }
6745e96a66cSDavid du Colombier 
6755e96a66cSDavid du Colombier int
6765e96a66cSDavid du Colombier fsSync(Fs *fs)
6775e96a66cSDavid du Colombier {
6785e96a66cSDavid du Colombier 	vtLock(fs->elk);
679*81cf8742SDavid du Colombier 	fileMetaFlush(fs->file, 1);
6805e96a66cSDavid du Colombier 	cacheFlush(fs->cache, 1);
6815e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
6825e96a66cSDavid du Colombier 	return 1;
6835e96a66cSDavid du Colombier }
6845e96a66cSDavid du Colombier 
6855e96a66cSDavid du Colombier int
686*81cf8742SDavid du Colombier fsHalt(Fs *fs)
687*81cf8742SDavid du Colombier {
688*81cf8742SDavid du Colombier 	vtLock(fs->elk);
689*81cf8742SDavid du Colombier 	fs->halted = 1;
690*81cf8742SDavid du Colombier 	fileMetaFlush(fs->file, 1);
691*81cf8742SDavid du Colombier 	cacheFlush(fs->cache, 1);
692*81cf8742SDavid du Colombier 	return 1;
693*81cf8742SDavid du Colombier }
694*81cf8742SDavid du Colombier 
695*81cf8742SDavid du Colombier int
696*81cf8742SDavid du Colombier fsUnhalt(Fs *fs)
697*81cf8742SDavid du Colombier {
698*81cf8742SDavid du Colombier 	if(!fs->halted)
699*81cf8742SDavid du Colombier 		return 0;
700*81cf8742SDavid du Colombier 	fs->halted = 0;
701*81cf8742SDavid du Colombier 	vtUnlock(fs->elk);
702*81cf8742SDavid du Colombier 	return 1;
703*81cf8742SDavid du Colombier }
704*81cf8742SDavid du Colombier 
705*81cf8742SDavid du Colombier int
7065e96a66cSDavid du Colombier fsNextQid(Fs *fs, u64int *qid)
7075e96a66cSDavid du Colombier {
7085e96a66cSDavid du Colombier 	Block *b;
7095e96a66cSDavid du Colombier 	Super super;
7105e96a66cSDavid du Colombier 
7115e96a66cSDavid du Colombier 	if((b = superGet(fs->cache, &super)) == nil)
7125e96a66cSDavid du Colombier 		return 0;
7135e96a66cSDavid du Colombier 
7145e96a66cSDavid du Colombier 	*qid = super.qid++;
7155e96a66cSDavid du Colombier 
7165e96a66cSDavid du Colombier 	/*
7175e96a66cSDavid du Colombier 	 * It's okay if the super block doesn't go to disk immediately,
7185e96a66cSDavid du Colombier 	 * since fileMetaAlloc will record a dependency between the
7195e96a66cSDavid du Colombier 	 * block holding this qid and the super block.  See file.c:/^fileMetaAlloc.
7205e96a66cSDavid du Colombier 	 */
7215e96a66cSDavid du Colombier 	superPut(b, &super, 0);
7225e96a66cSDavid du Colombier 	return 1;
7235e96a66cSDavid du Colombier }
7245e96a66cSDavid du Colombier 
7255e96a66cSDavid du Colombier static void
7265e96a66cSDavid du Colombier fsMetaFlush(void *a)
7275e96a66cSDavid du Colombier {
7285e96a66cSDavid du Colombier 	Fs *fs = a;
7295e96a66cSDavid du Colombier 
7305e96a66cSDavid du Colombier 	vtRLock(fs->elk);
7315e96a66cSDavid du Colombier 	fileMetaFlush(fs->file, 1);
7325e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
7335e96a66cSDavid du Colombier 	cacheFlush(fs->cache, 0);
7345e96a66cSDavid du Colombier }
7355e96a66cSDavid du Colombier 
7365e96a66cSDavid du Colombier struct Snap
7375e96a66cSDavid du Colombier {
7385e96a66cSDavid du Colombier 	Fs *fs;
7395e96a66cSDavid du Colombier 	Periodic *tick;
7405e96a66cSDavid du Colombier 	VtLock *lk;
7415e96a66cSDavid du Colombier 	uint snapMinutes;
7425e96a66cSDavid du Colombier 	uint archMinute;
7435e96a66cSDavid du Colombier 	u32int lastSnap;
7445e96a66cSDavid du Colombier 	u32int lastArch;
7455e96a66cSDavid du Colombier 	uint ignore;
7465e96a66cSDavid du Colombier };
7475e96a66cSDavid du Colombier 
7485e96a66cSDavid du Colombier static void
7495e96a66cSDavid du Colombier snapEvent(void *v)
7505e96a66cSDavid du Colombier {
7515e96a66cSDavid du Colombier 	Snap *s;
7525e96a66cSDavid du Colombier 	u32int now, min;
7535e96a66cSDavid du Colombier 	Tm tm;
7545e96a66cSDavid du Colombier 
7555e96a66cSDavid du Colombier 	s = v;
7565e96a66cSDavid du Colombier 
7575e96a66cSDavid du Colombier 	now = time(0)/60;
7585e96a66cSDavid du Colombier 	vtLock(s->lk);
7595e96a66cSDavid du Colombier 
7605e96a66cSDavid du Colombier 	/*
7615e96a66cSDavid du Colombier 	 * Snapshots happen every snapMinutes minutes.
7625e96a66cSDavid du Colombier 	 * If we miss a snapshot (for example, because we
7635e96a66cSDavid du Colombier 	 * were down), we wait for the next one.
7645e96a66cSDavid du Colombier 	 */
7655e96a66cSDavid du Colombier 	if(s->snapMinutes != ~0 && s->snapMinutes != 0
7665e96a66cSDavid du Colombier 	&& now%s->snapMinutes==0 && now != s->lastSnap){
7675e96a66cSDavid du Colombier if(0)fprint(2, "snapshot %02d%02d\n", now/60, now%60);
7685e96a66cSDavid du Colombier 		if(!fsSnapshot(s->fs, 0))
7695e96a66cSDavid du Colombier 			fprint(2, "fsSnapshot snap: %R\n");
7705e96a66cSDavid du Colombier 		s->lastSnap = now;
7715e96a66cSDavid du Colombier 	}
7725e96a66cSDavid du Colombier 
7735e96a66cSDavid du Colombier 	/*
7745e96a66cSDavid du Colombier 	 * Archival snapshots happen at archMinute.
7755e96a66cSDavid du Colombier 	 */
7765e96a66cSDavid du Colombier 	tm = *localtime(now*60);
7775e96a66cSDavid du Colombier 	min = tm.hour*60+tm.min;
7785e96a66cSDavid du Colombier 	if(s->archMinute != ~0 && min == s->archMinute && now != s->lastArch){
7795e96a66cSDavid du Colombier if(0)fprint(2, "archive %02d%02d\n", now/60, now%60);
7805e96a66cSDavid du Colombier 		if(!fsSnapshot(s->fs, 1))
7815e96a66cSDavid du Colombier 			fprint(2, "fsSnapshot arch: %R\n");
7825e96a66cSDavid du Colombier 		s->lastArch = now;
7835e96a66cSDavid du Colombier 	}
7845e96a66cSDavid du Colombier 	vtUnlock(s->lk);
7855e96a66cSDavid du Colombier }
7865e96a66cSDavid du Colombier 
7875e96a66cSDavid du Colombier static Snap*
7885e96a66cSDavid du Colombier snapInit(Fs *fs)
7895e96a66cSDavid du Colombier {
7905e96a66cSDavid du Colombier 	Snap *s;
7915e96a66cSDavid du Colombier 
7925e96a66cSDavid du Colombier 	s = vtMemAllocZ(sizeof(Snap));
7935e96a66cSDavid du Colombier 	s->fs = fs;
7945e96a66cSDavid du Colombier 	s->tick = periodicAlloc(snapEvent, s, 10*1000);
7955e96a66cSDavid du Colombier 	s->lk = vtLockAlloc();
7965e96a66cSDavid du Colombier 	s->snapMinutes = -1;
7975e96a66cSDavid du Colombier 	s->archMinute = -1;
7985e96a66cSDavid du Colombier 	s->ignore = 5*2;	/* wait five minutes for clock to stabilize */
7995e96a66cSDavid du Colombier 	return s;
8005e96a66cSDavid du Colombier }
8015e96a66cSDavid du Colombier 
8025e96a66cSDavid du Colombier void
8035e96a66cSDavid du Colombier snapGetTimes(Snap *s, u32int *arch, u32int *snap)
8045e96a66cSDavid du Colombier {
8055e96a66cSDavid du Colombier 	vtLock(s->lk);
8065e96a66cSDavid du Colombier 	*snap = s->snapMinutes;
8075e96a66cSDavid du Colombier 	*arch = s->archMinute;
8085e96a66cSDavid du Colombier 	vtUnlock(s->lk);
8095e96a66cSDavid du Colombier }
8105e96a66cSDavid du Colombier 
8115e96a66cSDavid du Colombier void
8125e96a66cSDavid du Colombier snapSetTimes(Snap *s, u32int arch, u32int snap)
8135e96a66cSDavid du Colombier {
8145e96a66cSDavid du Colombier 	vtLock(s->lk);
8155e96a66cSDavid du Colombier 	s->snapMinutes = snap;
8165e96a66cSDavid du Colombier 	s->archMinute = arch;
8175e96a66cSDavid du Colombier 	vtUnlock(s->lk);
8185e96a66cSDavid du Colombier }
8195e96a66cSDavid du Colombier 
8205e96a66cSDavid du Colombier static void
8215e96a66cSDavid du Colombier snapClose(Snap *s)
8225e96a66cSDavid du Colombier {
8235e96a66cSDavid du Colombier 	if(s == nil)
8245e96a66cSDavid du Colombier 		return;
8255e96a66cSDavid du Colombier 
8265e96a66cSDavid du Colombier 	periodicKill(s->tick);
8275e96a66cSDavid du Colombier 	vtMemFree(s);
8285e96a66cSDavid du Colombier }
8295e96a66cSDavid du Colombier 
830