xref: /plan9/sys/src/cmd/fossil/fs.c (revision e12a987081f10894b49298514b3a97b41db862b0)
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,VtSession * z,long ncache,int mode)11 fsOpen(char *file, VtSession *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 
20 	switch(mode){
21 	default:
22 		vtSetError(EBadMode);
23 		return nil;
24 	case OReadOnly:
25 		m = OREAD;
26 		break;
27 	case OReadWrite:
28 		m = ORDWR;
29 		break;
30 	}
31 	fd = open(file, m);
32 	if(fd < 0){
33 		vtSetError("open %s: %r", file);
34 		return nil;
35 	}
36 
37 	bwatchInit();
38 	disk = diskAlloc(fd);
39 	if(disk == nil){
40 		vtSetError("diskAlloc: %R");
41 		close(fd);
42 		return nil;
43 	}
44 
45 	fs = vtMemAllocZ(sizeof(Fs));
46 	fs->mode = mode;
47 	fs->name = vtStrDup(file);
48 	fs->blockSize = diskBlockSize(disk);
49 	fs->elk = vtLockAlloc();
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 		vtSetError("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 		if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0)
77 			goto Err;
78 		b = cacheLocalData(fs->cache, super.active, BtDir, RootTag,
79 			OReadWrite, 0);
80 		if(b == nil){
81 			vtSetError("cacheLocalData: %R");
82 			goto Err;
83 		}
84 		if(b->l.epoch == fs->ehi){
85 			blockPut(b);
86 			vtSetError("bad root source block");
87 			goto Err;
88 		}
89 		b = blockCopy(b, RootTag, fs->ehi, fs->elo);
90 		if(b == nil)
91 			goto Err;
92 		localToGlobal(super.active, oscore);
93 		super.active = b->addr;
94 		bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
95 		if(bs == nil){
96 			blockPut(b);
97 			vtSetError("cacheLocal: %R");
98 			goto Err;
99 		}
100 		superPack(&super, bs->data);
101 		blockDependency(bs, b, 0, oscore, nil);
102 		blockPut(b);
103 		blockDirty(bs);
104 		blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
105 		blockPut(bs);
106 		fs->source = sourceRoot(fs, super.active, mode);
107 		if(fs->source == nil){
108 			vtSetError("sourceRoot: %R");
109 			goto Err;
110 		}
111 	}
112 
113 //fprint(2, "%s: got fs source\n", argv0);
114 
115 	vtRLock(fs->elk);
116 	fs->file = fileRoot(fs->source);
117 	fs->source->file = fs->file;		/* point back */
118 	vtRUnlock(fs->elk);
119 	if(fs->file == nil){
120 		vtSetError("fileRoot: %R");
121 		goto Err;
122 	}
123 
124 //fprint(2, "%s: got file root\n", argv0);
125 
126 	if(mode == OReadWrite){
127 		fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
128 		fs->snap = snapInit(fs);
129 	}
130 	return fs;
131 
132 Err:
133 fprint(2, "%s: fsOpen error\n", argv0);
134 	fsClose(fs);
135 	return nil;
136 }
137 
138 void
fsClose(Fs * fs)139 fsClose(Fs *fs)
140 {
141 	vtRLock(fs->elk);
142 	periodicKill(fs->metaFlush);
143 	snapClose(fs->snap);
144 	if(fs->file){
145 		fileMetaFlush(fs->file, 0);
146 		if(!fileDecRef(fs->file))
147 			vtFatal("fsClose: files still in use: %r\n");
148 	}
149 	fs->file = nil;
150 	sourceClose(fs->source);
151 	cacheFree(fs->cache);
152 	if(fs->arch)
153 		archFree(fs->arch);
154 	vtMemFree(fs->name);
155 	vtRUnlock(fs->elk);
156 	vtLockFree(fs->elk);
157 	memset(fs, ~0, sizeof(Fs));
158 	vtMemFree(fs);
159 }
160 
161 int
fsRedial(Fs * fs,char * host)162 fsRedial(Fs *fs, char *host)
163 {
164 	if(!vtRedial(fs->z, host))
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 			vtSleep(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 	vtRLock(fs->elk);
365 	f = fileOpen(fs, buf);
366 	if(f){
367 		need = 0;
368 		fileDecRef(f);
369 	}
370 	vtRUnlock(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 	vtLock(fs->elk);
381 	if(low > fs->ehi){
382 		vtSetError("bad low epoch (must be <= %ud)", fs->ehi);
383 		vtUnlock(fs->elk);
384 		return 0;
385 	}
386 
387 	if((bs = superGet(fs->cache, &super)) == nil){
388 		vtUnlock(fs->elk);
389 		return 0;
390 	}
391 
392 	super.epochLow = low;
393 	fs->elo = low;
394 	superWrite(bs, &super, 1);
395 	blockPut(bs);
396 	vtUnlock(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 		vtSetError("file system is halted");
511 		return 0;
512 	}
513 
514 	/*
515 	 * Freeze file system activity.
516 	 */
517 	vtLock(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 	vtUnlock(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 	vtUnlock(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 	vtRLock(fs->elk);
629 	f = fileOpen(fs, name);
630 	if(f == nil){
631 		vtRUnlock(fs->elk);
632 		return 0;
633 	}
634 
635 	if(!fileGetSources(f, &e, &ee) || !fileGetDir(f, &de)){
636 		fileDecRef(f);
637 		vtRUnlock(fs->elk);
638 		return 0;
639 	}
640 	fileDecRef(f);
641 
642 	r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score);
643 	vtRUnlock(fs->elk);
644 	return r;
645 }
646 
647 static int
vtWriteBlock(VtSession * z,uchar * buf,uint n,uint type,uchar score[VtScoreSize])648 vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize])
649 {
650 	if(!vtWrite(z, score, type, buf, n))
651 		return 0;
652 	if(!vtSha1Check(score, buf, n))
653 		return 0;
654 	return 1;
655 }
656 
657 int
mkVac(VtSession * z,uint blockSize,Entry * pe,Entry * pee,DirEntry * pde,uchar score[VtScoreSize])658 mkVac(VtSession *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 		vtSetError("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 		vtSetError("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 	root.version = VtRootVersion;
725 	strecpy(root.type, root.type+sizeof root.type, "vac");
726 	strecpy(root.name, root.name+sizeof root.name, de.elem);
727 	root.blockSize = blockSize;
728 	vtRootPack(&root, buf);
729 	if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
730 		return 0;
731 
732 	return 1;
733 }
734 
735 int
fsSync(Fs * fs)736 fsSync(Fs *fs)
737 {
738 	vtLock(fs->elk);
739 	fileMetaFlush(fs->file, 1);
740 	cacheFlush(fs->cache, 1);
741 	vtUnlock(fs->elk);
742 	return 1;
743 }
744 
745 int
fsHalt(Fs * fs)746 fsHalt(Fs *fs)
747 {
748 	vtLock(fs->elk);
749 	fs->halted = 1;
750 	fileMetaFlush(fs->file, 1);
751 	cacheFlush(fs->cache, 1);
752 	return 1;
753 }
754 
755 int
fsUnhalt(Fs * fs)756 fsUnhalt(Fs *fs)
757 {
758 	if(!fs->halted)
759 		return 0;
760 	fs->halted = 0;
761 	vtUnlock(fs->elk);
762 	return 1;
763 }
764 
765 int
fsNextQid(Fs * fs,u64int * qid)766 fsNextQid(Fs *fs, u64int *qid)
767 {
768 	Block *b;
769 	Super super;
770 
771 	if((b = superGet(fs->cache, &super)) == nil)
772 		return 0;
773 
774 	*qid = super.qid++;
775 
776 	/*
777 	 * It's okay if the super block doesn't go to disk immediately,
778 	 * since fileMetaAlloc will record a dependency between the
779 	 * block holding this qid and the super block.  See file.c:/^fileMetaAlloc.
780 	 */
781 	superWrite(b, &super, 0);
782 	blockPut(b);
783 	return 1;
784 }
785 
786 static void
fsMetaFlush(void * a)787 fsMetaFlush(void *a)
788 {
789 	int rv;
790 	Fs *fs = a;
791 
792 	vtRLock(fs->elk);
793 	rv = fileMetaFlush(fs->file, 1);
794 	vtRUnlock(fs->elk);
795 	if(rv > 0)
796 		cacheFlush(fs->cache, 0);
797 }
798 
799 static int
fsEsearch1(File * f,char * path,u32int savetime,u32int * plo)800 fsEsearch1(File *f, char *path, u32int savetime, u32int *plo)
801 {
802 	int n, r;
803 	DirEntry de;
804 	DirEntryEnum *dee;
805 	File *ff;
806 	Entry e, ee;
807 	char *t;
808 
809 	dee = deeOpen(f);
810 	if(dee == nil)
811 		return 0;
812 
813 	n = 0;
814 	for(;;){
815 		r = deeRead(dee, &de);
816 		if(r <= 0)
817 			break;
818 		if(de.mode & ModeSnapshot){
819 			if((ff = fileWalk(f, de.elem)) != nil){
820 				if(fileGetSources(ff, &e, &ee))
821 					if(de.mtime >= savetime && e.snap != 0)
822 						if(e.snap < *plo)
823 							*plo = e.snap;
824 				fileDecRef(ff);
825 			}
826 		}
827 		else if(de.mode & ModeDir){
828 			if((ff = fileWalk(f, de.elem)) != nil){
829 				t = smprint("%s/%s", path, de.elem);
830 				n += fsEsearch1(ff, t, savetime, plo);
831 				vtMemFree(t);
832 				fileDecRef(ff);
833 			}
834 		}
835 		deCleanup(&de);
836 		if(r < 0)
837 			break;
838 	}
839 	deeClose(dee);
840 
841 	return n;
842 }
843 
844 static int
fsEsearch(Fs * fs,char * path,u32int savetime,u32int * plo)845 fsEsearch(Fs *fs, char *path, u32int savetime, u32int *plo)
846 {
847 	int n;
848 	File *f;
849 	DirEntry de;
850 
851 	f = fileOpen(fs, path);
852 	if(f == nil)
853 		return 0;
854 	if(!fileGetDir(f, &de)){
855 		fileDecRef(f);
856 		return 0;
857 	}
858 	if((de.mode & ModeDir) == 0){
859 		fileDecRef(f);
860 		deCleanup(&de);
861 		return 0;
862 	}
863 	deCleanup(&de);
864 	n = fsEsearch1(f, path, savetime, plo);
865 	fileDecRef(f);
866 	return n;
867 }
868 
869 void
fsSnapshotCleanup(Fs * fs,u32int age)870 fsSnapshotCleanup(Fs *fs, u32int age)
871 {
872 	u32int lo;
873 
874 	/*
875 	 * Find the best low epoch we can use,
876 	 * given that we need to save all the unventied archives
877 	 * and all the snapshots younger than age.
878 	 */
879 	vtRLock(fs->elk);
880 	lo = fs->ehi;
881 	fsEsearch(fs, "/archive", 0, &lo);
882 	fsEsearch(fs, "/snapshot", time(0)-age*60, &lo);
883 	vtRUnlock(fs->elk);
884 
885 	fsEpochLow(fs, lo);
886 	fsSnapshotRemove(fs);
887 }
888 
889 /* remove all snapshots that have expired */
890 /* return number of directory entries remaining */
891 static int
fsRsearch1(File * f,char * s)892 fsRsearch1(File *f, char *s)
893 {
894 	int n, r;
895 	DirEntry de;
896 	DirEntryEnum *dee;
897 	File *ff;
898 	char *t;
899 
900 	dee = deeOpen(f);
901 	if(dee == nil)
902 		return 0;
903 
904 	n = 0;
905 	for(;;){
906 		r = deeRead(dee, &de);
907 		if(r <= 0)
908 			break;
909 		n++;
910 		if(de.mode & ModeSnapshot){
911 			if((ff = fileWalk(f, de.elem)) != nil)
912 				fileDecRef(ff);
913 			else if(strcmp(vtGetError(), 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 				vtMemFree(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 	vtRLock(fs->elk);
965 	fsRsearch(fs, "/snapshot");
966 	vtRUnlock(fs->elk);
967 }
968 
969 struct Snap
970 {
971 	Fs	*fs;
972 	Periodic*tick;
973 	VtLock	*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 	vtLock(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 	vtUnlock(s->lk);
1042 }
1043 
1044 static Snap*
snapInit(Fs * fs)1045 snapInit(Fs *fs)
1046 {
1047 	Snap *s;
1048 
1049 	s = vtMemAllocZ(sizeof(Snap));
1050 	s->fs = fs;
1051 	s->tick = periodicAlloc(snapEvent, s, 10*1000);
1052 	s->lk = vtLockAlloc();
1053 	s->snapMinutes = -1;
1054 	s->archMinute = -1;
1055 	s->snapLife = -1;
1056 	s->ignore = 5*2;	/* wait five minutes for clock to stabilize */
1057 	return s;
1058 }
1059 
1060 void
snapGetTimes(Snap * s,u32int * arch,u32int * snap,u32int * snaplen)1061 snapGetTimes(Snap *s, u32int *arch, u32int *snap, u32int *snaplen)
1062 {
1063 	if(s == nil){
1064 		*snap = -1;
1065 		*arch = -1;
1066 		*snaplen = -1;
1067 		return;
1068 	}
1069 
1070 	vtLock(s->lk);
1071 	*snap = s->snapMinutes;
1072 	*arch = s->archMinute;
1073 	*snaplen = s->snapLife;
1074 	vtUnlock(s->lk);
1075 }
1076 
1077 void
snapSetTimes(Snap * s,u32int arch,u32int snap,u32int snaplen)1078 snapSetTimes(Snap *s, u32int arch, u32int snap, u32int snaplen)
1079 {
1080 	if(s == nil)
1081 		return;
1082 
1083 	vtLock(s->lk);
1084 	s->snapMinutes = snap;
1085 	s->archMinute = arch;
1086 	s->snapLife = snaplen;
1087 	vtUnlock(s->lk);
1088 }
1089 
1090 static void
snapClose(Snap * s)1091 snapClose(Snap *s)
1092 {
1093 	if(s == nil)
1094 		return;
1095 
1096 	periodicKill(s->tick);
1097 	vtMemFree(s);
1098 }
1099 
1100