diff --git a/lfs.c b/lfs.c index dc768324..ad2e4cff 100644 --- a/lfs.c +++ b/lfs.c @@ -5905,8 +5905,20 @@ static int lfsr_data_readmptr(lfs_t *lfs, lfsr_data_t *data, } -// opened mdir things -// +/// various flag things /// + +static inline bool lfsr_i_isuncompacted(uint32_t flags) { + return flags & LFS_I_UNCOMPACTED; +} + +static inline bool lfsr_f_hasorphans(uint32_t flags) { + return flags & LFS_F_ORPHANS; +} + + + +/// opened mdir things /// + // we maintain a linked-list of all opened mdirs, in order to keep // metadata state in-sync, these may be casted to specific file types @@ -7831,6 +7843,10 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs->seed ^= mdir_[1].rbyd.cksum; } + // we may have touched any number of mdirs, so assume uncompacted + // until lfsr_fs_gc can prove otherwise + lfs->flags |= LFS_I_UNCOMPACTED; + // update any gstate changes lfsr_fs_commitgdelta(lfs); @@ -8682,7 +8698,7 @@ dropped:; // mkconsistencing mdirs? if (lfsr_t_ismkconsistent(t->o.o.flags) && tag == LFSR_TAG_MDIR - && lfs->hasorphans) { + && lfsr_f_hasorphans(lfs->flags)) { lfsr_mdir_t *mdir = (lfsr_mdir_t*)bptr.data.u.buffer; err = lfsr_mdir_fixorphans(lfs, mdir); if (err) { @@ -8802,6 +8818,9 @@ dropped:; // reset to btree root t->u.bt.branch = &t->o.bshrub.u.btree; t->u.bt.rid = t->u.bt.bid; + + // mark as dirty, we need to do this manually here + t->o.o.flags |= LFS_F_DIRTY; } // swap back dirty/mutated flags @@ -8822,7 +8841,7 @@ eot:; // was mkconsistent successful? if (lfsr_t_ismkconsistent(t->o.o.flags) && !lfsr_f_isdirty(t->o.o.flags)) { - lfs->hasorphans = false; + lfs->flags &= ~LFS_F_ORPHANS; } // was lookahead scan successful? @@ -8832,6 +8851,14 @@ eot:; lfs_alloc_markfree(lfs); } + // was compaction successful? note we may need multiple passes if + // we want to be sure everything is compacted + if (lfsr_t_iscompact(t->o.o.flags) + && !lfsr_f_isdirty(t->o.o.flags) + && !lfsr_f_ismutated(t->o.o.flags)) { + lfs->flags &= ~LFS_I_UNCOMPACTED; + } + return LFS_ERR_NOENT; failed:; @@ -10118,7 +10145,7 @@ int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file) { // fallback to just marking the filesystem as orphaned } else { - lfs->hasorphans = true; + lfs->flags |= LFS_F_ORPHANS; } } @@ -11796,6 +11823,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { // fragment_size must be <= block_size/4 LFS_ASSERT(lfs->cfg->fragment_size <= lfs->cfg->block_size/4); + // zero flags + lfs->flags = 0; + // setup block_count so we can mutate it lfs->block_count = lfs->cfg->block_count; @@ -11870,10 +11900,6 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { // lfs->lfs1 = NULL; //#endif - // TODO maybe reorganize this function? - - lfs->hasorphans = false; - // TODO do we need to recalculate these after mount? // find the number of bits to use for recycle counters @@ -12460,7 +12486,7 @@ static int lfsr_mountinited(lfs_t *lfs) { "%"PRId32".%"PRId32, lfsr_mid_bid(lfs, mdir->mid) >> lfs->mdir_bits, rid); - lfs->hasorphans = true; + lfs->flags |= LFS_F_ORPHANS; // found an unknown file type? } else if (lfsr_tag_isunknown(tag)) { @@ -12521,6 +12547,10 @@ static int lfsr_mountinited(lfs_t *lfs) { lfsr_mid_rid(lfs, lfs->grm.mids[0])); } + // default to assuming we need a compaction somewhere, worst case this + // just makes lfsr_fs_gc read more than is strictly needed + lfs->flags |= LFS_I_UNCOMPACTED; + return 0; } @@ -12671,10 +12701,24 @@ int lfsr_format(lfs_t *lfs, const struct lfs_config *cfg) { /// Other filesystem things /// int lfsr_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { + // return various filesystem flags + fsinfo->flags = lfs->flags & LFS_I_UNCOMPACTED; + + // some flags we calculate on demand + if (lfsr_grm_count(lfs) > 0 || lfsr_f_hasorphans(lfs->flags)) { + fsinfo->flags |= LFS_I_INCONSISTENT; + } + + if (lfs->lookahead.next > 0 || lfs->lookahead.size == 0) { + fsinfo->flags |= LFS_I_CANLOOKAHEAD; + } + + // return filesystem config, this may come from disk fsinfo->block_size = lfs->cfg->block_size; fsinfo->block_count = lfs->block_count; fsinfo->name_limit = lfs->name_limit; fsinfo->file_limit = lfs->file_limit; + return 0; } @@ -12839,7 +12883,7 @@ int lfsr_fs_mkconsistent(lfs_t *lfs) { // this must happen after fixgrm, since removing orphaned files risks // outdating the grm // - if (lfs->hasorphans) { + if (lfsr_f_hasorphans(lfs->flags)) { LFS_DEBUG("Fixing orphans..."); int err = lfsr_fs_fixorphans(lfs); @@ -12886,10 +12930,11 @@ int lfsr_fs_gc(lfs_t *lfs, uint32_t flags) { } // do we need to do anything? - if (!((lfsr_t_ismkconsistent(flags) && lfs->hasorphans) + if (!((lfsr_t_ismkconsistent(flags) && lfsr_f_hasorphans(lfs->flags)) || (lfsr_t_islookahead(flags) && (lfs->lookahead.next > 0 || lfs->lookahead.size == 0)) - || lfsr_t_iscompact(flags) + || (lfsr_t_iscompact(flags) + && lfsr_i_isuncompacted(lfs->flags)) || lfsr_t_isckmeta(flags) || lfsr_t_isckdata(flags))) { return 0; @@ -12923,6 +12968,7 @@ int lfsr_fs_gc(lfs_t *lfs, uint32_t flags) { // shift the lookahead buffer if requested if (lfsr_t_islookahead(lfs->gc.o.o.flags)) { lfs_alloc_shift(lfs); + lfs_alloc_ckpoint(lfs); } } @@ -13173,6 +13219,7 @@ static int lfsr_traversal_rewind_(lfs_t *lfs, lfsr_traversal_t *t) { // shift the lookahead buffer if requested if (lfsr_t_islookahead(t->o.o.flags)) { lfs_alloc_shift(lfs); + lfs_alloc_ckpoint(lfs); } return 0; diff --git a/lfs.h b/lfs.h index 423db2b5..5bf61d05 100644 --- a/lfs.h +++ b/lfs.h @@ -125,6 +125,16 @@ enum lfs_type { LFS_TYPE_TRAVERSAL = 9, }; +// Block types +enum lfs_btype { + LFS_BTYPE_MDIR = 1, + LFS_BTYPE_BTREE = 2, + LFS_BTYPE_DATA = 3, +// TODO +// LFS_BTYPE_PARITY = 4, +// LFS_BTYPE_BAD = 5, +}; + // File open flags enum lfs_open_flags { // open flags @@ -155,14 +165,15 @@ enum lfs_whence_flags { LFS_SEEK_END = 2, // Seek relative to the end of the file }; -// Block types -enum lfs_btype { - LFS_BTYPE_MDIR = 1, - LFS_BTYPE_BTREE = 2, - LFS_BTYPE_DATA = 3, -// TODO -// LFS_BTYPE_PARITY = 4, -// LFS_BTYPE_BAD = 5, +// Filesystem info flags +enum lfs_fsinfo_flags { + // state flags + LFS_I_INCONSISTENT = 0x01, // Filesystem needs mkconsistent to write + LFS_I_CANLOOKAHEAD = 0x04, // Lookahead buffer is not full + LFS_I_UNCOMPACTED = 0x10, // Filesystem may have uncompacted metadata + + // internally used flags + LFS_F_ORPHANS = 0x80, // Filesystem may have untracked orphans }; // Traversal flags @@ -391,7 +402,8 @@ struct lfs_info { // Filesystem info structure struct lfs_fsinfo { - // TODO should we add rcompat/wcompat flags here? + // Filesystem flags + uint8_t flags; // Size of a logical block in bytes. lfs_size_t block_size; @@ -713,10 +725,7 @@ typedef struct lfs { lfs_size_t name_limit; lfs_off_t file_limit; - // TODO we should put this flag somewhere, should lfs_t have a general - // purpose flags field? this has been useful for lfsr_file_t - bool hasorphans; - + uint8_t flags; int8_t recycle_bits; uint8_t attr_estimate; uint8_t mdir_bits; diff --git a/tests/test_forphans.toml b/tests/test_forphans.toml index 3e610775..0d1b7ec8 100644 --- a/tests/test_forphans.toml +++ b/tests/test_forphans.toml @@ -4701,7 +4701,11 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); + + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS_I_INCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here @@ -4831,7 +4835,11 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); + + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS_I_INCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here @@ -4975,7 +4983,11 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); + + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS_I_INCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here diff --git a/tests/test_gc.toml b/tests/test_gc.toml index 23740bbb..1a8c5059 100644 --- a/tests/test_gc.toml +++ b/tests/test_gc.toml @@ -17,7 +17,6 @@ defines.SIZE = [ '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] -in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -37,8 +36,10 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC until our traversal is done while (true) { @@ -48,13 +49,14 @@ code = ''' | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; // internal traversal done? - if (!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + if (lfs.omdirs != &lfs.gc.o.o) { break; } } // we should have made progress - assert(!(lfs.lookahead.next > 0 || lfs.lookahead.size == 0)); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS_I_CANLOOKAHEAD)); // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; @@ -66,7 +68,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that lookahead clobbering still works with the GC API +# test that lookahead dirtying still works with the GC API [cases.test_gc_lookahead_mutation] defines.GC_STEPS = 1 defines.CKMETA = [false, true] @@ -79,7 +81,6 @@ defines.SIZE = [ '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] -in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -99,15 +100,17 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step lfsr_fs_gc(&lfs, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - assert(lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs == &lfs.gc.o.o); // mutate the filesystem lfsr_file_open(&lfs, &file, "spider", @@ -119,7 +122,7 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // run GC until our traversal is done - while (lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + while (lfs.omdirs == &lfs.gc.o.o) { lfsr_fs_gc(&lfs, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -127,7 +130,8 @@ code = ''' } // we should _not_ make progress - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; @@ -154,7 +158,6 @@ defines.SIZE = [ ] # we need something to keep the traversal running if = 'CKMETA || CKDATA' -in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -174,17 +177,19 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step lfsr_fs_gc(&lfs, ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - assert(lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs == &lfs.gc.o.o); // change flags and run GC until our traversal is done - while (lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + while (lfs.omdirs == &lfs.gc.o.o) { lfsr_fs_gc(&lfs, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) @@ -192,7 +197,8 @@ code = ''' } // we should _not_ make progress - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; @@ -219,7 +225,6 @@ defines.SIZE = [ ] # we need something to keep the traversal running if = 'CKMETA || CKDATA' -in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -239,25 +244,28 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step lfsr_fs_gc(&lfs, LFS_GC_LOOKAHEAD | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - assert(lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs == &lfs.gc.o.o); // change flags and run GC until our traversal is done - while (lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + while (lfs.omdirs == &lfs.gc.o.o) { lfsr_fs_gc(&lfs, ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; } // we should _not_ make progress - assert(lfs.lookahead.next > 0 || lfs.lookahead.size == 0); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_CANLOOKAHEAD); // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; @@ -270,6 +278,398 @@ code = ''' ''' +# test that compact can make progress in isolation +[cases.test_gc_compact_progress] +defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000] +defines.LOOKAHEAD = [false, true] +defines.CKMETA = [false, true] +defines.CKDATA = [false, true] +defines.SIZE = [ + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +# set compact thresh to minimum +defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // write to our mdir until >gc_compact_thresh full + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jellyfish", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // hack, don't use the internals like this + uint8_t wbuf[SIZE]; + while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { + lfsr_file_rewind(&lfs, &file) => 0; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &file) => 0; + } + + // expect dirty initial state or else our test doesn't work + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + assert(lfs.omdirs != &lfs.gc.o.o); + + // run GC until our traversal is done (twice for compact) + for (int i = 0; i < 2; i++) { + while (true) { + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + + // internal traversal done? + if (lfs.omdirs != &lfs.gc.o.o) { + break; + } + } + } + + // mdir should have been compacted + assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // we should have made progress + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS_I_UNCOMPACTED)); + + // check we can still read the file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0; + } + + lfsr_file_rewind(&lfs, &file) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } + + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; +''' + +# test that compact dirtying still works with the GC API +[cases.test_gc_compact_mutation] +defines.GC_STEPS = 1 +defines.LOOKAHEAD = [false, true] +defines.CKMETA = [false, true] +defines.CKDATA = [false, true] +defines.SIZE = [ + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +# set compact thresh to minimum +defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // write to our mdir until >gc_compact_thresh full + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jellyfish", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // hack, don't use the internals like this + uint8_t wbuf[SIZE]; + while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { + lfsr_file_rewind(&lfs, &file) => 0; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &file) => 0; + } + + // expect dirty initial state or else our test doesn't work + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + assert(lfs.omdirs != &lfs.gc.o.o); + + // run GC one traversal + one step + while (true) { + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + + // internal traversal done? + if (lfs.omdirs != &lfs.gc.o.o) { + break; + } + } + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + assert(lfs.omdirs == &lfs.gc.o.o); + + // mutate the filesystem + lfsr_file_rewind(&lfs, &file) => 0; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &file) => 0; + + // run GC until our traversal is done (twice for compact) + while (lfs.omdirs == &lfs.gc.o.o) { + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + } + + // we should _not_ make progress + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + + // check we can still read the file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0; + } + + lfsr_file_rewind(&lfs, &file) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } + + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; +''' + +# test that adding flags doesn't break compact +[cases.test_gc_compact_add_flags] +defines.GC_STEPS = 1 +defines.LOOKAHEAD = [false, true] +defines.CKMETA = [false, true] +defines.CKDATA = [false, true] +defines.SIZE = [ + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +# set compact thresh to minimum +defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' +# we need something to keep the traversal running +if = 'CKMETA || CKDATA' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // write to our mdir until >gc_compact_thresh full + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jellyfish", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // hack, don't use the internals like this + uint8_t wbuf[SIZE]; + while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { + lfsr_file_rewind(&lfs, &file) => 0; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &file) => 0; + } + + // expect dirty initial state or else our test doesn't work + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + assert(lfs.omdirs != &lfs.gc.o.o); + + // run GC one traversal + one step + while (true) { + lfsr_fs_gc(&lfs, + ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + + // internal traversal done? + if (lfs.omdirs != &lfs.gc.o.o) { + break; + } + } + lfsr_fs_gc(&lfs, + ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + assert(lfs.omdirs == &lfs.gc.o.o); + + // change flags and run GC until our traversal is done (twice for compact) + while (lfs.omdirs == &lfs.gc.o.o) { + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + } + + // we should _not_ make progress + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + + // check we can still read the file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0; + } + + lfsr_file_rewind(&lfs, &file) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } + + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; +''' + +# test that removing flags invalidates compact +[cases.test_gc_compact_remove_flags] +defines.GC_STEPS = 1 +defines.LOOKAHEAD = [false, true] +defines.CKMETA = [false, true] +defines.CKDATA = [false, true] +defines.SIZE = [ + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '8*BLOCK_SIZE', +] +# set compact thresh to minimum +defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' +# we need something to keep the traversal running +if = 'CKMETA || CKDATA' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // write to our mdir until >gc_compact_thresh full + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jellyfish", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // hack, don't use the internals like this + uint8_t wbuf[SIZE]; + while ((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { + lfsr_file_rewind(&lfs, &file) => 0; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &file) => 0; + } + + // expect dirty initial state or else our test doesn't work + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + assert(lfs.omdirs != &lfs.gc.o.o); + + // run GC one traversal + one step + while (true) { + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + + // internal traversal done? + if (lfs.omdirs != &lfs.gc.o.o) { + break; + } + } + lfsr_fs_gc(&lfs, + LFS_GC_COMPACT + | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + assert(lfs.omdirs == &lfs.gc.o.o); + + // change flags and run GC until our traversal is done (twice for compact) + while (lfs.omdirs == &lfs.gc.o.o) { + lfsr_fs_gc(&lfs, + ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) + | ((CKMETA) ? LFS_GC_CKMETA : 0) + | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; + } + + // we should _not_ make progress + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_UNCOMPACTED); + + // check we can still read the file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0; + } + + lfsr_file_rewind(&lfs, &file) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } + + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; +''' + + # test that mkconsistent can make progress in isolation [cases.test_gc_mkconsistent_progress] defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000] @@ -281,7 +681,6 @@ defines.SIZE = 'FILE_BUFFER_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [1, 2, 3, 100] -in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -329,8 +728,10 @@ code = ''' } // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC until our traversal is done while (true) { @@ -342,13 +743,14 @@ code = ''' | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; // internal traversal done? - if (!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + if (lfs.omdirs != &lfs.gc.o.o) { break; } } // we should have made progress - assert(!(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans)); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(!(fsinfo.flags & LFS_I_INCONSISTENT)); // check we can still read the files for (int remount = 0; remount < 2; remount++) { @@ -377,7 +779,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that mkconsistent clobbering still works with the GC API +# test that mkconsistent dirtying still works with the GC API [cases.test_gc_mkconsistent_mutation] defines.GC_STEPS = 1 defines.LOOKAHEAD = [false, true] @@ -388,7 +790,6 @@ defines.SIZE = 'FILE_BUFFER_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [3, 100] -in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -430,14 +831,14 @@ code = ''' } // run GC one step - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs != &lfs.gc.o.o); lfsr_fs_gc(&lfs, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - assert(lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs == &lfs.gc.o.o); // create the rest of the orphans after GC has started for (lfs_size_t i = 0; i < ORPHANS; i++) { @@ -451,10 +852,12 @@ code = ''' } // we should now have dirty state - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); // run GC until our traversal is done - while (lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + while (lfs.omdirs == &lfs.gc.o.o) { lfsr_fs_gc(&lfs, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) @@ -464,7 +867,8 @@ code = ''' } // we should _not_ make progress - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); // check we can still read the files for (int remount = 0; remount < 2; remount++) { @@ -505,7 +909,7 @@ defines.SIZE = 'FILE_BUFFER_SIZE/2' # >2 => requires orphans defines.ORPHANS = [3, 100] # we need something to keep the traversal running -if = 'COMPACT || CKMETA || CKDATA' +if = 'CKMETA || CKDATA' in = 'lfs.c' code = ''' lfs_t lfs; @@ -554,8 +958,10 @@ code = ''' } // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step lfsr_fs_gc(&lfs, @@ -563,10 +969,10 @@ code = ''' | ((COMPACT) ? LFS_GC_COMPACT : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - assert(lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs == &lfs.gc.o.o); // change flags and run GC until our traversal is done - while (lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + while (lfs.omdirs == &lfs.gc.o.o) { lfsr_fs_gc(&lfs, LFS_GC_MKCONSISTENT | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) @@ -576,7 +982,8 @@ code = ''' } // we should _not_ make progress - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); // check we can still read the files for (int remount = 0; remount < 2; remount++) { @@ -617,8 +1024,7 @@ defines.SIZE = 'FILE_BUFFER_SIZE/2' # >2 => requires orphans defines.ORPHANS = [3, 100] # we need something to keep the traversal running -if = 'COMPACT || CKMETA || CKDATA' -in = 'lfs.c' +if = 'CKMETA || CKDATA' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -666,8 +1072,10 @@ code = ''' } // expect dirty initial state or else our test doesn't work - assert(!lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); + assert(lfs.omdirs != &lfs.gc.o.o); // run GC one step lfsr_fs_gc(&lfs, @@ -676,10 +1084,10 @@ code = ''' | ((COMPACT) ? LFS_GC_COMPACT : 0) | ((CKMETA) ? LFS_GC_CKMETA : 0) | ((CKDATA) ? LFS_GC_CKDATA : 0)) => 0; - assert(lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)); + assert(lfs.omdirs == &lfs.gc.o.o); // change flags and run GC until our traversal is done - while (lfsr_omdir_isopen(&lfs, &lfs.gc.o.o)) { + while (lfs.omdirs == &lfs.gc.o.o) { lfsr_fs_gc(&lfs, ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) | ((COMPACT) ? LFS_GC_COMPACT : 0) @@ -688,7 +1096,8 @@ code = ''' } // we should _not_ make progress - assert(lfsr_grm_count(&lfs) > 0 || lfs.hasorphans); + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags & LFS_I_INCONSISTENT); // check we can still read the files for (int remount = 0; remount < 2; remount++) { @@ -718,7 +1127,7 @@ code = ''' ''' -# pseudo-fuzz test that clobbering still works with the GC API +# pseudo-fuzz test that dirtying still works with the GC API [cases.test_gc_mutation] defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000] defines.STEPS = 100 diff --git a/tests/test_traversal.toml b/tests/test_traversal.toml index e2390f80..17540a91 100644 --- a/tests/test_traversal.toml +++ b/tests/test_traversal.toml @@ -1667,6 +1667,52 @@ done:; +# test that in general fsinfo flags work +[cases.test_traversal_flags] +defines.MKCONSISTENT = [false, true] +defines.LOOKAHEAD = [false, true] +defines.COMPACT = [false, true] +defines.CKMETA = [false, true] +defines.CKDATA = [false, true] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // check flags before + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((MKCONSISTENT) ? LFS_T_MKCONSISTENT : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0) + | ((COMPACT) ? LFS_T_COMPACT : 0) + | ((CKMETA) ? LFS_T_CKMETA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0)) => 0; + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + lfsr_traversal_close(&lfs, &t) => 0; + + // check flags after + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0) + | ((!COMPACT) ? LFS_I_UNCOMPACTED : 0))); + + lfsr_unmount(&lfs) => 0; +''' + # test that we detect filesystem mutation during traversal [cases.test_traversal_mutation] defines.WHEN = [0, 1, 2] @@ -1718,6 +1764,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_unmount(&lfs) => 0; ''' @@ -1760,6 +1813,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try another mutation just for good measure lfsr_file_open(&lfs, &file, "tarantula", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; @@ -1772,6 +1832,12 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + // we should _not_ update lookahead/compact + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_traversal_close(&lfs, &t) => 0; lfsr_unmount(&lfs) => 0; @@ -1818,6 +1884,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_unmount(&lfs) => 0; ''' @@ -1868,6 +1941,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_unmount(&lfs) => 0; ''' @@ -1918,6 +1998,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_unmount(&lfs) => 0; ''' @@ -1980,6 +2067,13 @@ code = ''' lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -2052,6 +2146,13 @@ code = ''' lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_rewind(&lfs, &file) => 0; uint8_t rbuf[SIZE]; @@ -2136,6 +2237,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -2229,6 +2337,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -2315,6 +2430,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -2388,6 +2510,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_file_close(&lfs, &file1) => 0; lfsr_file_close(&lfs, &file2) => 0; @@ -2480,6 +2609,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_file_close(&lfs, &file1) => 0; lfsr_file_close(&lfs, &file2) => 0; @@ -2565,6 +2701,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + lfsr_file_close(&lfs, &file1) => 0; lfsr_file_close(&lfs, &file2) => 0; @@ -2642,6 +2785,14 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact, unless we're desynced + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((DESYNC) ? LFS_I_INCONSISTENT : 0) + | ((!(LOOKAHEAD && DESYNC)) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + lfsr_file_close(&lfs, &file2) => 0; // check the file contents @@ -2738,6 +2889,14 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact, unless we're desynced + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((DESYNC) ? LFS_I_INCONSISTENT : 0) + | ((!(LOOKAHEAD && DESYNC)) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + lfsr_file_close(&lfs, &file2) => 0; // check the file contents @@ -2827,6 +2986,14 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact, unless we're desynced + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((DESYNC) ? LFS_I_INCONSISTENT : 0) + | ((!(LOOKAHEAD && DESYNC)) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + lfsr_file_close(&lfs, &file2) => 0; // check the file contents @@ -2913,6 +3080,14 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact, unless we're desynced + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((DESYNC) ? LFS_I_INCONSISTENT : 0) + | ((!(LOOKAHEAD && DESYNC)) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT; @@ -3002,6 +3177,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT; @@ -3091,6 +3273,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3188,6 +3377,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3296,6 +3492,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3404,6 +3607,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3499,6 +3709,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3588,6 +3805,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3692,6 +3916,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3795,6 +4026,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -3951,6 +4189,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -4124,6 +4369,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -4291,6 +4543,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -4455,6 +4714,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -4626,6 +4892,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -4796,6 +5069,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -4977,6 +5257,13 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; + // we should _not_ update lookahead/compact + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check the file contents lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; @@ -5035,6 +5322,13 @@ code = ''' lfsr_file_sync(&lfs, &file) => 0; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5051,11 +5345,35 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_MDIR); assert(tinfo.block == 0 || tinfo.block == 1); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // mdir should have been compacted + assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // mdir should have been compacted assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -5127,6 +5445,13 @@ code = ''' lfsr_file_sync(&lfs, &file) => 0; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5148,7 +5473,6 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_MDIR); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // mrootanchor should have been compacted lfsr_mdir_t mrootanchor; @@ -5156,6 +5480,33 @@ code = ''' -1, &LFSR_MPTR_MROOTANCHOR()) => 0; assert(lfsr_rbyd_eoff(&mrootanchor.rbyd) <= GC_COMPACT_THRESH); + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // mrootanchor should have been compacted + lfsr_mdir_fetch(&lfs, &mrootanchor, + -1, &LFSR_MPTR_MROOTANCHOR()) => 0; + assert(lfsr_rbyd_eoff(&mrootanchor.rbyd) <= GC_COMPACT_THRESH); + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -5209,6 +5560,13 @@ code = ''' lfsr_file_sync(&lfs, &file) => 0; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5227,11 +5585,35 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_MDIR); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // mdir should have been compacted + assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // mdir should have been compacted assert((file.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -5310,6 +5692,13 @@ code = ''' i += 1; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5339,12 +5728,37 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_MDIR); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // mdirs should have been compacted + assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // mdirs should have been compacted assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -5479,6 +5893,13 @@ code = ''' } } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5513,6 +5934,29 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_MDIR); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + if (COMPACTSET) { + // mdirs should have been compacted + assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + } lfsr_traversal_close(&lfs, &t) => 0; // mdirs should have been compacted @@ -5520,6 +5964,11 @@ code = ''' assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -5656,6 +6105,13 @@ code = ''' i += 1; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5695,6 +6151,28 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_MDIR); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // mdirs should have been compacted + assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file4.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // mdirs should have been compacted @@ -5703,6 +6181,11 @@ code = ''' assert((file3.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file4.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -5838,6 +6321,13 @@ code = ''' lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDWR) => 0; lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDWR) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5867,7 +6357,6 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_MDIR); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // mtree should have been compacted lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); @@ -5880,6 +6369,37 @@ code = ''' assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer) <= GC_COMPACT_THRESH); + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // mtree should have been compacted + t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); + lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0; + assert(tag == LFSR_TAG_MDIR); + lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0; + assert(tag == LFSR_TAG_BRANCH); + assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer) + <= GC_COMPACT_THRESH); + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -5942,6 +6462,13 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -5961,12 +6488,36 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_BTREE); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // btree should have been compacted lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0; assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // btree should have been compacted + assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -6022,6 +6573,13 @@ code = ''' lfsr_file_sync(&lfs, &file) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6041,11 +6599,35 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_BTREE); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // btree should have been compacted + assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // btree should have been compacted assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -6099,6 +6681,13 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6118,11 +6707,35 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_BTREE); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // btree should have been compacted + assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // btree should have been compacted assert((file.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // file should not have accidentally been created or anything struct lfs_info info; lfsr_stat(&lfs, "jellyfish", &info) => LFS_ERR_NOENT; @@ -6201,6 +6814,13 @@ code = ''' lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE; lfsr_file_desync(&lfs, &file3) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6226,11 +6846,35 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => 0; assert(tinfo.btype == LFS_BTYPE_BTREE); lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; + + // btree should have been compacted + assert((file1.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } lfsr_traversal_close(&lfs, &t) => 0; // btree should have been compacted assert((file1.o.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files lfsr_file_rewind(&lfs, &file1) => 0; uint8_t rbuf[SIZE]; @@ -6313,6 +6957,13 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6336,7 +6987,6 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_BTREE); } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // bshrub should have been compacted lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); @@ -6355,6 +7005,43 @@ code = ''' <= GC_COMPACT_THRESH); } + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // bshrub should have been compacted + t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); + lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0; + assert(tag == LFSR_TAG_MDIR); + while (true) { + int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + assert(tag == LFSR_TAG_BRANCH); + assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer) + <= GC_COMPACT_THRESH); + } + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -6430,6 +7117,13 @@ code = ''' lfsr_file_sync(&lfs, &file) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6453,7 +7147,6 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_BTREE); } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // bshrub should have been compacted lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); @@ -6472,6 +7165,43 @@ code = ''' <= GC_COMPACT_THRESH); } + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // bshrub should have been compacted + t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); + lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0; + assert(tag == LFSR_TAG_MDIR); + while (true) { + int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + assert(tag == LFSR_TAG_BRANCH); + assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer) + <= GC_COMPACT_THRESH); + } + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -6546,6 +7276,13 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf, FRAGMENT_SIZE) => FRAGMENT_SIZE; } + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6569,7 +7306,6 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_BTREE); } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // bshrub should have been compacted lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); @@ -6588,6 +7324,43 @@ code = ''' <= GC_COMPACT_THRESH); } + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // bshrub should have been compacted + t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); + lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0; + assert(tag == LFSR_TAG_MDIR); + while (true) { + int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + assert(tag == LFSR_TAG_BRANCH); + assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer) + <= GC_COMPACT_THRESH); + } + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the file for (int remount = 0; remount < 2; remount++) { // remount? @@ -6683,6 +7456,13 @@ code = ''' lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE; lfsr_file_desync(&lfs, &file3) => 0; + // we should be marked as uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing and compacting lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6706,7 +7486,6 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_BTREE); } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // bshrub should have been compacted lfsr_traversal_t t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); @@ -6725,6 +7504,43 @@ code = ''' <= GC_COMPACT_THRESH); } + // but because we mutated, we're still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // bshrub should have been compacted + t_ = LFSR_TRAVERSAL(LFS_T_CKMETA); + lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr) => 0; + assert(tag == LFSR_TAG_MDIR); + while (true) { + int err = lfsr_mtree_traverse(&lfs, &t_, &tag, &bptr); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + assert(tag == LFSR_TAG_BRANCH); + assert(lfsr_rbyd_eoff((lfsr_rbyd_t*)bptr.data.u.buffer) + <= GC_COMPACT_THRESH); + } + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files lfsr_file_rewind(&lfs, &file1) => 0; uint8_t rbuf[SIZE]; @@ -6808,6 +7624,14 @@ code = ''' lfsr_file_close(&lfs, &orphans[i]) => 0; } + // we should be marked as inconsistent now + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6843,13 +7667,19 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); assert(file1.o.o.mdir.rbyd.weight <= 3); assert(file2.o.o.mdir.rbyd.weight <= 3); + // and we should be marked as consistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD || ORPHANS > 0) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -6913,6 +7743,13 @@ code = ''' lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE; lfsr_file_sync(&lfs, &file2) => 0; + // we should not be marked as inconsistent + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -6968,10 +7805,17 @@ code = ''' assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); // if we introduce actual orphans, me _must not_ clear the orphan flag - if (ORPHANS > 3) { - assert(lfs.hasorphans == true); + if (ORPHANS >= 3) { + assert(lfs.flags & LFS_F_ORPHANS); } + // if we introduced actual orphans, we _must_ be marked as inconsistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS >= 3) ? LFS_I_INCONSISTENT : 0) + | ((!LOOKAHEAD || ORPHANS > 0) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7057,6 +7901,14 @@ code = ''' lfsr_file_close(&lfs, &orphans[i]) => 0; } + // we should be marked as inconsistent now + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -7106,13 +7958,19 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); assert(file1.o.o.mdir.rbyd.weight <= 3); assert(file2.o.o.mdir.rbyd.weight <= 3); + // and we should be marked as consistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD || ORPHANS > 0) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7196,6 +8054,14 @@ code = ''' lfsr_file_close(&lfs, &orphans[i]) => 0; } + // we should be marked as inconsistent now + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -7245,13 +8111,19 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); assert(file1.o.o.mdir.rbyd.weight <= 3); assert(file2.o.o.mdir.rbyd.weight <= 3); + // and we should be marked as consistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD || ORPHANS > 0) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7338,6 +8210,14 @@ code = ''' lfsr_file_close(&lfs, &orphans[i]) => 0; } + // we should be marked as inconsistent now + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -7395,13 +8275,19 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); assert(file1.o.o.mdir.rbyd.weight <= 3); assert(file2.o.o.mdir.rbyd.weight <= 3); + // and we should be marked as consistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD || ORPHANS > 0) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7486,6 +8372,14 @@ code = ''' lfsr_file_close(&lfs, &orphans[i]) => 0; } + // we should be marked as inconsistent now + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -7543,13 +8437,19 @@ code = ''' // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); assert(file1.o.o.mdir.rbyd.weight <= 3); assert(file2.o.o.mdir.rbyd.weight <= 3); + // and we should be marked as consistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD || ORPHANS > 0) ? LFS_I_CANLOOKAHEAD : 0) + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7654,6 +8554,14 @@ code = ''' lfsr_file_sync(&lfs, &file2) => 0; } + // we should be marked as inconsistent and uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -7685,12 +8593,11 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_MDIR); } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); @@ -7701,6 +8608,33 @@ code = ''' assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // we should be marked as consistent, but because we mutated, we're + // still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // mdirs should have been compacted + assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7787,6 +8721,13 @@ code = ''' lfsr_file_sync(&lfs, &file2) => 0; } + // we should not be marked as inconsistent + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -7843,14 +8784,21 @@ code = ''' assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); // if we introduce actual orphans, me _must not_ clear the orphan flag - if (ORPHANS > 3) { - assert(lfs.hasorphans == true); + if (ORPHANS >= 3) { + assert(lfs.flags & LFS_F_ORPHANS); } // mdirs should have been compacted assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // if we introduced actual orphans, we _must_ be marked as inconsistent + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS >= 3) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -7959,6 +8907,14 @@ code = ''' lfsr_file_sync(&lfs, &file2) => 0; } + // we should be marked as inconsistent and uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -8004,12 +8960,11 @@ code = ''' assert(tinfo.btype == LFS_BTYPE_BTREE); } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); @@ -8020,6 +8975,33 @@ code = ''' assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // we should be marked as consistent, but because we mutated, we're + // still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // mdirs should have been compacted + assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount? @@ -8129,6 +9111,14 @@ code = ''' lfsr_file_sync(&lfs, &file2) => 0; } + // we should be marked as inconsistent and uncompacted + struct lfs_fsinfo fsinfo; + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 0) ? LFS_I_INCONSISTENT : 0) + | LFS_I_CANLOOKAHEAD + | LFS_I_UNCOMPACTED)); + // try traversing with mkconsistent lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -8225,12 +9215,11 @@ code = ''' } } lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; // we should have cleaned up all grms/orphans assert(lfs.grm.mids[0] == -1); assert(lfs.grm.mids[1] == -1); - assert(lfs.hasorphans == false); + assert(!(lfs.flags & LFS_F_ORPHANS)); // which means there shouldn't be that many files left assert((lfs.mtree.u.weight & 0x7fffffff) <= (2 << lfs.mdir_bits)); @@ -8241,6 +9230,33 @@ code = ''' assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + // we should be marked as consistent, but because we mutated, we're + // still marked as uncompacted + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((ORPHANS > 3) ? LFS_I_UNCOMPACTED : 0) + | ((!LOOKAHEAD || ORPHANS > 3) ? LFS_I_CANLOOKAHEAD : 0))); + + // running another traversal should clear the uncompacted flag + lfsr_traversal_rewind(&lfs, &t) => 0; + while (true) { + int err = lfsr_traversal_read(&lfs, &t, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + // mdirs should have been compacted + assert((file1.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + assert((file2.o.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); + + // uncompacted flag should have been cleared + lfsr_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.flags == ( + ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0))); + // check we can still read the files for (int remount = 0; remount < 2; remount++) { // remount?