From 5b72973f8f36cfd9859765fc012558a21684813a Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 18 Jun 2024 21:18:40 -0500 Subject: [PATCH] t: Filled out rest of test_traversal There can always be more tests, but I think these give a nice set of coverage over corner-cases in our traversal clobbering scheme. These did find a couple bugs: - If we clobber an inlined mroot, we need to adjust the mid by two mdirs, but only if there is no mtree/mdirs. To avoid this and other mid-related headaches, we just provide the new mid in lfsr_mdir_commit, since we always know it here. - lfsr_mdir_commit compares mdirs by mptr, which means we need to clobber traversal's mdir's mptrs or else lfsr_mdir_commit will clobber already-clobbered traversals. There may be a better way to solve this, but it will probably get into the weeds with how lfsr_mdir_commit relies on mids vs mptrs... Code changes: code stack before: 34570 2624 after: 34566 (-0.0%) 2624 (+0.0%) Now that the dust has settled and we sort of know what the traversal implementation will look like, we can look at the before and after to get a rough idea of how much the traversal API actually costs: code stack no-traversal (before): 33886 2560 yes-traversal (after): 34566 (+2.0%) 2624 (+2.5%) Note this still includes the annoying lfsr_btree_traverse inlining stack cost, which isn't really the traversal API's fault and may be avoidable in the future. --- lfs.c | 75 +- tests/test_badblocks.toml | 3 +- tests/test_powerloss.toml | 1 + tests/test_traversal.toml | 1748 ++++++++++++++++++++++++++++++++++++- 4 files changed, 1764 insertions(+), 63 deletions(-) diff --git a/lfs.c b/lfs.c index e00db850..f5c72a3a 100644 --- a/lfs.c +++ b/lfs.c @@ -5216,7 +5216,8 @@ static bool lfsr_mid_isopen(lfs_t *lfs, lfsr_smid_t mid) { } // needed in lfsr_opened_clobber -static void lfsr_traversal_clobberopen(lfs_t *lfs, lfsr_traversal_t *t); +static void lfsr_traversal_clobber(lfs_t *lfs, lfsr_traversal_t *t, + lfsr_smid_t mid); // traversal invalidation things static void lfsr_opened_clobber(lfs_t *lfs, lfsr_omdir_t *o, bool dirty) { @@ -5229,7 +5230,7 @@ static void lfsr_opened_clobber(lfs_t *lfs, lfsr_omdir_t *o, bool dirty) { // clobber any traversals referencing our mdir lfsr_traversal_t *t = (lfsr_traversal_t*)o_; if (t->mt.ot == o) { - lfsr_traversal_clobberopen(lfs, t); + lfsr_traversal_clobber(lfs, t, -1); } } } @@ -6866,9 +6867,6 @@ static int lfsr_mroot_parent(lfs_t *lfs, const lfsr_mptr_t *mptr, } } -// needed in lfsr_mdir_commit -static void lfsr_traversal_clobbermdir(lfs_t *lfs, lfsr_traversal_t *t); - // high-level mdir commit // // this is atomic and updates any opened mdirs, lfs_t, etc @@ -7436,25 +7434,24 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, } } + // clobber any related traversals for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) { if (o->type == LFS_TYPE_TRAVERSAL) { // mark all traversals as dirty o->flags |= LFS_F_DIRTY; - // clobber any related traversals + // clobber any mdir related traversals if (lfsr_mdir_cmp(&o->mdir, mdir) == 0) { - lfsr_traversal_clobbermdir(lfs, (lfsr_traversal_t*)o); + lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, + lfsr_mid_bid(lfs, mdir->mid) + 1); } - } - } - // if mroot/mtree changed, clobber any related traversals - if (lfsr_mdir_cmp(&mroot_, &lfs->mroot) != 0 - || lfsr_mtree_cmp(&mtree_, &lfs->mtree) != 0) { - for (lfsr_omdir_t *o = lfs->opened; o; o = o->next) { - if (o->type == LFS_TYPE_TRAVERSAL + // if mroot/mtree changed, clobber any mroot/mtree traversals + if ((lfsr_mdir_cmp(&mroot_, &lfs->mroot) != 0 + || lfsr_mtree_cmp(&mtree_, &lfs->mtree) != 0) && o->mdir.mid == -1) { - lfsr_traversal_clobbermdir(lfs, (lfsr_traversal_t*)o); + lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, + 0); } } } @@ -7837,6 +7834,7 @@ enum { .o.state=LFSR_TSTATE_MROOTANCHOR, \ .o.flags=_flags, \ .o.mdir.mid=-1, \ + .o.mdir.rbyd.blocks={0,0}, \ .ot=NULL, \ .u.mtortoise.mptr={{0, 0}}, \ .u.mtortoise.step=0, \ @@ -7885,6 +7883,8 @@ static void lfsr_fs_traverserewind(lfs_t *lfs, lfsr_mtraversal_t *mt) { mt->o.flags &= ~LFS_F_DIRTY; mt->o.state = LFSR_TSTATE_MROOTANCHOR; mt->o.mdir.mid = -1; + mt->o.mdir.rbyd.blocks[0] = 0; + mt->o.mdir.rbyd.blocks[1] = 0; mt->ot = NULL; mt->u.mtortoise.mptr.blocks[0] = 0; mt->u.mtortoise.mptr.blocks[1] = 0; @@ -7892,22 +7892,23 @@ static void lfsr_fs_traverserewind(lfs_t *lfs, lfsr_mtraversal_t *mt) { mt->u.mtortoise.power = 0; } -static void lfsr_fs_traverseclobbermdir(lfs_t *lfs, lfsr_mtraversal_t *mt) { +static void lfsr_fs_traverseclobber(lfs_t *lfs, lfsr_mtraversal_t *mt, + lfsr_smid_t mid) { (void)lfs; - // increment the mid (to make progress) and reset to mdir iteration - mt->o.state = LFSR_TSTATE_MDIRS; - mt->o.mdir.mid = lfsr_mid_bid(lfs, mt->o.mdir.mid) - + (1 << lfs->mdir_bits); - mt->ot = NULL; -} - -static void lfsr_fs_traverseclobberopen(lfs_t *lfs, lfsr_mtraversal_t *mt) { - (void)lfs; - // move to next omdir - LFS_ASSERT(mt->o.state == LFSR_TSTATE_OMDIRS - || mt->o.state == LFSR_TSTATE_OBTREE); - mt->o.state = LFSR_TSTATE_OMDIRS; - mt->ot = mt->ot->next; + if (mid != -1) { + // increment the mid (to make progress) and reset to mdir iteration + mt->o.state = LFSR_TSTATE_MDIRS; + mt->o.mdir.mid = mid; + mt->o.mdir.rbyd.blocks[0] = 0; + mt->o.mdir.rbyd.blocks[1] = 0; + mt->ot = NULL; + } else { + // move to next omdir + LFS_ASSERT(mt->o.state == LFSR_TSTATE_OMDIRS + || mt->o.state == LFSR_TSTATE_OBTREE); + mt->o.state = LFSR_TSTATE_OMDIRS; + mt->ot = mt->ot->next; + } } @@ -9420,18 +9421,10 @@ failed:; /// High-level filesystem traversal /// -static void lfsr_traversal_clobbermdir(lfs_t *lfs, lfsr_traversal_t *t) { +static void lfsr_traversal_clobber(lfs_t *lfs, lfsr_traversal_t *t, + lfsr_smid_t mid) { // clobber low-level traversal - lfsr_fs_traverseclobbermdir(lfs, &t->mt); - - // and clear any pending blocks - t->blocks[0] = -1; - t->blocks[1] = -1; -} - -static void lfsr_traversal_clobberopen(lfs_t *lfs, lfsr_traversal_t *t) { - // clobber low-level traversal - lfsr_fs_traverseclobberopen(lfs, &t->mt); + lfsr_fs_traverseclobber(lfs, &t->mt, mid); // and clear any pending blocks t->blocks[0] = -1; diff --git a/tests/test_badblocks.toml b/tests/test_badblocks.toml index 097844c0..d1b1de15 100644 --- a/tests/test_badblocks.toml +++ b/tests/test_badblocks.toml @@ -3,7 +3,8 @@ after = [ 'test_dirs', 'test_files', 'test_fwrite', - 'test_forphans' + 'test_forphans', + 'test_traversal', ] diff --git a/tests/test_powerloss.toml b/tests/test_powerloss.toml index a98e4ea3..4c9b6a7d 100644 --- a/tests/test_powerloss.toml +++ b/tests/test_powerloss.toml @@ -8,6 +8,7 @@ after = [ 'test_dirs', 'test_files', 'test_forphans', + 'test_traversal', ] diff --git a/tests/test_traversal.toml b/tests/test_traversal.toml index 9e769f09..bb179dca 100644 --- a/tests/test_traversal.toml +++ b/tests/test_traversal.toml @@ -1851,7 +1851,7 @@ done:; lfsr_unmount(&lfs) => 0; ''' -[cases.test_traversal_mutation_remove] +[cases.test_traversal_mutation_rm] defines.WHEN = [0, 1, 2] defines.EXCL = [false, true] defines.CKMETADATA = [false, true] @@ -1924,7 +1924,7 @@ done:; lfsr_unmount(&lfs) => 0; ''' -[cases.test_traversal_mutation_rename] +[cases.test_traversal_mutation_mv] defines.WHEN = [0, 1, 2] defines.EXCL = [false, true] defines.CKMETADATA = [false, true] @@ -2923,6 +2923,216 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +[cases.test_traversal_mutation_rm_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "tarantula", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // remove the file + lfsr_remove(&lfs, "spider") => 0; + + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // check the file contents + lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT; + + lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mv_src_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "tarantula", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // rename one file over another + lfsr_rename(&lfs, "spider", "tarantula") => 0; + + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // check the file contents + lfsr_file_open(&lfs, &file, "spider", LFS_O_RDONLY) => LFS_ERR_NOENT; + + lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mv_dst_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "tarantula", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // rename one file over another + lfsr_rename(&lfs, "tarantula", "spider") => 0; + + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => LFS_ERR_NOENT; + + lfsr_unmount(&lfs) => 0; +''' + [cases.test_traversal_mutation_mroot_split] defines.CKMETADATA = [false, true] defines.CKDATA = [false, true] @@ -2998,7 +3208,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_traversal_mutation_mroot_split_l] +[cases.test_traversal_mutation_mroot_split_bshrub_l] defines.CKMETADATA = [false, true] defines.CKDATA = [false, true] defines.LOOKAHEAD = [false, true] @@ -3093,7 +3303,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_traversal_mutation_mroot_split_r] +[cases.test_traversal_mutation_mroot_split_bshrub_r] defines.CKMETADATA = [false, true] defines.CKDATA = [false, true] defines.LOOKAHEAD = [false, true] @@ -3193,25 +3403,1521 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# TODO -#[cases.test_traversal_mutation_mroot_chain_split] -#[cases.test_traversal_mutation_mroot_chain_split_l] -#[cases.test_traversal_mutation_mroot_chain_split_r] -#[cases.test_traversal_mutation_mtree_split] -#[cases.test_traversal_mutation_mtree_split_l] -#[cases.test_traversal_mutation_mtree_split_r] +[cases.test_traversal_mutation_mroot_extend] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +# force early relocations +defines.BLOCK_RECYCLES = 0 +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; -# TODO also rename/remove -> mv/rm above? -#[cases.test_traversal_mutation_rm_bleaf] -#[cases.test_traversal_mutation_rm_btree] -#[cases.test_traversal_mutation_rm_bshrub] -#[cases.test_traversal_mutation_mv_src_bleaf] -#[cases.test_traversal_mutation_mv_src_btree] -#[cases.test_traversal_mutation_mv_src_bshrub] -#[cases.test_traversal_mutation_mv_dst_bleaf] -#[cases.test_traversal_mutation_mv_dst_btree] -#[cases.test_traversal_mutation_mv_dst_bshrub] + uint32_t prng = 42; + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "uloborus") => 0; + } + + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mroot_extend_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +# force early relocations +defines.BLOCK_RECYCLES = 0 +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse a data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "uloborus") => 0; + } + + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mroot_relocate] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +# force early relocations +defines.BLOCK_RECYCLES = 0 +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "uloborus") => 0; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + + // rewrite enough files for mroot to relocate + lfs_block_t orig = lfs.mroot.rbyd.blocks[0]; + while (lfs.mroot.rbyd.blocks[0] == orig + || lfs.mroot.rbyd.blocks[0] == orig) { + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "uloborus") => 0; + } + + // it's a bit unclear if clobbered mroot chain traversals should + // still traverse inlined mroots, so if this breaks in the future + // I wouldn't worry too much about it + // + // traverse mroot + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mroot_relocate_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +# force early relocations +defines.BLOCK_RECYCLES = 0 +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create two files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "uloborus") => 0; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroots + 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) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse a data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // rewrite enough files for mroot to relocate + lfs_block_t orig = lfs.mroot.rbyd.blocks[0]; + while (lfs.mroot.rbyd.blocks[0] == orig + || lfs.mroot.rbyd.blocks[0] == orig) { + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "uloborus") => 0; + } + + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_split] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + + // create enough files for mdir to split again + i = 0; + orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "vulsor%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_split_bshrub_l] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse mtree + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_BTREE); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // create enough files for mdir to split again + i = 0; + orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "vulsor%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // traverse untouched mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_split_bshrub_r] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse mtree + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_BTREE); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // create enough files for mdir to split again + i = 0; + orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "vulsor%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // traverse untouched mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_extend] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // switch to early relocations after extending + lfsr_unmount(&lfs) => 0; + struct lfs_config cfg = *CFG; + cfg.block_recycles = 0; + lfsr_mount(&lfs, &cfg) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "vulsor", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "vulsor") => 0; + } + + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_extend_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // switch to early relocations after extending + lfsr_unmount(&lfs) => 0; + struct lfs_config cfg = *CFG; + cfg.block_recycles = 0; + lfsr_mount(&lfs, &cfg) => 0; + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + 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); + // traverse mtree + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_BTREE); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "vulsor", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "vulsor") => 0; + } + + // traverse untouched mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_relocate] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +# force early relocations +defines.BLOCK_RECYCLES = 0 +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "vulsor", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "vulsor") => 0; + } + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroot + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + assert(tinfo.block == 0 || tinfo.block == 1); + + // rewrite enough files for mroot to relocate + orig = lfs.mroot.rbyd.blocks[0]; + while (lfs.mroot.rbyd.blocks[0] == orig + || lfs.mroot.rbyd.blocks[0] == orig) { + lfsr_file_open(&lfs, &file, "vulsor", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "vulsor") => 0; + } + + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_traversal_mutation_mtree_relocate_bshrub] +defines.CKMETADATA = [false, true] +defines.CKDATA = [false, true] +defines.LOOKAHEAD = [false, true] +defines.SIZE = '2*BLOCK_SIZE' +# force early relocations +defines.BLOCK_RECYCLES = 0 +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + uint32_t prng = 42; + + // create four files + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "spider", + LFS_O_WRONLY | 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, &file, wbuf1, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", + LFS_O_WRONLY | 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, &file, wbuf2, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf3[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf3, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint8_t wbuf4[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf4[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf4, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // rewrite enough files for mroot to extend + while (lfs.mroot.rbyd.blocks[0] == 0 + || lfs.mroot.rbyd.blocks[0] == 1) { + lfsr_file_open(&lfs, &file, "vulsor", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "vulsor") => 0; + } + + // create enough files for mroot to split twice + lfs_size_t i = 0; + while (lfs.mtree.u.weight == 0x80000000) { + char name[256]; + sprintf(name, "tarantula%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + i = 0; + lfs_size_t orig = lfs.mtree.u.weight; + while (lfs.mtree.u.weight == orig) { + char name[256]; + sprintf(name, "xnotata%03x", i); + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + i += 1; + } + + // try traversing + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, + ((CKMETADATA) ? LFS_T_CKMETADATA : 0) + | ((CKDATA) ? LFS_T_CKDATA : 0) + | ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0; + + // traverse mroots + 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) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse mtree + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_BTREE); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // traverse mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse one data block + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + + // rewrite enough files for mroot to relocate + orig = lfs.mroot.rbyd.blocks[0]; + while (lfs.mroot.rbyd.blocks[0] == orig + || lfs.mroot.rbyd.blocks[0] == orig) { + lfsr_file_open(&lfs, &file, "vulsor", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_close(&lfs, &file) => 0; + lfsr_remove(&lfs, "vulsor") => 0; + } + + // traverse untouched mdir + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_MDIR); + // traverse two data blocks + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_DATA); + // we should be at end of traversal now + lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; + lfsr_traversal_close(&lfs, &t) => 0; + + // 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, wbuf1, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "uloborus", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf2, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "wolfspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "yellowcrabspider", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf4, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +'''