xref: /plan9/sys/src/cmd/fossil/fs.c (revision fe853e2326f51910bb38886e9bfc22ecdef993d7)
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){
34dc5a79c1SDavid du Colombier 		vtSetError("open %s: %r", file);
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){
41dc5a79c1SDavid du Colombier 		vtSetError("diskAlloc: %R");
425e96a66cSDavid du Colombier 		close(fd);
435e96a66cSDavid du Colombier 		return nil;
445e96a66cSDavid du Colombier 	}
455e96a66cSDavid du Colombier 
465e96a66cSDavid du Colombier 	fs = vtMemAllocZ(sizeof(Fs));
475e96a66cSDavid du Colombier 	fs->mode = mode;
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 
68dc5a79c1SDavid du Colombier //fprint(2, "fs->ehi %d fs->elo %d active=%d\n", 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;
785e96a66cSDavid du Colombier 		b = cacheLocalData(fs->cache, super.active, BtDir, RootTag, OReadWrite, 0);
79dc5a79c1SDavid du Colombier 		if(b == nil){
80dc5a79c1SDavid du Colombier 			vtSetError("cacheLocalData: %R");
815e96a66cSDavid du Colombier 			goto Err;
82dc5a79c1SDavid du Colombier 		}
835e96a66cSDavid du Colombier 		if(!(b->l.state&BsClosed) && b->l.epoch == fs->ehi){
845e96a66cSDavid du Colombier 			blockPut(b);
85dc5a79c1SDavid du Colombier 			vtSetError("bad root source block");
865e96a66cSDavid du Colombier 			goto Err;
875e96a66cSDavid du Colombier 		}
885e96a66cSDavid du Colombier 		b = blockCopy(b, RootTag, fs->ehi, fs->elo);
895e96a66cSDavid du Colombier 		if(b == nil)
905e96a66cSDavid du Colombier 			goto Err;
9161201b97SDavid du Colombier 		localToGlobal(super.active, oscore);
925e96a66cSDavid du Colombier 		super.active = b->addr;
935e96a66cSDavid du Colombier 		bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
945e96a66cSDavid du Colombier 		if(bs == nil){
955e96a66cSDavid du Colombier 			blockPut(b);
96dc5a79c1SDavid du Colombier 			vtSetError("cacheLocal: %R");
975e96a66cSDavid du Colombier 			goto Err;
985e96a66cSDavid du Colombier 		}
995e96a66cSDavid du Colombier 		superPack(&super, bs->data);
10061201b97SDavid du Colombier 		blockDependency(bs, b, 0, oscore, nil);
101*fe853e23SDavid du Colombier 		blockPut(b);
1025e96a66cSDavid du Colombier 		blockDirty(bs);
1035e96a66cSDavid du Colombier 		blockPut(bs);
1045e96a66cSDavid du Colombier 		fs->source = sourceRoot(fs, super.active, mode);
105dc5a79c1SDavid du Colombier 		if(fs->source == nil){
106dc5a79c1SDavid du Colombier 			vtSetError("sourceRoot: %R");
1075e96a66cSDavid du Colombier 			goto Err;
1085e96a66cSDavid du Colombier 		}
109dc5a79c1SDavid du Colombier 	}
1105e96a66cSDavid du Colombier 
111dc5a79c1SDavid du Colombier //fprint(2, "got fs source\n");
1125e96a66cSDavid du Colombier 
1135e96a66cSDavid du Colombier 	vtRLock(fs->elk);
1145e96a66cSDavid du Colombier 	fs->file = fileRoot(fs->source);
1155e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
116dc5a79c1SDavid du Colombier 	if(fs->file == nil){
117dc5a79c1SDavid du Colombier 		vtSetError("fileRoot: %R");
1185e96a66cSDavid du Colombier 		goto Err;
119dc5a79c1SDavid du Colombier 	}
1205e96a66cSDavid du Colombier 
121dc5a79c1SDavid du Colombier //fprint(2, "got file root\n");
1225e96a66cSDavid du Colombier 
1235e96a66cSDavid du Colombier 	if(mode == OReadWrite){
1245e96a66cSDavid du Colombier 		fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
1255e96a66cSDavid du Colombier 		fs->snap = snapInit(fs);
1265e96a66cSDavid du Colombier 	}
1275e96a66cSDavid du Colombier 	return fs;
1285e96a66cSDavid du Colombier 
1295e96a66cSDavid du Colombier Err:
130dc5a79c1SDavid du Colombier fprint(2, "fsOpen error\n");
1315e96a66cSDavid du Colombier 	fsClose(fs);
1325e96a66cSDavid du Colombier 	return nil;
1335e96a66cSDavid du Colombier }
1345e96a66cSDavid du Colombier 
1355e96a66cSDavid du Colombier void
1365e96a66cSDavid du Colombier fsClose(Fs *fs)
1375e96a66cSDavid du Colombier {
1385e96a66cSDavid du Colombier 	vtRLock(fs->elk);
1395e96a66cSDavid du Colombier 	periodicKill(fs->metaFlush);
1405e96a66cSDavid du Colombier 	snapClose(fs->snap);
1415e96a66cSDavid du Colombier 	if(fs->file){
1425e96a66cSDavid du Colombier 		fileMetaFlush(fs->file, 0);
1435e96a66cSDavid du Colombier 		if(!fileDecRef(fs->file))
1445e96a66cSDavid du Colombier 			vtFatal("fsClose: files still in use: %r\n");
1455e96a66cSDavid du Colombier 	}
1465e96a66cSDavid du Colombier 	fs->file = nil;
1475e96a66cSDavid du Colombier 	sourceClose(fs->source);
1485e96a66cSDavid du Colombier 	cacheFree(fs->cache);
1495e96a66cSDavid du Colombier 	if(fs->arch)
1505e96a66cSDavid du Colombier 		archFree(fs->arch);
1515e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
1525e96a66cSDavid du Colombier 	vtLockFree(fs->elk);
1535e96a66cSDavid du Colombier 	memset(fs, ~0, sizeof(Fs));
1545e96a66cSDavid du Colombier 	vtMemFree(fs);
1555e96a66cSDavid du Colombier }
1565e96a66cSDavid du Colombier 
1575e96a66cSDavid du Colombier int
1585e96a66cSDavid du Colombier fsRedial(Fs *fs, char *host)
1595e96a66cSDavid du Colombier {
1605e96a66cSDavid du Colombier 	if(!vtRedial(fs->z, host))
1615e96a66cSDavid du Colombier 		return 0;
1625e96a66cSDavid du Colombier 	if(!vtConnect(fs->z, 0))
1635e96a66cSDavid du Colombier 		return 0;
1645e96a66cSDavid du Colombier 	return 1;
1655e96a66cSDavid du Colombier }
1665e96a66cSDavid du Colombier 
1675e96a66cSDavid du Colombier File *
1685e96a66cSDavid du Colombier fsGetRoot(Fs *fs)
1695e96a66cSDavid du Colombier {
1705e96a66cSDavid du Colombier 	return fileIncRef(fs->file);
1715e96a66cSDavid du Colombier }
1725e96a66cSDavid du Colombier 
1735e96a66cSDavid du Colombier int
1745e96a66cSDavid du Colombier fsGetBlockSize(Fs *fs)
1755e96a66cSDavid du Colombier {
1765e96a66cSDavid du Colombier 	return fs->blockSize;
1775e96a66cSDavid du Colombier }
1785e96a66cSDavid du Colombier 
1795e96a66cSDavid du Colombier Block*
1805e96a66cSDavid du Colombier superGet(Cache *c, Super* super)
1815e96a66cSDavid du Colombier {
1825e96a66cSDavid du Colombier 	Block *b;
1835e96a66cSDavid du Colombier 
1845e96a66cSDavid du Colombier 	if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){
1855e96a66cSDavid du Colombier 		fprint(2, "superGet: cacheLocal failed: %R");
1865e96a66cSDavid du Colombier 		return nil;
1875e96a66cSDavid du Colombier 	}
1885e96a66cSDavid du Colombier 	if(!superUnpack(super, b->data)){
1895e96a66cSDavid du Colombier 		fprint(2, "superGet: superUnpack failed: %R");
1905e96a66cSDavid du Colombier 		blockPut(b);
1915e96a66cSDavid du Colombier 		return nil;
1925e96a66cSDavid du Colombier 	}
1935e96a66cSDavid du Colombier 
1945e96a66cSDavid du Colombier 	return b;
1955e96a66cSDavid du Colombier }
1965e96a66cSDavid du Colombier 
1975e96a66cSDavid du Colombier void
1985e96a66cSDavid du Colombier superPut(Block* b, Super* super, int forceWrite)
1995e96a66cSDavid du Colombier {
2005e96a66cSDavid du Colombier 	superPack(super, b->data);
2015e96a66cSDavid du Colombier 	blockDirty(b);
2025e96a66cSDavid du Colombier 	if(forceWrite){
2035e96a66cSDavid du Colombier 		while(!blockWrite(b)){
2045e96a66cSDavid du Colombier 			/* BUG: what should really happen here? */
2055e96a66cSDavid du Colombier 			fprint(2, "could not write super block; waiting 10 seconds\n");
2065e96a66cSDavid du Colombier 			sleep(10*000);
2075e96a66cSDavid du Colombier 		}
2085e96a66cSDavid du Colombier 		while(b->iostate != BioClean && b->iostate != BioDirty){
2095e96a66cSDavid du Colombier 			assert(b->iostate == BioWriting);
2105e96a66cSDavid du Colombier 			vtSleep(b->ioready);
2115e96a66cSDavid du Colombier 		}
2125e96a66cSDavid du Colombier 		/*
2135e96a66cSDavid du Colombier 		 * it's okay that b might still be dirty.
2145e96a66cSDavid du Colombier 		 * that means it got written out but with an old root pointer,
2155e96a66cSDavid du Colombier 		 * but the other fields went out, and those are the ones
2165e96a66cSDavid du Colombier 		 * we really care about.  (specifically, epochHigh; see fsSnapshot).
2175e96a66cSDavid du Colombier 		 */
2185e96a66cSDavid du Colombier 	}
2195e96a66cSDavid du Colombier 	blockPut(b);
2205e96a66cSDavid du Colombier }
2215e96a66cSDavid du Colombier 
2225e96a66cSDavid du Colombier /*
2235e96a66cSDavid du Colombier  * Prepare the directory to store a snapshot.
22461201b97SDavid du Colombier  * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#]
2255e96a66cSDavid du Colombier  * Archival snapshots go into /archive/yyyy/mmdd[.#].
2265e96a66cSDavid du Colombier  *
2275e96a66cSDavid du Colombier  * TODO This should be rewritten to eliminate most of the duplication.
2285e96a66cSDavid du Colombier  */
2295e96a66cSDavid du Colombier static File*
2305e96a66cSDavid du Colombier fileOpenSnapshot(Fs *fs, int doarchive)
2315e96a66cSDavid du Colombier {
2325e96a66cSDavid du Colombier 	int n;
2335e96a66cSDavid du Colombier 	char buf[30], *s;
2345e96a66cSDavid du Colombier 	File *dir, *f;
2355e96a66cSDavid du Colombier 	Tm now;
2365e96a66cSDavid du Colombier 
2375e96a66cSDavid du Colombier 	if(doarchive){
2385e96a66cSDavid du Colombier 		/*
2395e96a66cSDavid du Colombier 		 * a snapshot intended to be archived to venti.
2405e96a66cSDavid du Colombier 		 */
2415e96a66cSDavid du Colombier 		dir = fileOpen(fs, "/archive");
2425e96a66cSDavid du Colombier 		if(dir == nil)
2435e96a66cSDavid du Colombier 			return nil;
2445e96a66cSDavid du Colombier 		now = *localtime(time(0));
2455e96a66cSDavid du Colombier 
2465e96a66cSDavid du Colombier 		/* yyyy */
2475e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%d", now.year+1900);
2485e96a66cSDavid du Colombier 		f = fileWalk(dir, buf);
2495e96a66cSDavid du Colombier 		if(f == nil)
2505e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
2515e96a66cSDavid du Colombier 		fileDecRef(dir);
2525e96a66cSDavid du Colombier 		if(f == nil)
2535e96a66cSDavid du Colombier 			return nil;
2545e96a66cSDavid du Colombier 		dir = f;
2555e96a66cSDavid du Colombier 
2565e96a66cSDavid du Colombier 		/* mmdd[#] */
2575e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
2585e96a66cSDavid du Colombier 		s = buf+strlen(buf);
2595e96a66cSDavid du Colombier 		for(n=0;; n++){
2605e96a66cSDavid du Colombier 			if(n)
2615e96a66cSDavid du Colombier 				seprint(s, buf+sizeof(buf), ".%d", n);
2625e96a66cSDavid du Colombier 			f = fileWalk(dir, buf);
2635e96a66cSDavid du Colombier 			if(f != nil){
2645e96a66cSDavid du Colombier 				fileDecRef(f);
2655e96a66cSDavid du Colombier 				continue;
2665e96a66cSDavid du Colombier 			}
2675e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
2685e96a66cSDavid du Colombier 			break;
2695e96a66cSDavid du Colombier 		}
2705e96a66cSDavid du Colombier 		fileDecRef(dir);
2715e96a66cSDavid du Colombier 		return f;
2725e96a66cSDavid du Colombier 	}else{
2735e96a66cSDavid du Colombier 		/*
2745e96a66cSDavid du Colombier 		 * Just a temporary snapshot
2755e96a66cSDavid du Colombier 		 * We'll use /snapshot/yyyy/mmdd/hhmm.
2765e96a66cSDavid du Colombier 		 * There may well be a better naming scheme.
2775e96a66cSDavid du Colombier 		 * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.)
2785e96a66cSDavid du Colombier 		 */
2795e96a66cSDavid du Colombier 		dir = fileOpen(fs, "/snapshot");
2805e96a66cSDavid du Colombier 		if(dir == nil)
2815e96a66cSDavid du Colombier 			return nil;
2825e96a66cSDavid du Colombier 
2835e96a66cSDavid du Colombier 		now = *localtime(time(0));
2845e96a66cSDavid du Colombier 
2855e96a66cSDavid du Colombier 		/* yyyy */
2865e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%d", now.year+1900);
2875e96a66cSDavid du Colombier 		f = fileWalk(dir, buf);
2885e96a66cSDavid du Colombier 		if(f == nil)
2895e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
2905e96a66cSDavid du Colombier 		fileDecRef(dir);
2915e96a66cSDavid du Colombier 		if(f == nil)
2925e96a66cSDavid du Colombier 			return nil;
2935e96a66cSDavid du Colombier 		dir = f;
2945e96a66cSDavid du Colombier 
2955e96a66cSDavid du Colombier 		/* mmdd */
2965e96a66cSDavid du Colombier 		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
2975e96a66cSDavid du Colombier 		f = fileWalk(dir, buf);
2985e96a66cSDavid du Colombier 		if(f == nil)
2995e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
3005e96a66cSDavid du Colombier 		fileDecRef(dir);
3015e96a66cSDavid du Colombier 		if(f == nil)
3025e96a66cSDavid du Colombier 			return nil;
3035e96a66cSDavid du Colombier 		dir = f;
3045e96a66cSDavid du Colombier 
30561201b97SDavid du Colombier 		/* hhmm[.#] */
3065e96a66cSDavid du Colombier 		snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min);
30761201b97SDavid du Colombier 		s = buf+strlen(buf);
30861201b97SDavid du Colombier 		for(n=0;; n++){
30961201b97SDavid du Colombier 			if(n)
31061201b97SDavid du Colombier 				seprint(s, buf+sizeof(buf), ".%d", n);
3115e96a66cSDavid du Colombier 			f = fileWalk(dir, buf);
3125e96a66cSDavid du Colombier 			if(f != nil){
3135e96a66cSDavid du Colombier 				fileDecRef(f);
31461201b97SDavid du Colombier 				continue;
3155e96a66cSDavid du Colombier 			}
3165e96a66cSDavid du Colombier 			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
31761201b97SDavid du Colombier 			break;
31861201b97SDavid du Colombier 		}
3195e96a66cSDavid du Colombier 		fileDecRef(dir);
3205e96a66cSDavid du Colombier 		return f;
3215e96a66cSDavid du Colombier 	}
3225e96a66cSDavid du Colombier }
3235e96a66cSDavid du Colombier 
324dc5a79c1SDavid du Colombier static int
325dc5a79c1SDavid du Colombier fsNeedArch(Fs *fs, uint archMinute)
326dc5a79c1SDavid du Colombier {
327dc5a79c1SDavid du Colombier 	int need;
328dc5a79c1SDavid du Colombier 	File *f;
329dc5a79c1SDavid du Colombier 	char buf[100];
330dc5a79c1SDavid du Colombier 	Tm now;
331dc5a79c1SDavid du Colombier 	ulong then;
332dc5a79c1SDavid du Colombier 
333dc5a79c1SDavid du Colombier 	then = time(0);
334dc5a79c1SDavid du Colombier 	now = *localtime(then);
335dc5a79c1SDavid du Colombier 
336dc5a79c1SDavid du Colombier 	/* back up to yesterday if necessary */
337dc5a79c1SDavid du Colombier 	if(now.hour < archMinute/60
338dc5a79c1SDavid du Colombier 	|| now.hour == archMinute/60 && now.min < archMinute%60)
339dc5a79c1SDavid du Colombier 		now = *localtime(then-86400);
340dc5a79c1SDavid du Colombier 
341dc5a79c1SDavid du Colombier 	snprint(buf, sizeof buf, "/archive/%d/%02d%02d",
342dc5a79c1SDavid du Colombier 		now.year+1900, now.mon+1, now.mday);
343dc5a79c1SDavid du Colombier 	need = 1;
344dc5a79c1SDavid du Colombier 	vtRLock(fs->elk);
345dc5a79c1SDavid du Colombier 	f = fileOpen(fs, buf);
346dc5a79c1SDavid du Colombier 	if(f){
347dc5a79c1SDavid du Colombier 		need = 0;
348dc5a79c1SDavid du Colombier 		fileDecRef(f);
349dc5a79c1SDavid du Colombier 	}
350dc5a79c1SDavid du Colombier 	vtRUnlock(fs->elk);
351dc5a79c1SDavid du Colombier 	return need;
352dc5a79c1SDavid du Colombier }
353dc5a79c1SDavid du Colombier 
3545e96a66cSDavid du Colombier int
3555e96a66cSDavid du Colombier fsEpochLow(Fs *fs, u32int low)
3565e96a66cSDavid du Colombier {
3575e96a66cSDavid du Colombier 	Block *bs;
3585e96a66cSDavid du Colombier 	Super super;
3595e96a66cSDavid du Colombier 
3605e96a66cSDavid du Colombier 	vtLock(fs->elk);
3615e96a66cSDavid du Colombier 	if(low > fs->ehi){
3625e96a66cSDavid du Colombier 		vtSetError("bad low epoch (must be <= %ud)", fs->ehi);
3635e96a66cSDavid du Colombier 		vtUnlock(fs->elk);
3645e96a66cSDavid du Colombier 		return 0;
3655e96a66cSDavid du Colombier 	}
3665e96a66cSDavid du Colombier 
3675e96a66cSDavid du Colombier 	if((bs = superGet(fs->cache, &super)) == nil){
3685e96a66cSDavid du Colombier 		vtUnlock(fs->elk);
3695e96a66cSDavid du Colombier 		return 0;
3705e96a66cSDavid du Colombier 	}
3715e96a66cSDavid du Colombier 
3725e96a66cSDavid du Colombier 	super.epochLow = low;
3735e96a66cSDavid du Colombier 	fs->elo = low;
3745e96a66cSDavid du Colombier 	superPut(bs, &super, 1);
3755e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
3765e96a66cSDavid du Colombier 
3775e96a66cSDavid du Colombier 	return 1;
3785e96a66cSDavid du Colombier }
3795e96a66cSDavid du Colombier 
3805e96a66cSDavid du Colombier static int
3815e96a66cSDavid du Colombier bumpEpoch(Fs *fs, int doarchive)
3825e96a66cSDavid du Colombier {
38361201b97SDavid du Colombier 	uchar oscore[VtScoreSize];
3845e96a66cSDavid du Colombier 	u32int oldaddr;
3855e96a66cSDavid du Colombier 	Block *b, *bs;
3865e96a66cSDavid du Colombier 	Entry e;
3875e96a66cSDavid du Colombier 	Source *r;
3885e96a66cSDavid du Colombier 	Super super;
3895e96a66cSDavid du Colombier 
3905e96a66cSDavid du Colombier 	/*
3915e96a66cSDavid du Colombier 	 * Duplicate the root block.
3925e96a66cSDavid du Colombier 	 *
3935e96a66cSDavid du Colombier 	 * As a hint to flchk, the garbage collector,
3945e96a66cSDavid du Colombier 	 * and any (human) debuggers, store a pointer
3955e96a66cSDavid du Colombier 	 * to the old root block in entry 1 of the new root block.
3965e96a66cSDavid du Colombier 	 */
3975e96a66cSDavid du Colombier 	r = fs->source;
3985e96a66cSDavid du Colombier 	b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly);
3995e96a66cSDavid du Colombier 	if(b == nil)
4005e96a66cSDavid du Colombier 		return 0;
4015e96a66cSDavid du Colombier 
4025e96a66cSDavid du Colombier 	memset(&e, 0, sizeof e);
4035e96a66cSDavid du Colombier 	e.flags = VtEntryActive | VtEntryLocal | VtEntryDir;
4045e96a66cSDavid du Colombier 	memmove(e.score, b->score, VtScoreSize);
4055e96a66cSDavid du Colombier 	e.tag = RootTag;
4065e96a66cSDavid du Colombier 	e.snap = b->l.epoch;
4075e96a66cSDavid du Colombier 
4085e96a66cSDavid du Colombier 	b = blockCopy(b, RootTag, fs->ehi+1, fs->elo);
4095e96a66cSDavid du Colombier 	if(b == nil){
4105e96a66cSDavid du Colombier 		fprint(2, "bumpEpoch: blockCopy: %R\n");
4115e96a66cSDavid du Colombier 		return 0;
4125e96a66cSDavid du Colombier 	}
4135e96a66cSDavid du Colombier 
4145e96a66cSDavid du Colombier 	if(0) fprint(2, "snapshot root from %d to %d\n", oldaddr, b->addr);
4155e96a66cSDavid du Colombier 	entryPack(&e, b->data, 1);
4165e96a66cSDavid du Colombier 	blockDirty(b);
4175e96a66cSDavid du Colombier 
4185e96a66cSDavid du Colombier 	/*
4195e96a66cSDavid du Colombier 	 * Update the superblock with the new root and epoch.
4205e96a66cSDavid du Colombier 	 */
4215e96a66cSDavid du Colombier 	if((bs = superGet(fs->cache, &super)) == nil)
4225e96a66cSDavid du Colombier 		return 0;
4235e96a66cSDavid du Colombier 
4245e96a66cSDavid du Colombier 	fs->ehi++;
4255e96a66cSDavid du Colombier 	memmove(r->score, b->score, VtScoreSize);
4265e96a66cSDavid du Colombier 	r->epoch = fs->ehi;
4275e96a66cSDavid du Colombier 
4285e96a66cSDavid du Colombier 	super.epochHigh = fs->ehi;
4295e96a66cSDavid du Colombier 	oldaddr = super.active;
4305e96a66cSDavid du Colombier 	super.active = b->addr;
4315e96a66cSDavid du Colombier 	if(doarchive)
4325e96a66cSDavid du Colombier 		super.next = oldaddr;
4335e96a66cSDavid du Colombier 
4345e96a66cSDavid du Colombier 	/*
4355e96a66cSDavid du Colombier 	 * Record that the new super.active can't get written out until
4365e96a66cSDavid du Colombier 	 * the new b gets written out.  Until then, use the old value.
4375e96a66cSDavid du Colombier 	 */
43861201b97SDavid du Colombier 	localToGlobal(oldaddr, oscore);
43961201b97SDavid du Colombier 	blockDependency(bs, b, 0, oscore, nil);
4405e96a66cSDavid du Colombier 	blockPut(b);
4415e96a66cSDavid du Colombier 
4425e96a66cSDavid du Colombier 	/*
4435e96a66cSDavid du Colombier 	 * We force the super block to disk so that super.epochHigh gets updated.
4445e96a66cSDavid du Colombier 	 * Otherwise, if we crash and come back, we might incorrectly treat as active
4455e96a66cSDavid du Colombier 	 * some of the blocks that making up the snapshot we just created.
4465e96a66cSDavid du Colombier 	 * Basically every block in the active file system and all the blocks in
4475e96a66cSDavid du Colombier 	 * the recently-created snapshot depend on the super block now.
4485e96a66cSDavid du Colombier 	 * Rather than record all those dependencies, we just force the block to disk.
4495e96a66cSDavid du Colombier 	 *
4505e96a66cSDavid du Colombier 	 * Note that blockWrite might actually (will probably) send a slightly outdated
4515e96a66cSDavid du Colombier 	 * super.active to disk.  It will be the address of the most recent root that has
4525e96a66cSDavid du Colombier 	 * gone to disk.
4535e96a66cSDavid du Colombier 	 */
4545e96a66cSDavid du Colombier 	superPut(bs, &super, 1);
4555e96a66cSDavid du Colombier 
4565e96a66cSDavid du Colombier 	return 1;
4575e96a66cSDavid du Colombier }
4585e96a66cSDavid du Colombier 
4595e96a66cSDavid du Colombier int
4605e96a66cSDavid du Colombier saveQid(Fs *fs)
4615e96a66cSDavid du Colombier {
4625e96a66cSDavid du Colombier 	Block *b;
4635e96a66cSDavid du Colombier 	Super super;
4645e96a66cSDavid du Colombier 	u64int qidMax;
4655e96a66cSDavid du Colombier 
4665e96a66cSDavid du Colombier 	if((b = superGet(fs->cache, &super)) == nil)
4675e96a66cSDavid du Colombier 		return 0;
4685e96a66cSDavid du Colombier 	qidMax = super.qid;
4695e96a66cSDavid du Colombier 	blockPut(b);
4705e96a66cSDavid du Colombier 
4715e96a66cSDavid du Colombier 	if(!fileSetQidSpace(fs->file, 0, qidMax))
4725e96a66cSDavid du Colombier 		return 0;
4735e96a66cSDavid du Colombier 
4745e96a66cSDavid du Colombier 	return 1;
4755e96a66cSDavid du Colombier }
4765e96a66cSDavid du Colombier 
4775e96a66cSDavid du Colombier int
4785e96a66cSDavid du Colombier fsSnapshot(Fs *fs, int doarchive)
4795e96a66cSDavid du Colombier {
4805e96a66cSDavid du Colombier 	File *src, *dst;
4815e96a66cSDavid du Colombier 
4825e96a66cSDavid du Colombier 	assert(fs->mode == OReadWrite);
4835e96a66cSDavid du Colombier 
4845e96a66cSDavid du Colombier 	dst = nil;
4855e96a66cSDavid du Colombier 
48681cf8742SDavid du Colombier 	if(fs->halted){
48781cf8742SDavid du Colombier 		vtSetError("file system is halted");
48881cf8742SDavid du Colombier 		return 0;
48981cf8742SDavid du Colombier 	}
49081cf8742SDavid du Colombier 
4915e96a66cSDavid du Colombier 	/*
4925e96a66cSDavid du Colombier 	 * Freeze file system activity.
4935e96a66cSDavid du Colombier 	 */
4945e96a66cSDavid du Colombier 	vtLock(fs->elk);
4955e96a66cSDavid du Colombier 
4965e96a66cSDavid du Colombier 	/*
4975e96a66cSDavid du Colombier 	 * Get the root of the directory we're going to save.
4985e96a66cSDavid du Colombier 	 */
4995e96a66cSDavid du Colombier 	src = fileOpen(fs, "/active");
5005e96a66cSDavid du Colombier 	if(src == nil)
5015e96a66cSDavid du Colombier 		goto Err;
5025e96a66cSDavid du Colombier 
5035e96a66cSDavid du Colombier 	/*
5045e96a66cSDavid du Colombier 	 * It is important that we maintain the invariant that:
5055e96a66cSDavid du Colombier 	 *	if both b and bb are marked as Active with epoch e
5065e96a66cSDavid du Colombier 	 *	and b points at bb, then no other pointers to bb exist.
5075e96a66cSDavid du Colombier 	 *
5085e96a66cSDavid du Colombier 	 * The archiver uses this property to aggressively reclaim
5095e96a66cSDavid du Colombier 	 * such blocks once they have been stored on Venti, and
5105e96a66cSDavid du Colombier 	 * blockCleanup knows about this property as well.
5115e96a66cSDavid du Colombier 	 *
5125e96a66cSDavid du Colombier 	 * Let's say src->source is block sb, and src->msource is block
5135e96a66cSDavid du Colombier 	 * mb.  Let's also say that block b holds the Entry structures for
5145e96a66cSDavid du Colombier 	 * both src->source and src->msource (their Entry structures might
5155e96a66cSDavid du Colombier 	 * be in different blocks, but the argument is the same).
5165e96a66cSDavid du Colombier 	 * That is, right now we have:
5175e96a66cSDavid du Colombier 	 *
5185e96a66cSDavid du Colombier 	 *	b	Active w/ epoch e, holds ptrs to sb and mb.
5195e96a66cSDavid du Colombier 	 *	sb	Active w/ epoch e.
5205e96a66cSDavid du Colombier 	 *	mb	Active w/ epoch e.
5215e96a66cSDavid du Colombier 	 *
5225e96a66cSDavid du Colombier 	 * With things as they are now, the invariant requires that
5235e96a66cSDavid du Colombier 	 * b holds the only pointers to sb and mb.  We want to record
5245e96a66cSDavid du Colombier 	 * pointers to sb and mb in new Entries corresponding to dst,
5255e96a66cSDavid du Colombier 	 * which breaks the invariant.  Thus we need to do something
5265e96a66cSDavid du Colombier 	 * about b.  Specifically, we bump the file system's epoch and
5275e96a66cSDavid du Colombier 	 * then rewalk the path from the root down to and including b.
5285e96a66cSDavid du Colombier 	 * This will copy-on-write as we walk, so now the state will be:
5295e96a66cSDavid du Colombier 	 *
5305e96a66cSDavid du Colombier 	 *	b	Snap w/ epoch e, holds ptrs to sb and mb.
5315e96a66cSDavid du Colombier 	 *	new-b	Active w/ epoch e+1, holds ptrs to sb and mb.
5325e96a66cSDavid du Colombier 	 *	sb	Active w/ epoch e.
5335e96a66cSDavid du Colombier 	 *	mb	Active w/ epoch e.
5345e96a66cSDavid du Colombier 	 *
5355e96a66cSDavid du Colombier 	 * In this state, it's perfectly okay to add pointers to dst, which
5365e96a66cSDavid du Colombier 	 * will live in a block marked Active with epoch e+1.
5375e96a66cSDavid du Colombier 	 *
5385e96a66cSDavid du Colombier 	 * Of course, we need to make sure that the copied path makes
5395e96a66cSDavid du Colombier 	 * it out to disk before the new dst block; if the dst block goes out
5405e96a66cSDavid du Colombier 	 * first and then we crash, the invariant is violated.  Rather than
5415e96a66cSDavid du Colombier 	 * deal with the dependencies, we just sync the file system to disk
5425e96a66cSDavid du Colombier 	 * right now.
5435e96a66cSDavid du Colombier 	 */
5445e96a66cSDavid du Colombier 	if(!bumpEpoch(fs, 0) || !fileWalkSources(src))
5455e96a66cSDavid du Colombier 		goto Err;
5465e96a66cSDavid du Colombier 
5475e96a66cSDavid du Colombier 	/*
5485e96a66cSDavid du Colombier 	 * Sync to disk.
5495e96a66cSDavid du Colombier 	 */
5505e96a66cSDavid du Colombier 	cacheFlush(fs->cache, 1);
5515e96a66cSDavid du Colombier 
5525e96a66cSDavid du Colombier 	/*
5535e96a66cSDavid du Colombier 	 * Create the directory where we will store the copy of src.
5545e96a66cSDavid du Colombier 	 */
5555e96a66cSDavid du Colombier 	dst = fileOpenSnapshot(fs, doarchive);
5565e96a66cSDavid du Colombier 	if(dst == nil)
5575e96a66cSDavid du Colombier 		goto Err;
5585e96a66cSDavid du Colombier 
5595e96a66cSDavid du Colombier 	/*
5605e96a66cSDavid du Colombier 	 * Actually make the copy by setting dst's source and msource
5615e96a66cSDavid du Colombier 	 * to be src's.
5625e96a66cSDavid du Colombier 	 */
5635e96a66cSDavid du Colombier 	if(!fileSnapshot(dst, src, fs->ehi-1, doarchive))
5645e96a66cSDavid du Colombier 		goto Err;
5655e96a66cSDavid du Colombier 
5665e96a66cSDavid du Colombier 	fileDecRef(src);
5675e96a66cSDavid du Colombier 	fileDecRef(dst);
568fb7f0c93SDavid du Colombier 	src = nil;
569fb7f0c93SDavid du Colombier 	dst = nil;
570fb7f0c93SDavid du Colombier 
5715e96a66cSDavid du Colombier 	/*
5725e96a66cSDavid du Colombier 	 * Make another copy of the file system.  This one is for the
5735e96a66cSDavid du Colombier 	 * archiver, so that the file system we archive has the recently
5745e96a66cSDavid du Colombier 	 * added snapshot both in /active and in /archive/yyyy/mmdd[.#].
5755e96a66cSDavid du Colombier 	 */
5765e96a66cSDavid du Colombier 	if(doarchive){
5775e96a66cSDavid du Colombier 		if(!saveQid(fs))
5785e96a66cSDavid du Colombier 			goto Err;
5795e96a66cSDavid du Colombier 		if(!bumpEpoch(fs, 1))
5805e96a66cSDavid du Colombier 			goto Err;
5815e96a66cSDavid du Colombier 	}
5825e96a66cSDavid du Colombier 
5835e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
5845e96a66cSDavid du Colombier 
5855e96a66cSDavid du Colombier 	/* BUG? can fs->arch fall out from under us here? */
5865e96a66cSDavid du Colombier 	if(doarchive && fs->arch)
5875e96a66cSDavid du Colombier 		archKick(fs->arch);
5885e96a66cSDavid du Colombier 
5895e96a66cSDavid du Colombier 	return 1;
5905e96a66cSDavid du Colombier 
5915e96a66cSDavid du Colombier Err:
5925e96a66cSDavid du Colombier 	fprint(2, "fsSnapshot: %R\n");
5935e96a66cSDavid du Colombier 	if(src)
5945e96a66cSDavid du Colombier 		fileDecRef(src);
5955e96a66cSDavid du Colombier 	if(dst)
5965e96a66cSDavid du Colombier 		fileDecRef(dst);
5975e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
5985e96a66cSDavid du Colombier 	return 0;
5995e96a66cSDavid du Colombier }
6005e96a66cSDavid du Colombier 
6015e96a66cSDavid du Colombier int
6025e96a66cSDavid du Colombier fsVac(Fs *fs, char *name, uchar score[VtScoreSize])
6035e96a66cSDavid du Colombier {
6045e96a66cSDavid du Colombier 	int r;
6055e96a66cSDavid du Colombier 	DirEntry de;
6065e96a66cSDavid du Colombier 	Entry e, ee;
6075e96a66cSDavid du Colombier 	File *f;
6085e96a66cSDavid du Colombier 
6095e96a66cSDavid du Colombier 	vtRLock(fs->elk);
6105e96a66cSDavid du Colombier 	f = fileOpen(fs, name);
6115e96a66cSDavid du Colombier 	if(f == nil){
6125e96a66cSDavid du Colombier 		vtRUnlock(fs->elk);
6135e96a66cSDavid du Colombier 		return 0;
6145e96a66cSDavid du Colombier 	}
6155e96a66cSDavid du Colombier 
6165e96a66cSDavid du Colombier 	if(!fileGetSources(f, &e, &ee, 0) || !fileGetDir(f, &de)){
6175e96a66cSDavid du Colombier 		fileDecRef(f);
6185e96a66cSDavid du Colombier 		vtRUnlock(fs->elk);
6195e96a66cSDavid du Colombier 		return 0;
6205e96a66cSDavid du Colombier 	}
6215e96a66cSDavid du Colombier 	fileDecRef(f);
6225e96a66cSDavid du Colombier 
6235e96a66cSDavid du Colombier 	r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score);
6245e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
6255e96a66cSDavid du Colombier 	return r;
6265e96a66cSDavid du Colombier }
6275e96a66cSDavid du Colombier 
6285e96a66cSDavid du Colombier static int
6295e96a66cSDavid du Colombier vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize])
6305e96a66cSDavid du Colombier {
6315e96a66cSDavid du Colombier 	if(!vtWrite(z, score, type, buf, n))
6325e96a66cSDavid du Colombier 		return 0;
6335e96a66cSDavid du Colombier 	if(!vtSha1Check(score, buf, n))
6345e96a66cSDavid du Colombier 		return 0;
6355e96a66cSDavid du Colombier 	return 1;
6365e96a66cSDavid du Colombier }
6375e96a66cSDavid du Colombier 
6385e96a66cSDavid du Colombier int
6395e96a66cSDavid du Colombier mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize])
6405e96a66cSDavid du Colombier {
6415e96a66cSDavid du Colombier 	uchar buf[8192];
6425e96a66cSDavid du Colombier 	int i;
6435e96a66cSDavid du Colombier 	uchar *p;
6445e96a66cSDavid du Colombier 	uint n;
6455e96a66cSDavid du Colombier 	DirEntry de;
6465e96a66cSDavid du Colombier 	Entry e, ee, eee;
6475e96a66cSDavid du Colombier 	MetaBlock mb;
6485e96a66cSDavid du Colombier 	MetaEntry me;
6495e96a66cSDavid du Colombier 	VtRoot root;
6505e96a66cSDavid du Colombier 
6515e96a66cSDavid du Colombier 	e = *pe;
6525e96a66cSDavid du Colombier 	ee = *pee;
6535e96a66cSDavid du Colombier 	de = *pde;
6545e96a66cSDavid du Colombier 
6555e96a66cSDavid du Colombier 	if(globalToLocal(e.score) != NilBlock
6565e96a66cSDavid du Colombier 	|| (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){
6575e96a66cSDavid du Colombier 		vtSetError("can only vac paths already stored on venti");
6585e96a66cSDavid du Colombier 		return 0;
6595e96a66cSDavid du Colombier 	}
6605e96a66cSDavid du Colombier 
6615e96a66cSDavid du Colombier 	/*
6625e96a66cSDavid du Colombier 	 * Build metadata source for root.
6635e96a66cSDavid du Colombier 	 */
6645e96a66cSDavid du Colombier 	n = deSize(&de);
6655e96a66cSDavid du Colombier 	if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){
6665e96a66cSDavid du Colombier 		vtSetError("DirEntry too big");
6675e96a66cSDavid du Colombier 		return 0;
6685e96a66cSDavid du Colombier 	}
6695e96a66cSDavid du Colombier 	memset(buf, 0, sizeof buf);
6705e96a66cSDavid du Colombier 	mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1);
6715e96a66cSDavid du Colombier 	p = mbAlloc(&mb, n);
6725e96a66cSDavid du Colombier 	if(p == nil)
6735e96a66cSDavid du Colombier 		abort();
6745e96a66cSDavid du Colombier 	mbSearch(&mb, de.elem, &i, &me);
6755e96a66cSDavid du Colombier 	assert(me.p == nil);
6765e96a66cSDavid du Colombier 	me.p = p;
6775e96a66cSDavid du Colombier 	me.size = n;
6785e96a66cSDavid du Colombier 	dePack(&de, &me);
6795e96a66cSDavid du Colombier 	mbInsert(&mb, i, &me);
6805e96a66cSDavid du Colombier 	mbPack(&mb);
6815e96a66cSDavid du Colombier 
6825e96a66cSDavid du Colombier 	eee.size = n+MetaHeaderSize+MetaIndexSize;
6835e96a66cSDavid du Colombier 	if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score))
6845e96a66cSDavid du Colombier 		return 0;
6855e96a66cSDavid du Colombier 	eee.psize = 8192;
6865e96a66cSDavid du Colombier 	eee.dsize = 8192;
6875e96a66cSDavid du Colombier 	eee.depth = 0;
6885e96a66cSDavid du Colombier 	eee.flags = VtEntryActive;
6895e96a66cSDavid du Colombier 
6905e96a66cSDavid du Colombier 	/*
6915e96a66cSDavid du Colombier 	 * Build root source with three entries in it.
6925e96a66cSDavid du Colombier 	 */
6935e96a66cSDavid du Colombier 	entryPack(&e, buf, 0);
6945e96a66cSDavid du Colombier 	entryPack(&ee, buf, 1);
6955e96a66cSDavid du Colombier 	entryPack(&eee, buf, 2);
6965e96a66cSDavid du Colombier 
6975e96a66cSDavid du Colombier 	n = VtEntrySize*3;
6985e96a66cSDavid du Colombier 	memset(&root, 0, sizeof root);
6995e96a66cSDavid du Colombier 	if(!vtWriteBlock(z, buf, n, VtDirType, root.score))
7005e96a66cSDavid du Colombier 		return 0;
7015e96a66cSDavid du Colombier 
7025e96a66cSDavid du Colombier 	/*
7035e96a66cSDavid du Colombier 	 * Save root.
7045e96a66cSDavid du Colombier 	 */
7055e96a66cSDavid du Colombier 	root.version = VtRootVersion;
70634e04225SDavid du Colombier 	strecpy(root.type, root.type+sizeof root.type, "vac");
7075e96a66cSDavid du Colombier 	strecpy(root.name, root.name+sizeof root.name, de.elem);
7085e96a66cSDavid du Colombier 	root.blockSize = blockSize;
7095e96a66cSDavid du Colombier 	vtRootPack(&root, buf);
7105e96a66cSDavid du Colombier 	if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
7115e96a66cSDavid du Colombier 		return 0;
7125e96a66cSDavid du Colombier 
7135e96a66cSDavid du Colombier 	return 1;
7145e96a66cSDavid du Colombier }
7155e96a66cSDavid du Colombier 
7165e96a66cSDavid du Colombier int
7175e96a66cSDavid du Colombier fsSync(Fs *fs)
7185e96a66cSDavid du Colombier {
7195e96a66cSDavid du Colombier 	vtLock(fs->elk);
72081cf8742SDavid du Colombier 	fileMetaFlush(fs->file, 1);
7215e96a66cSDavid du Colombier 	cacheFlush(fs->cache, 1);
7225e96a66cSDavid du Colombier 	vtUnlock(fs->elk);
7235e96a66cSDavid du Colombier 	return 1;
7245e96a66cSDavid du Colombier }
7255e96a66cSDavid du Colombier 
7265e96a66cSDavid du Colombier int
72781cf8742SDavid du Colombier fsHalt(Fs *fs)
72881cf8742SDavid du Colombier {
72981cf8742SDavid du Colombier 	vtLock(fs->elk);
73081cf8742SDavid du Colombier 	fs->halted = 1;
73181cf8742SDavid du Colombier 	fileMetaFlush(fs->file, 1);
73281cf8742SDavid du Colombier 	cacheFlush(fs->cache, 1);
73381cf8742SDavid du Colombier 	return 1;
73481cf8742SDavid du Colombier }
73581cf8742SDavid du Colombier 
73681cf8742SDavid du Colombier int
73781cf8742SDavid du Colombier fsUnhalt(Fs *fs)
73881cf8742SDavid du Colombier {
73981cf8742SDavid du Colombier 	if(!fs->halted)
74081cf8742SDavid du Colombier 		return 0;
74181cf8742SDavid du Colombier 	fs->halted = 0;
74281cf8742SDavid du Colombier 	vtUnlock(fs->elk);
74381cf8742SDavid du Colombier 	return 1;
74481cf8742SDavid du Colombier }
74581cf8742SDavid du Colombier 
74681cf8742SDavid du Colombier int
7475e96a66cSDavid du Colombier fsNextQid(Fs *fs, u64int *qid)
7485e96a66cSDavid du Colombier {
7495e96a66cSDavid du Colombier 	Block *b;
7505e96a66cSDavid du Colombier 	Super super;
7515e96a66cSDavid du Colombier 
7525e96a66cSDavid du Colombier 	if((b = superGet(fs->cache, &super)) == nil)
7535e96a66cSDavid du Colombier 		return 0;
7545e96a66cSDavid du Colombier 
7555e96a66cSDavid du Colombier 	*qid = super.qid++;
7565e96a66cSDavid du Colombier 
7575e96a66cSDavid du Colombier 	/*
7585e96a66cSDavid du Colombier 	 * It's okay if the super block doesn't go to disk immediately,
7595e96a66cSDavid du Colombier 	 * since fileMetaAlloc will record a dependency between the
7605e96a66cSDavid du Colombier 	 * block holding this qid and the super block.  See file.c:/^fileMetaAlloc.
7615e96a66cSDavid du Colombier 	 */
7625e96a66cSDavid du Colombier 	superPut(b, &super, 0);
7635e96a66cSDavid du Colombier 	return 1;
7645e96a66cSDavid du Colombier }
7655e96a66cSDavid du Colombier 
7665e96a66cSDavid du Colombier static void
7675e96a66cSDavid du Colombier fsMetaFlush(void *a)
7685e96a66cSDavid du Colombier {
7695e96a66cSDavid du Colombier 	Fs *fs = a;
7705e96a66cSDavid du Colombier 
7715e96a66cSDavid du Colombier 	vtRLock(fs->elk);
7725e96a66cSDavid du Colombier 	fileMetaFlush(fs->file, 1);
7735e96a66cSDavid du Colombier 	vtRUnlock(fs->elk);
7745e96a66cSDavid du Colombier 	cacheFlush(fs->cache, 0);
7755e96a66cSDavid du Colombier }
7765e96a66cSDavid du Colombier 
777dc5a79c1SDavid du Colombier static int
778dc5a79c1SDavid du Colombier fsEsearch1(File *f, char *path, u32int savetime, u32int *plo)
779dc5a79c1SDavid du Colombier {
780dc5a79c1SDavid du Colombier 	int n, r;
781dc5a79c1SDavid du Colombier 	DirEntry de;
782dc5a79c1SDavid du Colombier 	DirEntryEnum *dee;
783dc5a79c1SDavid du Colombier 	File *ff;
784dc5a79c1SDavid du Colombier 	Entry e, ee;
785dc5a79c1SDavid du Colombier 	char *t;
786dc5a79c1SDavid du Colombier 
787dc5a79c1SDavid du Colombier 	dee = deeOpen(f);
788dc5a79c1SDavid du Colombier 	if(dee == nil)
789dc5a79c1SDavid du Colombier 		return 0;
790dc5a79c1SDavid du Colombier 
791dc5a79c1SDavid du Colombier 	n = 0;
792dc5a79c1SDavid du Colombier 	for(;;){
793dc5a79c1SDavid du Colombier 		r = deeRead(dee, &de);
794dc5a79c1SDavid du Colombier 		if(r <= 0)
795dc5a79c1SDavid du Colombier 			break;
796dc5a79c1SDavid du Colombier 		if(de.mode & ModeSnapshot){
797dc5a79c1SDavid du Colombier 			if((ff = fileWalk(f, de.elem)) != nil){
798dc5a79c1SDavid du Colombier 				if(fileGetSources(ff, &e, &ee, 0))
799dc5a79c1SDavid du Colombier 					if(de.mtime >= savetime && e.snap != 0)
800dc5a79c1SDavid du Colombier 						if(e.snap < *plo)
801dc5a79c1SDavid du Colombier 							*plo = e.snap;
802dc5a79c1SDavid du Colombier 				fileDecRef(ff);
803dc5a79c1SDavid du Colombier 			}
804dc5a79c1SDavid du Colombier 		}
805dc5a79c1SDavid du Colombier 		else if(de.mode & ModeDir){
806dc5a79c1SDavid du Colombier 			if((ff = fileWalk(f, de.elem)) != nil){
807dc5a79c1SDavid du Colombier 				t = smprint("%s/%s", path, de.elem);
808dc5a79c1SDavid du Colombier 				n += fsEsearch1(ff, t, savetime, plo);
809dc5a79c1SDavid du Colombier 				vtMemFree(t);
810dc5a79c1SDavid du Colombier 				fileDecRef(ff);
811dc5a79c1SDavid du Colombier 			}
812dc5a79c1SDavid du Colombier 		}
813dc5a79c1SDavid du Colombier 		deCleanup(&de);
814dc5a79c1SDavid du Colombier 		if(r < 0)
815dc5a79c1SDavid du Colombier 			break;
816dc5a79c1SDavid du Colombier 	}
817dc5a79c1SDavid du Colombier 	deeClose(dee);
818dc5a79c1SDavid du Colombier 
819dc5a79c1SDavid du Colombier 	return n;
820dc5a79c1SDavid du Colombier }
821dc5a79c1SDavid du Colombier 
822dc5a79c1SDavid du Colombier static int
823dc5a79c1SDavid du Colombier fsEsearch(Fs *fs, char *path, u32int savetime, u32int *plo)
824dc5a79c1SDavid du Colombier {
825dc5a79c1SDavid du Colombier 	int n;
826dc5a79c1SDavid du Colombier 	File *f;
827dc5a79c1SDavid du Colombier 	DirEntry de;
828dc5a79c1SDavid du Colombier 
829dc5a79c1SDavid du Colombier 	f = fileOpen(fs, path);
830dc5a79c1SDavid du Colombier 	if(f == nil)
831dc5a79c1SDavid du Colombier 		return 0;
832dc5a79c1SDavid du Colombier 	if(!fileGetDir(f, &de)){
833dc5a79c1SDavid du Colombier 		fileDecRef(f);
834dc5a79c1SDavid du Colombier 		return 0;
835dc5a79c1SDavid du Colombier 	}
836dc5a79c1SDavid du Colombier 	if((de.mode & ModeDir) == 0){
837dc5a79c1SDavid du Colombier 		fileDecRef(f);
838dc5a79c1SDavid du Colombier 		deCleanup(&de);
839dc5a79c1SDavid du Colombier 		return 0;
840dc5a79c1SDavid du Colombier 	}
841dc5a79c1SDavid du Colombier 	deCleanup(&de);
842dc5a79c1SDavid du Colombier 	n = fsEsearch1(f, path, savetime, plo);
843dc5a79c1SDavid du Colombier 	fileDecRef(f);
844dc5a79c1SDavid du Colombier 	return n;
845dc5a79c1SDavid du Colombier }
846dc5a79c1SDavid du Colombier 
847dc5a79c1SDavid du Colombier void
848dc5a79c1SDavid du Colombier fsSnapshotCleanup(Fs *fs, u32int age)
849dc5a79c1SDavid du Colombier {
850dc5a79c1SDavid du Colombier 	u32int lo;
851dc5a79c1SDavid du Colombier 
852dc5a79c1SDavid du Colombier 	/*
853dc5a79c1SDavid du Colombier 	 * Find the best low epoch we can use,
854dc5a79c1SDavid du Colombier 	 * given that we need to save all the unventied archives
855dc5a79c1SDavid du Colombier 	 * and all the snapshots younger than age.
856dc5a79c1SDavid du Colombier 	 */
857dc5a79c1SDavid du Colombier 	vtRLock(fs->elk);
858dc5a79c1SDavid du Colombier 	lo = fs->ehi;
859dc5a79c1SDavid du Colombier 	fsEsearch(fs, "/archive", 0, &lo);
860dc5a79c1SDavid du Colombier 	fsEsearch(fs, "/snapshot", time(0)-age*60, &lo);
861dc5a79c1SDavid du Colombier 	vtRUnlock(fs->elk);
862dc5a79c1SDavid du Colombier 
863dc5a79c1SDavid du Colombier 	fsEpochLow(fs, lo);
864dc5a79c1SDavid du Colombier 	fsSnapshotRemove(fs);
865dc5a79c1SDavid du Colombier }
866dc5a79c1SDavid du Colombier 
867dc5a79c1SDavid du Colombier /* remove all snapshots that have expired */
868dc5a79c1SDavid du Colombier /* return number of directory entries remaining */
869dc5a79c1SDavid du Colombier static int
870dc5a79c1SDavid du Colombier fsRsearch1(File *f, char *s)
871dc5a79c1SDavid du Colombier {
872dc5a79c1SDavid du Colombier 	int n, r;
873dc5a79c1SDavid du Colombier 	DirEntry de;
874dc5a79c1SDavid du Colombier 	DirEntryEnum *dee;
875dc5a79c1SDavid du Colombier 	File *ff;
876dc5a79c1SDavid du Colombier 	char *t;
877dc5a79c1SDavid du Colombier 
878dc5a79c1SDavid du Colombier 	dee = deeOpen(f);
879dc5a79c1SDavid du Colombier 	if(dee == nil)
880dc5a79c1SDavid du Colombier 		return 0;
881dc5a79c1SDavid du Colombier 
882dc5a79c1SDavid du Colombier 	n = 0;
883dc5a79c1SDavid du Colombier 	for(;;){
884dc5a79c1SDavid du Colombier 		r = deeRead(dee, &de);
885dc5a79c1SDavid du Colombier 		if(r <= 0)
886dc5a79c1SDavid du Colombier 			break;
887dc5a79c1SDavid du Colombier 		n++;
888dc5a79c1SDavid du Colombier 		if(de.mode & ModeSnapshot){
889dc5a79c1SDavid du Colombier 			if((ff = fileWalk(f, de.elem)) != nil)
890dc5a79c1SDavid du Colombier 				fileDecRef(ff);
891dc5a79c1SDavid du Colombier 			else if(strcmp(vtGetError(), ESnapOld) == 0){
892dc5a79c1SDavid du Colombier 				if(fileClri(f, de.elem, "adm"))
893dc5a79c1SDavid du Colombier 					n--;
894dc5a79c1SDavid du Colombier 			}
895dc5a79c1SDavid du Colombier 		}
896dc5a79c1SDavid du Colombier 		else if(de.mode & ModeDir){
897dc5a79c1SDavid du Colombier 			if((ff = fileWalk(f, de.elem)) != nil){
898dc5a79c1SDavid du Colombier 				t = smprint("%s/%s", s, de.elem);
899dc5a79c1SDavid du Colombier 				if(fsRsearch1(ff, t) == 0)
900dc5a79c1SDavid du Colombier 					if(fileRemove(ff, "adm"))
901dc5a79c1SDavid du Colombier 						n--;
902dc5a79c1SDavid du Colombier 				vtMemFree(t);
903dc5a79c1SDavid du Colombier 				fileDecRef(ff);
904dc5a79c1SDavid du Colombier 			}
905dc5a79c1SDavid du Colombier 		}
906dc5a79c1SDavid du Colombier 		deCleanup(&de);
907dc5a79c1SDavid du Colombier 		if(r < 0)
908dc5a79c1SDavid du Colombier 			break;
909dc5a79c1SDavid du Colombier 	}
910dc5a79c1SDavid du Colombier 	deeClose(dee);
911dc5a79c1SDavid du Colombier 
912dc5a79c1SDavid du Colombier 	return n;
913dc5a79c1SDavid du Colombier }
914dc5a79c1SDavid du Colombier 
915dc5a79c1SDavid du Colombier static int
916dc5a79c1SDavid du Colombier fsRsearch(Fs *fs, char *path)
917dc5a79c1SDavid du Colombier {
918dc5a79c1SDavid du Colombier 	File *f;
919dc5a79c1SDavid du Colombier 	DirEntry de;
920dc5a79c1SDavid du Colombier 
921dc5a79c1SDavid du Colombier 	f = fileOpen(fs, path);
922dc5a79c1SDavid du Colombier 	if(f == nil)
923dc5a79c1SDavid du Colombier 		return 0;
924dc5a79c1SDavid du Colombier 	if(!fileGetDir(f, &de)){
925dc5a79c1SDavid du Colombier 		fileDecRef(f);
926dc5a79c1SDavid du Colombier 		return 0;
927dc5a79c1SDavid du Colombier 	}
928dc5a79c1SDavid du Colombier 	if((de.mode & ModeDir) == 0){
929dc5a79c1SDavid du Colombier 		fileDecRef(f);
930dc5a79c1SDavid du Colombier 		deCleanup(&de);
931dc5a79c1SDavid du Colombier 		return 0;
932dc5a79c1SDavid du Colombier 	}
933dc5a79c1SDavid du Colombier 	deCleanup(&de);
934dc5a79c1SDavid du Colombier 	fsRsearch1(f, path);
935dc5a79c1SDavid du Colombier 	fileDecRef(f);
936dc5a79c1SDavid du Colombier 	return 1;
937dc5a79c1SDavid du Colombier }
938dc5a79c1SDavid du Colombier 
939dc5a79c1SDavid du Colombier void
940dc5a79c1SDavid du Colombier fsSnapshotRemove(Fs *fs)
941dc5a79c1SDavid du Colombier {
942dc5a79c1SDavid du Colombier 	vtRLock(fs->elk);
943dc5a79c1SDavid du Colombier 	fsRsearch(fs, "/snapshot");
944dc5a79c1SDavid du Colombier 	vtRUnlock(fs->elk);
945dc5a79c1SDavid du Colombier }
946dc5a79c1SDavid du Colombier 
9475e96a66cSDavid du Colombier struct Snap
9485e96a66cSDavid du Colombier {
9495e96a66cSDavid du Colombier 	Fs *fs;
9505e96a66cSDavid du Colombier 	Periodic *tick;
9515e96a66cSDavid du Colombier 	VtLock *lk;
9525e96a66cSDavid du Colombier 	uint snapMinutes;
9535e96a66cSDavid du Colombier 	uint archMinute;
954dc5a79c1SDavid du Colombier 	uint snapLife;
9555e96a66cSDavid du Colombier 	u32int lastSnap;
9565e96a66cSDavid du Colombier 	u32int lastArch;
957dc5a79c1SDavid du Colombier 	u32int lastCleanup;
9585e96a66cSDavid du Colombier 	uint ignore;
9595e96a66cSDavid du Colombier };
9605e96a66cSDavid du Colombier 
9615e96a66cSDavid du Colombier static void
9625e96a66cSDavid du Colombier snapEvent(void *v)
9635e96a66cSDavid du Colombier {
9645e96a66cSDavid du Colombier 	Snap *s;
9655e96a66cSDavid du Colombier 	u32int now, min;
9665e96a66cSDavid du Colombier 	Tm tm;
967dc5a79c1SDavid du Colombier 	int need;
9685e96a66cSDavid du Colombier 
9695e96a66cSDavid du Colombier 	s = v;
9705e96a66cSDavid du Colombier 
9715e96a66cSDavid du Colombier 	now = time(0)/60;
9725e96a66cSDavid du Colombier 	vtLock(s->lk);
9735e96a66cSDavid du Colombier 
9745e96a66cSDavid du Colombier 	/*
9755e96a66cSDavid du Colombier 	 * Snapshots happen every snapMinutes minutes.
9765e96a66cSDavid du Colombier 	 * If we miss a snapshot (for example, because we
9775e96a66cSDavid du Colombier 	 * were down), we wait for the next one.
9785e96a66cSDavid du Colombier 	 */
9795e96a66cSDavid du Colombier 	if(s->snapMinutes != ~0 && s->snapMinutes != 0
9805e96a66cSDavid du Colombier 	&& now%s->snapMinutes==0 && now != s->lastSnap){
9815e96a66cSDavid du Colombier 		if(!fsSnapshot(s->fs, 0))
9825e96a66cSDavid du Colombier 			fprint(2, "fsSnapshot snap: %R\n");
9835e96a66cSDavid du Colombier 		s->lastSnap = now;
9845e96a66cSDavid du Colombier 	}
9855e96a66cSDavid du Colombier 
9865e96a66cSDavid du Colombier 	/*
9875e96a66cSDavid du Colombier 	 * Archival snapshots happen at archMinute.
988dc5a79c1SDavid du Colombier 	 * If we miss an archive (for example, because we
989dc5a79c1SDavid du Colombier 	 * were down), we do it as soon as possible.
9905e96a66cSDavid du Colombier 	 */
9915e96a66cSDavid du Colombier 	tm = *localtime(now*60);
9925e96a66cSDavid du Colombier 	min = tm.hour*60+tm.min;
993dc5a79c1SDavid du Colombier 	if(s->archMinute != ~0){
994dc5a79c1SDavid du Colombier 		need = 0;
995dc5a79c1SDavid du Colombier 		if(min == s->archMinute && now != s->lastArch)
996dc5a79c1SDavid du Colombier 			need = 1;
997dc5a79c1SDavid du Colombier 		if(s->lastArch == 0){
998dc5a79c1SDavid du Colombier 			s->lastArch = 1;
999dc5a79c1SDavid du Colombier 			if(fsNeedArch(s->fs, s->archMinute))
1000dc5a79c1SDavid du Colombier 				need = 1;
1001dc5a79c1SDavid du Colombier 		}
1002dc5a79c1SDavid du Colombier 		if(need){
1003dc5a79c1SDavid du Colombier 			fsSnapshot(s->fs, 1);
10045e96a66cSDavid du Colombier 			s->lastArch = now;
10055e96a66cSDavid du Colombier 		}
1006dc5a79c1SDavid du Colombier 	}
1007dc5a79c1SDavid du Colombier 
1008dc5a79c1SDavid du Colombier 	/*
1009dc5a79c1SDavid du Colombier 	 * Snapshot cleanup happens every snaplife or every day.
1010dc5a79c1SDavid du Colombier 	 */
1011dc5a79c1SDavid du Colombier 	if(s->snapLife != ~0
1012dc5a79c1SDavid du Colombier 	&& (s->lastCleanup+s->snapLife < now || s->lastCleanup+24*60 < now)){
1013dc5a79c1SDavid du Colombier 		fsSnapshotCleanup(s->fs, s->snapLife);
1014dc5a79c1SDavid du Colombier 		s->lastCleanup = now;
1015dc5a79c1SDavid du Colombier 	}
10165e96a66cSDavid du Colombier 	vtUnlock(s->lk);
10175e96a66cSDavid du Colombier }
10185e96a66cSDavid du Colombier 
10195e96a66cSDavid du Colombier static Snap*
10205e96a66cSDavid du Colombier snapInit(Fs *fs)
10215e96a66cSDavid du Colombier {
10225e96a66cSDavid du Colombier 	Snap *s;
10235e96a66cSDavid du Colombier 
10245e96a66cSDavid du Colombier 	s = vtMemAllocZ(sizeof(Snap));
10255e96a66cSDavid du Colombier 	s->fs = fs;
10265e96a66cSDavid du Colombier 	s->tick = periodicAlloc(snapEvent, s, 10*1000);
10275e96a66cSDavid du Colombier 	s->lk = vtLockAlloc();
10285e96a66cSDavid du Colombier 	s->snapMinutes = -1;
10295e96a66cSDavid du Colombier 	s->archMinute = -1;
1030dc5a79c1SDavid du Colombier 	s->snapLife = -1;
10315e96a66cSDavid du Colombier 	s->ignore = 5*2;	/* wait five minutes for clock to stabilize */
10325e96a66cSDavid du Colombier 	return s;
10335e96a66cSDavid du Colombier }
10345e96a66cSDavid du Colombier 
10355e96a66cSDavid du Colombier void
1036dc5a79c1SDavid du Colombier snapGetTimes(Snap *s, u32int *arch, u32int *snap, u32int *snaplen)
10375e96a66cSDavid du Colombier {
10385e96a66cSDavid du Colombier 	vtLock(s->lk);
10395e96a66cSDavid du Colombier 	*snap = s->snapMinutes;
10405e96a66cSDavid du Colombier 	*arch = s->archMinute;
1041dc5a79c1SDavid du Colombier 	*snaplen = s->snapLife;
10425e96a66cSDavid du Colombier 	vtUnlock(s->lk);
10435e96a66cSDavid du Colombier }
10445e96a66cSDavid du Colombier 
10455e96a66cSDavid du Colombier void
1046dc5a79c1SDavid du Colombier snapSetTimes(Snap *s, u32int arch, u32int snap, u32int snaplen)
10475e96a66cSDavid du Colombier {
10485e96a66cSDavid du Colombier 	vtLock(s->lk);
10495e96a66cSDavid du Colombier 	s->snapMinutes = snap;
10505e96a66cSDavid du Colombier 	s->archMinute = arch;
1051dc5a79c1SDavid du Colombier 	s->snapLife = snaplen;
10525e96a66cSDavid du Colombier 	vtUnlock(s->lk);
10535e96a66cSDavid du Colombier }
10545e96a66cSDavid du Colombier 
10555e96a66cSDavid du Colombier static void
10565e96a66cSDavid du Colombier snapClose(Snap *s)
10575e96a66cSDavid du Colombier {
10585e96a66cSDavid du Colombier 	if(s == nil)
10595e96a66cSDavid du Colombier 		return;
10605e96a66cSDavid du Colombier 
10615e96a66cSDavid du Colombier 	periodicKill(s->tick);
10625e96a66cSDavid du Colombier 	vtMemFree(s);
10635e96a66cSDavid du Colombier }
10645e96a66cSDavid du Colombier 
1065