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){ 34dc5a79c1SDavid du Colombier vtSetError("open %s: %r", file); 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){ 41dc5a79c1SDavid du Colombier vtSetError("diskAlloc: %R"); 425e96a66cSDavid du Colombier close(fd); 435e96a66cSDavid du Colombier return nil; 445e96a66cSDavid du Colombier } 455e96a66cSDavid du Colombier 465e96a66cSDavid du Colombier fs = vtMemAllocZ(sizeof(Fs)); 475e96a66cSDavid du Colombier fs->mode = mode; 485e96a66cSDavid du Colombier fs->blockSize = diskBlockSize(disk); 495e96a66cSDavid du Colombier fs->elk = vtLockAlloc(); 505e96a66cSDavid du Colombier fs->cache = cacheAlloc(disk, z, ncache, mode); 516042bf6dSDavid du Colombier if(mode == OReadWrite && z) 525e96a66cSDavid du Colombier fs->arch = archInit(fs->cache, disk, fs, z); 535e96a66cSDavid du Colombier fs->z = z; 545e96a66cSDavid du Colombier 555e96a66cSDavid du Colombier b = cacheLocal(fs->cache, PartSuper, 0, mode); 565e96a66cSDavid du Colombier if(b == nil) 575e96a66cSDavid du Colombier goto Err; 585e96a66cSDavid du Colombier if(!superUnpack(&super, b->data)){ 595e96a66cSDavid du Colombier blockPut(b); 60dc5a79c1SDavid du Colombier vtSetError("bad super block"); 615e96a66cSDavid du Colombier goto Err; 625e96a66cSDavid du Colombier } 635e96a66cSDavid du Colombier blockPut(b); 645e96a66cSDavid du Colombier 655e96a66cSDavid du Colombier fs->ehi = super.epochHigh; 665e96a66cSDavid du Colombier fs->elo = super.epochLow; 675e96a66cSDavid du Colombier 68dc5a79c1SDavid du Colombier //fprint(2, "fs->ehi %d fs->elo %d active=%d\n", fs->ehi, fs->elo, super.active); 695e96a66cSDavid du Colombier 705e96a66cSDavid du Colombier fs->source = sourceRoot(fs, super.active, mode); 715e96a66cSDavid du Colombier if(fs->source == nil){ 725e96a66cSDavid du Colombier /* 735e96a66cSDavid du Colombier * Perhaps it failed because the block is copy-on-write. 745e96a66cSDavid du Colombier * Do the copy and try again. 755e96a66cSDavid du Colombier */ 765e96a66cSDavid du Colombier if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0) 775e96a66cSDavid du Colombier goto Err; 785e96a66cSDavid du Colombier b = cacheLocalData(fs->cache, super.active, BtDir, RootTag, OReadWrite, 0); 79dc5a79c1SDavid du Colombier if(b == nil){ 80dc5a79c1SDavid du Colombier vtSetError("cacheLocalData: %R"); 815e96a66cSDavid du Colombier goto Err; 82dc5a79c1SDavid du Colombier } 835e96a66cSDavid du Colombier if(!(b->l.state&BsClosed) && b->l.epoch == fs->ehi){ 845e96a66cSDavid du Colombier blockPut(b); 85dc5a79c1SDavid du Colombier vtSetError("bad root source block"); 865e96a66cSDavid du Colombier goto Err; 875e96a66cSDavid du Colombier } 885e96a66cSDavid du Colombier b = blockCopy(b, RootTag, fs->ehi, fs->elo); 895e96a66cSDavid du Colombier if(b == nil) 905e96a66cSDavid du Colombier goto Err; 9161201b97SDavid du Colombier localToGlobal(super.active, oscore); 925e96a66cSDavid du Colombier super.active = b->addr; 935e96a66cSDavid du Colombier bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite); 945e96a66cSDavid du Colombier if(bs == nil){ 955e96a66cSDavid du Colombier blockPut(b); 96dc5a79c1SDavid du Colombier vtSetError("cacheLocal: %R"); 975e96a66cSDavid du Colombier goto Err; 985e96a66cSDavid du Colombier } 995e96a66cSDavid du Colombier superPack(&super, bs->data); 10061201b97SDavid du Colombier blockDependency(bs, b, 0, oscore, nil); 101*fe853e23SDavid du Colombier blockPut(b); 1025e96a66cSDavid du Colombier blockDirty(bs); 1035e96a66cSDavid du Colombier blockPut(bs); 1045e96a66cSDavid du Colombier fs->source = sourceRoot(fs, super.active, mode); 105dc5a79c1SDavid du Colombier if(fs->source == nil){ 106dc5a79c1SDavid du Colombier vtSetError("sourceRoot: %R"); 1075e96a66cSDavid du Colombier goto Err; 1085e96a66cSDavid du Colombier } 109dc5a79c1SDavid du Colombier } 1105e96a66cSDavid du Colombier 111dc5a79c1SDavid du Colombier //fprint(2, "got fs source\n"); 1125e96a66cSDavid du Colombier 1135e96a66cSDavid du Colombier vtRLock(fs->elk); 1145e96a66cSDavid du Colombier fs->file = fileRoot(fs->source); 1155e96a66cSDavid du Colombier vtRUnlock(fs->elk); 116dc5a79c1SDavid du Colombier if(fs->file == nil){ 117dc5a79c1SDavid du Colombier vtSetError("fileRoot: %R"); 1185e96a66cSDavid du Colombier goto Err; 119dc5a79c1SDavid du Colombier } 1205e96a66cSDavid du Colombier 121dc5a79c1SDavid du Colombier //fprint(2, "got file root\n"); 1225e96a66cSDavid du Colombier 1235e96a66cSDavid du Colombier if(mode == OReadWrite){ 1245e96a66cSDavid du Colombier fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000); 1255e96a66cSDavid du Colombier fs->snap = snapInit(fs); 1265e96a66cSDavid du Colombier } 1275e96a66cSDavid du Colombier return fs; 1285e96a66cSDavid du Colombier 1295e96a66cSDavid du Colombier Err: 130dc5a79c1SDavid du Colombier fprint(2, "fsOpen error\n"); 1315e96a66cSDavid du Colombier fsClose(fs); 1325e96a66cSDavid du Colombier return nil; 1335e96a66cSDavid du Colombier } 1345e96a66cSDavid du Colombier 1355e96a66cSDavid du Colombier void 1365e96a66cSDavid du Colombier fsClose(Fs *fs) 1375e96a66cSDavid du Colombier { 1385e96a66cSDavid du Colombier vtRLock(fs->elk); 1395e96a66cSDavid du Colombier periodicKill(fs->metaFlush); 1405e96a66cSDavid du Colombier snapClose(fs->snap); 1415e96a66cSDavid du Colombier if(fs->file){ 1425e96a66cSDavid du Colombier fileMetaFlush(fs->file, 0); 1435e96a66cSDavid du Colombier if(!fileDecRef(fs->file)) 1445e96a66cSDavid du Colombier vtFatal("fsClose: files still in use: %r\n"); 1455e96a66cSDavid du Colombier } 1465e96a66cSDavid du Colombier fs->file = nil; 1475e96a66cSDavid du Colombier sourceClose(fs->source); 1485e96a66cSDavid du Colombier cacheFree(fs->cache); 1495e96a66cSDavid du Colombier if(fs->arch) 1505e96a66cSDavid du Colombier archFree(fs->arch); 1515e96a66cSDavid du Colombier vtRUnlock(fs->elk); 1525e96a66cSDavid du Colombier vtLockFree(fs->elk); 1535e96a66cSDavid du Colombier memset(fs, ~0, sizeof(Fs)); 1545e96a66cSDavid du Colombier vtMemFree(fs); 1555e96a66cSDavid du Colombier } 1565e96a66cSDavid du Colombier 1575e96a66cSDavid du Colombier int 1585e96a66cSDavid du Colombier fsRedial(Fs *fs, char *host) 1595e96a66cSDavid du Colombier { 1605e96a66cSDavid du Colombier if(!vtRedial(fs->z, host)) 1615e96a66cSDavid du Colombier return 0; 1625e96a66cSDavid du Colombier if(!vtConnect(fs->z, 0)) 1635e96a66cSDavid du Colombier return 0; 1645e96a66cSDavid du Colombier return 1; 1655e96a66cSDavid du Colombier } 1665e96a66cSDavid du Colombier 1675e96a66cSDavid du Colombier File * 1685e96a66cSDavid du Colombier fsGetRoot(Fs *fs) 1695e96a66cSDavid du Colombier { 1705e96a66cSDavid du Colombier return fileIncRef(fs->file); 1715e96a66cSDavid du Colombier } 1725e96a66cSDavid du Colombier 1735e96a66cSDavid du Colombier int 1745e96a66cSDavid du Colombier fsGetBlockSize(Fs *fs) 1755e96a66cSDavid du Colombier { 1765e96a66cSDavid du Colombier return fs->blockSize; 1775e96a66cSDavid du Colombier } 1785e96a66cSDavid du Colombier 1795e96a66cSDavid du Colombier Block* 1805e96a66cSDavid du Colombier superGet(Cache *c, Super* super) 1815e96a66cSDavid du Colombier { 1825e96a66cSDavid du Colombier Block *b; 1835e96a66cSDavid du Colombier 1845e96a66cSDavid du Colombier if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){ 1855e96a66cSDavid du Colombier fprint(2, "superGet: cacheLocal failed: %R"); 1865e96a66cSDavid du Colombier return nil; 1875e96a66cSDavid du Colombier } 1885e96a66cSDavid du Colombier if(!superUnpack(super, b->data)){ 1895e96a66cSDavid du Colombier fprint(2, "superGet: superUnpack failed: %R"); 1905e96a66cSDavid du Colombier blockPut(b); 1915e96a66cSDavid du Colombier return nil; 1925e96a66cSDavid du Colombier } 1935e96a66cSDavid du Colombier 1945e96a66cSDavid du Colombier return b; 1955e96a66cSDavid du Colombier } 1965e96a66cSDavid du Colombier 1975e96a66cSDavid du Colombier void 1985e96a66cSDavid du Colombier superPut(Block* b, Super* super, int forceWrite) 1995e96a66cSDavid du Colombier { 2005e96a66cSDavid du Colombier superPack(super, b->data); 2015e96a66cSDavid du Colombier blockDirty(b); 2025e96a66cSDavid du Colombier if(forceWrite){ 2035e96a66cSDavid du Colombier while(!blockWrite(b)){ 2045e96a66cSDavid du Colombier /* BUG: what should really happen here? */ 2055e96a66cSDavid du Colombier fprint(2, "could not write super block; waiting 10 seconds\n"); 2065e96a66cSDavid du Colombier sleep(10*000); 2075e96a66cSDavid du Colombier } 2085e96a66cSDavid du Colombier while(b->iostate != BioClean && b->iostate != BioDirty){ 2095e96a66cSDavid du Colombier assert(b->iostate == BioWriting); 2105e96a66cSDavid du Colombier vtSleep(b->ioready); 2115e96a66cSDavid du Colombier } 2125e96a66cSDavid du Colombier /* 2135e96a66cSDavid du Colombier * it's okay that b might still be dirty. 2145e96a66cSDavid du Colombier * that means it got written out but with an old root pointer, 2155e96a66cSDavid du Colombier * but the other fields went out, and those are the ones 2165e96a66cSDavid du Colombier * we really care about. (specifically, epochHigh; see fsSnapshot). 2175e96a66cSDavid du Colombier */ 2185e96a66cSDavid du Colombier } 2195e96a66cSDavid du Colombier blockPut(b); 2205e96a66cSDavid du Colombier } 2215e96a66cSDavid du Colombier 2225e96a66cSDavid du Colombier /* 2235e96a66cSDavid du Colombier * Prepare the directory to store a snapshot. 22461201b97SDavid du Colombier * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#] 2255e96a66cSDavid du Colombier * Archival snapshots go into /archive/yyyy/mmdd[.#]. 2265e96a66cSDavid du Colombier * 2275e96a66cSDavid du Colombier * TODO This should be rewritten to eliminate most of the duplication. 2285e96a66cSDavid du Colombier */ 2295e96a66cSDavid du Colombier static File* 2305e96a66cSDavid du Colombier fileOpenSnapshot(Fs *fs, int doarchive) 2315e96a66cSDavid du Colombier { 2325e96a66cSDavid du Colombier int n; 2335e96a66cSDavid du Colombier char buf[30], *s; 2345e96a66cSDavid du Colombier File *dir, *f; 2355e96a66cSDavid du Colombier Tm now; 2365e96a66cSDavid du Colombier 2375e96a66cSDavid du Colombier if(doarchive){ 2385e96a66cSDavid du Colombier /* 2395e96a66cSDavid du Colombier * a snapshot intended to be archived to venti. 2405e96a66cSDavid du Colombier */ 2415e96a66cSDavid du Colombier dir = fileOpen(fs, "/archive"); 2425e96a66cSDavid du Colombier if(dir == nil) 2435e96a66cSDavid du Colombier return nil; 2445e96a66cSDavid du Colombier now = *localtime(time(0)); 2455e96a66cSDavid du Colombier 2465e96a66cSDavid du Colombier /* yyyy */ 2475e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%d", now.year+1900); 2485e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2495e96a66cSDavid du Colombier if(f == nil) 2505e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm"); 2515e96a66cSDavid du Colombier fileDecRef(dir); 2525e96a66cSDavid du Colombier if(f == nil) 2535e96a66cSDavid du Colombier return nil; 2545e96a66cSDavid du Colombier dir = f; 2555e96a66cSDavid du Colombier 2565e96a66cSDavid du Colombier /* mmdd[#] */ 2575e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); 2585e96a66cSDavid du Colombier s = buf+strlen(buf); 2595e96a66cSDavid du Colombier for(n=0;; n++){ 2605e96a66cSDavid du Colombier if(n) 2615e96a66cSDavid du Colombier seprint(s, buf+sizeof(buf), ".%d", n); 2625e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2635e96a66cSDavid du Colombier if(f != nil){ 2645e96a66cSDavid du Colombier fileDecRef(f); 2655e96a66cSDavid du Colombier continue; 2665e96a66cSDavid du Colombier } 2675e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); 2685e96a66cSDavid du Colombier break; 2695e96a66cSDavid du Colombier } 2705e96a66cSDavid du Colombier fileDecRef(dir); 2715e96a66cSDavid du Colombier return f; 2725e96a66cSDavid du Colombier }else{ 2735e96a66cSDavid du Colombier /* 2745e96a66cSDavid du Colombier * Just a temporary snapshot 2755e96a66cSDavid du Colombier * We'll use /snapshot/yyyy/mmdd/hhmm. 2765e96a66cSDavid du Colombier * There may well be a better naming scheme. 2775e96a66cSDavid du Colombier * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.) 2785e96a66cSDavid du Colombier */ 2795e96a66cSDavid du Colombier dir = fileOpen(fs, "/snapshot"); 2805e96a66cSDavid du Colombier if(dir == nil) 2815e96a66cSDavid du Colombier return nil; 2825e96a66cSDavid du Colombier 2835e96a66cSDavid du Colombier now = *localtime(time(0)); 2845e96a66cSDavid du Colombier 2855e96a66cSDavid du Colombier /* yyyy */ 2865e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%d", now.year+1900); 2875e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2885e96a66cSDavid du Colombier if(f == nil) 2895e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm"); 2905e96a66cSDavid du Colombier fileDecRef(dir); 2915e96a66cSDavid du Colombier if(f == nil) 2925e96a66cSDavid du Colombier return nil; 2935e96a66cSDavid du Colombier dir = f; 2945e96a66cSDavid du Colombier 2955e96a66cSDavid du Colombier /* mmdd */ 2965e96a66cSDavid du Colombier snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); 2975e96a66cSDavid du Colombier f = fileWalk(dir, buf); 2985e96a66cSDavid du Colombier if(f == nil) 2995e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|0555, "adm"); 3005e96a66cSDavid du Colombier fileDecRef(dir); 3015e96a66cSDavid du Colombier if(f == nil) 3025e96a66cSDavid du Colombier return nil; 3035e96a66cSDavid du Colombier dir = f; 3045e96a66cSDavid du Colombier 30561201b97SDavid du Colombier /* hhmm[.#] */ 3065e96a66cSDavid du Colombier snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min); 30761201b97SDavid du Colombier s = buf+strlen(buf); 30861201b97SDavid du Colombier for(n=0;; n++){ 30961201b97SDavid du Colombier if(n) 31061201b97SDavid du Colombier seprint(s, buf+sizeof(buf), ".%d", n); 3115e96a66cSDavid du Colombier f = fileWalk(dir, buf); 3125e96a66cSDavid du Colombier if(f != nil){ 3135e96a66cSDavid du Colombier fileDecRef(f); 31461201b97SDavid du Colombier continue; 3155e96a66cSDavid du Colombier } 3165e96a66cSDavid du Colombier f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); 31761201b97SDavid du Colombier break; 31861201b97SDavid du Colombier } 3195e96a66cSDavid du Colombier fileDecRef(dir); 3205e96a66cSDavid du Colombier return f; 3215e96a66cSDavid du Colombier } 3225e96a66cSDavid du Colombier } 3235e96a66cSDavid du Colombier 324dc5a79c1SDavid du Colombier static int 325dc5a79c1SDavid du Colombier fsNeedArch(Fs *fs, uint archMinute) 326dc5a79c1SDavid du Colombier { 327dc5a79c1SDavid du Colombier int need; 328dc5a79c1SDavid du Colombier File *f; 329dc5a79c1SDavid du Colombier char buf[100]; 330dc5a79c1SDavid du Colombier Tm now; 331dc5a79c1SDavid du Colombier ulong then; 332dc5a79c1SDavid du Colombier 333dc5a79c1SDavid du Colombier then = time(0); 334dc5a79c1SDavid du Colombier now = *localtime(then); 335dc5a79c1SDavid du Colombier 336dc5a79c1SDavid du Colombier /* back up to yesterday if necessary */ 337dc5a79c1SDavid du Colombier if(now.hour < archMinute/60 338dc5a79c1SDavid du Colombier || now.hour == archMinute/60 && now.min < archMinute%60) 339dc5a79c1SDavid du Colombier now = *localtime(then-86400); 340dc5a79c1SDavid du Colombier 341dc5a79c1SDavid du Colombier snprint(buf, sizeof buf, "/archive/%d/%02d%02d", 342dc5a79c1SDavid du Colombier now.year+1900, now.mon+1, now.mday); 343dc5a79c1SDavid du Colombier need = 1; 344dc5a79c1SDavid du Colombier vtRLock(fs->elk); 345dc5a79c1SDavid du Colombier f = fileOpen(fs, buf); 346dc5a79c1SDavid du Colombier if(f){ 347dc5a79c1SDavid du Colombier need = 0; 348dc5a79c1SDavid du Colombier fileDecRef(f); 349dc5a79c1SDavid du Colombier } 350dc5a79c1SDavid du Colombier vtRUnlock(fs->elk); 351dc5a79c1SDavid du Colombier return need; 352dc5a79c1SDavid du Colombier } 353dc5a79c1SDavid du Colombier 3545e96a66cSDavid du Colombier int 3555e96a66cSDavid du Colombier fsEpochLow(Fs *fs, u32int low) 3565e96a66cSDavid du Colombier { 3575e96a66cSDavid du Colombier Block *bs; 3585e96a66cSDavid du Colombier Super super; 3595e96a66cSDavid du Colombier 3605e96a66cSDavid du Colombier vtLock(fs->elk); 3615e96a66cSDavid du Colombier if(low > fs->ehi){ 3625e96a66cSDavid du Colombier vtSetError("bad low epoch (must be <= %ud)", fs->ehi); 3635e96a66cSDavid du Colombier vtUnlock(fs->elk); 3645e96a66cSDavid du Colombier return 0; 3655e96a66cSDavid du Colombier } 3665e96a66cSDavid du Colombier 3675e96a66cSDavid du Colombier if((bs = superGet(fs->cache, &super)) == nil){ 3685e96a66cSDavid du Colombier vtUnlock(fs->elk); 3695e96a66cSDavid du Colombier return 0; 3705e96a66cSDavid du Colombier } 3715e96a66cSDavid du Colombier 3725e96a66cSDavid du Colombier super.epochLow = low; 3735e96a66cSDavid du Colombier fs->elo = low; 3745e96a66cSDavid du Colombier superPut(bs, &super, 1); 3755e96a66cSDavid du Colombier vtUnlock(fs->elk); 3765e96a66cSDavid du Colombier 3775e96a66cSDavid du Colombier return 1; 3785e96a66cSDavid du Colombier } 3795e96a66cSDavid du Colombier 3805e96a66cSDavid du Colombier static int 3815e96a66cSDavid du Colombier bumpEpoch(Fs *fs, int doarchive) 3825e96a66cSDavid du Colombier { 38361201b97SDavid du Colombier uchar oscore[VtScoreSize]; 3845e96a66cSDavid du Colombier u32int oldaddr; 3855e96a66cSDavid du Colombier Block *b, *bs; 3865e96a66cSDavid du Colombier Entry e; 3875e96a66cSDavid du Colombier Source *r; 3885e96a66cSDavid du Colombier Super super; 3895e96a66cSDavid du Colombier 3905e96a66cSDavid du Colombier /* 3915e96a66cSDavid du Colombier * Duplicate the root block. 3925e96a66cSDavid du Colombier * 3935e96a66cSDavid du Colombier * As a hint to flchk, the garbage collector, 3945e96a66cSDavid du Colombier * and any (human) debuggers, store a pointer 3955e96a66cSDavid du Colombier * to the old root block in entry 1 of the new root block. 3965e96a66cSDavid du Colombier */ 3975e96a66cSDavid du Colombier r = fs->source; 3985e96a66cSDavid du Colombier b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly); 3995e96a66cSDavid du Colombier if(b == nil) 4005e96a66cSDavid du Colombier return 0; 4015e96a66cSDavid du Colombier 4025e96a66cSDavid du Colombier memset(&e, 0, sizeof e); 4035e96a66cSDavid du Colombier e.flags = VtEntryActive | VtEntryLocal | VtEntryDir; 4045e96a66cSDavid du Colombier memmove(e.score, b->score, VtScoreSize); 4055e96a66cSDavid du Colombier e.tag = RootTag; 4065e96a66cSDavid du Colombier e.snap = b->l.epoch; 4075e96a66cSDavid du Colombier 4085e96a66cSDavid du Colombier b = blockCopy(b, RootTag, fs->ehi+1, fs->elo); 4095e96a66cSDavid du Colombier if(b == nil){ 4105e96a66cSDavid du Colombier fprint(2, "bumpEpoch: blockCopy: %R\n"); 4115e96a66cSDavid du Colombier return 0; 4125e96a66cSDavid du Colombier } 4135e96a66cSDavid du Colombier 4145e96a66cSDavid du Colombier if(0) fprint(2, "snapshot root from %d to %d\n", oldaddr, b->addr); 4155e96a66cSDavid du Colombier entryPack(&e, b->data, 1); 4165e96a66cSDavid du Colombier blockDirty(b); 4175e96a66cSDavid du Colombier 4185e96a66cSDavid du Colombier /* 4195e96a66cSDavid du Colombier * Update the superblock with the new root and epoch. 4205e96a66cSDavid du Colombier */ 4215e96a66cSDavid du Colombier if((bs = superGet(fs->cache, &super)) == nil) 4225e96a66cSDavid du Colombier return 0; 4235e96a66cSDavid du Colombier 4245e96a66cSDavid du Colombier fs->ehi++; 4255e96a66cSDavid du Colombier memmove(r->score, b->score, VtScoreSize); 4265e96a66cSDavid du Colombier r->epoch = fs->ehi; 4275e96a66cSDavid du Colombier 4285e96a66cSDavid du Colombier super.epochHigh = fs->ehi; 4295e96a66cSDavid du Colombier oldaddr = super.active; 4305e96a66cSDavid du Colombier super.active = b->addr; 4315e96a66cSDavid du Colombier if(doarchive) 4325e96a66cSDavid du Colombier super.next = oldaddr; 4335e96a66cSDavid du Colombier 4345e96a66cSDavid du Colombier /* 4355e96a66cSDavid du Colombier * Record that the new super.active can't get written out until 4365e96a66cSDavid du Colombier * the new b gets written out. Until then, use the old value. 4375e96a66cSDavid du Colombier */ 43861201b97SDavid du Colombier localToGlobal(oldaddr, oscore); 43961201b97SDavid du Colombier blockDependency(bs, b, 0, oscore, nil); 4405e96a66cSDavid du Colombier blockPut(b); 4415e96a66cSDavid du Colombier 4425e96a66cSDavid du Colombier /* 4435e96a66cSDavid du Colombier * We force the super block to disk so that super.epochHigh gets updated. 4445e96a66cSDavid du Colombier * Otherwise, if we crash and come back, we might incorrectly treat as active 4455e96a66cSDavid du Colombier * some of the blocks that making up the snapshot we just created. 4465e96a66cSDavid du Colombier * Basically every block in the active file system and all the blocks in 4475e96a66cSDavid du Colombier * the recently-created snapshot depend on the super block now. 4485e96a66cSDavid du Colombier * Rather than record all those dependencies, we just force the block to disk. 4495e96a66cSDavid du Colombier * 4505e96a66cSDavid du Colombier * Note that blockWrite might actually (will probably) send a slightly outdated 4515e96a66cSDavid du Colombier * super.active to disk. It will be the address of the most recent root that has 4525e96a66cSDavid du Colombier * gone to disk. 4535e96a66cSDavid du Colombier */ 4545e96a66cSDavid du Colombier superPut(bs, &super, 1); 4555e96a66cSDavid du Colombier 4565e96a66cSDavid du Colombier return 1; 4575e96a66cSDavid du Colombier } 4585e96a66cSDavid du Colombier 4595e96a66cSDavid du Colombier int 4605e96a66cSDavid du Colombier saveQid(Fs *fs) 4615e96a66cSDavid du Colombier { 4625e96a66cSDavid du Colombier Block *b; 4635e96a66cSDavid du Colombier Super super; 4645e96a66cSDavid du Colombier u64int qidMax; 4655e96a66cSDavid du Colombier 4665e96a66cSDavid du Colombier if((b = superGet(fs->cache, &super)) == nil) 4675e96a66cSDavid du Colombier return 0; 4685e96a66cSDavid du Colombier qidMax = super.qid; 4695e96a66cSDavid du Colombier blockPut(b); 4705e96a66cSDavid du Colombier 4715e96a66cSDavid du Colombier if(!fileSetQidSpace(fs->file, 0, qidMax)) 4725e96a66cSDavid du Colombier return 0; 4735e96a66cSDavid du Colombier 4745e96a66cSDavid du Colombier return 1; 4755e96a66cSDavid du Colombier } 4765e96a66cSDavid du Colombier 4775e96a66cSDavid du Colombier int 4785e96a66cSDavid du Colombier fsSnapshot(Fs *fs, int doarchive) 4795e96a66cSDavid du Colombier { 4805e96a66cSDavid du Colombier File *src, *dst; 4815e96a66cSDavid du Colombier 4825e96a66cSDavid du Colombier assert(fs->mode == OReadWrite); 4835e96a66cSDavid du Colombier 4845e96a66cSDavid du Colombier dst = nil; 4855e96a66cSDavid du Colombier 48681cf8742SDavid du Colombier if(fs->halted){ 48781cf8742SDavid du Colombier vtSetError("file system is halted"); 48881cf8742SDavid du Colombier return 0; 48981cf8742SDavid du Colombier } 49081cf8742SDavid du Colombier 4915e96a66cSDavid du Colombier /* 4925e96a66cSDavid du Colombier * Freeze file system activity. 4935e96a66cSDavid du Colombier */ 4945e96a66cSDavid du Colombier vtLock(fs->elk); 4955e96a66cSDavid du Colombier 4965e96a66cSDavid du Colombier /* 4975e96a66cSDavid du Colombier * Get the root of the directory we're going to save. 4985e96a66cSDavid du Colombier */ 4995e96a66cSDavid du Colombier src = fileOpen(fs, "/active"); 5005e96a66cSDavid du Colombier if(src == nil) 5015e96a66cSDavid du Colombier goto Err; 5025e96a66cSDavid du Colombier 5035e96a66cSDavid du Colombier /* 5045e96a66cSDavid du Colombier * It is important that we maintain the invariant that: 5055e96a66cSDavid du Colombier * if both b and bb are marked as Active with epoch e 5065e96a66cSDavid du Colombier * and b points at bb, then no other pointers to bb exist. 5075e96a66cSDavid du Colombier * 5085e96a66cSDavid du Colombier * The archiver uses this property to aggressively reclaim 5095e96a66cSDavid du Colombier * such blocks once they have been stored on Venti, and 5105e96a66cSDavid du Colombier * blockCleanup knows about this property as well. 5115e96a66cSDavid du Colombier * 5125e96a66cSDavid du Colombier * Let's say src->source is block sb, and src->msource is block 5135e96a66cSDavid du Colombier * mb. Let's also say that block b holds the Entry structures for 5145e96a66cSDavid du Colombier * both src->source and src->msource (their Entry structures might 5155e96a66cSDavid du Colombier * be in different blocks, but the argument is the same). 5165e96a66cSDavid du Colombier * That is, right now we have: 5175e96a66cSDavid du Colombier * 5185e96a66cSDavid du Colombier * b Active w/ epoch e, holds ptrs to sb and mb. 5195e96a66cSDavid du Colombier * sb Active w/ epoch e. 5205e96a66cSDavid du Colombier * mb Active w/ epoch e. 5215e96a66cSDavid du Colombier * 5225e96a66cSDavid du Colombier * With things as they are now, the invariant requires that 5235e96a66cSDavid du Colombier * b holds the only pointers to sb and mb. We want to record 5245e96a66cSDavid du Colombier * pointers to sb and mb in new Entries corresponding to dst, 5255e96a66cSDavid du Colombier * which breaks the invariant. Thus we need to do something 5265e96a66cSDavid du Colombier * about b. Specifically, we bump the file system's epoch and 5275e96a66cSDavid du Colombier * then rewalk the path from the root down to and including b. 5285e96a66cSDavid du Colombier * This will copy-on-write as we walk, so now the state will be: 5295e96a66cSDavid du Colombier * 5305e96a66cSDavid du Colombier * b Snap w/ epoch e, holds ptrs to sb and mb. 5315e96a66cSDavid du Colombier * new-b Active w/ epoch e+1, holds ptrs to sb and mb. 5325e96a66cSDavid du Colombier * sb Active w/ epoch e. 5335e96a66cSDavid du Colombier * mb Active w/ epoch e. 5345e96a66cSDavid du Colombier * 5355e96a66cSDavid du Colombier * In this state, it's perfectly okay to add pointers to dst, which 5365e96a66cSDavid du Colombier * will live in a block marked Active with epoch e+1. 5375e96a66cSDavid du Colombier * 5385e96a66cSDavid du Colombier * Of course, we need to make sure that the copied path makes 5395e96a66cSDavid du Colombier * it out to disk before the new dst block; if the dst block goes out 5405e96a66cSDavid du Colombier * first and then we crash, the invariant is violated. Rather than 5415e96a66cSDavid du Colombier * deal with the dependencies, we just sync the file system to disk 5425e96a66cSDavid du Colombier * right now. 5435e96a66cSDavid du Colombier */ 5445e96a66cSDavid du Colombier if(!bumpEpoch(fs, 0) || !fileWalkSources(src)) 5455e96a66cSDavid du Colombier goto Err; 5465e96a66cSDavid du Colombier 5475e96a66cSDavid du Colombier /* 5485e96a66cSDavid du Colombier * Sync to disk. 5495e96a66cSDavid du Colombier */ 5505e96a66cSDavid du Colombier cacheFlush(fs->cache, 1); 5515e96a66cSDavid du Colombier 5525e96a66cSDavid du Colombier /* 5535e96a66cSDavid du Colombier * Create the directory where we will store the copy of src. 5545e96a66cSDavid du Colombier */ 5555e96a66cSDavid du Colombier dst = fileOpenSnapshot(fs, doarchive); 5565e96a66cSDavid du Colombier if(dst == nil) 5575e96a66cSDavid du Colombier goto Err; 5585e96a66cSDavid du Colombier 5595e96a66cSDavid du Colombier /* 5605e96a66cSDavid du Colombier * Actually make the copy by setting dst's source and msource 5615e96a66cSDavid du Colombier * to be src's. 5625e96a66cSDavid du Colombier */ 5635e96a66cSDavid du Colombier if(!fileSnapshot(dst, src, fs->ehi-1, doarchive)) 5645e96a66cSDavid du Colombier goto Err; 5655e96a66cSDavid du Colombier 5665e96a66cSDavid du Colombier fileDecRef(src); 5675e96a66cSDavid du Colombier fileDecRef(dst); 568fb7f0c93SDavid du Colombier src = nil; 569fb7f0c93SDavid du Colombier dst = nil; 570fb7f0c93SDavid du Colombier 5715e96a66cSDavid du Colombier /* 5725e96a66cSDavid du Colombier * Make another copy of the file system. This one is for the 5735e96a66cSDavid du Colombier * archiver, so that the file system we archive has the recently 5745e96a66cSDavid du Colombier * added snapshot both in /active and in /archive/yyyy/mmdd[.#]. 5755e96a66cSDavid du Colombier */ 5765e96a66cSDavid du Colombier if(doarchive){ 5775e96a66cSDavid du Colombier if(!saveQid(fs)) 5785e96a66cSDavid du Colombier goto Err; 5795e96a66cSDavid du Colombier if(!bumpEpoch(fs, 1)) 5805e96a66cSDavid du Colombier goto Err; 5815e96a66cSDavid du Colombier } 5825e96a66cSDavid du Colombier 5835e96a66cSDavid du Colombier vtUnlock(fs->elk); 5845e96a66cSDavid du Colombier 5855e96a66cSDavid du Colombier /* BUG? can fs->arch fall out from under us here? */ 5865e96a66cSDavid du Colombier if(doarchive && fs->arch) 5875e96a66cSDavid du Colombier archKick(fs->arch); 5885e96a66cSDavid du Colombier 5895e96a66cSDavid du Colombier return 1; 5905e96a66cSDavid du Colombier 5915e96a66cSDavid du Colombier Err: 5925e96a66cSDavid du Colombier fprint(2, "fsSnapshot: %R\n"); 5935e96a66cSDavid du Colombier if(src) 5945e96a66cSDavid du Colombier fileDecRef(src); 5955e96a66cSDavid du Colombier if(dst) 5965e96a66cSDavid du Colombier fileDecRef(dst); 5975e96a66cSDavid du Colombier vtUnlock(fs->elk); 5985e96a66cSDavid du Colombier return 0; 5995e96a66cSDavid du Colombier } 6005e96a66cSDavid du Colombier 6015e96a66cSDavid du Colombier int 6025e96a66cSDavid du Colombier fsVac(Fs *fs, char *name, uchar score[VtScoreSize]) 6035e96a66cSDavid du Colombier { 6045e96a66cSDavid du Colombier int r; 6055e96a66cSDavid du Colombier DirEntry de; 6065e96a66cSDavid du Colombier Entry e, ee; 6075e96a66cSDavid du Colombier File *f; 6085e96a66cSDavid du Colombier 6095e96a66cSDavid du Colombier vtRLock(fs->elk); 6105e96a66cSDavid du Colombier f = fileOpen(fs, name); 6115e96a66cSDavid du Colombier if(f == nil){ 6125e96a66cSDavid du Colombier vtRUnlock(fs->elk); 6135e96a66cSDavid du Colombier return 0; 6145e96a66cSDavid du Colombier } 6155e96a66cSDavid du Colombier 6165e96a66cSDavid du Colombier if(!fileGetSources(f, &e, &ee, 0) || !fileGetDir(f, &de)){ 6175e96a66cSDavid du Colombier fileDecRef(f); 6185e96a66cSDavid du Colombier vtRUnlock(fs->elk); 6195e96a66cSDavid du Colombier return 0; 6205e96a66cSDavid du Colombier } 6215e96a66cSDavid du Colombier fileDecRef(f); 6225e96a66cSDavid du Colombier 6235e96a66cSDavid du Colombier r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score); 6245e96a66cSDavid du Colombier vtRUnlock(fs->elk); 6255e96a66cSDavid du Colombier return r; 6265e96a66cSDavid du Colombier } 6275e96a66cSDavid du Colombier 6285e96a66cSDavid du Colombier static int 6295e96a66cSDavid du Colombier vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize]) 6305e96a66cSDavid du Colombier { 6315e96a66cSDavid du Colombier if(!vtWrite(z, score, type, buf, n)) 6325e96a66cSDavid du Colombier return 0; 6335e96a66cSDavid du Colombier if(!vtSha1Check(score, buf, n)) 6345e96a66cSDavid du Colombier return 0; 6355e96a66cSDavid du Colombier return 1; 6365e96a66cSDavid du Colombier } 6375e96a66cSDavid du Colombier 6385e96a66cSDavid du Colombier int 6395e96a66cSDavid du Colombier mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize]) 6405e96a66cSDavid du Colombier { 6415e96a66cSDavid du Colombier uchar buf[8192]; 6425e96a66cSDavid du Colombier int i; 6435e96a66cSDavid du Colombier uchar *p; 6445e96a66cSDavid du Colombier uint n; 6455e96a66cSDavid du Colombier DirEntry de; 6465e96a66cSDavid du Colombier Entry e, ee, eee; 6475e96a66cSDavid du Colombier MetaBlock mb; 6485e96a66cSDavid du Colombier MetaEntry me; 6495e96a66cSDavid du Colombier VtRoot root; 6505e96a66cSDavid du Colombier 6515e96a66cSDavid du Colombier e = *pe; 6525e96a66cSDavid du Colombier ee = *pee; 6535e96a66cSDavid du Colombier de = *pde; 6545e96a66cSDavid du Colombier 6555e96a66cSDavid du Colombier if(globalToLocal(e.score) != NilBlock 6565e96a66cSDavid du Colombier || (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){ 6575e96a66cSDavid du Colombier vtSetError("can only vac paths already stored on venti"); 6585e96a66cSDavid du Colombier return 0; 6595e96a66cSDavid du Colombier } 6605e96a66cSDavid du Colombier 6615e96a66cSDavid du Colombier /* 6625e96a66cSDavid du Colombier * Build metadata source for root. 6635e96a66cSDavid du Colombier */ 6645e96a66cSDavid du Colombier n = deSize(&de); 6655e96a66cSDavid du Colombier if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){ 6665e96a66cSDavid du Colombier vtSetError("DirEntry too big"); 6675e96a66cSDavid du Colombier return 0; 6685e96a66cSDavid du Colombier } 6695e96a66cSDavid du Colombier memset(buf, 0, sizeof buf); 6705e96a66cSDavid du Colombier mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1); 6715e96a66cSDavid du Colombier p = mbAlloc(&mb, n); 6725e96a66cSDavid du Colombier if(p == nil) 6735e96a66cSDavid du Colombier abort(); 6745e96a66cSDavid du Colombier mbSearch(&mb, de.elem, &i, &me); 6755e96a66cSDavid du Colombier assert(me.p == nil); 6765e96a66cSDavid du Colombier me.p = p; 6775e96a66cSDavid du Colombier me.size = n; 6785e96a66cSDavid du Colombier dePack(&de, &me); 6795e96a66cSDavid du Colombier mbInsert(&mb, i, &me); 6805e96a66cSDavid du Colombier mbPack(&mb); 6815e96a66cSDavid du Colombier 6825e96a66cSDavid du Colombier eee.size = n+MetaHeaderSize+MetaIndexSize; 6835e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score)) 6845e96a66cSDavid du Colombier return 0; 6855e96a66cSDavid du Colombier eee.psize = 8192; 6865e96a66cSDavid du Colombier eee.dsize = 8192; 6875e96a66cSDavid du Colombier eee.depth = 0; 6885e96a66cSDavid du Colombier eee.flags = VtEntryActive; 6895e96a66cSDavid du Colombier 6905e96a66cSDavid du Colombier /* 6915e96a66cSDavid du Colombier * Build root source with three entries in it. 6925e96a66cSDavid du Colombier */ 6935e96a66cSDavid du Colombier entryPack(&e, buf, 0); 6945e96a66cSDavid du Colombier entryPack(&ee, buf, 1); 6955e96a66cSDavid du Colombier entryPack(&eee, buf, 2); 6965e96a66cSDavid du Colombier 6975e96a66cSDavid du Colombier n = VtEntrySize*3; 6985e96a66cSDavid du Colombier memset(&root, 0, sizeof root); 6995e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, n, VtDirType, root.score)) 7005e96a66cSDavid du Colombier return 0; 7015e96a66cSDavid du Colombier 7025e96a66cSDavid du Colombier /* 7035e96a66cSDavid du Colombier * Save root. 7045e96a66cSDavid du Colombier */ 7055e96a66cSDavid du Colombier root.version = VtRootVersion; 70634e04225SDavid du Colombier strecpy(root.type, root.type+sizeof root.type, "vac"); 7075e96a66cSDavid du Colombier strecpy(root.name, root.name+sizeof root.name, de.elem); 7085e96a66cSDavid du Colombier root.blockSize = blockSize; 7095e96a66cSDavid du Colombier vtRootPack(&root, buf); 7105e96a66cSDavid du Colombier if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score)) 7115e96a66cSDavid du Colombier return 0; 7125e96a66cSDavid du Colombier 7135e96a66cSDavid du Colombier return 1; 7145e96a66cSDavid du Colombier } 7155e96a66cSDavid du Colombier 7165e96a66cSDavid du Colombier int 7175e96a66cSDavid du Colombier fsSync(Fs *fs) 7185e96a66cSDavid du Colombier { 7195e96a66cSDavid du Colombier vtLock(fs->elk); 72081cf8742SDavid du Colombier fileMetaFlush(fs->file, 1); 7215e96a66cSDavid du Colombier cacheFlush(fs->cache, 1); 7225e96a66cSDavid du Colombier vtUnlock(fs->elk); 7235e96a66cSDavid du Colombier return 1; 7245e96a66cSDavid du Colombier } 7255e96a66cSDavid du Colombier 7265e96a66cSDavid du Colombier int 72781cf8742SDavid du Colombier fsHalt(Fs *fs) 72881cf8742SDavid du Colombier { 72981cf8742SDavid du Colombier vtLock(fs->elk); 73081cf8742SDavid du Colombier fs->halted = 1; 73181cf8742SDavid du Colombier fileMetaFlush(fs->file, 1); 73281cf8742SDavid du Colombier cacheFlush(fs->cache, 1); 73381cf8742SDavid du Colombier return 1; 73481cf8742SDavid du Colombier } 73581cf8742SDavid du Colombier 73681cf8742SDavid du Colombier int 73781cf8742SDavid du Colombier fsUnhalt(Fs *fs) 73881cf8742SDavid du Colombier { 73981cf8742SDavid du Colombier if(!fs->halted) 74081cf8742SDavid du Colombier return 0; 74181cf8742SDavid du Colombier fs->halted = 0; 74281cf8742SDavid du Colombier vtUnlock(fs->elk); 74381cf8742SDavid du Colombier return 1; 74481cf8742SDavid du Colombier } 74581cf8742SDavid du Colombier 74681cf8742SDavid du Colombier int 7475e96a66cSDavid du Colombier fsNextQid(Fs *fs, u64int *qid) 7485e96a66cSDavid du Colombier { 7495e96a66cSDavid du Colombier Block *b; 7505e96a66cSDavid du Colombier Super super; 7515e96a66cSDavid du Colombier 7525e96a66cSDavid du Colombier if((b = superGet(fs->cache, &super)) == nil) 7535e96a66cSDavid du Colombier return 0; 7545e96a66cSDavid du Colombier 7555e96a66cSDavid du Colombier *qid = super.qid++; 7565e96a66cSDavid du Colombier 7575e96a66cSDavid du Colombier /* 7585e96a66cSDavid du Colombier * It's okay if the super block doesn't go to disk immediately, 7595e96a66cSDavid du Colombier * since fileMetaAlloc will record a dependency between the 7605e96a66cSDavid du Colombier * block holding this qid and the super block. See file.c:/^fileMetaAlloc. 7615e96a66cSDavid du Colombier */ 7625e96a66cSDavid du Colombier superPut(b, &super, 0); 7635e96a66cSDavid du Colombier return 1; 7645e96a66cSDavid du Colombier } 7655e96a66cSDavid du Colombier 7665e96a66cSDavid du Colombier static void 7675e96a66cSDavid du Colombier fsMetaFlush(void *a) 7685e96a66cSDavid du Colombier { 7695e96a66cSDavid du Colombier Fs *fs = a; 7705e96a66cSDavid du Colombier 7715e96a66cSDavid du Colombier vtRLock(fs->elk); 7725e96a66cSDavid du Colombier fileMetaFlush(fs->file, 1); 7735e96a66cSDavid du Colombier vtRUnlock(fs->elk); 7745e96a66cSDavid du Colombier cacheFlush(fs->cache, 0); 7755e96a66cSDavid du Colombier } 7765e96a66cSDavid du Colombier 777dc5a79c1SDavid du Colombier static int 778dc5a79c1SDavid du Colombier fsEsearch1(File *f, char *path, u32int savetime, u32int *plo) 779dc5a79c1SDavid du Colombier { 780dc5a79c1SDavid du Colombier int n, r; 781dc5a79c1SDavid du Colombier DirEntry de; 782dc5a79c1SDavid du Colombier DirEntryEnum *dee; 783dc5a79c1SDavid du Colombier File *ff; 784dc5a79c1SDavid du Colombier Entry e, ee; 785dc5a79c1SDavid du Colombier char *t; 786dc5a79c1SDavid du Colombier 787dc5a79c1SDavid du Colombier dee = deeOpen(f); 788dc5a79c1SDavid du Colombier if(dee == nil) 789dc5a79c1SDavid du Colombier return 0; 790dc5a79c1SDavid du Colombier 791dc5a79c1SDavid du Colombier n = 0; 792dc5a79c1SDavid du Colombier for(;;){ 793dc5a79c1SDavid du Colombier r = deeRead(dee, &de); 794dc5a79c1SDavid du Colombier if(r <= 0) 795dc5a79c1SDavid du Colombier break; 796dc5a79c1SDavid du Colombier if(de.mode & ModeSnapshot){ 797dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil){ 798dc5a79c1SDavid du Colombier if(fileGetSources(ff, &e, &ee, 0)) 799dc5a79c1SDavid du Colombier if(de.mtime >= savetime && e.snap != 0) 800dc5a79c1SDavid du Colombier if(e.snap < *plo) 801dc5a79c1SDavid du Colombier *plo = e.snap; 802dc5a79c1SDavid du Colombier fileDecRef(ff); 803dc5a79c1SDavid du Colombier } 804dc5a79c1SDavid du Colombier } 805dc5a79c1SDavid du Colombier else if(de.mode & ModeDir){ 806dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil){ 807dc5a79c1SDavid du Colombier t = smprint("%s/%s", path, de.elem); 808dc5a79c1SDavid du Colombier n += fsEsearch1(ff, t, savetime, plo); 809dc5a79c1SDavid du Colombier vtMemFree(t); 810dc5a79c1SDavid du Colombier fileDecRef(ff); 811dc5a79c1SDavid du Colombier } 812dc5a79c1SDavid du Colombier } 813dc5a79c1SDavid du Colombier deCleanup(&de); 814dc5a79c1SDavid du Colombier if(r < 0) 815dc5a79c1SDavid du Colombier break; 816dc5a79c1SDavid du Colombier } 817dc5a79c1SDavid du Colombier deeClose(dee); 818dc5a79c1SDavid du Colombier 819dc5a79c1SDavid du Colombier return n; 820dc5a79c1SDavid du Colombier } 821dc5a79c1SDavid du Colombier 822dc5a79c1SDavid du Colombier static int 823dc5a79c1SDavid du Colombier fsEsearch(Fs *fs, char *path, u32int savetime, u32int *plo) 824dc5a79c1SDavid du Colombier { 825dc5a79c1SDavid du Colombier int n; 826dc5a79c1SDavid du Colombier File *f; 827dc5a79c1SDavid du Colombier DirEntry de; 828dc5a79c1SDavid du Colombier 829dc5a79c1SDavid du Colombier f = fileOpen(fs, path); 830dc5a79c1SDavid du Colombier if(f == nil) 831dc5a79c1SDavid du Colombier return 0; 832dc5a79c1SDavid du Colombier if(!fileGetDir(f, &de)){ 833dc5a79c1SDavid du Colombier fileDecRef(f); 834dc5a79c1SDavid du Colombier return 0; 835dc5a79c1SDavid du Colombier } 836dc5a79c1SDavid du Colombier if((de.mode & ModeDir) == 0){ 837dc5a79c1SDavid du Colombier fileDecRef(f); 838dc5a79c1SDavid du Colombier deCleanup(&de); 839dc5a79c1SDavid du Colombier return 0; 840dc5a79c1SDavid du Colombier } 841dc5a79c1SDavid du Colombier deCleanup(&de); 842dc5a79c1SDavid du Colombier n = fsEsearch1(f, path, savetime, plo); 843dc5a79c1SDavid du Colombier fileDecRef(f); 844dc5a79c1SDavid du Colombier return n; 845dc5a79c1SDavid du Colombier } 846dc5a79c1SDavid du Colombier 847dc5a79c1SDavid du Colombier void 848dc5a79c1SDavid du Colombier fsSnapshotCleanup(Fs *fs, u32int age) 849dc5a79c1SDavid du Colombier { 850dc5a79c1SDavid du Colombier u32int lo; 851dc5a79c1SDavid du Colombier 852dc5a79c1SDavid du Colombier /* 853dc5a79c1SDavid du Colombier * Find the best low epoch we can use, 854dc5a79c1SDavid du Colombier * given that we need to save all the unventied archives 855dc5a79c1SDavid du Colombier * and all the snapshots younger than age. 856dc5a79c1SDavid du Colombier */ 857dc5a79c1SDavid du Colombier vtRLock(fs->elk); 858dc5a79c1SDavid du Colombier lo = fs->ehi; 859dc5a79c1SDavid du Colombier fsEsearch(fs, "/archive", 0, &lo); 860dc5a79c1SDavid du Colombier fsEsearch(fs, "/snapshot", time(0)-age*60, &lo); 861dc5a79c1SDavid du Colombier vtRUnlock(fs->elk); 862dc5a79c1SDavid du Colombier 863dc5a79c1SDavid du Colombier fsEpochLow(fs, lo); 864dc5a79c1SDavid du Colombier fsSnapshotRemove(fs); 865dc5a79c1SDavid du Colombier } 866dc5a79c1SDavid du Colombier 867dc5a79c1SDavid du Colombier /* remove all snapshots that have expired */ 868dc5a79c1SDavid du Colombier /* return number of directory entries remaining */ 869dc5a79c1SDavid du Colombier static int 870dc5a79c1SDavid du Colombier fsRsearch1(File *f, char *s) 871dc5a79c1SDavid du Colombier { 872dc5a79c1SDavid du Colombier int n, r; 873dc5a79c1SDavid du Colombier DirEntry de; 874dc5a79c1SDavid du Colombier DirEntryEnum *dee; 875dc5a79c1SDavid du Colombier File *ff; 876dc5a79c1SDavid du Colombier char *t; 877dc5a79c1SDavid du Colombier 878dc5a79c1SDavid du Colombier dee = deeOpen(f); 879dc5a79c1SDavid du Colombier if(dee == nil) 880dc5a79c1SDavid du Colombier return 0; 881dc5a79c1SDavid du Colombier 882dc5a79c1SDavid du Colombier n = 0; 883dc5a79c1SDavid du Colombier for(;;){ 884dc5a79c1SDavid du Colombier r = deeRead(dee, &de); 885dc5a79c1SDavid du Colombier if(r <= 0) 886dc5a79c1SDavid du Colombier break; 887dc5a79c1SDavid du Colombier n++; 888dc5a79c1SDavid du Colombier if(de.mode & ModeSnapshot){ 889dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil) 890dc5a79c1SDavid du Colombier fileDecRef(ff); 891dc5a79c1SDavid du Colombier else if(strcmp(vtGetError(), ESnapOld) == 0){ 892dc5a79c1SDavid du Colombier if(fileClri(f, de.elem, "adm")) 893dc5a79c1SDavid du Colombier n--; 894dc5a79c1SDavid du Colombier } 895dc5a79c1SDavid du Colombier } 896dc5a79c1SDavid du Colombier else if(de.mode & ModeDir){ 897dc5a79c1SDavid du Colombier if((ff = fileWalk(f, de.elem)) != nil){ 898dc5a79c1SDavid du Colombier t = smprint("%s/%s", s, de.elem); 899dc5a79c1SDavid du Colombier if(fsRsearch1(ff, t) == 0) 900dc5a79c1SDavid du Colombier if(fileRemove(ff, "adm")) 901dc5a79c1SDavid du Colombier n--; 902dc5a79c1SDavid du Colombier vtMemFree(t); 903dc5a79c1SDavid du Colombier fileDecRef(ff); 904dc5a79c1SDavid du Colombier } 905dc5a79c1SDavid du Colombier } 906dc5a79c1SDavid du Colombier deCleanup(&de); 907dc5a79c1SDavid du Colombier if(r < 0) 908dc5a79c1SDavid du Colombier break; 909dc5a79c1SDavid du Colombier } 910dc5a79c1SDavid du Colombier deeClose(dee); 911dc5a79c1SDavid du Colombier 912dc5a79c1SDavid du Colombier return n; 913dc5a79c1SDavid du Colombier } 914dc5a79c1SDavid du Colombier 915dc5a79c1SDavid du Colombier static int 916dc5a79c1SDavid du Colombier fsRsearch(Fs *fs, char *path) 917dc5a79c1SDavid du Colombier { 918dc5a79c1SDavid du Colombier File *f; 919dc5a79c1SDavid du Colombier DirEntry de; 920dc5a79c1SDavid du Colombier 921dc5a79c1SDavid du Colombier f = fileOpen(fs, path); 922dc5a79c1SDavid du Colombier if(f == nil) 923dc5a79c1SDavid du Colombier return 0; 924dc5a79c1SDavid du Colombier if(!fileGetDir(f, &de)){ 925dc5a79c1SDavid du Colombier fileDecRef(f); 926dc5a79c1SDavid du Colombier return 0; 927dc5a79c1SDavid du Colombier } 928dc5a79c1SDavid du Colombier if((de.mode & ModeDir) == 0){ 929dc5a79c1SDavid du Colombier fileDecRef(f); 930dc5a79c1SDavid du Colombier deCleanup(&de); 931dc5a79c1SDavid du Colombier return 0; 932dc5a79c1SDavid du Colombier } 933dc5a79c1SDavid du Colombier deCleanup(&de); 934dc5a79c1SDavid du Colombier fsRsearch1(f, path); 935dc5a79c1SDavid du Colombier fileDecRef(f); 936dc5a79c1SDavid du Colombier return 1; 937dc5a79c1SDavid du Colombier } 938dc5a79c1SDavid du Colombier 939dc5a79c1SDavid du Colombier void 940dc5a79c1SDavid du Colombier fsSnapshotRemove(Fs *fs) 941dc5a79c1SDavid du Colombier { 942dc5a79c1SDavid du Colombier vtRLock(fs->elk); 943dc5a79c1SDavid du Colombier fsRsearch(fs, "/snapshot"); 944dc5a79c1SDavid du Colombier vtRUnlock(fs->elk); 945dc5a79c1SDavid du Colombier } 946dc5a79c1SDavid du Colombier 9475e96a66cSDavid du Colombier struct Snap 9485e96a66cSDavid du Colombier { 9495e96a66cSDavid du Colombier Fs *fs; 9505e96a66cSDavid du Colombier Periodic *tick; 9515e96a66cSDavid du Colombier VtLock *lk; 9525e96a66cSDavid du Colombier uint snapMinutes; 9535e96a66cSDavid du Colombier uint archMinute; 954dc5a79c1SDavid du Colombier uint snapLife; 9555e96a66cSDavid du Colombier u32int lastSnap; 9565e96a66cSDavid du Colombier u32int lastArch; 957dc5a79c1SDavid du Colombier u32int lastCleanup; 9585e96a66cSDavid du Colombier uint ignore; 9595e96a66cSDavid du Colombier }; 9605e96a66cSDavid du Colombier 9615e96a66cSDavid du Colombier static void 9625e96a66cSDavid du Colombier snapEvent(void *v) 9635e96a66cSDavid du Colombier { 9645e96a66cSDavid du Colombier Snap *s; 9655e96a66cSDavid du Colombier u32int now, min; 9665e96a66cSDavid du Colombier Tm tm; 967dc5a79c1SDavid du Colombier int need; 9685e96a66cSDavid du Colombier 9695e96a66cSDavid du Colombier s = v; 9705e96a66cSDavid du Colombier 9715e96a66cSDavid du Colombier now = time(0)/60; 9725e96a66cSDavid du Colombier vtLock(s->lk); 9735e96a66cSDavid du Colombier 9745e96a66cSDavid du Colombier /* 9755e96a66cSDavid du Colombier * Snapshots happen every snapMinutes minutes. 9765e96a66cSDavid du Colombier * If we miss a snapshot (for example, because we 9775e96a66cSDavid du Colombier * were down), we wait for the next one. 9785e96a66cSDavid du Colombier */ 9795e96a66cSDavid du Colombier if(s->snapMinutes != ~0 && s->snapMinutes != 0 9805e96a66cSDavid du Colombier && now%s->snapMinutes==0 && now != s->lastSnap){ 9815e96a66cSDavid du Colombier if(!fsSnapshot(s->fs, 0)) 9825e96a66cSDavid du Colombier fprint(2, "fsSnapshot snap: %R\n"); 9835e96a66cSDavid du Colombier s->lastSnap = now; 9845e96a66cSDavid du Colombier } 9855e96a66cSDavid du Colombier 9865e96a66cSDavid du Colombier /* 9875e96a66cSDavid du Colombier * Archival snapshots happen at archMinute. 988dc5a79c1SDavid du Colombier * If we miss an archive (for example, because we 989dc5a79c1SDavid du Colombier * were down), we do it as soon as possible. 9905e96a66cSDavid du Colombier */ 9915e96a66cSDavid du Colombier tm = *localtime(now*60); 9925e96a66cSDavid du Colombier min = tm.hour*60+tm.min; 993dc5a79c1SDavid du Colombier if(s->archMinute != ~0){ 994dc5a79c1SDavid du Colombier need = 0; 995dc5a79c1SDavid du Colombier if(min == s->archMinute && now != s->lastArch) 996dc5a79c1SDavid du Colombier need = 1; 997dc5a79c1SDavid du Colombier if(s->lastArch == 0){ 998dc5a79c1SDavid du Colombier s->lastArch = 1; 999dc5a79c1SDavid du Colombier if(fsNeedArch(s->fs, s->archMinute)) 1000dc5a79c1SDavid du Colombier need = 1; 1001dc5a79c1SDavid du Colombier } 1002dc5a79c1SDavid du Colombier if(need){ 1003dc5a79c1SDavid du Colombier fsSnapshot(s->fs, 1); 10045e96a66cSDavid du Colombier s->lastArch = now; 10055e96a66cSDavid du Colombier } 1006dc5a79c1SDavid du Colombier } 1007dc5a79c1SDavid du Colombier 1008dc5a79c1SDavid du Colombier /* 1009dc5a79c1SDavid du Colombier * Snapshot cleanup happens every snaplife or every day. 1010dc5a79c1SDavid du Colombier */ 1011dc5a79c1SDavid du Colombier if(s->snapLife != ~0 1012dc5a79c1SDavid du Colombier && (s->lastCleanup+s->snapLife < now || s->lastCleanup+24*60 < now)){ 1013dc5a79c1SDavid du Colombier fsSnapshotCleanup(s->fs, s->snapLife); 1014dc5a79c1SDavid du Colombier s->lastCleanup = now; 1015dc5a79c1SDavid du Colombier } 10165e96a66cSDavid du Colombier vtUnlock(s->lk); 10175e96a66cSDavid du Colombier } 10185e96a66cSDavid du Colombier 10195e96a66cSDavid du Colombier static Snap* 10205e96a66cSDavid du Colombier snapInit(Fs *fs) 10215e96a66cSDavid du Colombier { 10225e96a66cSDavid du Colombier Snap *s; 10235e96a66cSDavid du Colombier 10245e96a66cSDavid du Colombier s = vtMemAllocZ(sizeof(Snap)); 10255e96a66cSDavid du Colombier s->fs = fs; 10265e96a66cSDavid du Colombier s->tick = periodicAlloc(snapEvent, s, 10*1000); 10275e96a66cSDavid du Colombier s->lk = vtLockAlloc(); 10285e96a66cSDavid du Colombier s->snapMinutes = -1; 10295e96a66cSDavid du Colombier s->archMinute = -1; 1030dc5a79c1SDavid du Colombier s->snapLife = -1; 10315e96a66cSDavid du Colombier s->ignore = 5*2; /* wait five minutes for clock to stabilize */ 10325e96a66cSDavid du Colombier return s; 10335e96a66cSDavid du Colombier } 10345e96a66cSDavid du Colombier 10355e96a66cSDavid du Colombier void 1036dc5a79c1SDavid du Colombier snapGetTimes(Snap *s, u32int *arch, u32int *snap, u32int *snaplen) 10375e96a66cSDavid du Colombier { 10385e96a66cSDavid du Colombier vtLock(s->lk); 10395e96a66cSDavid du Colombier *snap = s->snapMinutes; 10405e96a66cSDavid du Colombier *arch = s->archMinute; 1041dc5a79c1SDavid du Colombier *snaplen = s->snapLife; 10425e96a66cSDavid du Colombier vtUnlock(s->lk); 10435e96a66cSDavid du Colombier } 10445e96a66cSDavid du Colombier 10455e96a66cSDavid du Colombier void 1046dc5a79c1SDavid du Colombier snapSetTimes(Snap *s, u32int arch, u32int snap, u32int snaplen) 10475e96a66cSDavid du Colombier { 10485e96a66cSDavid du Colombier vtLock(s->lk); 10495e96a66cSDavid du Colombier s->snapMinutes = snap; 10505e96a66cSDavid du Colombier s->archMinute = arch; 1051dc5a79c1SDavid du Colombier s->snapLife = snaplen; 10525e96a66cSDavid du Colombier vtUnlock(s->lk); 10535e96a66cSDavid du Colombier } 10545e96a66cSDavid du Colombier 10555e96a66cSDavid du Colombier static void 10565e96a66cSDavid du Colombier snapClose(Snap *s) 10575e96a66cSDavid du Colombier { 10585e96a66cSDavid du Colombier if(s == nil) 10595e96a66cSDavid du Colombier return; 10605e96a66cSDavid du Colombier 10615e96a66cSDavid du Colombier periodicKill(s->tick); 10625e96a66cSDavid du Colombier vtMemFree(s); 10635e96a66cSDavid du Colombier } 10645e96a66cSDavid du Colombier 1065