15e96a66cSDavid du Colombier #include "stdinc.h" 25e96a66cSDavid du Colombier #include "dat.h" 35e96a66cSDavid du Colombier #include "fns.h" 45e96a66cSDavid du Colombier #include "error.h" 55e96a66cSDavid du Colombier 65e96a66cSDavid du Colombier static void fsMetaFlush(void *a); 75e96a66cSDavid du Colombier static Snap *snapInit(Fs*); 85e96a66cSDavid du Colombier static void snapClose(Snap*); 95e96a66cSDavid du Colombier 105e96a66cSDavid du Colombier Fs * 115e96a66cSDavid du Colombier fsOpen(char *file, VtSession *z, long ncache, int mode) 125e96a66cSDavid du Colombier { 135e96a66cSDavid du Colombier Fs *fs; 145e96a66cSDavid du Colombier Disk *disk; 155e96a66cSDavid du Colombier int fd; 165e96a66cSDavid du Colombier Block *b, *bs; 175e96a66cSDavid du Colombier Super super; 185e96a66cSDavid du Colombier int m; 1961201b97SDavid du Colombier uchar oscore[VtScoreSize]; 205e96a66cSDavid du Colombier 215e96a66cSDavid du Colombier switch(mode){ 225e96a66cSDavid du Colombier default: 235e96a66cSDavid du Colombier vtSetError(EBadMode); 245e96a66cSDavid du Colombier return nil; 255e96a66cSDavid du Colombier case OReadOnly: 265e96a66cSDavid du Colombier m = OREAD; 275e96a66cSDavid du Colombier break; 285e96a66cSDavid du Colombier case OReadWrite: 295e96a66cSDavid du Colombier m = ORDWR; 305e96a66cSDavid du Colombier break; 315e96a66cSDavid du Colombier } 325e96a66cSDavid du Colombier fd = open(file, m); 335e96a66cSDavid du Colombier if(fd < 0){ 345e96a66cSDavid du Colombier vtOSError(); 355e96a66cSDavid du Colombier return nil; 365e96a66cSDavid du Colombier } 375e96a66cSDavid du Colombier 385e96a66cSDavid du Colombier bwatchInit(); 395e96a66cSDavid du Colombier disk = diskAlloc(fd); 405e96a66cSDavid du Colombier if(disk == nil){ 415e96a66cSDavid du Colombier close(fd); 425e96a66cSDavid du Colombier return nil; 435e96a66cSDavid du Colombier } 445e96a66cSDavid du Colombier 455e96a66cSDavid du Colombier fs = vtMemAllocZ(sizeof(Fs)); 465e96a66cSDavid du Colombier fs->mode = mode; 475e96a66cSDavid du Colombier fs->blockSize = diskBlockSize(disk); 485e96a66cSDavid du Colombier fs->elk = vtLockAlloc(); 495e96a66cSDavid du Colombier fs->cache = cacheAlloc(disk, z, ncache, mode); 505e96a66cSDavid du Colombier if(mode == OReadWrite) 515e96a66cSDavid du Colombier fs->arch = archInit(fs->cache, disk, fs, z); 525e96a66cSDavid du Colombier fs->z = z; 535e96a66cSDavid du Colombier 545e96a66cSDavid du Colombier b = cacheLocal(fs->cache, PartSuper, 0, mode); 555e96a66cSDavid du Colombier if(b == nil) 565e96a66cSDavid du Colombier goto Err; 575e96a66cSDavid du Colombier if(!superUnpack(&super, b->data)){ 585e96a66cSDavid du Colombier blockPut(b); 595e96a66cSDavid du Colombier goto Err; 605e96a66cSDavid du Colombier } 615e96a66cSDavid du Colombier blockPut(b); 625e96a66cSDavid du Colombier 635e96a66cSDavid du Colombier fs->ehi = super.epochHigh; 645e96a66cSDavid du Colombier fs->elo = super.epochLow; 655e96a66cSDavid du Colombier 665e96a66cSDavid du Colombier fprint(2, "fs->ehi %d fs->elo %d active=%d\n", fs->ehi, fs->elo, super.active); 675e96a66cSDavid du Colombier 685e96a66cSDavid du Colombier fs->source = sourceRoot(fs, super.active, mode); 695e96a66cSDavid du Colombier if(fs->source == nil){ 705e96a66cSDavid du Colombier /* 715e96a66cSDavid du Colombier * Perhaps it failed because the block is copy-on-write. 725e96a66cSDavid du Colombier * Do the copy and try again. 735e96a66cSDavid du Colombier */ 745e96a66cSDavid du Colombier if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0) 755e96a66cSDavid du Colombier goto Err; 765e96a66cSDavid du Colombier b = cacheLocalData(fs->cache, super.active, BtDir, RootTag, OReadWrite, 0); 775e96a66cSDavid du Colombier if(b == nil) 785e96a66cSDavid du Colombier goto Err; 795e96a66cSDavid du Colombier if(!(b->l.state&BsClosed) && b->l.epoch == fs->ehi){ 805e96a66cSDavid du Colombier blockPut(b); 815e96a66cSDavid du Colombier goto Err; 825e96a66cSDavid du Colombier } 835e96a66cSDavid du Colombier b = blockCopy(b, RootTag, fs->ehi, fs->elo); 845e96a66cSDavid du Colombier if(b == nil) 855e96a66cSDavid du Colombier goto Err; 8661201b97SDavid du Colombier localToGlobal(super.active, oscore); 875e96a66cSDavid du Colombier super.active = b->addr; 885e96a66cSDavid du Colombier bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite); 895e96a66cSDavid du Colombier if(bs == nil){ 905e96a66cSDavid du Colombier blockPut(b); 915e96a66cSDavid du Colombier goto Err; 925e96a66cSDavid du Colombier } 935e96a66cSDavid du Colombier superPack(&super, bs->data); 9461201b97SDavid du Colombier blockDependency(bs, b, 0, oscore, nil); 955e96a66cSDavid du Colombier blockDirty(bs); 965e96a66cSDavid du Colombier blockPut(bs); 975e96a66cSDavid du Colombier blockPut(b); 985e96a66cSDavid du Colombier fs->source = sourceRoot(fs, super.active, mode); 995e96a66cSDavid du Colombier if(fs->source == nil) 1005e96a66cSDavid du Colombier goto Err; 1015e96a66cSDavid du Colombier } 1025e96a66cSDavid du Colombier 1035e96a66cSDavid du Colombier fprint(2, "got fs source\n"); 1045e96a66cSDavid du Colombier 1055e96a66cSDavid du Colombier vtRLock(fs->elk); 1065e96a66cSDavid du Colombier fs->file = fileRoot(fs->source); 1075e96a66cSDavid du Colombier vtRUnlock(fs->elk); 1085e96a66cSDavid du Colombier if(fs->file == nil) 1095e96a66cSDavid du Colombier goto Err; 1105e96a66cSDavid du Colombier 1115e96a66cSDavid du Colombier fprint(2, "got file root\n"); 1125e96a66cSDavid du Colombier 1135e96a66cSDavid du Colombier if(mode == OReadWrite){ 1145e96a66cSDavid du Colombier fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000); 1155e96a66cSDavid du Colombier fs->snap = snapInit(fs); 1165e96a66cSDavid du Colombier } 1175e96a66cSDavid du Colombier return fs; 1185e96a66cSDavid du Colombier 1195e96a66cSDavid du Colombier Err: 1205e96a66cSDavid du Colombier fsClose(fs); 1215e96a66cSDavid du Colombier return nil; 1225e96a66cSDavid du Colombier } 1235e96a66cSDavid du Colombier 1245e96a66cSDavid du Colombier void 1255e96a66cSDavid du Colombier fsClose(Fs *fs) 1265e96a66cSDavid du Colombier { 1275e96a66cSDavid du Colombier vtRLock(fs->elk); 1285e96a66cSDavid du Colombier periodicKill(fs->metaFlush); 1295e96a66cSDavid du Colombier snapClose(fs->snap); 1305e96a66cSDavid du Colombier if(fs->file){ 1315e96a66cSDavid du Colombier fileMetaFlush(fs->file, 0); 1325e96a66cSDavid du Colombier if(!fileDecRef(fs->file)) 1335e96a66cSDavid du Colombier vtFatal("fsClose: files still in use: %r\n"); 1345e96a66cSDavid du Colombier } 1355e96a66cSDavid du Colombier fs->file = nil; 1365e96a66cSDavid du Colombier sourceClose(fs->source); 1375e96a66cSDavid du Colombier cacheFree(fs->cache); 1385e96a66cSDavid du Colombier if(fs->arch) 1395e96a66cSDavid du Colombier archFree(fs->arch); 1405e96a66cSDavid du Colombier vtRUnlock(fs->elk); 1415e96a66cSDavid du Colombier vtLockFree(fs->elk); 1425e96a66cSDavid du Colombier memset(fs, ~0, sizeof(Fs)); 1435e96a66cSDavid du Colombier vtMemFree(fs); 1445e96a66cSDavid du Colombier } 1455e96a66cSDavid du Colombier 1465e96a66cSDavid du Colombier int 1475e96a66cSDavid du Colombier fsRedial(Fs *fs, char *host) 1485e96a66cSDavid du Colombier { 1495e96a66cSDavid du Colombier if(!vtRedial(fs->z, host)) 1505e96a66cSDavid du Colombier return 0; 1515e96a66cSDavid du Colombier if(!vtConnect(fs->z, 0)) 1525e96a66cSDavid du Colombier return 0; 1535e96a66cSDavid du Colombier return 1; 1545e96a66cSDavid du Colombier } 1555e96a66cSDavid du Colombier 1565e96a66cSDavid du Colombier File * 1575e96a66cSDavid du Colombier fsGetRoot(Fs *fs) 1585e96a66cSDavid du Colombier { 1595e96a66cSDavid du Colombier return fileIncRef(fs->file); 1605e96a66cSDavid du Colombier } 1615e96a66cSDavid du Colombier 1625e96a66cSDavid du Colombier int 1635e96a66cSDavid du Colombier fsGetBlockSize(Fs *fs) 1645e96a66cSDavid du Colombier { 1655e96a66cSDavid du Colombier return fs->blockSize; 1665e96a66cSDavid du Colombier } 1675e96a66cSDavid du Colombier 1685e96a66cSDavid du Colombier Block* 1695e96a66cSDavid du Colombier superGet(Cache *c, Super* super) 1705e96a66cSDavid du Colombier { 1715e96a66cSDavid du Colombier Block *b; 1725e96a66cSDavid du Colombier 1735e96a66cSDavid du Colombier if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){ 1745e96a66cSDavid du Colombier fprint(2, "superGet: cacheLocal failed: %R"); 1755e96a66cSDavid du Colombier return nil; 1765e96a66cSDavid du Colombier } 1775e96a66cSDavid du Colombier if(!superUnpack(super, b->data)){ 1785e96a66cSDavid du Colombier fprint(2, "superGet: superUnpack failed: %R"); 1795e96a66cSDavid du Colombier blockPut(b); 1805e96a66cSDavid du Colombier return nil; 1815e96a66cSDavid du Colombier } 1825e96a66cSDavid du Colombier 1835e96a66cSDavid du Colombier return b; 1845e96a66cSDavid du Colombier } 1855e96a66cSDavid du Colombier 1865e96a66cSDavid du Colombier void 1875e96a66cSDavid du Colombier superPut(Block* b, Super* super, int forceWrite) 1885e96a66cSDavid du Colombier { 1895e96a66cSDavid du Colombier superPack(super, b->data); 1905e96a66cSDavid du Colombier blockDirty(b); 1915e96a66cSDavid du Colombier if(forceWrite){ 1925e96a66cSDavid du Colombier while(!blockWrite(b)){ 1935e96a66cSDavid du Colombier /* BUG: what should really happen here? */ 1945e96a66cSDavid du Colombier fprint(2, "could not write super block; waiting 10 seconds\n"); 1955e96a66cSDavid du Colombier sleep(10*000); 1965e96a66cSDavid du Colombier } 1975e96a66cSDavid du Colombier while(b->iostate != BioClean && b->iostate != BioDirty){ 1985e96a66cSDavid du Colombier assert(b->iostate == BioWriting); 1995e96a66cSDavid du Colombier vtSleep(b->ioready); 2005e96a66cSDavid du Colombier } 2015e96a66cSDavid du Colombier /* 2025e96a66cSDavid du Colombier * it's okay that b might still be dirty. 2035e96a66cSDavid du Colombier * that means it got written out but with an old root pointer, 2045e96a66cSDavid du Colombier * but the other fields went out, and those are the ones 2055e96a66cSDavid du Colombier * we really care about. (specifically, epochHigh; see fsSnapshot). 2065e96a66cSDavid du Colombier */ 2075e96a66cSDavid du Colombier } 2085e96a66cSDavid du Colombier blockPut(b); 2095e96a66cSDavid du Colombier } 2105e96a66cSDavid du Colombier 2115e96a66cSDavid du Colombier /* 2125e96a66cSDavid du Colombier * Prepare the directory to store a snapshot. 21361201b97SDavid du Colombier * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#] 2145e96a66cSDavid du Colombier * Archival snapshots go into /archive/yyyy/mmdd[.#]. 2155e96a66cSDavid du Colombier * 2165e96a66cSDavid du Colombier * TODO This should be rewritten to eliminate most of the duplication. 2175e96a66cSDavid du Colombier */ 2185e96a66cSDavid du Colombier static File* 2195e96a66cSDavid du Colombier fileOpenSnapshot(Fs *fs, int doarchive) 2205e96a66cSDavid du Colombier { 2215e96a66cSDavid du Colombier int n; 2225e96a66cSDavid du Colombier char buf[30], *s; 2235e96a66cSDavid du Colombier File *dir, *f; 2245e96a66cSDavid du Colombier Tm now; 2255e96a66cSDavid du Colombier 2265e96a66cSDavid du Colombier if(doarchive){ 2275e96a66cSDavid du Colombier /* 2285e96a66cSDavid du Colombier * a snapshot intended to be archived to venti. 2295e96a66cSDavid du Colombier */ 2305e96a66cSDavid du Colombier dir = fileOpen(fs, "/archive"); 2315e96a66cSDavid du Colombier if(dir == nil) 2325e96a66cSDavid du Colombier return nil; 2335e96a66cSDavid du Colombier now = *localtime(time(0)); 2345e96a66cSDavid du Colombier 2355e96a66cSDavid du Colombier /* yyyy */ 2365e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%d", now.year+1900); 2375e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2385e96a66cSDavid du Colombier if(f == nil) 2395e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm"); 2405e96a66cSDavid du Colombier fileDecRef(dir); 2415e96a66cSDavid du Colombier if(f == nil) 2425e96a66cSDavid du Colombier return nil; 2435e96a66cSDavid du Colombier dir = f; 2445e96a66cSDavid du Colombier 2455e96a66cSDavid du Colombier /* mmdd[#] */ 2465e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); 2475e96a66cSDavid du Colombier s = buf+strlen(buf); 2485e96a66cSDavid du Colombier for(n=0;; n++){ 2495e96a66cSDavid du Colombier if(n) 2505e96a66cSDavid du Colombier seprint(s, buf+sizeof(buf), ".%d", n); 2515e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2525e96a66cSDavid du Colombier if(f != nil){ 2535e96a66cSDavid du Colombier fileDecRef(f); 2545e96a66cSDavid du Colombier continue; 2555e96a66cSDavid du Colombier } 2565e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); 2575e96a66cSDavid du Colombier break; 2585e96a66cSDavid du Colombier } 2595e96a66cSDavid du Colombier fileDecRef(dir); 2605e96a66cSDavid du Colombier return f; 2615e96a66cSDavid du Colombier }else{ 2625e96a66cSDavid du Colombier /* 2635e96a66cSDavid du Colombier * Just a temporary snapshot 2645e96a66cSDavid du Colombier * We'll use /snapshot/yyyy/mmdd/hhmm. 2655e96a66cSDavid du Colombier * There may well be a better naming scheme. 2665e96a66cSDavid du Colombier * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.) 2675e96a66cSDavid du Colombier */ 2685e96a66cSDavid du Colombier dir = fileOpen(fs, "/snapshot"); 2695e96a66cSDavid du Colombier if(dir == nil) 2705e96a66cSDavid du Colombier return nil; 2715e96a66cSDavid du Colombier 2725e96a66cSDavid du Colombier now = *localtime(time(0)); 2735e96a66cSDavid du Colombier 2745e96a66cSDavid du Colombier /* yyyy */ 2755e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%d", now.year+1900); 2765e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2775e96a66cSDavid du Colombier if(f == nil) 2785e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm"); 2795e96a66cSDavid du Colombier fileDecRef(dir); 2805e96a66cSDavid du Colombier if(f == nil) 2815e96a66cSDavid du Colombier return nil; 2825e96a66cSDavid du Colombier dir = f; 2835e96a66cSDavid du Colombier 2845e96a66cSDavid du Colombier /* mmdd */ 2855e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); 2865e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2875e96a66cSDavid du Colombier if(f == nil) 2885e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm"); 2895e96a66cSDavid du Colombier fileDecRef(dir); 2905e96a66cSDavid du Colombier if(f == nil) 2915e96a66cSDavid du Colombier return nil; 2925e96a66cSDavid du Colombier dir = f; 2935e96a66cSDavid du Colombier 29461201b97SDavid du Colombier /* hhmm[.#] */ 2955e96a66cSDavid du Colombier snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min); 29661201b97SDavid du Colombier s = buf+strlen(buf); 29761201b97SDavid du Colombier for(n=0;; n++){ 29861201b97SDavid du Colombier if(n) 29961201b97SDavid du Colombier seprint(s, buf+sizeof(buf), ".%d", n); 3005e96a66cSDavid du Colombier f = fileWalk(dir, buf); 3015e96a66cSDavid du Colombier if(f != nil){ 3025e96a66cSDavid du Colombier fileDecRef(f); 30361201b97SDavid du Colombier continue; 3045e96a66cSDavid du Colombier } 3055e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); 30661201b97SDavid du Colombier break; 30761201b97SDavid du Colombier } 3085e96a66cSDavid du Colombier fileDecRef(dir); 3095e96a66cSDavid du Colombier return f; 3105e96a66cSDavid du Colombier } 3115e96a66cSDavid du Colombier } 3125e96a66cSDavid du Colombier 3135e96a66cSDavid du Colombier int 3145e96a66cSDavid du Colombier fsEpochLow(Fs *fs, u32int low) 3155e96a66cSDavid du Colombier { 3165e96a66cSDavid du Colombier Block *bs; 3175e96a66cSDavid du Colombier Super super; 3185e96a66cSDavid du Colombier 3195e96a66cSDavid du Colombier vtLock(fs->elk); 3205e96a66cSDavid du Colombier if(low > fs->ehi){ 3215e96a66cSDavid du Colombier vtSetError("bad low epoch (must be <= %ud)", fs->ehi); 3225e96a66cSDavid du Colombier vtUnlock(fs->elk); 3235e96a66cSDavid du Colombier return 0; 3245e96a66cSDavid du Colombier } 3255e96a66cSDavid du Colombier 3265e96a66cSDavid du Colombier if((bs = superGet(fs->cache, &super)) == nil){ 3275e96a66cSDavid du Colombier vtUnlock(fs->elk); 3285e96a66cSDavid du Colombier return 0; 3295e96a66cSDavid du Colombier } 3305e96a66cSDavid du Colombier 3315e96a66cSDavid du Colombier super.epochLow = low; 3325e96a66cSDavid du Colombier fs->elo = low; 3335e96a66cSDavid du Colombier superPut(bs, &super, 1); 3345e96a66cSDavid du Colombier vtUnlock(fs->elk); 3355e96a66cSDavid du Colombier 3365e96a66cSDavid du Colombier return 1; 3375e96a66cSDavid du Colombier } 3385e96a66cSDavid du Colombier 3395e96a66cSDavid du Colombier static int 3405e96a66cSDavid du Colombier bumpEpoch(Fs *fs, int doarchive) 3415e96a66cSDavid du Colombier { 34261201b97SDavid du Colombier uchar oscore[VtScoreSize]; 3435e96a66cSDavid du Colombier u32int oldaddr; 3445e96a66cSDavid du Colombier Block *b, *bs; 3455e96a66cSDavid du Colombier Entry e; 3465e96a66cSDavid du Colombier Source *r; 3475e96a66cSDavid du Colombier Super super; 3485e96a66cSDavid du Colombier 3495e96a66cSDavid du Colombier /* 3505e96a66cSDavid du Colombier * Duplicate the root block. 3515e96a66cSDavid du Colombier * 3525e96a66cSDavid du Colombier * As a hint to flchk, the garbage collector, 3535e96a66cSDavid du Colombier * and any (human) debuggers, store a pointer 3545e96a66cSDavid du Colombier * to the old root block in entry 1 of the new root block. 3555e96a66cSDavid du Colombier */ 3565e96a66cSDavid du Colombier r = fs->source; 3575e96a66cSDavid du Colombier b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly); 3585e96a66cSDavid du Colombier if(b == nil) 3595e96a66cSDavid du Colombier return 0; 3605e96a66cSDavid du Colombier 3615e96a66cSDavid du Colombier memset(&e, 0, sizeof e); 3625e96a66cSDavid du Colombier e.flags = VtEntryActive | VtEntryLocal | VtEntryDir; 3635e96a66cSDavid du Colombier memmove(e.score, b->score, VtScoreSize); 3645e96a66cSDavid du Colombier e.tag = RootTag; 3655e96a66cSDavid du Colombier e.snap = b->l.epoch; 3665e96a66cSDavid du Colombier 3675e96a66cSDavid du Colombier b = blockCopy(b, RootTag, fs->ehi+1, fs->elo); 3685e96a66cSDavid du Colombier if(b == nil){ 3695e96a66cSDavid du Colombier fprint(2, "bumpEpoch: blockCopy: %R\n"); 3705e96a66cSDavid du Colombier return 0; 3715e96a66cSDavid du Colombier } 3725e96a66cSDavid du Colombier 3735e96a66cSDavid du Colombier if(0) fprint(2, "snapshot root from %d to %d\n", oldaddr, b->addr); 3745e96a66cSDavid du Colombier entryPack(&e, b->data, 1); 3755e96a66cSDavid du Colombier blockDirty(b); 3765e96a66cSDavid du Colombier 3775e96a66cSDavid du Colombier /* 3785e96a66cSDavid du Colombier * Update the superblock with the new root and epoch. 3795e96a66cSDavid du Colombier */ 3805e96a66cSDavid du Colombier if((bs = superGet(fs->cache, &super)) == nil) 3815e96a66cSDavid du Colombier return 0; 3825e96a66cSDavid du Colombier 3835e96a66cSDavid du Colombier fs->ehi++; 3845e96a66cSDavid du Colombier memmove(r->score, b->score, VtScoreSize); 3855e96a66cSDavid du Colombier r->epoch = fs->ehi; 3865e96a66cSDavid du Colombier 3875e96a66cSDavid du Colombier super.epochHigh = fs->ehi; 3885e96a66cSDavid du Colombier oldaddr = super.active; 3895e96a66cSDavid du Colombier super.active = b->addr; 3905e96a66cSDavid du Colombier if(doarchive) 3915e96a66cSDavid du Colombier super.next = oldaddr; 3925e96a66cSDavid du Colombier 3935e96a66cSDavid du Colombier /* 3945e96a66cSDavid du Colombier * Record that the new super.active can't get written out until 3955e96a66cSDavid du Colombier * the new b gets written out. Until then, use the old value. 3965e96a66cSDavid du Colombier */ 39761201b97SDavid du Colombier localToGlobal(oldaddr, oscore); 39861201b97SDavid du Colombier blockDependency(bs, b, 0, oscore, nil); 3995e96a66cSDavid du Colombier blockPut(b); 4005e96a66cSDavid du Colombier 4015e96a66cSDavid du Colombier /* 4025e96a66cSDavid du Colombier * We force the super block to disk so that super.epochHigh gets updated. 4035e96a66cSDavid du Colombier * Otherwise, if we crash and come back, we might incorrectly treat as active 4045e96a66cSDavid du Colombier * some of the blocks that making up the snapshot we just created. 4055e96a66cSDavid du Colombier * Basically every block in the active file system and all the blocks in 4065e96a66cSDavid du Colombier * the recently-created snapshot depend on the super block now. 4075e96a66cSDavid du Colombier * Rather than record all those dependencies, we just force the block to disk. 4085e96a66cSDavid du Colombier * 4095e96a66cSDavid du Colombier * Note that blockWrite might actually (will probably) send a slightly outdated 4105e96a66cSDavid du Colombier * super.active to disk. It will be the address of the most recent root that has 4115e96a66cSDavid du Colombier * gone to disk. 4125e96a66cSDavid du Colombier */ 4135e96a66cSDavid du Colombier superPut(bs, &super, 1); 4145e96a66cSDavid du Colombier 4155e96a66cSDavid du Colombier return 1; 4165e96a66cSDavid du Colombier } 4175e96a66cSDavid du Colombier 4185e96a66cSDavid du Colombier int 4195e96a66cSDavid du Colombier saveQid(Fs *fs) 4205e96a66cSDavid du Colombier { 4215e96a66cSDavid du Colombier Block *b; 4225e96a66cSDavid du Colombier Super super; 4235e96a66cSDavid du Colombier u64int qidMax; 4245e96a66cSDavid du Colombier 4255e96a66cSDavid du Colombier if((b = superGet(fs->cache, &super)) == nil) 4265e96a66cSDavid du Colombier return 0; 4275e96a66cSDavid du Colombier qidMax = super.qid; 4285e96a66cSDavid du Colombier blockPut(b); 4295e96a66cSDavid du Colombier 4305e96a66cSDavid du Colombier if(!fileSetQidSpace(fs->file, 0, qidMax)) 4315e96a66cSDavid du Colombier return 0; 4325e96a66cSDavid du Colombier 4335e96a66cSDavid du Colombier return 1; 4345e96a66cSDavid du Colombier } 4355e96a66cSDavid du Colombier 4365e96a66cSDavid du Colombier int 4375e96a66cSDavid du Colombier fsSnapshot(Fs *fs, int doarchive) 4385e96a66cSDavid du Colombier { 4395e96a66cSDavid du Colombier File *src, *dst; 4405e96a66cSDavid du Colombier 4415e96a66cSDavid du Colombier assert(fs->mode == OReadWrite); 4425e96a66cSDavid du Colombier 4435e96a66cSDavid du Colombier dst = nil; 4445e96a66cSDavid du Colombier 445*81cf8742SDavid du Colombier if(fs->halted){ 446*81cf8742SDavid du Colombier vtSetError("file system is halted"); 447*81cf8742SDavid du Colombier return 0; 448*81cf8742SDavid du Colombier } 449*81cf8742SDavid du Colombier 4505e96a66cSDavid du Colombier /* 4515e96a66cSDavid du Colombier * Freeze file system activity. 4525e96a66cSDavid du Colombier */ 4535e96a66cSDavid du Colombier vtLock(fs->elk); 4545e96a66cSDavid du Colombier 4555e96a66cSDavid du Colombier /* 4565e96a66cSDavid du Colombier * Get the root of the directory we're going to save. 4575e96a66cSDavid du Colombier */ 4585e96a66cSDavid du Colombier src = fileOpen(fs, "/active"); 4595e96a66cSDavid du Colombier if(src == nil) 4605e96a66cSDavid du Colombier goto Err; 4615e96a66cSDavid du Colombier 4625e96a66cSDavid du Colombier /* 4635e96a66cSDavid du Colombier * It is important that we maintain the invariant that: 4645e96a66cSDavid du Colombier * if both b and bb are marked as Active with epoch e 4655e96a66cSDavid du Colombier * and b points at bb, then no other pointers to bb exist. 4665e96a66cSDavid du Colombier * 4675e96a66cSDavid du Colombier * The archiver uses this property to aggressively reclaim 4685e96a66cSDavid du Colombier * such blocks once they have been stored on Venti, and 4695e96a66cSDavid du Colombier * blockCleanup knows about this property as well. 4705e96a66cSDavid du Colombier * 4715e96a66cSDavid du Colombier * Let's say src->source is block sb, and src->msource is block 4725e96a66cSDavid du Colombier * mb. Let's also say that block b holds the Entry structures for 4735e96a66cSDavid du Colombier * both src->source and src->msource (their Entry structures might 4745e96a66cSDavid du Colombier * be in different blocks, but the argument is the same). 4755e96a66cSDavid du Colombier * That is, right now we have: 4765e96a66cSDavid du Colombier * 4775e96a66cSDavid du Colombier * b Active w/ epoch e, holds ptrs to sb and mb. 4785e96a66cSDavid du Colombier * sb Active w/ epoch e. 4795e96a66cSDavid du Colombier * mb Active w/ epoch e. 4805e96a66cSDavid du Colombier * 4815e96a66cSDavid du Colombier * With things as they are now, the invariant requires that 4825e96a66cSDavid du Colombier * b holds the only pointers to sb and mb. We want to record 4835e96a66cSDavid du Colombier * pointers to sb and mb in new Entries corresponding to dst, 4845e96a66cSDavid du Colombier * which breaks the invariant. Thus we need to do something 4855e96a66cSDavid du Colombier * about b. Specifically, we bump the file system's epoch and 4865e96a66cSDavid du Colombier * then rewalk the path from the root down to and including b. 4875e96a66cSDavid du Colombier * This will copy-on-write as we walk, so now the state will be: 4885e96a66cSDavid du Colombier * 4895e96a66cSDavid du Colombier * b Snap w/ epoch e, holds ptrs to sb and mb. 4905e96a66cSDavid du Colombier * new-b Active w/ epoch e+1, holds ptrs to sb and mb. 4915e96a66cSDavid du Colombier * sb Active w/ epoch e. 4925e96a66cSDavid du Colombier * mb Active w/ epoch e. 4935e96a66cSDavid du Colombier * 4945e96a66cSDavid du Colombier * In this state, it's perfectly okay to add pointers to dst, which 4955e96a66cSDavid du Colombier * will live in a block marked Active with epoch e+1. 4965e96a66cSDavid du Colombier * 4975e96a66cSDavid du Colombier * Of course, we need to make sure that the copied path makes 4985e96a66cSDavid du Colombier * it out to disk before the new dst block; if the dst block goes out 4995e96a66cSDavid du Colombier * first and then we crash, the invariant is violated. Rather than 5005e96a66cSDavid du Colombier * deal with the dependencies, we just sync the file system to disk 5015e96a66cSDavid du Colombier * right now. 5025e96a66cSDavid du Colombier */ 5035e96a66cSDavid du Colombier if(!bumpEpoch(fs, 0) || !fileWalkSources(src)) 5045e96a66cSDavid du Colombier goto Err; 5055e96a66cSDavid du Colombier 5065e96a66cSDavid du Colombier /* 5075e96a66cSDavid du Colombier * Sync to disk. 5085e96a66cSDavid du Colombier */ 5095e96a66cSDavid du Colombier cacheFlush(fs->cache, 1); 5105e96a66cSDavid du Colombier 5115e96a66cSDavid du Colombier /* 5125e96a66cSDavid du Colombier * Create the directory where we will store the copy of src. 5135e96a66cSDavid du Colombier */ 5145e96a66cSDavid du Colombier dst = fileOpenSnapshot(fs, doarchive); 5155e96a66cSDavid du Colombier if(dst == nil) 5165e96a66cSDavid du Colombier goto Err; 5175e96a66cSDavid du Colombier 5185e96a66cSDavid du Colombier /* 5195e96a66cSDavid du Colombier * Actually make the copy by setting dst's source and msource 5205e96a66cSDavid du Colombier * to be src's. 5215e96a66cSDavid du Colombier */ 5225e96a66cSDavid du Colombier if(!fileSnapshot(dst, src, fs->ehi-1, doarchive)) 5235e96a66cSDavid du Colombier goto Err; 5245e96a66cSDavid du Colombier 5255e96a66cSDavid du Colombier fileDecRef(src); 5265e96a66cSDavid du Colombier fileDecRef(dst); 527fb7f0c93SDavid du Colombier src = nil; 528fb7f0c93SDavid du Colombier dst = nil; 529fb7f0c93SDavid du Colombier 5305e96a66cSDavid du Colombier /* 5315e96a66cSDavid du Colombier * Make another copy of the file system. This one is for the 5325e96a66cSDavid du Colombier * archiver, so that the file system we archive has the recently 5335e96a66cSDavid du Colombier * added snapshot both in /active and in /archive/yyyy/mmdd[.#]. 5345e96a66cSDavid du Colombier */ 5355e96a66cSDavid du Colombier if(doarchive){ 5365e96a66cSDavid du Colombier if(!saveQid(fs)) 5375e96a66cSDavid du Colombier goto Err; 5385e96a66cSDavid du Colombier if(!bumpEpoch(fs, 1)) 5395e96a66cSDavid du Colombier goto Err; 5405e96a66cSDavid du Colombier } 5415e96a66cSDavid du Colombier 5425e96a66cSDavid du Colombier vtUnlock(fs->elk); 5435e96a66cSDavid du Colombier 5445e96a66cSDavid du Colombier /* BUG? can fs->arch fall out from under us here? */ 5455e96a66cSDavid du Colombier if(doarchive && fs->arch) 5465e96a66cSDavid du Colombier archKick(fs->arch); 5475e96a66cSDavid du Colombier 5485e96a66cSDavid du Colombier return 1; 5495e96a66cSDavid du Colombier 5505e96a66cSDavid du Colombier Err: 5515e96a66cSDavid du Colombier fprint(2, "fsSnapshot: %R\n"); 5525e96a66cSDavid du Colombier if(src) 5535e96a66cSDavid du Colombier fileDecRef(src); 5545e96a66cSDavid du Colombier if(dst) 5555e96a66cSDavid du Colombier fileDecRef(dst); 5565e96a66cSDavid du Colombier vtUnlock(fs->elk); 5575e96a66cSDavid du Colombier return 0; 5585e96a66cSDavid du Colombier } 5595e96a66cSDavid du Colombier 5605e96a66cSDavid du Colombier int 5615e96a66cSDavid du Colombier fsVac(Fs *fs, char *name, uchar score[VtScoreSize]) 5625e96a66cSDavid du Colombier { 5635e96a66cSDavid du Colombier int r; 5645e96a66cSDavid du Colombier DirEntry de; 5655e96a66cSDavid du Colombier Entry e, ee; 5665e96a66cSDavid du Colombier File *f; 5675e96a66cSDavid du Colombier 5685e96a66cSDavid du Colombier vtRLock(fs->elk); 5695e96a66cSDavid du Colombier f = fileOpen(fs, name); 5705e96a66cSDavid du Colombier if(f == nil){ 5715e96a66cSDavid du Colombier vtRUnlock(fs->elk); 5725e96a66cSDavid du Colombier return 0; 5735e96a66cSDavid du Colombier } 5745e96a66cSDavid du Colombier 5755e96a66cSDavid du Colombier if(!fileGetSources(f, &e, &ee, 0) || !fileGetDir(f, &de)){ 5765e96a66cSDavid du Colombier fileDecRef(f); 5775e96a66cSDavid du Colombier vtRUnlock(fs->elk); 5785e96a66cSDavid du Colombier return 0; 5795e96a66cSDavid du Colombier } 5805e96a66cSDavid du Colombier fileDecRef(f); 5815e96a66cSDavid du Colombier 5825e96a66cSDavid du Colombier r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score); 5835e96a66cSDavid du Colombier vtRUnlock(fs->elk); 5845e96a66cSDavid du Colombier return r; 5855e96a66cSDavid du Colombier } 5865e96a66cSDavid du Colombier 5875e96a66cSDavid du Colombier static int 5885e96a66cSDavid du Colombier vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize]) 5895e96a66cSDavid du Colombier { 5905e96a66cSDavid du Colombier if(!vtWrite(z, score, type, buf, n)) 5915e96a66cSDavid du Colombier return 0; 5925e96a66cSDavid du Colombier if(!vtSha1Check(score, buf, n)) 5935e96a66cSDavid du Colombier return 0; 5945e96a66cSDavid du Colombier return 1; 5955e96a66cSDavid du Colombier } 5965e96a66cSDavid du Colombier 5975e96a66cSDavid du Colombier int 5985e96a66cSDavid du Colombier mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize]) 5995e96a66cSDavid du Colombier { 6005e96a66cSDavid du Colombier uchar buf[8192]; 6015e96a66cSDavid du Colombier int i; 6025e96a66cSDavid du Colombier uchar *p; 6035e96a66cSDavid du Colombier uint n; 6045e96a66cSDavid du Colombier DirEntry de; 6055e96a66cSDavid du Colombier Entry e, ee, eee; 6065e96a66cSDavid du Colombier MetaBlock mb; 6075e96a66cSDavid du Colombier MetaEntry me; 6085e96a66cSDavid du Colombier VtRoot root; 6095e96a66cSDavid du Colombier 6105e96a66cSDavid du Colombier e = *pe; 6115e96a66cSDavid du Colombier ee = *pee; 6125e96a66cSDavid du Colombier de = *pde; 6135e96a66cSDavid du Colombier 6145e96a66cSDavid du Colombier if(globalToLocal(e.score) != NilBlock 6155e96a66cSDavid du Colombier || (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){ 6165e96a66cSDavid du Colombier vtSetError("can only vac paths already stored on venti"); 6175e96a66cSDavid du Colombier return 0; 6185e96a66cSDavid du Colombier } 6195e96a66cSDavid du Colombier 6205e96a66cSDavid du Colombier /* 6215e96a66cSDavid du Colombier * Build metadata source for root. 6225e96a66cSDavid du Colombier */ 6235e96a66cSDavid du Colombier n = deSize(&de); 6245e96a66cSDavid du Colombier if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){ 6255e96a66cSDavid du Colombier vtSetError("DirEntry too big"); 6265e96a66cSDavid du Colombier return 0; 6275e96a66cSDavid du Colombier } 6285e96a66cSDavid du Colombier memset(buf, 0, sizeof buf); 6295e96a66cSDavid du Colombier mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1); 6305e96a66cSDavid du Colombier p = mbAlloc(&mb, n); 6315e96a66cSDavid du Colombier if(p == nil) 6325e96a66cSDavid du Colombier abort(); 6335e96a66cSDavid du Colombier mbSearch(&mb, de.elem, &i, &me); 6345e96a66cSDavid du Colombier assert(me.p == nil); 6355e96a66cSDavid du Colombier me.p = p; 6365e96a66cSDavid du Colombier me.size = n; 6375e96a66cSDavid du Colombier dePack(&de, &me); 6385e96a66cSDavid du Colombier mbInsert(&mb, i, &me); 6395e96a66cSDavid du Colombier mbPack(&mb); 6405e96a66cSDavid du Colombier 6415e96a66cSDavid du Colombier eee.size = n+MetaHeaderSize+MetaIndexSize; 6425e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score)) 6435e96a66cSDavid du Colombier return 0; 6445e96a66cSDavid du Colombier eee.psize = 8192; 6455e96a66cSDavid du Colombier eee.dsize = 8192; 6465e96a66cSDavid du Colombier eee.depth = 0; 6475e96a66cSDavid du Colombier eee.flags = VtEntryActive; 6485e96a66cSDavid du Colombier 6495e96a66cSDavid du Colombier /* 6505e96a66cSDavid du Colombier * Build root source with three entries in it. 6515e96a66cSDavid du Colombier */ 6525e96a66cSDavid du Colombier entryPack(&e, buf, 0); 6535e96a66cSDavid du Colombier entryPack(&ee, buf, 1); 6545e96a66cSDavid du Colombier entryPack(&eee, buf, 2); 6555e96a66cSDavid du Colombier 6565e96a66cSDavid du Colombier n = VtEntrySize*3; 6575e96a66cSDavid du Colombier memset(&root, 0, sizeof root); 6585e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, n, VtDirType, root.score)) 6595e96a66cSDavid du Colombier return 0; 6605e96a66cSDavid du Colombier 6615e96a66cSDavid du Colombier /* 6625e96a66cSDavid du Colombier * Save root. 6635e96a66cSDavid du Colombier */ 6645e96a66cSDavid du Colombier root.version = VtRootVersion; 66534e04225SDavid du Colombier strecpy(root.type, root.type+sizeof root.type, "vac"); 6665e96a66cSDavid du Colombier strecpy(root.name, root.name+sizeof root.name, de.elem); 6675e96a66cSDavid du Colombier root.blockSize = blockSize; 6685e96a66cSDavid du Colombier vtRootPack(&root, buf); 6695e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score)) 6705e96a66cSDavid du Colombier return 0; 6715e96a66cSDavid du Colombier 6725e96a66cSDavid du Colombier return 1; 6735e96a66cSDavid du Colombier } 6745e96a66cSDavid du Colombier 6755e96a66cSDavid du Colombier int 6765e96a66cSDavid du Colombier fsSync(Fs *fs) 6775e96a66cSDavid du Colombier { 6785e96a66cSDavid du Colombier vtLock(fs->elk); 679*81cf8742SDavid du Colombier fileMetaFlush(fs->file, 1); 6805e96a66cSDavid du Colombier cacheFlush(fs->cache, 1); 6815e96a66cSDavid du Colombier vtUnlock(fs->elk); 6825e96a66cSDavid du Colombier return 1; 6835e96a66cSDavid du Colombier } 6845e96a66cSDavid du Colombier 6855e96a66cSDavid du Colombier int 686*81cf8742SDavid du Colombier fsHalt(Fs *fs) 687*81cf8742SDavid du Colombier { 688*81cf8742SDavid du Colombier vtLock(fs->elk); 689*81cf8742SDavid du Colombier fs->halted = 1; 690*81cf8742SDavid du Colombier fileMetaFlush(fs->file, 1); 691*81cf8742SDavid du Colombier cacheFlush(fs->cache, 1); 692*81cf8742SDavid du Colombier return 1; 693*81cf8742SDavid du Colombier } 694*81cf8742SDavid du Colombier 695*81cf8742SDavid du Colombier int 696*81cf8742SDavid du Colombier fsUnhalt(Fs *fs) 697*81cf8742SDavid du Colombier { 698*81cf8742SDavid du Colombier if(!fs->halted) 699*81cf8742SDavid du Colombier return 0; 700*81cf8742SDavid du Colombier fs->halted = 0; 701*81cf8742SDavid du Colombier vtUnlock(fs->elk); 702*81cf8742SDavid du Colombier return 1; 703*81cf8742SDavid du Colombier } 704*81cf8742SDavid du Colombier 705*81cf8742SDavid du Colombier int 7065e96a66cSDavid du Colombier fsNextQid(Fs *fs, u64int *qid) 7075e96a66cSDavid du Colombier { 7085e96a66cSDavid du Colombier Block *b; 7095e96a66cSDavid du Colombier Super super; 7105e96a66cSDavid du Colombier 7115e96a66cSDavid du Colombier if((b = superGet(fs->cache, &super)) == nil) 7125e96a66cSDavid du Colombier return 0; 7135e96a66cSDavid du Colombier 7145e96a66cSDavid du Colombier *qid = super.qid++; 7155e96a66cSDavid du Colombier 7165e96a66cSDavid du Colombier /* 7175e96a66cSDavid du Colombier * It's okay if the super block doesn't go to disk immediately, 7185e96a66cSDavid du Colombier * since fileMetaAlloc will record a dependency between the 7195e96a66cSDavid du Colombier * block holding this qid and the super block. See file.c:/^fileMetaAlloc. 7205e96a66cSDavid du Colombier */ 7215e96a66cSDavid du Colombier superPut(b, &super, 0); 7225e96a66cSDavid du Colombier return 1; 7235e96a66cSDavid du Colombier } 7245e96a66cSDavid du Colombier 7255e96a66cSDavid du Colombier static void 7265e96a66cSDavid du Colombier fsMetaFlush(void *a) 7275e96a66cSDavid du Colombier { 7285e96a66cSDavid du Colombier Fs *fs = a; 7295e96a66cSDavid du Colombier 7305e96a66cSDavid du Colombier vtRLock(fs->elk); 7315e96a66cSDavid du Colombier fileMetaFlush(fs->file, 1); 7325e96a66cSDavid du Colombier vtRUnlock(fs->elk); 7335e96a66cSDavid du Colombier cacheFlush(fs->cache, 0); 7345e96a66cSDavid du Colombier } 7355e96a66cSDavid du Colombier 7365e96a66cSDavid du Colombier struct Snap 7375e96a66cSDavid du Colombier { 7385e96a66cSDavid du Colombier Fs *fs; 7395e96a66cSDavid du Colombier Periodic *tick; 7405e96a66cSDavid du Colombier VtLock *lk; 7415e96a66cSDavid du Colombier uint snapMinutes; 7425e96a66cSDavid du Colombier uint archMinute; 7435e96a66cSDavid du Colombier u32int lastSnap; 7445e96a66cSDavid du Colombier u32int lastArch; 7455e96a66cSDavid du Colombier uint ignore; 7465e96a66cSDavid du Colombier }; 7475e96a66cSDavid du Colombier 7485e96a66cSDavid du Colombier static void 7495e96a66cSDavid du Colombier snapEvent(void *v) 7505e96a66cSDavid du Colombier { 7515e96a66cSDavid du Colombier Snap *s; 7525e96a66cSDavid du Colombier u32int now, min; 7535e96a66cSDavid du Colombier Tm tm; 7545e96a66cSDavid du Colombier 7555e96a66cSDavid du Colombier s = v; 7565e96a66cSDavid du Colombier 7575e96a66cSDavid du Colombier now = time(0)/60; 7585e96a66cSDavid du Colombier vtLock(s->lk); 7595e96a66cSDavid du Colombier 7605e96a66cSDavid du Colombier /* 7615e96a66cSDavid du Colombier * Snapshots happen every snapMinutes minutes. 7625e96a66cSDavid du Colombier * If we miss a snapshot (for example, because we 7635e96a66cSDavid du Colombier * were down), we wait for the next one. 7645e96a66cSDavid du Colombier */ 7655e96a66cSDavid du Colombier if(s->snapMinutes != ~0 && s->snapMinutes != 0 7665e96a66cSDavid du Colombier && now%s->snapMinutes==0 && now != s->lastSnap){ 7675e96a66cSDavid du Colombier if(0)fprint(2, "snapshot %02d%02d\n", now/60, now%60); 7685e96a66cSDavid du Colombier if(!fsSnapshot(s->fs, 0)) 7695e96a66cSDavid du Colombier fprint(2, "fsSnapshot snap: %R\n"); 7705e96a66cSDavid du Colombier s->lastSnap = now; 7715e96a66cSDavid du Colombier } 7725e96a66cSDavid du Colombier 7735e96a66cSDavid du Colombier /* 7745e96a66cSDavid du Colombier * Archival snapshots happen at archMinute. 7755e96a66cSDavid du Colombier */ 7765e96a66cSDavid du Colombier tm = *localtime(now*60); 7775e96a66cSDavid du Colombier min = tm.hour*60+tm.min; 7785e96a66cSDavid du Colombier if(s->archMinute != ~0 && min == s->archMinute && now != s->lastArch){ 7795e96a66cSDavid du Colombier if(0)fprint(2, "archive %02d%02d\n", now/60, now%60); 7805e96a66cSDavid du Colombier if(!fsSnapshot(s->fs, 1)) 7815e96a66cSDavid du Colombier fprint(2, "fsSnapshot arch: %R\n"); 7825e96a66cSDavid du Colombier s->lastArch = now; 7835e96a66cSDavid du Colombier } 7845e96a66cSDavid du Colombier vtUnlock(s->lk); 7855e96a66cSDavid du Colombier } 7865e96a66cSDavid du Colombier 7875e96a66cSDavid du Colombier static Snap* 7885e96a66cSDavid du Colombier snapInit(Fs *fs) 7895e96a66cSDavid du Colombier { 7905e96a66cSDavid du Colombier Snap *s; 7915e96a66cSDavid du Colombier 7925e96a66cSDavid du Colombier s = vtMemAllocZ(sizeof(Snap)); 7935e96a66cSDavid du Colombier s->fs = fs; 7945e96a66cSDavid du Colombier s->tick = periodicAlloc(snapEvent, s, 10*1000); 7955e96a66cSDavid du Colombier s->lk = vtLockAlloc(); 7965e96a66cSDavid du Colombier s->snapMinutes = -1; 7975e96a66cSDavid du Colombier s->archMinute = -1; 7985e96a66cSDavid du Colombier s->ignore = 5*2; /* wait five minutes for clock to stabilize */ 7995e96a66cSDavid du Colombier return s; 8005e96a66cSDavid du Colombier } 8015e96a66cSDavid du Colombier 8025e96a66cSDavid du Colombier void 8035e96a66cSDavid du Colombier snapGetTimes(Snap *s, u32int *arch, u32int *snap) 8045e96a66cSDavid du Colombier { 8055e96a66cSDavid du Colombier vtLock(s->lk); 8065e96a66cSDavid du Colombier *snap = s->snapMinutes; 8075e96a66cSDavid du Colombier *arch = s->archMinute; 8085e96a66cSDavid du Colombier vtUnlock(s->lk); 8095e96a66cSDavid du Colombier } 8105e96a66cSDavid du Colombier 8115e96a66cSDavid du Colombier void 8125e96a66cSDavid du Colombier snapSetTimes(Snap *s, u32int arch, u32int snap) 8135e96a66cSDavid du Colombier { 8145e96a66cSDavid du Colombier vtLock(s->lk); 8155e96a66cSDavid du Colombier s->snapMinutes = snap; 8165e96a66cSDavid du Colombier s->archMinute = arch; 8175e96a66cSDavid du Colombier vtUnlock(s->lk); 8185e96a66cSDavid du Colombier } 8195e96a66cSDavid du Colombier 8205e96a66cSDavid du Colombier static void 8215e96a66cSDavid du Colombier snapClose(Snap *s) 8225e96a66cSDavid du Colombier { 8235e96a66cSDavid du Colombier if(s == nil) 8245e96a66cSDavid du Colombier return; 8255e96a66cSDavid du Colombier 8265e96a66cSDavid du Colombier periodicKill(s->tick); 8275e96a66cSDavid du Colombier vtMemFree(s); 8285e96a66cSDavid du Colombier } 8295e96a66cSDavid du Colombier 830