fs.c (21701B)
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 * 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 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 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 * 172 fsGetRoot(Fs *fs) 173 { 174 return fileIncRef(fs->file); 175 } 176 177 int 178 fsGetBlockSize(Fs *fs) 179 { 180 return fs->blockSize; 181 } 182 183 Block* 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 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* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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* 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 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 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 1090 snapClose(Snap *s) 1091 { 1092 if(s == nil) 1093 return; 1094 1095 periodicKill(s->tick); 1096 vtfree(s); 1097 }