xref: /plan9-contrib/sys/src/cmd/fossil/fs.c (revision d7aba6c3b511bc618cf0c53345848188fc02611a)
1 #include "stdinc.h"
2 #include "dat.h"
3 #include "fns.h"
4 #include "error.h"
5 
6 static void fsMetaFlush(void *a);
7 static Snap *snapInit(Fs*);
8 static void snapClose(Snap*);
9 
10 Fs *
fsOpen(char * file,VtConn * z,long ncache,int mode)11 fsOpen(char *file, VtConn *z, long ncache, int mode)
12 {
13 	int fd, m;
14 	uchar oscore[VtScoreSize];
15 	Block *b, *bs;
16 	Disk *disk;
17 	Fs *fs;
18 	Super super;
19 	char e[ERRMAX];
20 
21 	switch(mode){
22 	default:
23 		werrstr(EBadMode);
24 		return nil;
25 	case OReadOnly:
26 		m = OREAD;
27 		break;
28 	case OReadWrite:
29 		m = ORDWR;
30 		break;
31 	}
32 	fd = open(file, m);
33 	if(fd < 0){
34 		werrstr("open %s: %r", file);
35 		return nil;
36 	}
37 
38 	bwatchInit();
39 	disk = diskAlloc(fd);
40 	if(disk == nil){
41 		werrstr("diskAlloc: %r");
42 		close(fd);
43 		return nil;
44 	}
45 
46 	fs = vtmallocz(sizeof(Fs));
47 	fs->mode = mode;
48 	fs->name = vtstrdup(file);
49 	fs->blockSize = diskBlockSize(disk);
50 	fs->cache = cacheAlloc(disk, z, ncache, mode);
51 	if(mode == OReadWrite && z)
52 		fs->arch = archInit(fs->cache, disk, fs, z);
53 	fs->z = z;
54 
55 	b = cacheLocal(fs->cache, PartSuper, 0, mode);
56 	if(b == nil)
57 		goto Err;
58 	if(!superUnpack(&super, b->data)){
59 		blockPut(b);
60 		werrstr("bad super block");
61 		goto Err;
62 	}
63 	blockPut(b);
64 
65 	fs->ehi = super.epochHigh;
66 	fs->elo = super.epochLow;
67 
68 //fprint(2, "%s: fs->ehi %d fs->elo %d active=%d\n", argv0, fs->ehi, fs->elo, super.active);
69 
70 	fs->source = sourceRoot(fs, super.active, mode);
71 	if(fs->source == nil){
72 		/*
73 		 * Perhaps it failed because the block is copy-on-write.
74 		 * Do the copy and try again.
75 		 */
76 		rerrstr(e, sizeof e);
77 		if(mode == OReadOnly || strcmp(e, EBadRoot) != 0)
78 			goto Err;
79 		b = cacheLocalData(fs->cache, super.active, BtDir, RootTag,
80 			OReadWrite, 0);
81 		if(b == nil){
82 			werrstr("cacheLocalData: %r");
83 			goto Err;
84 		}
85 		if(b->l.epoch == fs->ehi){
86 			blockPut(b);
87 			werrstr("bad root source block");
88 			goto Err;
89 		}
90 		b = blockCopy(b, RootTag, fs->ehi, fs->elo);
91 		if(b == nil)
92 			goto Err;
93 		localToGlobal(super.active, oscore);
94 		super.active = b->addr;
95 		bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
96 		if(bs == nil){
97 			blockPut(b);
98 			werrstr("cacheLocal: %r");
99 			goto Err;
100 		}
101 		superPack(&super, bs->data);
102 		blockDependency(bs, b, 0, oscore, nil);
103 		blockPut(b);
104 		blockDirty(bs);
105 		blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
106 		blockPut(bs);
107 		fs->source = sourceRoot(fs, super.active, mode);
108 		if(fs->source == nil){
109 			werrstr("sourceRoot: %r");
110 			goto Err;
111 		}
112 	}
113 
114 //fprint(2, "%s: got fs source\n", argv0);
115 
116 	rlock(&fs->elk);
117 	fs->file = fileRoot(fs->source);
118 	fs->source->file = fs->file;		/* point back */
119 	runlock(&fs->elk);
120 	if(fs->file == nil){
121 		werrstr("fileRoot: %r");
122 		goto Err;
123 	}
124 
125 //fprint(2, "%s: got file root\n", argv0);
126 
127 	if(mode == OReadWrite){
128 		fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
129 		fs->snap = snapInit(fs);
130 	}
131 	return fs;
132 
133 Err:
134 fprint(2, "%s: fsOpen error\n", argv0);
135 	fsClose(fs);
136 	return nil;
137 }
138 
139 void
fsClose(Fs * fs)140 fsClose(Fs *fs)
141 {
142 	rlock(&fs->elk);
143 	periodicKill(fs->metaFlush);
144 	snapClose(fs->snap);
145 	if(fs->file){
146 		fileMetaFlush(fs->file, 0);
147 		if(!fileDecRef(fs->file))
148 			sysfatal("fsClose: files still in use: %r");
149 	}
150 	fs->file = nil;
151 	sourceClose(fs->source);
152 	cacheFree(fs->cache);
153 	if(fs->arch)
154 		archFree(fs->arch);
155 	vtfree(fs->name);
156 	runlock(&fs->elk);
157 	memset(fs, ~0, sizeof(Fs));
158 	vtfree(fs);
159 }
160 
161 int
fsRedial(Fs * fs,char * host)162 fsRedial(Fs *fs, char *host)
163 {
164 	if(vtredial(fs->z, host) < 0)
165 		return 0;
166 	if(vtconnect(fs->z) < 0)
167 		return 0;
168 	return 1;
169 }
170 
171 File *
fsGetRoot(Fs * fs)172 fsGetRoot(Fs *fs)
173 {
174 	return fileIncRef(fs->file);
175 }
176 
177 int
fsGetBlockSize(Fs * fs)178 fsGetBlockSize(Fs *fs)
179 {
180 	return fs->blockSize;
181 }
182 
183 Block*
superGet(Cache * c,Super * super)184 superGet(Cache *c, Super* super)
185 {
186 	Block *b;
187 
188 	if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){
189 		fprint(2, "%s: superGet: cacheLocal failed: %r\n", argv0);
190 		return nil;
191 	}
192 	if(!superUnpack(super, b->data)){
193 		fprint(2, "%s: superGet: superUnpack failed: %r\n", argv0);
194 		blockPut(b);
195 		return nil;
196 	}
197 
198 	return b;
199 }
200 
201 void
superWrite(Block * b,Super * super,int forceWrite)202 superWrite(Block* b, Super* super, int forceWrite)
203 {
204 	superPack(super, b->data);
205 	blockDirty(b);
206 	if(forceWrite){
207 		while(!blockWrite(b, Waitlock)){
208 			/* this should no longer happen */
209 			fprint(2, "%s: could not write super block; "
210 				"waiting 10 seconds\n", argv0);
211 			sleep(10*1000);
212 		}
213 		while(b->iostate != BioClean && b->iostate != BioDirty){
214 			assert(b->iostate == BioWriting);
215 			rsleep(&b->ioready);
216 		}
217 		/*
218 		 * it's okay that b might still be dirty.
219 		 * that means it got written out but with an old root pointer,
220 		 * but the other fields went out, and those are the ones
221 		 * we really care about.  (specifically, epochHigh; see fsSnapshot).
222 		 */
223 	}
224 }
225 
226 /*
227  * Prepare the directory to store a snapshot.
228  * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#]
229  * Archival snapshots go into /archive/yyyy/mmdd[.#].
230  *
231  * TODO This should be rewritten to eliminate most of the duplication.
232  */
233 static File*
fileOpenSnapshot(Fs * fs,char * dstpath,int doarchive)234 fileOpenSnapshot(Fs *fs, char *dstpath, int doarchive)
235 {
236 	int n;
237 	char buf[30], *s, *p, *elem;
238 	File *dir, *f;
239 	Tm now;
240 
241 	if(dstpath){
242 		if((p = strrchr(dstpath, '/')) != nil){
243 			*p++ = '\0';
244 			elem = p;
245 			p = dstpath;
246 			if(*p == '\0')
247 				p = "/";
248 		}else{
249 			p = "/";
250 			elem = dstpath;
251 		}
252 		if((dir = fileOpen(fs, p)) == nil)
253 			return nil;
254 		f = fileCreate(dir, elem, ModeDir|ModeSnapshot|0555, "adm");
255 		fileDecRef(dir);
256 		return f;
257 	}else if(doarchive){
258 		/*
259 		 * a snapshot intended to be archived to venti.
260 		 */
261 		dir = fileOpen(fs, "/archive");
262 		if(dir == nil)
263 			return nil;
264 		now = *localtime(time(0));
265 
266 		/* yyyy */
267 		snprint(buf, sizeof(buf), "%d", now.year+1900);
268 		f = fileWalk(dir, buf);
269 		if(f == nil)
270 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
271 		fileDecRef(dir);
272 		if(f == nil)
273 			return nil;
274 		dir = f;
275 
276 		/* mmdd[#] */
277 		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
278 		s = buf+strlen(buf);
279 		for(n=0;; n++){
280 			if(n)
281 				seprint(s, buf+sizeof(buf), ".%d", n);
282 			f = fileWalk(dir, buf);
283 			if(f != nil){
284 				fileDecRef(f);
285 				continue;
286 			}
287 			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
288 			break;
289 		}
290 		fileDecRef(dir);
291 		return f;
292 	}else{
293 		/*
294 		 * Just a temporary snapshot
295 		 * We'll use /snapshot/yyyy/mmdd/hhmm.
296 		 * There may well be a better naming scheme.
297 		 * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.)
298 		 */
299 		dir = fileOpen(fs, "/snapshot");
300 		if(dir == nil)
301 			return nil;
302 
303 		now = *localtime(time(0));
304 
305 		/* yyyy */
306 		snprint(buf, sizeof(buf), "%d", now.year+1900);
307 		f = fileWalk(dir, buf);
308 		if(f == nil)
309 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
310 		fileDecRef(dir);
311 		if(f == nil)
312 			return nil;
313 		dir = f;
314 
315 		/* mmdd */
316 		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
317 		f = fileWalk(dir, buf);
318 		if(f == nil)
319 			f = fileCreate(dir, buf, ModeDir|0555, "adm");
320 		fileDecRef(dir);
321 		if(f == nil)
322 			return nil;
323 		dir = f;
324 
325 		/* hhmm[.#] */
326 		snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min);
327 		s = buf+strlen(buf);
328 		for(n=0;; n++){
329 			if(n)
330 				seprint(s, buf+sizeof(buf), ".%d", n);
331 			f = fileWalk(dir, buf);
332 			if(f != nil){
333 				fileDecRef(f);
334 				continue;
335 			}
336 			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
337 			break;
338 		}
339 		fileDecRef(dir);
340 		return f;
341 	}
342 }
343 
344 static int
fsNeedArch(Fs * fs,uint archMinute)345 fsNeedArch(Fs *fs, uint archMinute)
346 {
347 	int need;
348 	File *f;
349 	char buf[100];
350 	Tm now;
351 	ulong then;
352 
353 	then = time(0);
354 	now = *localtime(then);
355 
356 	/* back up to yesterday if necessary */
357 	if(now.hour < archMinute/60
358 	|| now.hour == archMinute/60 && now.min < archMinute%60)
359 		now = *localtime(then-86400);
360 
361 	snprint(buf, sizeof buf, "/archive/%d/%02d%02d",
362 		now.year+1900, now.mon+1, now.mday);
363 	need = 1;
364 	rlock(&fs->elk);
365 	f = fileOpen(fs, buf);
366 	if(f){
367 		need = 0;
368 		fileDecRef(f);
369 	}
370 	runlock(&fs->elk);
371 	return need;
372 }
373 
374 int
fsEpochLow(Fs * fs,u32int low)375 fsEpochLow(Fs *fs, u32int low)
376 {
377 	Block *bs;
378 	Super super;
379 
380 	wlock(&fs->elk);
381 	if(low > fs->ehi){
382 		werrstr("bad low epoch (must be <= %ud)", fs->ehi);
383 		wunlock(&fs->elk);
384 		return 0;
385 	}
386 
387 	if((bs = superGet(fs->cache, &super)) == nil){
388 		wunlock(&fs->elk);
389 		return 0;
390 	}
391 
392 	super.epochLow = low;
393 	fs->elo = low;
394 	superWrite(bs, &super, 1);
395 	blockPut(bs);
396 	wunlock(&fs->elk);
397 
398 	return 1;
399 }
400 
401 static int
bumpEpoch(Fs * fs,int doarchive)402 bumpEpoch(Fs *fs, int doarchive)
403 {
404 	uchar oscore[VtScoreSize];
405 	u32int oldaddr;
406 	Block *b, *bs;
407 	Entry e;
408 	Source *r;
409 	Super super;
410 
411 	/*
412 	 * Duplicate the root block.
413 	 *
414 	 * As a hint to flchk, the garbage collector,
415 	 * and any (human) debuggers, store a pointer
416 	 * to the old root block in entry 1 of the new root block.
417 	 */
418 	r = fs->source;
419 	b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly);
420 	if(b == nil)
421 		return 0;
422 
423 	memset(&e, 0, sizeof e);
424 	e.flags = VtEntryActive | VtEntryLocal | _VtEntryDir;
425 	memmove(e.score, b->score, VtScoreSize);
426 	e.tag = RootTag;
427 	e.snap = b->l.epoch;
428 
429 	b = blockCopy(b, RootTag, fs->ehi+1, fs->elo);
430 	if(b == nil){
431 		fprint(2, "%s: bumpEpoch: blockCopy: %r\n", argv0);
432 		return 0;
433 	}
434 
435 	if(0) fprint(2, "%s: snapshot root from %d to %d\n", argv0, oldaddr, b->addr);
436 	entryPack(&e, b->data, 1);
437 	blockDirty(b);
438 
439 	/*
440 	 * Update the superblock with the new root and epoch.
441 	 */
442 	if((bs = superGet(fs->cache, &super)) == nil)
443 		return 0;
444 
445 	fs->ehi++;
446 	memmove(r->score, b->score, VtScoreSize);
447 	r->epoch = fs->ehi;
448 
449 	super.epochHigh = fs->ehi;
450 	oldaddr = super.active;
451 	super.active = b->addr;
452 	if(doarchive)
453 		super.next = oldaddr;
454 
455 	/*
456 	 * Record that the new super.active can't get written out until
457 	 * the new b gets written out.  Until then, use the old value.
458 	 */
459 	localToGlobal(oldaddr, oscore);
460 	blockDependency(bs, b, 0, oscore, nil);
461 	blockPut(b);
462 
463 	/*
464 	 * We force the super block to disk so that super.epochHigh gets updated.
465 	 * Otherwise, if we crash and come back, we might incorrectly treat as active
466 	 * some of the blocks that making up the snapshot we just created.
467 	 * Basically every block in the active file system and all the blocks in
468 	 * the recently-created snapshot depend on the super block now.
469 	 * Rather than record all those dependencies, we just force the block to disk.
470 	 *
471 	 * Note that blockWrite might actually (will probably) send a slightly outdated
472 	 * super.active to disk.  It will be the address of the most recent root that has
473 	 * gone to disk.
474 	 */
475 	superWrite(bs, &super, 1);
476 	blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
477 	blockPut(bs);
478 
479 	return 1;
480 }
481 
482 int
saveQid(Fs * fs)483 saveQid(Fs *fs)
484 {
485 	Block *b;
486 	Super super;
487 	u64int qidMax;
488 
489 	if((b = superGet(fs->cache, &super)) == nil)
490 		return 0;
491 	qidMax = super.qid;
492 	blockPut(b);
493 
494 	if(!fileSetQidSpace(fs->file, 0, qidMax))
495 		return 0;
496 
497 	return 1;
498 }
499 
500 int
fsSnapshot(Fs * fs,char * srcpath,char * dstpath,int doarchive)501 fsSnapshot(Fs *fs, char *srcpath, char *dstpath, int doarchive)
502 {
503 	File *src, *dst;
504 
505 	assert(fs->mode == OReadWrite);
506 
507 	dst = nil;
508 
509 	if(fs->halted){
510 		werrstr("file system is halted");
511 		return 0;
512 	}
513 
514 	/*
515 	 * Freeze file system activity.
516 	 */
517 	wlock(&fs->elk);
518 
519 	/*
520 	 * Get the root of the directory we're going to save.
521 	 */
522 	if(srcpath == nil)
523 		srcpath = "/active";
524 	src = fileOpen(fs, srcpath);
525 	if(src == nil)
526 		goto Err;
527 
528 	/*
529 	 * It is important that we maintain the invariant that:
530 	 *	if both b and bb are marked as Active with start epoch e
531 	 *	and b points at bb, then no other pointers to bb exist.
532 	 *
533 	 * When bb is unlinked from b, its close epoch is set to b's epoch.
534 	 * A block with epoch == close epoch is
535 	 * treated as free by cacheAllocBlock; this aggressively
536 	 * reclaims blocks after they have been stored to Venti.
537 	 *
538 	 * Let's say src->source is block sb, and src->msource is block
539 	 * mb.  Let's also say that block b holds the Entry structures for
540 	 * both src->source and src->msource (their Entry structures might
541 	 * be in different blocks, but the argument is the same).
542 	 * That is, right now we have:
543 	 *
544 	 *	b	Active w/ epoch e, holds ptrs to sb and mb.
545 	 *	sb	Active w/ epoch e.
546 	 *	mb	Active w/ epoch e.
547 	 *
548 	 * With things as they are now, the invariant requires that
549 	 * b holds the only pointers to sb and mb.  We want to record
550 	 * pointers to sb and mb in new Entries corresponding to dst,
551 	 * which breaks the invariant.  Thus we need to do something
552 	 * about b.  Specifically, we bump the file system's epoch and
553 	 * then rewalk the path from the root down to and including b.
554 	 * This will copy-on-write as we walk, so now the state will be:
555 	 *
556 	 *	b	Snap w/ epoch e, holds ptrs to sb and mb.
557 	 *	new-b	Active w/ epoch e+1, holds ptrs to sb and mb.
558 	 *	sb	Active w/ epoch e.
559 	 *	mb	Active w/ epoch e.
560 	 *
561 	 * In this state, it's perfectly okay to make more pointers to sb and mb.
562 	 */
563 	if(!bumpEpoch(fs, 0) || !fileWalkSources(src))
564 		goto Err;
565 
566 	/*
567 	 * Sync to disk.  I'm not sure this is necessary, but better safe than sorry.
568 	 */
569 	cacheFlush(fs->cache, 1);
570 
571 	/*
572 	 * Create the directory where we will store the copy of src.
573 	 */
574 	dst = fileOpenSnapshot(fs, dstpath, doarchive);
575 	if(dst == nil)
576 		goto Err;
577 
578 	/*
579 	 * Actually make the copy by setting dst's source and msource
580 	 * to be src's.
581 	 */
582 	if(!fileSnapshot(dst, src, fs->ehi-1, doarchive))
583 		goto Err;
584 
585 	fileDecRef(src);
586 	fileDecRef(dst);
587 	src = nil;
588 	dst = nil;
589 
590 	/*
591 	 * Make another copy of the file system.  This one is for the
592 	 * archiver, so that the file system we archive has the recently
593 	 * added snapshot both in /active and in /archive/yyyy/mmdd[.#].
594 	 */
595 	if(doarchive){
596 		if(!saveQid(fs))
597 			goto Err;
598 		if(!bumpEpoch(fs, 1))
599 			goto Err;
600 	}
601 
602 	wunlock(&fs->elk);
603 
604 	/* BUG? can fs->arch fall out from under us here? */
605 	if(doarchive && fs->arch)
606 		archKick(fs->arch);
607 
608 	return 1;
609 
610 Err:
611 	fprint(2, "%s: fsSnapshot: %r\n", argv0);
612 	if(src)
613 		fileDecRef(src);
614 	if(dst)
615 		fileDecRef(dst);
616 	wunlock(&fs->elk);
617 	return 0;
618 }
619 
620 int
fsVac(Fs * fs,char * name,uchar score[VtScoreSize])621 fsVac(Fs *fs, char *name, uchar score[VtScoreSize])
622 {
623 	int r;
624 	DirEntry de;
625 	Entry e, ee;
626 	File *f;
627 
628 	rlock(&fs->elk);
629 	f = fileOpen(fs, name);
630 	if(f == nil){
631 		runlock(&fs->elk);
632 		return 0;
633 	}
634 
635 	if(!fileGetSources(f, &e, &ee) || !fileGetDir(f, &de)){
636 		fileDecRef(f);
637 		runlock(&fs->elk);
638 		return 0;
639 	}
640 	fileDecRef(f);
641 
642 	r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score);
643 	runlock(&fs->elk);
644 	return r;
645 }
646 
647 static int
vtWriteBlock(VtConn * z,uchar * buf,uint n,uint type,uchar score[VtScoreSize])648 vtWriteBlock(VtConn *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize])
649 {
650 	if(vtwrite(z, score, type, buf, n) < 0)
651 		return 0;
652 	if(vtsha1check(score, buf, n) < 0)
653 		return 0;
654 	return 1;
655 }
656 
657 int
mkVac(VtConn * z,uint blockSize,Entry * pe,Entry * pee,DirEntry * pde,uchar score[VtScoreSize])658 mkVac(VtConn *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize])
659 {
660 	uchar buf[8192];
661 	int i;
662 	uchar *p;
663 	uint n;
664 	DirEntry de;
665 	Entry e, ee, eee;
666 	MetaBlock mb;
667 	MetaEntry me;
668 	VtRoot root;
669 
670 	e = *pe;
671 	ee = *pee;
672 	de = *pde;
673 
674 	if(globalToLocal(e.score) != NilBlock
675 	|| (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){
676 		werrstr("can only vac paths already stored on venti");
677 		return 0;
678 	}
679 
680 	/*
681 	 * Build metadata source for root.
682 	 */
683 	n = deSize(&de);
684 	if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){
685 		werrstr("DirEntry too big");
686 		return 0;
687 	}
688 	memset(buf, 0, sizeof buf);
689 	mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1);
690 	p = mbAlloc(&mb, n);
691 	if(p == nil)
692 		abort();
693 	mbSearch(&mb, de.elem, &i, &me);
694 	assert(me.p == nil);
695 	me.p = p;
696 	me.size = n;
697 	dePack(&de, &me);
698 	mbInsert(&mb, i, &me);
699 	mbPack(&mb);
700 
701 	eee.size = n+MetaHeaderSize+MetaIndexSize;
702 	if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score))
703 		return 0;
704 	eee.psize = 8192;
705 	eee.dsize = 8192;
706 	eee.depth = 0;
707 	eee.flags = VtEntryActive;
708 
709 	/*
710 	 * Build root source with three entries in it.
711 	 */
712 	entryPack(&e, buf, 0);
713 	entryPack(&ee, buf, 1);
714 	entryPack(&eee, buf, 2);
715 
716 	n = VtEntrySize*3;
717 	memset(&root, 0, sizeof root);
718 	if(!vtWriteBlock(z, buf, n, VtDirType, root.score))
719 		return 0;
720 
721 	/*
722 	 * Save root.
723 	 */
724 	strecpy(root.type, root.type+sizeof root.type, "vac");
725 	strecpy(root.name, root.name+sizeof root.name, de.elem);
726 	root.blocksize = blockSize;
727 	vtrootpack(&root, buf);
728 	if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
729 		return 0;
730 
731 	return 1;
732 }
733 
734 int
fsSync(Fs * fs)735 fsSync(Fs *fs)
736 {
737 	wlock(&fs->elk);
738 	fileMetaFlush(fs->file, 1);
739 	cacheFlush(fs->cache, 1);
740 	wunlock(&fs->elk);
741 	return 1;
742 }
743 
744 int
fsHalt(Fs * fs)745 fsHalt(Fs *fs)
746 {
747 	wlock(&fs->elk);
748 	fs->halted = 1;
749 	fileMetaFlush(fs->file, 1);
750 	cacheFlush(fs->cache, 1);
751 	return 1;
752 }
753 
754 int
fsUnhalt(Fs * fs)755 fsUnhalt(Fs *fs)
756 {
757 	if(!fs->halted)
758 		return 0;
759 	fs->halted = 0;
760 	wunlock(&fs->elk);
761 	return 1;
762 }
763 
764 int
fsNextQid(Fs * fs,u64int * qid)765 fsNextQid(Fs *fs, u64int *qid)
766 {
767 	Block *b;
768 	Super super;
769 
770 	if((b = superGet(fs->cache, &super)) == nil)
771 		return 0;
772 
773 	*qid = super.qid++;
774 
775 	/*
776 	 * It's okay if the super block doesn't go to disk immediately,
777 	 * since fileMetaAlloc will record a dependency between the
778 	 * block holding this qid and the super block.  See file.c:/^fileMetaAlloc.
779 	 */
780 	superWrite(b, &super, 0);
781 	blockPut(b);
782 	return 1;
783 }
784 
785 static void
fsMetaFlush(void * a)786 fsMetaFlush(void *a)
787 {
788 	int rv;
789 	Fs *fs = a;
790 
791 	rlock(&fs->elk);
792 	rv = fileMetaFlush(fs->file, 1);
793 	runlock(&fs->elk);
794 	if(rv > 0)
795 		cacheFlush(fs->cache, 0);
796 }
797 
798 static int
fsEsearch1(File * f,char * path,u32int savetime,u32int * plo)799 fsEsearch1(File *f, char *path, u32int savetime, u32int *plo)
800 {
801 	int n, r;
802 	DirEntry de;
803 	DirEntryEnum *dee;
804 	File *ff;
805 	Entry e, ee;
806 	char *t;
807 
808 	dee = deeOpen(f);
809 	if(dee == nil)
810 		return 0;
811 
812 	n = 0;
813 	for(;;){
814 		r = deeRead(dee, &de);
815 		if(r <= 0)
816 			break;
817 		if(de.mode & ModeSnapshot){
818 			if((ff = fileWalk(f, de.elem)) != nil){
819 				if(fileGetSources(ff, &e, &ee))
820 					if(de.mtime >= savetime && e.snap != 0)
821 						if(e.snap < *plo)
822 							*plo = e.snap;
823 				fileDecRef(ff);
824 			}
825 		}
826 		else if(de.mode & ModeDir){
827 			if((ff = fileWalk(f, de.elem)) != nil){
828 				t = smprint("%s/%s", path, de.elem);
829 				n += fsEsearch1(ff, t, savetime, plo);
830 				vtfree(t);
831 				fileDecRef(ff);
832 			}
833 		}
834 		deCleanup(&de);
835 		if(r < 0)
836 			break;
837 	}
838 	deeClose(dee);
839 
840 	return n;
841 }
842 
843 static int
fsEsearch(Fs * fs,char * path,u32int savetime,u32int * plo)844 fsEsearch(Fs *fs, char *path, u32int savetime, u32int *plo)
845 {
846 	int n;
847 	File *f;
848 	DirEntry de;
849 
850 	f = fileOpen(fs, path);
851 	if(f == nil)
852 		return 0;
853 	if(!fileGetDir(f, &de)){
854 		fileDecRef(f);
855 		return 0;
856 	}
857 	if((de.mode & ModeDir) == 0){
858 		fileDecRef(f);
859 		deCleanup(&de);
860 		return 0;
861 	}
862 	deCleanup(&de);
863 	n = fsEsearch1(f, path, savetime, plo);
864 	fileDecRef(f);
865 	return n;
866 }
867 
868 void
fsSnapshotCleanup(Fs * fs,u32int age)869 fsSnapshotCleanup(Fs *fs, u32int age)
870 {
871 	u32int lo;
872 
873 	/*
874 	 * Find the best low epoch we can use,
875 	 * given that we need to save all the unventied archives
876 	 * and all the snapshots younger than age.
877 	 */
878 	rlock(&fs->elk);
879 	lo = fs->ehi;
880 	fsEsearch(fs, "/archive", 0, &lo);
881 	fsEsearch(fs, "/snapshot", time(0)-age*60, &lo);
882 	runlock(&fs->elk);
883 
884 	fsEpochLow(fs, lo);
885 	fsSnapshotRemove(fs);
886 }
887 
888 /* remove all snapshots that have expired */
889 /* return number of directory entries remaining */
890 static int
fsRsearch1(File * f,char * s)891 fsRsearch1(File *f, char *s)
892 {
893 	int n, r;
894 	DirEntry de;
895 	DirEntryEnum *dee;
896 	File *ff;
897 	char *t, e[ERRMAX];
898 
899 	dee = deeOpen(f);
900 	if(dee == nil)
901 		return 0;
902 
903 	n = 0;
904 	for(;;){
905 		r = deeRead(dee, &de);
906 		if(r <= 0)
907 			break;
908 		n++;
909 		if(de.mode & ModeSnapshot){
910 			rerrstr(e, sizeof e);
911 			if((ff = fileWalk(f, de.elem)) != nil)
912 				fileDecRef(ff);
913 			else if(strcmp(e, ESnapOld) == 0){
914 				if(fileClri(f, de.elem, "adm"))
915 					n--;
916 			}
917 		}
918 		else if(de.mode & ModeDir){
919 			if((ff = fileWalk(f, de.elem)) != nil){
920 				t = smprint("%s/%s", s, de.elem);
921 				if(fsRsearch1(ff, t) == 0)
922 					if(fileRemove(ff, "adm"))
923 						n--;
924 				vtfree(t);
925 				fileDecRef(ff);
926 			}
927 		}
928 		deCleanup(&de);
929 		if(r < 0)
930 			break;
931 	}
932 	deeClose(dee);
933 
934 	return n;
935 }
936 
937 static int
fsRsearch(Fs * fs,char * path)938 fsRsearch(Fs *fs, char *path)
939 {
940 	File *f;
941 	DirEntry de;
942 
943 	f = fileOpen(fs, path);
944 	if(f == nil)
945 		return 0;
946 	if(!fileGetDir(f, &de)){
947 		fileDecRef(f);
948 		return 0;
949 	}
950 	if((de.mode & ModeDir) == 0){
951 		fileDecRef(f);
952 		deCleanup(&de);
953 		return 0;
954 	}
955 	deCleanup(&de);
956 	fsRsearch1(f, path);
957 	fileDecRef(f);
958 	return 1;
959 }
960 
961 void
fsSnapshotRemove(Fs * fs)962 fsSnapshotRemove(Fs *fs)
963 {
964 	rlock(&fs->elk);
965 	fsRsearch(fs, "/snapshot");
966 	runlock(&fs->elk);
967 }
968 
969 struct Snap
970 {
971 	Fs	*fs;
972 	Periodic*tick;
973 	QLock	lk;
974 	uint	snapMinutes;
975 	uint	archMinute;
976 	uint	snapLife;
977 	u32int	lastSnap;
978 	u32int	lastArch;
979 	u32int	lastCleanup;
980 	uint	ignore;
981 };
982 
983 static void
snapEvent(void * v)984 snapEvent(void *v)
985 {
986 	Snap *s;
987 	u32int now, min;
988 	Tm tm;
989 	int need;
990 	u32int snaplife;
991 
992 	s = v;
993 
994 	now = time(0)/60;
995 	qlock(&s->lk);
996 
997 	/*
998 	 * Snapshots happen every snapMinutes minutes.
999 	 * If we miss a snapshot (for example, because we
1000 	 * were down), we wait for the next one.
1001 	 */
1002 	if(s->snapMinutes != ~0 && s->snapMinutes != 0
1003 	&& now%s->snapMinutes==0 && now != s->lastSnap){
1004 		if(!fsSnapshot(s->fs, nil, nil, 0))
1005 			fprint(2, "%s: fsSnapshot snap: %r\n", argv0);
1006 		s->lastSnap = now;
1007 	}
1008 
1009 	/*
1010 	 * Archival snapshots happen at archMinute.
1011 	 * If we miss an archive (for example, because we
1012 	 * were down), we do it as soon as possible.
1013 	 */
1014 	tm = *localtime(now*60);
1015 	min = tm.hour*60+tm.min;
1016 	if(s->archMinute != ~0){
1017 		need = 0;
1018 		if(min == s->archMinute && now != s->lastArch)
1019 			need = 1;
1020 		if(s->lastArch == 0){
1021 			s->lastArch = 1;
1022 			if(fsNeedArch(s->fs, s->archMinute))
1023 				need = 1;
1024 		}
1025 		if(need){
1026 			fsSnapshot(s->fs, nil, nil, 1);
1027 			s->lastArch = now;
1028 		}
1029 	}
1030 
1031 	/*
1032 	 * Snapshot cleanup happens every snaplife or every day.
1033 	 */
1034 	snaplife = s->snapLife;
1035 	if(snaplife == ~0)
1036 		snaplife = 24*60;
1037 	if(s->lastCleanup+snaplife < now){
1038 		fsSnapshotCleanup(s->fs, s->snapLife);
1039 		s->lastCleanup = now;
1040 	}
1041 	qunlock(&s->lk);
1042 }
1043 
1044 static Snap*
snapInit(Fs * fs)1045 snapInit(Fs *fs)
1046 {
1047 	Snap *s;
1048 
1049 	s = vtmallocz(sizeof(Snap));
1050 	s->fs = fs;
1051 	s->tick = periodicAlloc(snapEvent, s, 10*1000);
1052 	s->snapMinutes = -1;
1053 	s->archMinute = -1;
1054 	s->snapLife = -1;
1055 	s->ignore = 5*2;	/* wait five minutes for clock to stabilize */
1056 	return s;
1057 }
1058 
1059 void
snapGetTimes(Snap * s,u32int * arch,u32int * snap,u32int * snaplen)1060 snapGetTimes(Snap *s, u32int *arch, u32int *snap, u32int *snaplen)
1061 {
1062 	if(s == nil){
1063 		*snap = -1;
1064 		*arch = -1;
1065 		*snaplen = -1;
1066 		return;
1067 	}
1068 
1069 	qlock(&s->lk);
1070 	*snap = s->snapMinutes;
1071 	*arch = s->archMinute;
1072 	*snaplen = s->snapLife;
1073 	qunlock(&s->lk);
1074 }
1075 
1076 void
snapSetTimes(Snap * s,u32int arch,u32int snap,u32int snaplen)1077 snapSetTimes(Snap *s, u32int arch, u32int snap, u32int snaplen)
1078 {
1079 	if(s == nil)
1080 		return;
1081 
1082 	qlock(&s->lk);
1083 	s->snapMinutes = snap;
1084 	s->archMinute = arch;
1085 	s->snapLife = snaplen;
1086 	qunlock(&s->lk);
1087 }
1088 
1089 static void
snapClose(Snap * s)1090 snapClose(Snap *s)
1091 {
1092 	if(s == nil)
1093 		return;
1094 
1095 	periodicKill(s->tick);
1096 	vtfree(s);
1097 }
1098 
1099