diff --git a/lfs.c b/lfs.c index 394ff9dd..d2438595 100644 --- a/lfs.c +++ b/lfs.c @@ -12928,97 +12928,32 @@ failed:; return err; } -static int lfsr_fs_fixorphans(lfs_t *lfs) { - // LFS_T_MKCONSISTENT really just removes orphans - lfsr_traversal_t t = LFSR_TRAVERSAL( - LFS_T_MTREEONLY | LFS_T_MKCONSISTENT); - while (true) { - int err = lfsr_mtree_gc(lfs, &t, - NULL, NULL); - if (err) { - if (err == LFS_ERR_NOENT) { - break; - } - return err; - } - } - - return 0; -} - // prepare the filesystem for mutation int lfsr_fs_mkconsistent(lfs_t *lfs) { - // fix pending grms - if (lfsr_grm_count(lfs) > 0) { - if (lfsr_grm_count(lfs) == 2) { - LFS_DEBUG("Fixing grm %"PRId32".%"PRId32" %"PRId32".%"PRId32, - lfsr_mid_bid(lfs, lfs->grm.mids[0]) >> lfs->mdir_bits, - lfsr_mid_rid(lfs, lfs->grm.mids[0]), - lfsr_mid_bid(lfs, lfs->grm.mids[1]) >> lfs->mdir_bits, - lfsr_mid_rid(lfs, lfs->grm.mids[1])); - } else if (lfsr_grm_count(lfs) == 1) { - LFS_DEBUG("Fixing grm %"PRId32".%"PRId32, - lfsr_mid_bid(lfs, lfs->grm.mids[0]) >> lfs->mdir_bits, - lfsr_mid_rid(lfs, lfs->grm.mids[0])); - } - - int err = lfsr_fs_fixgrm(lfs); - if (err) { - return err; - } - } - - // fix orphaned files - // - // this must happen after fixgrm, since removing orphaned files risks - // outdating the grm - // - if (lfsr_f_hasorphans(lfs->flags)) { - LFS_DEBUG("Fixing orphans..."); - - int err = lfsr_fs_fixorphans(lfs); - if (err) { - return err; - } - } - - return 0; + // leave this up to lfsr_fs_gc + return lfsr_fs_gc(lfs, -1, LFS_GC_MTREEONLY | LFS_GC_MKCONSISTENT); } // check the filesystem for metadata errors int lfsr_fs_ckmeta(lfs_t *lfs) { - // we leave this up to lfsr_mtree_gc - lfsr_traversal_t t = LFSR_TRAVERSAL(LFS_T_CKMETA); - while (true) { - int err = lfsr_mtree_gc(lfs, &t, - NULL, NULL); - if (err) { - if (err == LFS_ERR_NOENT) { - break; - } - return err; - } + // we want a full traversal, so make sure no gc is currently running + if (lfsr_omdir_isopen(lfs, &lfs->gc.o.o)) { + lfsr_omdir_close(lfs, &lfs->gc.o.o); } - return 0; + // leave this up to lfsr_fs_gc + return lfsr_fs_gc(lfs, -1, LFS_GC_CKMETA); } // check the filesystem for metadata + data errors int lfsr_fs_ckdata(lfs_t *lfs) { - // we leave this up to lfsr_mtree_gc - lfsr_traversal_t t = LFSR_TRAVERSAL(LFS_T_CKMETA | LFS_T_CKDATA); - while (true) { - int err = lfsr_mtree_gc(lfs, &t, - NULL, NULL); - if (err) { - if (err == LFS_ERR_NOENT) { - break; - } - return err; - } + // we want a full traversal, so make sure no gc is currently running + if (lfsr_omdir_isopen(lfs, &lfs->gc.o.o)) { + lfsr_omdir_close(lfs, &lfs->gc.o.o); } - return 0; + // leave this up to lfsr_fs_gc + return lfsr_fs_gc(lfs, -1, LFS_GC_CKMETA | LFS_GC_CKDATA); } // perform any pending janitorial work @@ -13035,6 +12970,14 @@ int lfsr_fs_gc(lfs_t *lfs, lfs_soff_t steps, uint32_t flags) { LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_islookahead(flags)); LFS_ASSERT(!lfsr_t_ismtreeonly(flags) || !lfsr_t_isckdata(flags)); + // do we really need a full traversal? + if (!(lfsr_t_islookahead(flags) + || lfsr_t_iscompact(flags) + || lfsr_t_isckmeta(flags) + || lfsr_t_isckdata(flags))) { + flags |= LFS_T_MTREEONLY; + } + // fix pending grms if requested if (lfsr_t_ismkconsistent(flags) && lfsr_grm_count(lfs) > 0) { @@ -13072,33 +13015,21 @@ int lfsr_fs_gc(lfs_t *lfs, lfs_soff_t steps, uint32_t flags) { // checkpoint the allocator to maximize any lookahead scans lfs_alloc_ckpoint(lfs); - // existing traversal? - if (lfsr_omdir_isopen(lfs, &lfs->gc.o.o)) { - // note that we mask flags (except mtreeonly)! if you change flags - // mid-traversal, the result is equivalent to the worst-case set - // of flags - lfs->gc.o.o.flags &= ( - ~LFS_GC_MKCONSISTENT - & ~LFS_GC_LOOKAHEAD - & ~LFS_GC_COMPACT - & ~LFS_GC_CKMETA - & ~LFS_GC_CKDATA - ) | flags; - // start a new traversal - } else { - lfs->gc = LFSR_TRAVERSAL( - flags - // do we really need a full traversal? - | ((!((lfsr_t_islookahead(flags) - && lfsr_fs_canlookahead(lfs)) - || (lfsr_t_iscompact(flags) - && lfsr_i_isuncompacted(lfs->flags)) - || (lfsr_t_isckmeta(flags) - && !cked) - || (lfsr_t_isckdata(flags) - && !cked))) - ? LFS_T_MTREEONLY - : 0)); + // flags mismatch? restart traversal + if (lfsr_omdir_isopen(lfs, &lfs->gc.o.o) + && (flags != (lfs->gc.o.o.flags & ( + LFS_T_MTREEONLY + | LFS_T_MKCONSISTENT + | LFS_T_LOOKAHEAD + | LFS_T_COMPACT + | LFS_T_CKMETA + | LFS_T_CKDATA)))) { + lfsr_omdir_close(lfs, &lfs->gc.o.o); + } + + // start a new traversal? + if (!lfsr_omdir_isopen(lfs, &lfs->gc.o.o)) { + lfs->gc = LFSR_TRAVERSAL(flags); lfsr_omdir_open(lfs, &lfs->gc.o.o); } diff --git a/tests/test_gc.toml b/tests/test_gc.toml index 8e6270bf..decafcef 100644 --- a/tests/test_gc.toml +++ b/tests/test_gc.toml @@ -142,140 +142,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that adding flags doesn't break lookahead -[cases.test_gc_lookahead_add_flags] -defines.GC_STEPS = 1 -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', -] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - uint8_t wbuf[SIZE]; - 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_close(&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_CANLOOKAHEAD); - assert(lfs.omdirs != &lfs.gc.o.o); - - // run GC one step - lfsr_fs_gc(&lfs, GC_STEPS, - ((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 - while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, GC_STEPS, - LFS_GC_LOOKAHEAD - | ((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_CANLOOKAHEAD); - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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 lookahead -[cases.test_gc_lookahead_remove_flags] -defines.GC_STEPS = 1 -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', -] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "spider", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - uint8_t wbuf[SIZE]; - 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_close(&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_CANLOOKAHEAD); - assert(lfs.omdirs != &lfs.gc.o.o); - - // run GC one step - lfsr_fs_gc(&lfs, GC_STEPS, - LFS_GC_LOOKAHEAD - | ((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 - while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, GC_STEPS, - ((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_CANLOOKAHEAD); - - // check the file contents - lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => 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 can make progress in isolation [cases.test_gc_compact_progress] @@ -468,203 +334,6 @@ code = ''' 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, LFS_M_RDWR, 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, GC_STEPS, - ((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, GC_STEPS, - ((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, GC_STEPS, - 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, LFS_M_RDWR, 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, LFS_M_RDWR, 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, GC_STEPS, - 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, GC_STEPS, - 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, GC_STEPS, - ((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, LFS_M_RDWR, 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] @@ -985,235 +654,6 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# test that adding flags doesn't break mkconsistent -[cases.test_gc_mkconsistent_add_flags] -defines.GC_STEPS = 1 -defines.LOOKAHEAD = [false, true] -defines.COMPACT = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.SIZE = 'FILE_BUFFER_SIZE/2' -# <=2 => grm-able -# >2 => requires orphans -defines.ORPHANS = [3, 100] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create two files - lfsr_file_t file1; - lfsr_file_open(&lfs, &file1, "cuttlefish", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - uint8_t wbuf1[SIZE]; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE; - lfsr_file_sync(&lfs, &file1) => 0; - - lfsr_file_t file2; - lfsr_file_open(&lfs, &file2, "octopus", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - uint8_t wbuf2[SIZE]; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE; - lfsr_file_sync(&lfs, &file2) => 0; - - // create this many orphaned files - // - // anytime we close a not-yet-created desync file, we create an - // orphan, but note we need these to be different files, and we need - // to close them after all open calls, otherwise we just end up with - // one orphan (littlefs is eager to clean up orphans) - // - lfsr_file_t orphans[ORPHANS]; - for (lfs_size_t i = 0; i < ORPHANS; i++) { - char name[256]; - sprintf(name, "jellyfish%03x", i); - lfsr_file_open(&lfs, &orphans[i], name, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; - } - for (lfs_size_t i = 0; i < ORPHANS; i++) { - lfsr_file_close(&lfs, &orphans[i]) => 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_INCONSISTENT); - assert(lfs.omdirs != &lfs.gc.o.o); - - // run GC one step - lfsr_fs_gc(&lfs, GC_STEPS, - ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((COMPACT) ? LFS_GC_COMPACT : 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 - while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, GC_STEPS, - LFS_GC_MKCONSISTENT - | ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((COMPACT) ? LFS_GC_COMPACT : 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_INCONSISTENT); - - // check we can still read the files - for (int remount = 0; remount < 2; remount++) { - // remount? - if (remount) { - lfsr_file_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - lfsr_file_open(&lfs, &file1, "cuttlefish", LFS_O_RDONLY) => 0; - lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0; - } - - lfsr_file_rewind(&lfs, &file1) => 0; - uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf1, SIZE) == 0); - - lfsr_file_rewind(&lfs, &file2) => 0; - lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf2, SIZE) == 0); - } - - lfsr_file_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; -''' - -# test that removing flags invalidates mkconsistent -[cases.test_gc_mkconsistent_remove_flags] -defines.GC_STEPS = 1 -defines.LOOKAHEAD = [false, true] -defines.COMPACT = [false, true] -defines.CKMETA = [false, true] -defines.CKDATA = [false, true] -defines.SIZE = 'FILE_BUFFER_SIZE/2' -# <=2 => grm-able -# >2 => requires orphans -defines.ORPHANS = [3, 100] -# we need something to keep the traversal running -if = 'CKMETA || CKDATA' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - - uint32_t prng = 42; - - // create two files - lfsr_file_t file1; - lfsr_file_open(&lfs, &file1, "cuttlefish", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - uint8_t wbuf1[SIZE]; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE; - lfsr_file_sync(&lfs, &file1) => 0; - - lfsr_file_t file2; - lfsr_file_open(&lfs, &file2, "octopus", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - uint8_t wbuf2[SIZE]; - for (lfs_size_t j = 0; j < SIZE; j++) { - wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE; - lfsr_file_sync(&lfs, &file2) => 0; - - // create this many orphaned files - // - // anytime we close a not-yet-created desync file, we create an - // orphan, but note we need these to be different files, and we need - // to close them after all open calls, otherwise we just end up with - // one orphan (littlefs is eager to clean up orphans) - // - lfsr_file_t orphans[ORPHANS]; - for (lfs_size_t i = 0; i < ORPHANS; i++) { - char name[256]; - sprintf(name, "jellyfish%03x", i); - lfsr_file_open(&lfs, &orphans[i], name, - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; - } - for (lfs_size_t i = 0; i < ORPHANS; i++) { - lfsr_file_close(&lfs, &orphans[i]) => 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_INCONSISTENT); - assert(lfs.omdirs != &lfs.gc.o.o); - - // run GC one step - lfsr_fs_gc(&lfs, GC_STEPS, - 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(lfs.omdirs == &lfs.gc.o.o); - - // change flags and run GC until our traversal is done - while (lfs.omdirs == &lfs.gc.o.o) { - lfsr_fs_gc(&lfs, GC_STEPS, - ((LOOKAHEAD) ? LFS_GC_LOOKAHEAD : 0) - | ((COMPACT) ? LFS_GC_COMPACT : 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_INCONSISTENT); - - // check we can still read the files - for (int remount = 0; remount < 2; remount++) { - // remount? - if (remount) { - lfsr_file_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - lfsr_file_open(&lfs, &file1, "cuttlefish", LFS_O_RDONLY) => 0; - lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0; - } - - lfsr_file_rewind(&lfs, &file1) => 0; - uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf1, SIZE) == 0); - - lfsr_file_rewind(&lfs, &file2) => 0; - lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf2, SIZE) == 0); - } - - lfsr_file_close(&lfs, &file1) => 0; - lfsr_file_close(&lfs, &file2) => 0; - lfsr_unmount(&lfs) => 0; -''' - # test we can detect at least fully clobbered blocks [cases.test_gc_ckmeta]