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