From 7fdf0b7d23f806b014c2af2fad7370b43e3d77b3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 2 Jul 2024 17:34:21 -0500 Subject: [PATCH] t: Switched back to mid-based traversal clobbering Implementing gc_compact_thresh over bshrubs highlighted that it's really not that difficult, and probably required, for traversal bshrubs to be tracked correctly during mdir commits/compacts/splits/etc. And if we track bshrubs across mdir commits, we might as well clobber traversals at the mid level, allowing traversals to always reach btrees/bshrubs not under active mutation. One key thing to note: we should never be traversing a bshrub that is not referenced elsewhere, either on-disk in an mdir or in-ram via an opened file. So any compacted traversal bshrubs are not wasted prog cycles. This moves most of the clobbering logic back up into the high-level functions (lfsr_remove/rename mainly), where we know which mids may be clobbered. This has a code cost, but it's really not all that much for more thorough/correct filesystem traversals under mutation: code stack before: 35268 2680 after: 35368 (+0.3%) 2680 (+0.0%) Unfortunately, lingering rbyd references in our btraversal structs are still an issue, and some bshrub tests are failing... Though I do have some ideas on how to fix this. --- lfs.c | 90 ++++++++------- tests/test_traversal.toml | 236 +++++++++++++++++++++++++++++++------- 2 files changed, 242 insertions(+), 84 deletions(-) diff --git a/lfs.c b/lfs.c index 101a74bf..26e125e9 100644 --- a/lfs.c +++ b/lfs.c @@ -5945,7 +5945,7 @@ static bool lfsr_omdir_ismidopen(lfs_t *lfs, lfsr_smid_t mid) { // needed in lfsr_omdir_clobber static void lfsr_traversal_clobber(lfs_t *lfs, lfsr_traversal_t *t, - lfsr_smid_t mid); + lfsr_smid_t shift); // traversal invalidation things static void lfsr_omdir_clobber(lfs_t *lfs, lfsr_omdir_t *o, bool dirty) { @@ -5958,7 +5958,7 @@ static void lfsr_omdir_clobber(lfs_t *lfs, lfsr_omdir_t *o, bool dirty) { // clobber any traversals referencing our mdir if (t->mt.o == o) { - lfsr_traversal_clobber(lfs, t, -1); + lfsr_traversal_clobber(lfs, t, +1); } } } @@ -7839,30 +7839,23 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, } } - // clobber any related traversals + // mark all traversals as dirty for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { if (o->type == LFS_TYPE_TRAVERSAL) { - // don't clobber the current mdir, we assume upper layers know - // what they're doing - if (&o->mdir == mdir) { - continue; - } - - // mark all traversals as dirty ((lfsr_traversal_t*)o)->mt.flags |= LFS_F_DIRTY; + } + } - // clobber any mdir related traversals - if (lfsr_mdir_cmp(&o->mdir, mdir) == 0) { - lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, - lfsr_mid_bid(lfs, mdir->mid) + 1); - } - - // 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_clobber(lfs, (lfsr_traversal_t*)o, - 0); + // if mroot/mtree changed, clobber any mroot/mtree traversals + if (lfsr_mdir_cmp(&mroot_, &lfs->mroot) != 0 + || lfsr_mtree_cmp(&mtree_, &lfs->mtree) != 0) { + for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { + if (o->type == LFS_TYPE_TRAVERSAL + && o->mdir.mid == -1 + // don't clobber the current mdir, assume upper layers + // know what they're doing + && &o->mdir != mdir) { + lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, 0); } } } @@ -8419,8 +8412,9 @@ static int lfsr_mtree_traverse_(lfs_t *lfs, // not traversing all blocks? have we exceeded our mdir's weight? // return to mtree iteration if (lfsr_t_ismtreeonly(mt->flags) - || lfsr_mid_rid(lfs, mdir->mid) - >= (lfsr_srid_t)mdir->rbyd.weight) { + // mid may be -1 here if we were clobbered + || (lfsr_rid_t)lfsr_mid_rid(lfs, mdir->mid) + >= mdir->rbyd.weight) { mdir->mid = lfsr_mid_bid(lfs, mdir->mid) + 1; mt->state = LFSR_MTRAVERSAL_MDIRS; continue; @@ -9210,6 +9204,13 @@ int lfsr_remove(lfs_t *lfs, const char *path) { } else { ((lfsr_dir_t*)o)->pos -= 1; } + + // clobber entangled traversals + } else if (o->type == LFS_TYPE_TRAVERSAL) { + if (lfsr_f_iszombie(o->flags)) { + o->flags &= ~LFS_F_ZOMBIE; + lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, 0); + } } } @@ -9363,6 +9364,12 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { ((lfsr_dir_t*)o)->pos -= 1; } } + + // clobber entangled traversals + } else if (o->type == LFS_TYPE_TRAVERSAL + && ((exists && o->mdir.mid == new_mdir.mid) + || o->mdir.mid == lfs->grm.mids[0])) { + lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, +1); } } @@ -10961,7 +10968,7 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, // checkpoint the allocator lfs_alloc_ckpoint(lfs); - // clobber any entangled traversals + // clobber entangled traversals lfsr_omdir_clobber(lfs, &file->o, true); // mark as unsynced in case we fail file->o.flags |= LFS_F_UNSYNC; @@ -11121,7 +11128,7 @@ int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) { // checkpoint the allocator lfs_alloc_ckpoint(lfs); - // clobber any entangled traversals + // clobber entangled traversals lfsr_omdir_clobber(lfs, &file->o, true); // flush our buffer if it contains any unwritten data @@ -11296,6 +11303,11 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { file->buffer.size); file_->buffer.size = file->buffer.size; } + + // clobber entangled traversals + } else if (o->type == LFS_TYPE_TRAVERSAL + && o->mdir.mid == file->o.mdir.mid) { + lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o, +1); } } @@ -11384,7 +11396,7 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) { // checkpoint the allocator lfs_alloc_ckpoint(lfs); - // clobber any entangled traversals + // clobber entangled traversals lfsr_omdir_clobber(lfs, &file->o, true); // mark as unsynced in case we fail file->o.flags |= LFS_F_UNSYNC; @@ -11490,7 +11502,7 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) { // checkpoint the allocator lfs_alloc_ckpoint(lfs); - // clobber any entangled traversals + // clobber entangled traversals lfsr_omdir_clobber(lfs, &file->o, true); // mark as unsynced in case we fail file->o.flags |= LFS_F_UNSYNC; @@ -12881,23 +12893,13 @@ done:; } static void lfsr_traversal_clobber(lfs_t *lfs, lfsr_traversal_t *t, - lfsr_smid_t mid) { + lfsr_smid_t shift) { (void)lfs; - // clobber low-level traversal - if (mid != -1) { - // increment the mid (to make progress) and reset to mdir iteration - t->mt.state = LFSR_MTRAVERSAL_MDIRS; - t->o.mdir.mid = mid; - t->o.mdir.rbyd.blocks[0] = -1; - t->o.mdir.rbyd.blocks[1] = -1; - t->mt.o = NULL; - } else { - // move to next omdir - LFS_ASSERT(t->mt.state == LFSR_MTRAVERSAL_OMDIRS - || t->mt.state == LFSR_MTRAVERSAL_OBTREE); - t->mt.state = LFSR_MTRAVERSAL_OMDIRS; - t->mt.o = t->mt.o->next; - } + // increment the mid (to make progress) and reset to mdir iteration + t->mt.state = LFSR_MTRAVERSAL_MDIR; + t->o.mdir.mid += shift; + t->mt.o = NULL; + t->mt.bshrub.u.bshrub.blocks[0] = -1; // and clear any pending blocks t->blocks[0] = -1; diff --git a/tests/test_traversal.toml b/tests/test_traversal.toml index d335b963..0c91464b 100644 --- a/tests/test_traversal.toml +++ b/tests/test_traversal.toml @@ -1696,10 +1696,6 @@ code = ''' lfsr_file_open(&lfs, &file, "spider", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfsr_file_close(&lfs, &file) => 0; - - lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - lfsr_traversal_close(&lfs, &t) => 0; - goto done; } lfsr_traversal_read(&lfs, &t, &tinfo) => 0; @@ -1716,7 +1712,6 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; -done:; lfsr_unmount(&lfs) => 0; ''' @@ -1815,6 +1810,9 @@ code = ''' LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfsr_file_close(&lfs, &file) => 0; + 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_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; @@ -1938,10 +1936,6 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; goto done; } - - struct lfs_tinfo tinfo; - lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - goto done; } lfsr_traversal_read(&lfs, &t, &tinfo) => 0; @@ -2016,10 +2010,6 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; goto done; } - - struct lfs_tinfo tinfo; - lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - goto done; } lfsr_traversal_read(&lfs, &t, &tinfo) => 0; @@ -2094,10 +2084,6 @@ code = ''' lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_BUSY; goto done; } - - struct lfs_tinfo tinfo; - lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; - goto done; } lfsr_traversal_read(&lfs, &t, &tinfo) => 0; @@ -2428,6 +2414,14 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; + // traverse btree + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_BTREE); + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -2509,6 +2503,11 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf1, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -2754,6 +2753,11 @@ code = ''' } lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE; + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -2919,16 +2923,14 @@ code = ''' } lfsr_file_close(&lfs, &file1) => 0; - if (DESYNC) { - // traverse btree - lfsr_traversal_read(&lfs, &t, &tinfo) => 0; - assert(tinfo.btype == LFS_BTYPE_BTREE); - // 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 btree + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + assert(tinfo.btype == LFS_BTYPE_BTREE); + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3013,13 +3015,11 @@ code = ''' } lfsr_file_close(&lfs, &file1) => 0; - if (DESYNC) { - // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3101,6 +3101,11 @@ code = ''' // remove the file lfsr_remove(&lfs, "spider") => 0; + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3129,7 +3134,7 @@ code = ''' uint32_t prng = 42; - // create two files + // create three files lfsr_file_t file; lfsr_file_open(&lfs, &file, "spider", LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; @@ -3149,6 +3154,15 @@ code = ''' 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; + // try traversing lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -3171,6 +3185,16 @@ code = ''' // rename one file over another lfsr_rename(&lfs, "spider", "tarantula") => 0; + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3184,6 +3208,11 @@ code = ''' 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, wbuf3, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; ''' @@ -3219,6 +3248,15 @@ code = ''' 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; + // try traversing lfsr_traversal_t t; lfsr_traversal_open(&lfs, &t, @@ -3241,6 +3279,11 @@ code = ''' // rename one file over another lfsr_rename(&lfs, "tarantula", "spider") => 0; + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3254,6 +3297,11 @@ code = ''' lfsr_file_open(&lfs, &file, "tarantula", LFS_O_RDONLY) => LFS_ERR_NOENT; + 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_unmount(&lfs) => 0; ''' @@ -3313,6 +3361,26 @@ code = ''' 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); // we should be at end of traversal now lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3403,6 +3471,24 @@ code = ''' i += 1; } + // traverse another data block + 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3503,6 +3589,14 @@ code = ''' i += 1; } + // traverse another data block + 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3583,6 +3677,21 @@ code = ''' lfsr_remove(&lfs, "uloborus") => 0; } + // 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3664,6 +3773,14 @@ code = ''' lfsr_remove(&lfs, "uloborus") => 0; } + // traverse another data block + 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -3863,6 +3980,14 @@ code = ''' lfsr_remove(&lfs, "uloborus") => 0; } + // traverse another data block + 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_NOENT; lfsr_traversal_close(&lfs, &t) => 0; @@ -4169,7 +4294,20 @@ code = ''' i += 1; } - // traverse untouched mdir + // traverse another data block + 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; @@ -4333,8 +4471,10 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; i += 1; } - - // traverse untouched mdir + // traverse another data block + 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; @@ -4661,7 +4801,15 @@ code = ''' lfsr_remove(&lfs, "vulsor") => 0; } - // traverse untouched mdir + // traverse another data block + 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; @@ -5004,7 +5152,15 @@ code = ''' lfsr_remove(&lfs, "vulsor") => 0; } - // traverse untouched mdir + // traverse another data block + 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;