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