From d30ed42c7f3f84e85aa59c734e30e1b9417dddaa Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sat, 27 Jan 2024 17:38:32 -0600 Subject: [PATCH] Ported/cleaned up mtree tests Well this was quite tedious, but these tests are valuable since our mtree has a number of hard-to-reach edge cases. This mainly ports over to the new attr-list format for mdir commits, but also cleans up a couple of lingering tedious TODO things: - mtree tests now use the new mdir commit attr-list format. - Reoriented most tests to use namelookups instead of mid lookups. Using mid lookups in testing is/was really fragile, since it depends on exactly how mids get split and moved around. namelookups are more robust, by design they don't care about the underlying mtree structure. And really, namelookups are what we care about in the mtree, mids are just a mechanism for mtree updates to work. We don't remove all mid checks though, we just compare against namelookup-derived mids when it matters (the mtree_opened tests for example). - The names we use in testing have also been updated to no longer create invalid mtrees, i.e. names are ordered correctly and always have a did. The previous mess always risked triggering asserts with false positives. - By adopting namelookup in the tests, we can actually test the on-disk state of fuzz testing. Though note we can't change names once written, without invalidating our mtree. This limits fuzz testing a little bit, but it's still a big improvement over the previous fuzz tests. - Dropped mtree tests that no longer really make sense. Mainly that the did should never be deleted, so you can never end up with an empty mtree, dropping the left-most mdir, etc. There were still a few of things lingering around. With this, all tests are working again with the attr-list changes. Wooh. --- tests/test_mtree.toml | 3994 +++++++++++++++++++++-------------------- 1 file changed, 2025 insertions(+), 1969 deletions(-) diff --git a/tests/test_mtree.toml b/tests/test_mtree.toml index 3c491771..d5875195 100644 --- a/tests/test_mtree.toml +++ b/tests/test_mtree.toml @@ -42,7 +42,7 @@ code = ''' for (lfs_size_t i = 0; i < N; i++) { lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(i), 0, BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(UATTR(i), 0, BUF(&alphas[i % 26], 1)))) => 0; } for (lfs_size_t i = 0; i < N; i++) { @@ -84,7 +84,7 @@ code = ''' lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(i), 0, BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(UATTR(i), 0, BUF(&alphas[i % 26], 1)))) => 0; } for (lfs_size_t i = 0; i < N; i++) { @@ -123,7 +123,7 @@ code = ''' for (lfs_size_t i = 0; i < N; i++) { lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(UATTR(1), 0, BUF(&alphas[i % 26], 1)))) => 0; uint8_t buffer[4]; lfsr_mdir_get(&lfs, &lfs.mroot, @@ -163,63 +163,57 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with a large attr so the next entry can not fit + // create a 2 large attrs that needs to be uninlined uint8_t buffer[SIZE]; memset(buffer, 'a', SIZE); lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - // create a large entry that needs to be uninlined (but not split!) memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot + // assert that our attrs are still in the mroot/mtree lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[0] == 'a'); - // assert that our entry is still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[0] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot + // assert that our attrs are still in the mroot/mtree lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[0] == 'a'); - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[0] == 'b'); lfsr_unmount(&lfs) => 0; ''' @@ -236,13 +230,19 @@ code = ''' // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; @@ -254,23 +254,20 @@ code = ''' assert(lfs.mroot.rbyd.weight == 0); // assert that our entries are still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdirs were unininlined and split @@ -279,17 +276,17 @@ code = ''' assert(lfs.mroot.rbyd.weight == 0); // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' @@ -304,88 +301,93 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // now add another large entry to the mdir, forcing a split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - memset(buffer, 'c', SIZE); + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mdir to compact mdir.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; // assert mdir was split correctly - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'b'); - lfsr_unmount(&lfs) => 0; + lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdir was split correctly - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'b'); + + lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); lfsr_unmount(&lfs) => 0; ''' @@ -397,94 +399,66 @@ defines.N = [5, 10, 20, 40, 80, 160, 320] defines.FORCE_COMPACTION = [false, true] in = 'lfs.c' code = ''' - const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); // create entries - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, - lfs_smax32( - lfsr_mtree_weight(&lfs) - lfsr_mweight(&lfs), - 0), - &mdir) => 0; - mdir.mid += 1; for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; // force a compaction? if (FORCE_COMPACTION) { - mdir.rbyd.eoff = -1; lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; } - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, - BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(REG, +1, BUF(name, 4)))) => 0; - uint8_t buffer[4]; + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - - mdir.mid += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } // try looking up each entry - lfs_size_t i = 0; - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - i += 1; - } + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + uint8_t buffer[256]; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + lfsr_mtree_seek(&lfs, &mdir, +1) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } - assert(i == N); - - lfsr_unmount(&lfs) => 0; - // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // try looking up each entry - i = 0; - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - i += 1; - } + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + lfsr_mtree_seek(&lfs, &mdir, +1) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } - assert(i == N); lfsr_unmount(&lfs) => 0; ''' @@ -496,115 +470,96 @@ defines.FORCE_COMPACTION = [false, true] defines.SEED = 'range(100)' in = 'lfs.c' code = ''' - const char *alphas = "abcdefghijklmnopqrstuvwxyz"; - lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // at least keep track of the number of entries we expect - lfs_size_t count = 0; + bool sim[N]; + for (lfs_size_t i = 0; i < N; i++) { + sim[i] = false; + } uint32_t prng = SEED; for (lfs_size_t i = 0; i < N; i++) { - // choose a pseudo-random mid - lfs_ssize_t mid = TEST_PRNG(&prng) % lfs_max32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - // fetch mdir - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - // limit our mid to our mdir's weight - mdir.mid = lfs_max32( - lfsr_mid_bid(&lfs, mdir.mid)-(lfsr_mweight(&lfs)-1) - + (mdir.mid % (mdir.rbyd.weight+1)), - 1); + // choose a pseudo-random name + lfs_size_t x = TEST_PRNG(&prng) % N; + // update sim + sim[x] = true; + + // update mtree + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", x); + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (!err) { + continue; + } // force a compaction? if (FORCE_COMPACTION) { - mdir.rbyd.eoff = -1; lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; } - - // add to rbyd, potentially splitting the mdir lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, - BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(REG, +1, BUF(name, 4)))) => 0; - // make sure we can look up the new entry - uint8_t buffer[4]; + // double check + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - - count += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } // try looking up each entry - lfs_size_t count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + uint8_t buffer[256]; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - - lfsr_unmount(&lfs) => 0; - - // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // try looking up each entry - count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - lfsr_unmount(&lfs) => 0; ''' @@ -622,47 +577,52 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined and split assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // remove the entry, forcing the mdir to be dropped - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // remove an entry, forcing the mdir to be dropped + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(0, RM, -1, NULL()))) => 0; + LFSR_ATTR(RM, -1, NULL()))) => 0; // assert mdir was dropped assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdir was dropped @@ -670,10 +630,12 @@ code = ''' // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' @@ -688,50 +650,54 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined and split assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // remove the entry, forcing the mdir to be dropped - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // remove an entry, forcing the mdir to be dropped + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; // force mdir to compact while we're removing mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(0, RM, -1, NULL()))) => 0; + LFSR_ATTR(RM, -1, NULL()))) => 0; // assert mdir was dropped assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdir was dropped @@ -739,15 +705,17 @@ code = ''' // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_drop_uninline] +[cases.test_mtree_drop_uninline_split] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' in = 'lfs.c' @@ -757,48 +725,59 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mroot to compact + // remove an entry, forcing the mdir to be dropped + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; + // force mdir to compact while we're removing lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; - // remove the entry as we compact, forcing the mdir to be dropped - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, RM, -1, NULL()))) => 0; - - // assert mdir was dropped + // assert split/drop worked out assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - // assert mroot has no entries + // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdir was dropped assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - // assert mroot has no entries + // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' @@ -813,84 +792,86 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined and split assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // now add another large entry to the mdir, forcing a split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - memset(buffer, 'c', SIZE); + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mdir to compact + // remove an entry, forcing the mdir to be dropped + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; + // force mdir to compact while we're removing mdir.rbyd.eoff = -1; - - // remove the left entry as we compact, forcing the left - // mdir to be dropped - mdir.mid = 1*lfsr_mweight(&lfs)+0; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; + LFSR_ATTR(RM, -1, NULL()))) => 0; - // assert mdir was dropped + // assert split/drop worked out assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - // assert mroot has no entries + // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that one entry is still in the mtree - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - lfsr_unmount(&lfs) => 0; + assert(buffer[1] == 'a'); + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert mdir was dropped + // assert split/drop worked out assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - // assert mroot has no entries + // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that one entry is still in the mtree - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'a'); + + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); lfsr_unmount(&lfs) => 0; ''' @@ -905,159 +886,86 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined and split assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // now add another large entry to the mdir, forcing a split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - memset(buffer, 'c', SIZE); + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mdir to compact + // remove an entry, forcing the mdir to be dropped + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; + // force mdir to compact while we're removing mdir.rbyd.eoff = -1; - - // remove the right entry as we compact, forcing the right - // mdir to be dropped lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; + LFSR_ATTR(RM, -1, NULL()))) => 0; - // assert mdir was dropped + // assert split/drop worked out assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - // assert mroot has no entries + // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that one entry is still in the mtree - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); - lfsr_unmount(&lfs) => 0; - - - // check things stay sane after remount - lfsr_mount(&lfs, CFG) => 0; - - // assert mdir was dropped - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - // assert mroot has no entries - assert(lfs.mroot.rbyd.weight == 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that one entry is still in the mtree lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_drop_split_both] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - // create an uninlined mdir - uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mroot to compact - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - - // assert mdir was unininlined correctly - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - // assert mroot now has no entries - assert(lfs.mroot.rbyd.weight == 0); - - // now add another large entry to the mdir, forcing a split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mdir to compact - mdir.rbyd.eoff = -1; - - // remove both entries as we compact, forcing both mdirs to be dropped - mdir.mid = 1*lfsr_mweight(&lfs)+0; - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()), - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; - - // assert mdir was dropped - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - // assert mroot has no entries - assert(lfs.mroot.rbyd.weight == 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert mdir was dropped - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - // assert mroot has no entries + // assert split/drop worked out + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); + + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' @@ -1068,139 +976,122 @@ defines.FORCE_COMPACTION = [false, true] defines.SEED = 'range(100)' in = 'lfs.c' code = ''' - const char *alphas = "abcdefghijklmnopqrstuvwxyz"; - lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // at least keep track of the number of entries we expect - lfs_size_t count = 0; + bool sim[N]; + for (lfs_size_t i = 0; i < N; i++) { + sim[i] = false; + } uint32_t prng = SEED; for (lfs_size_t i = 0; i < N; i++) { - // choose a pseudo-random mid - lfs_ssize_t mid = TEST_PRNG(&prng) % lfs_max32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - // fetch mdir - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - // limit our mid to our mdir's weight - mdir.mid = lfs_max32( - lfsr_mid_bid(&lfs, mdir.mid)-(lfsr_mweight(&lfs)-1) - + (mdir.mid % (mdir.rbyd.weight+1)), - 1); - // choose to create or delete, note we make sure to never delete to zero - uint8_t op = (lfsr_mid_rid(&lfs, mdir.mid) == mdir.rbyd.weight - || (lfsr_mid_rid(&lfs, mdir.mid) == mdir.rbyd.weight-1 - && lfsr_mtree_weight(&lfs) == lfsr_mweight(&lfs))) - ? 0 - : TEST_PRNG(&prng) % 2; - - // force a compaction? - if (FORCE_COMPACTION) { - mdir.rbyd.eoff = -1; - lfs.mroot.rbyd.eoff = -1; - } + // choose a pseudo-random name + lfs_size_t x = TEST_PRNG(&prng) % N; + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", x); + // choose to create or delete + uint8_t op = TEST_PRNG(&prng) % 2; // create if (op == 0) { - // add to rbyd, potentially splitting the mdir + // update sim + sim[x] = true; + + // update mtree + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (!err) { + continue; + } + // force a compaction? + if (FORCE_COMPACTION) { + lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; + } lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, - BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(REG, +1, BUF(name, 4)))) => 0; - // make sure we can look up the new entry - uint8_t buffer[4]; + // double check + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - - count += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); // delete } else { - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; + // update sim + sim[x] = false; - count -= 1; + // update mtree + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + continue; + } + // force a compaction? + if (FORCE_COMPACTION) { + lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; + } + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; } } // try looking up each entry - lfs_size_t count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - - // drop should make sure we never have empty mdirs - assert(lfsr_mtree_ismptr(&lfs) || mdir.rbyd.weight > 0); - - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + uint8_t buffer[256]; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - - lfsr_unmount(&lfs) => 0; - - // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // try looking up each entry - count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - - // drop should make sure we never have empty mdirs - assert(lfsr_mtree_ismptr(&lfs) || mdir.rbyd.weight > 0); - - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - lfsr_unmount(&lfs) => 0; ''' @@ -1220,87 +1111,79 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with a large attr so the next entry can not fit + // create a 2 large attrs that needs to be uninlined uint8_t buffer[SIZE]; memset(buffer, 'a', SIZE); lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - // create a large entry that needs to be uninlined (but not split!) memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mtree has one mdir + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); // force mdir to compact twice, this should relocate - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; lfsr_mdir_t old_mdir = mdir; - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - mdir.rbyd.eoff = -1; - memset(buffer, 'c', SIZE); lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; - + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - lfsr_unmount(&lfs) => 0; - - - // check things stay sane after remount - lfsr_mount(&lfs, CFG) => 0; - - // assert mtree has one mdir + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated - assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - - // assert that our attr is still in the mroot + // assert that our attrs are still in the mroot/mtree lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[0] == 'a'); - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[0] == 'b'); + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert mdirs were unininlined + assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our attrs are still in the mroot/mtree + lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + buffer, SIZE) => SIZE; + assert(buffer[0] == 'a'); + + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), + buffer, SIZE) => SIZE; + assert(buffer[0] == 'b'); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_relocate_sibling_l] +[cases.test_mtree_relocate_l] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' # make it so blocks relocate every two compacts @@ -1314,13 +1197,19 @@ code = ''' // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; @@ -1332,39 +1221,39 @@ code = ''' assert(lfs.mroot.rbyd.weight == 0); // force mdir to compact twice, this should relocate - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + memset(buffer+1, 'a', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; lfsr_mdir_t old_mdir = mdir; - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - mdir.rbyd.eoff = -1; - memset(buffer, 'c', SIZE); lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; - + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdirs were unininlined and split @@ -1372,26 +1261,23 @@ code = ''' // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated - assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_relocate_sibling_r] +[cases.test_mtree_relocate_r] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' # make it so blocks relocate every two compacts @@ -1405,13 +1291,19 @@ code = ''' // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; @@ -1423,39 +1315,39 @@ code = ''' assert(lfs.mroot.rbyd.weight == 0); // force mdir to compact twice, this should relocate - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; lfsr_mdir_t old_mdir = mdir; - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - mdir.rbyd.eoff = -1; - memset(buffer, 'c', SIZE); lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, 0, BUF(buffer, SIZE)))) => 0; - + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdirs were unininlined and split @@ -1463,21 +1355,18 @@ code = ''' // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated - assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' @@ -1494,43 +1383,42 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with an attr + // prepare mroot with an entry uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact twice, this should extend the mroot lfsr_mdir_t old_mroot = lfs.mroot; - lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entry is still in the mroot + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert we relocated - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' @@ -1541,7 +1429,7 @@ defines.SIZE = 'BLOCK_SIZE / 4' # make it so blocks relocate every two compacts defines.BLOCK_CYCLES = 2 # force our block to compact by setting prog_size=block_size, we don't have -# any way to indirectly force the intermediary mroots to compact otherwise +# an easy way to force the intermediary mroots to compact otherwise defines.PROG_SIZE = 'BLOCK_SIZE' in = 'lfs.c' code = ''' @@ -1550,45 +1438,56 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with an attr + // prepare mroot with an entry uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mroot to compact 2x2 times, this should extend the mroot twice + // force mroot to compact twice, this should extend the mroot lfsr_mdir_t old_mroot = lfs.mroot; - - for (int i = 0; i < 4; i++) { - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - } lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + // assert we relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); + // force mroot to compact four times, this should relocate the mroot + // twice, forcing a second mroot extension + old_mroot = lfs.mroot; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert we relocated - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' @@ -1605,54 +1504,51 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with an attr + // prepare mroot with an entry uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact twice, this should extend the mroot lfsr_mdir_t old_mroot = lfs.mroot; - lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); // force mroot to compact twice again, this should relocate the mroot old_mroot = lfs.mroot; - lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entry is still in the mroot + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert we relocated - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' @@ -1669,93 +1565,89 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with a large attr so the next entry can not fit + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // create a large entry that needs to be uninlined (but not split!) - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mtree has one mdir - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // setup mroot to need to compact, this should trigger a relocation when - // we relocate the mdir below + // setup mroot to compact and relocate on next commit lfs.mroot.rbyd.eoff = -1; lfsr_mdir_t old_mroot = lfs.mroot; - // force mdir to compact twice, this should relocate - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; lfsr_mdir_t old_mdir = mdir; - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - mdir.rbyd.eoff = -1; - memset(buffer, 'c', SIZE); lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; - - // assert we relocated our mdir + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + // assert we relocated assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - - // assert we relocated our mroot + // assert mroot relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - lfsr_unmount(&lfs) => 0; - - - // check things stay sane after remount - lfsr_mount(&lfs, CFG) => 0; - - // assert mtree has one mdir - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mdir - assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'b'); + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'a'); + + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' @@ -1772,100 +1664,105 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // setup mroot to need to compact, this should trigger a relocation when - // we relocate the mdir below + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // setup mroot to compact and relocate on next commit lfs.mroot.rbyd.eoff = -1; lfsr_mdir_t old_mroot = lfs.mroot; - - // now add another large entry to the mdir, forcing a split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mdir to compact + // force mdir to compact twice, this should relocate + lfsr_mdir_t old_mdir = mdir; mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + // assert we relocated + assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); + // assert mroot relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); // assert mdir was split correctly - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - lfsr_unmount(&lfs) => 0; + assert(buffer[1] == 'b'); + lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdir was split correctly - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); + assert(buffer[1] == 'b'); + + lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); lfsr_unmount(&lfs) => 0; ''' @@ -1882,55 +1779,63 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined and split assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // setup mroot to need to compact, this should trigger a relocation when - // we relocate the mdir below + // setup mroot to compact and relocate on next commit lfs.mroot.rbyd.eoff = -1; lfsr_mdir_t old_mroot = lfs.mroot; - - // remove the entry, forcing the mdir to be dropped - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // remove an entry, forcing the mdir to be dropped + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; + // force mdir to compact twice, this should relocate + mdir.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + // force mdir to compact while we're removing + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; + // assert mroot relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); // assert mdir was dropped assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'a'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdir was dropped @@ -1938,13 +1843,12 @@ code = ''' // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; ''' @@ -1961,75 +1865,65 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // force mroot to compact once, so the second compact below will trigger - // a relocation + // force mroot to compact once, so the second compact below will + // trigger a relocation lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // prepare mroot with a large attr so the next entry can not fit + // create a 2 large attrs that needs to be uninlined uint8_t buffer[SIZE]; memset(buffer, 'a', SIZE); lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - // create a large entry that needs to be uninlined (but not split!) memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - // force mroot to compact, this should trigger a relocation + // force mroot to compact, this should both uninline and relocate lfsr_mdir_t old_mroot = lfs.mroot; lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + // assert mroot relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert mdir was unininlined correctly + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot + // assert that our attrs are still in the mroot/mtree lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[0] == 'a'); - // assert that our entry is still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[0] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot + // assert that our attrs are still in the mroot/mtree lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[0] == 'a'); - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[0] == 'b'); lfsr_unmount(&lfs) => 0; ''' @@ -2046,52 +1940,54 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // force mroot to compact once, so the second compact below will trigger - // a relocation + // force mroot to compact once, so the second compact below will + // trigger a relocation lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mroot to compact, this should trigger a relocation + // force mroot to compact, this should both split and relocate lfsr_mdir_t old_mroot = lfs.mroot; lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + // assert mroot relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); // assert mdirs were unininlined and split assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our entries are still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - lfsr_unmount(&lfs) => 0; - + assert(buffer[1] == 'b'); // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // assert mdirs were unininlined and split @@ -2099,26 +1995,23 @@ code = ''' // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert we relocated our mroot - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; assert(mdir.rbyd.weight == 1); lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' -# this fuzz covers a lot of configuratinos +# this fuzz covers a lot of configurations [cases.test_mtree_relocate_fuzz] defines.N = [5, 10, 20, 40] defines.FORCE_COMPACTION = [false, true] @@ -2126,159 +2019,157 @@ defines.BLOCK_CYCLES = [5, 2, 1] defines.SEED = 'range(500)' in = 'lfs.c' code = ''' - const char *alphas = "abcdefghijklmnopqrstuvwxyz"; - lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // at least keep track of the number of entries we expect - lfs_size_t count = 0; + bool sim[N]; + for (lfs_size_t i = 0; i < N; i++) { + sim[i] = false; + } uint32_t prng = SEED; for (lfs_size_t i = 0; i < N; i++) { - // choose a pseudo-random mid - lfs_ssize_t mid = TEST_PRNG(&prng) % lfs_max32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - // fetch mdir - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - // limit our mid to our mdir's weight - mdir.mid = lfs_max32( - lfsr_mid_bid(&lfs, mdir.mid)-(lfsr_mweight(&lfs)-1) - + (mdir.mid % (mdir.rbyd.weight+1)), - 1); - // choose to create or delete, note we make sure to never delete to zero - uint8_t op = (lfsr_mid_rid(&lfs, mdir.mid) == mdir.rbyd.weight - || (lfsr_mid_rid(&lfs, mdir.mid) == mdir.rbyd.weight-1 - && lfsr_mtree_weight(&lfs) == lfsr_mweight(&lfs))) - ? 0 - : TEST_PRNG(&prng) % 3; - - // force a compaction? - if (FORCE_COMPACTION) { - mdir.rbyd.eoff = -1; - lfs.mroot.rbyd.eoff = -1; - } + // choose a pseudo-random name + lfs_size_t x = TEST_PRNG(&prng) % N; + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", x); + // choose to create or delete + uint8_t op = TEST_PRNG(&prng) % 2; // create if (op == 0) { - // add to rbyd + // update sim + sim[x] = true; + + // update mtree + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (!err) { + continue; + } + // force a compaction? + if (FORCE_COMPACTION) { + lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; + } lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, - BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(REG, +1, BUF(name, 4)))) => 0; - // make sure we can look up the new entry - uint8_t buffer[4]; + // double check + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - - count += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); // update } else if (op == 1) { - // update rbyd - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, 0, - BUF(&alphas[i % 26], 1)))) => 0; + // sim update is a noop - // make sure we can look up the new entry - uint8_t buffer[4]; + // update mtree + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + continue; + } + // force a compaction? + if (FORCE_COMPACTION) { + lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; + } + // we can't really change metadata names, but commits still + // trigger writes to the mdir + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(name, 4)))) => 0; + + // double check + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); // delete } else { - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; + // update sim + sim[x] = false; - count -= 1; + // update mtree + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + continue; + } + // force a compaction? + if (FORCE_COMPACTION) { + lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; + } + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; } } // try looking up each entry - lfs_size_t count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - - // drop should make sure we never have empty mdirs - assert(lfsr_mtree_ismptr(&lfs) || mdir.rbyd.weight > 0); - - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + uint8_t buffer[256]; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - - lfsr_unmount(&lfs) => 0; - - // check things stay sane after remount + lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // try looking up each entry - count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - - // drop should make sure we never have empty mdirs - assert(lfsr_mtree_ismptr(&lfs) || mdir.rbyd.weight > 0); - - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - lfsr_unmount(&lfs) => 0; ''' -## Neighboring mdir updates ## +## Opened mdir tracking ## -[cases.test_mtree_neighbor] +[cases.test_mtree_opened] in = 'lfs.c' code = ''' lfs_t lfs; @@ -2287,202 +2178,147 @@ code = ''' lfs_alloc_ckpoint(&lfs); // setup our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF("a", 1)), - LFSR_ATTR(2, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mroot - assert(lfsr_mtree_ismptr(&lfs)); + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=1, .rbyd=lfs.mroot.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=2, .rbyd=lfs.mroot.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "c", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0c", 2)))) => 0; + lfsr_addopened(&lfs, &right); // insert a new entry, this should update our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(2, REG, +1, BUF("c", 1)))) => 0; - - // assert that our entry is still in the mtree - assert(lfs.mroot.rbyd.weight == 4); - - uint8_t buffer[1]; - lfsr_mdir_get(&lfs, &lfs.mroot, 2, LFSR_TAG_REG, - buffer, 1) => 1; - assert(memcmp(buffer, "c", 1) == 0); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 1); - assert(memcmp(&left_neighbor.mdir.rbyd, &lfs.mroot.rbyd, - sizeof(lfs.mroot.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 3); - assert(memcmp(&right_neighbor.mdir.rbyd, &lfs.mroot.rbyd, - sizeof(lfs.mroot.rbyd)) == 0); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_remove_l] -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - // setup our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF("a", 1)), - LFSR_ATTR(2, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mroot - assert(lfsr_mtree_ismptr(&lfs)); - - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=1, .rbyd=lfs.mroot.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=2, .rbyd=lfs.mroot.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // try removing our left entry - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, RM, -1, NULL()))) => 0; - - // assert that an entry was removed - assert(lfs.mroot.rbyd.weight == 2); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 1); - assert(right_neighbor.mdir.mid == 1); - assert(memcmp(&right_neighbor.mdir.rbyd, &lfs.mroot.rbyd, - sizeof(lfs.mroot.rbyd)) == 0); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_remove_r] -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - // setup our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF("a", 1)), - LFSR_ATTR(2, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mroot - assert(lfsr_mtree_ismptr(&lfs)); - - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=1, .rbyd=lfs.mroot.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=2, .rbyd=lfs.mroot.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // try removing our right entry - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(2, RM, -1, NULL()))) => 0; - - // assert that an entry was removed - assert(lfs.mroot.rbyd.weight == 2); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 1); - assert(memcmp(&left_neighbor.mdir.rbyd, &lfs.mroot.rbyd, - sizeof(lfs.mroot.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 2); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_uninline] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - // setup our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF("a", 1)), - LFSR_ATTR(1, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mroot - assert(lfsr_mtree_ismptr(&lfs)); - - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=0, .rbyd=lfs.mroot.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=1, .rbyd=lfs.mroot.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // prepare mroot with a large attr so the next entry can not fit - uint8_t buffer[SIZE]; - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - - // create a large entry that needs to be uninlined (but not split!) - memset(buffer, 'd', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mroot to compact - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - - // assert mdir was unininlined correctly - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - // assert mroot now has no entries - assert(lfs.mroot.rbyd.weight == 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - // assert that our entry is still in the mtree lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 2); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "d", 1) == 0); + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0b", 2)))) => 0; - // note that our current implementation splits here, which is suboptimal - // but saves on code size - lfsr_mdir_t msibling; - lfsr_mtree_lookup(&lfs, - 1*lfsr_mweight(&lfs)+0, &msibling) => 0; - assert(msibling.rbyd.weight == 1); + // assert our entry was created + uint8_t buffer[2]; + lfsr_mdir_get(&lfs, &mdir, 2, LFSR_TAG_REG, + buffer, 2) => 2; + assert(buffer[1] == 'b'); // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+0); - assert(memcmp(&left_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 1*lfsr_mweight(&lfs)+0); - assert(memcmp(&right_neighbor.mdir.rbyd, &msibling.rbyd, - sizeof(msibling.rbyd)) == 0); + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "c", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_neighbor_uninline_split] +[cases.test_mtree_opened_remove_l] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0b", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // try removing left neighbor + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; + + // assert neighbor was removed + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + + // assert that our neighbors were updated correctly + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_remove_r] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0b", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // try removing right neighbor + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; + + // assert neighbor was removed + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + + // assert that our neighbors were updated correctly + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_uninline_split] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' in = 'lfs.c' @@ -2493,28 +2329,35 @@ code = ''' lfs_alloc_ckpoint(&lfs); // setup our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF("a", 1)), - LFSR_ATTR(1, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mroot - assert(lfsr_mtree_ismptr(&lfs)); + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=0, .rbyd=lfs.mroot.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=1, .rbyd=lfs.mroot.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "d", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0d", 2)))) => 0; + lfsr_addopened(&lfs, &right); // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'b', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'd', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(2, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; @@ -2525,36 +2368,26 @@ code = ''' // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our entries are still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 2); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - lfsr_mdir_t msibling; - lfsr_mtree_lookup(&lfs, - 1*lfsr_mweight(&lfs)+0, &msibling) => 0; - assert(msibling.rbyd.weight == 2); - lfsr_mdir_get(&lfs, &msibling, msibling.mid, LFSR_TAG_REG, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "d", 1) == 0); - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+0); - assert(memcmp(&left_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 1*lfsr_mweight(&lfs)+1); - assert(memcmp(&right_neighbor.mdir.rbyd, &msibling.rbyd, - sizeof(msibling.rbyd)) == 0); + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "d", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_neighbor_split] +[cases.test_mtree_opened_split] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' in = 'lfs.c' @@ -2564,426 +2397,482 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // create an uninlined mdir - uint8_t buffer[SIZE]; - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); - memset(buffer, 'd', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "e", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0e", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // create 2 large entries that needs to be uninlined and split + uint8_t buffer[SIZE]; + buffer[0] = '\0'; + memset(buffer+1, 'b', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + memset(buffer+1, 'd', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - // assert mroot now has no entries - assert(lfs.mroot.rbyd.weight == 0); - - // setup our neighbors - // - // note we do this after uninlining! this is because uninlining may - // aggresively split the mtree if there are already neighbors in the mdir - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid+0, REG, +1, BUF("a", 1)), - LFSR_ATTR(mdir.mid+2, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mdir - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - assert(mdir.rbyd.weight == 3); - - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=mdir.mid+0, .rbyd=mdir.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=mdir.mid+2, .rbyd=mdir.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // now add another large entry to the mdir, forcing a split - memset(buffer, 'e', SIZE); - mdir.mid = 0*lfsr_mweight(&lfs)+2; - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mdir to compact mdir.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our neighbors were updated correctly + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "e", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_extend] +# make it so blocks relocate every two compacts +defines.BLOCK_CYCLES = 2 +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0b", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // force mroot to compact twice, this should extend the mroot + lfsr_mdir_t old_mroot = lfs.mroot; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + // assert we relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); + + // assert that our neighbors were updated correctly + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "b", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_relocate_l] +# this should be set so only one entry can fit in a metadata block +defines.SIZE = 'BLOCK_SIZE / 4' +# make it so blocks relocate every two compacts +defines.BLOCK_CYCLES = 2 +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "d", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0d", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // create 2 large entries that needs to be uninlined and split + uint8_t buffer[SIZE]; + buffer[0] = '\0'; + memset(buffer+1, 'b', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mroot to compact + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // force mdir to compact twice, this should relocate + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_t old_mdir = mdir; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + // assert we relocated + assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); + + // assert that our neighbors were updated correctly + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "d", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_relocate_r] +# this should be set so only one entry can fit in a metadata block +defines.SIZE = 'BLOCK_SIZE / 4' +# make it so blocks relocate every two compacts +defines.BLOCK_CYCLES = 2 +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "d", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0d", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // create 2 large entries that needs to be uninlined and split + uint8_t buffer[SIZE]; + buffer[0] = '\0'; + memset(buffer+1, 'b', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mroot to compact + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // force mdir to compact twice, this should relocate + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_t old_mdir = mdir; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, 0, BUF(buffer, SIZE)))) => 0; + // assert we relocated + assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); + + // assert that our neighbors were updated correctly + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "d", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_middle_split] +# this should be set so only one entry can fit in a metadata block +defines.SIZE = 'BLOCK_SIZE / 4' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "f", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0f", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // create 2 large entries that needs to be uninlined and split + uint8_t buffer[SIZE]; + buffer[0] = '\0'; + memset(buffer+1, 'b', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + memset(buffer+1, 'e', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mroot to compact + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mdir to compact + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; + + // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // now add _another_ large entry to the middle mdir, forcing another split + memset(buffer+1, 'd', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mdir to compact + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; + + // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 4*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our neighbors were updated correctly + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "f", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); + + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_opened_middle_drop] +# this should be set so only one entry can fit in a metadata block +defines.SIZE = 'BLOCK_SIZE / 4' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // setup our neighbors + lfsr_opened_t left = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &left.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &left.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + lfsr_addopened(&lfs, &left); + + lfsr_opened_t right = {.type=0}; + lfsr_mtree_namelookup(&lfs, 0, "e", 1, + &right.mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &right.mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0e", 2)))) => 0; + lfsr_addopened(&lfs, &right); + + // create 2 large entries that needs to be uninlined and split + uint8_t buffer[SIZE]; + buffer[0] = '\0'; + memset(buffer+1, 'b', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + memset(buffer+1, 'd', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mroot to compact + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mdir to compact + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; + + // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // now remove the middle entry, forcing a drop + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(RM, -1, NULL()))) => 0; + + // assert mdir was dropped correctly assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); // assert mroot still has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 2); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "d", 1) == 0); - - lfsr_mdir_t msibling; - lfsr_mtree_lookup(&lfs, - 1*lfsr_mweight(&lfs)+0, &msibling) => 0; - assert(msibling.rbyd.weight == 2); - lfsr_mdir_get(&lfs, &msibling, msibling.mid, LFSR_TAG_REG, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "e", 1) == 0); - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+0); - assert(memcmp(&left_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 1*lfsr_mweight(&lfs)+1); - assert(memcmp(&right_neighbor.mdir.rbyd, &msibling.rbyd, - sizeof(msibling.rbyd)) == 0); + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + assert(left.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&left.mdir, &mdir) == 0); + assert(left.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(left.mdir.rbyd.cksum == mdir.rbyd.cksum); + lfsr_mtree_namelookup(&lfs, 0, "e", 1, + &mdir, NULL, NULL) => 0; + assert(right.mdir.mid == mdir.mid); + assert(lfsr_mdir_cmp(&right.mdir, &mdir) == 0); + assert(right.mdir.rbyd.trunk == mdir.rbyd.trunk); + assert(right.mdir.rbyd.cksum == mdir.rbyd.cksum); - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_extend] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' -# make it so blocks relocate every two compacts -defines.BLOCK_CYCLES = 2 -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - // setup our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF("a", 1)), - LFSR_ATTR(1, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mroot - assert(lfsr_mtree_ismptr(&lfs)); - - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=0, .rbyd=lfs.mroot.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=1, .rbyd=lfs.mroot.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // prepare mroot with an attr - uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - - // force mroot to compact twice, this should extend the mroot - lfsr_mdir_t old_mroot = lfs.mroot; - - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - - // assert we relocated - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0); - assert(memcmp(&left_neighbor.mdir.rbyd, &lfs.mroot.rbyd, - sizeof(lfs.mroot.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 1); - assert(memcmp(&right_neighbor.mdir.rbyd, &lfs.mroot.rbyd, - sizeof(lfs.mroot.rbyd)) == 0); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_relocate] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' -# make it so blocks relocate every two compacts -defines.BLOCK_CYCLES = 2 -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - // create an uninlined mdir - uint8_t buffer[SIZE]; - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - - memset(buffer, 'd', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; - - // force mroot to compact - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - - // assert mdir was unininlined correctly - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - // assert mroot now has no entries - assert(lfs.mroot.rbyd.weight == 0); - - // setup our neighbors - // - // note we do this after uninlining! this is because uninlining may - // aggresively split the mtree if there are already neighbors in the mdir - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid+0, REG, +1, BUF("a", 1)), - LFSR_ATTR(mdir.mid+2, REG, +1, BUF("b", 1)))) => 0; - // this test only works if these all fit in the mdir - assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); - assert(mdir.rbyd.weight == 3); - - lfsr_opened_t left_neighbor = { - .type=0, .mdir={.mid=mdir.mid+0, .rbyd=mdir.rbyd}}; - lfsr_opened_t right_neighbor = { - .type=0, .mdir={.mid=mdir.mid+2, .rbyd=mdir.rbyd}}; - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // force mdir to compact twice, this should relocate - lfsr_mdir_t old_mdir = mdir; - - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - mdir.rbyd.eoff = -1; - memset(buffer, 'e', SIZE); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(1, REG, 0, BUF(buffer, SIZE)))) => 0; - - // assert we relocated - assert(lfsr_mdir_cmp(&old_mdir, &mdir) != 0); - - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "c", 1) == 0); - - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 3); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "e", 1) == 0); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+0); - assert(memcmp(&left_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - assert(right_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+2); - assert(memcmp(&right_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_middle_split] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' -# make it so blocks relocate every two compacts -defines.BLOCK_CYCLES = 2 -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - //// create a situation where we have 3 mdirs in our tree - - // first force mroot to uninlined+split - uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mroot to compact - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - - // we should now have 2 mdirs - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - - // now force one of our siblings to split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mdir to compact - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - - // we should now have 3 mdirs - assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); - - - //// Now test splitting updates mids correctly - - // setup our neighbors - lfsr_opened_t left_neighbor = {.type=0}; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, - &left_neighbor.mdir) => 0; - assert(left_neighbor.mdir.rbyd.weight == 1); - - lfsr_opened_t right_neighbor = {.type=0}; - lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, - &right_neighbor.mdir) => 0; - assert(right_neighbor.mdir.rbyd.weight == 1); - - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // cause middle mdir to split - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - memset(buffer, 'd', SIZE); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mdir to compact - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - - // we should now have 4 mdirs - assert(lfsr_mtree_weight(&lfs) == 4*lfsr_mweight(&lfs)); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+0); - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(memcmp(&left_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - - assert(right_neighbor.mdir.mid == 3*lfsr_mweight(&lfs)+0); - lfsr_mtree_lookup(&lfs, 3*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(memcmp(&right_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_mtree_neighbor_middle_drop] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' -# make it so blocks relocate every two compacts -defines.BLOCK_CYCLES = 2 -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfs_alloc_ckpoint(&lfs); - - //// create a situation where we have 3 mdirs in our tree - - // first force mroot to uninlined+split - uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mroot to compact - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - - // we should now have 2 mdirs - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - - // now force one of our siblings to split - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+1, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - memset(buffer, 'c', SIZE); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, BUF(buffer, SIZE)))) => 0; - - // force mdir to compact - mdir.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; - - // we should now have 3 mdirs - assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); - - - //// Now test dropping updates mids correctly - - // setup our neighbors - lfsr_opened_t left_neighbor = {.type=0}; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, - &left_neighbor.mdir) => 0; - assert(left_neighbor.mdir.rbyd.weight == 1); - - lfsr_opened_t right_neighbor = {.type=0}; - lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, - &right_neighbor.mdir) => 0; - assert(right_neighbor.mdir.rbyd.weight == 1); - - lfsr_addopened(&lfs, &left_neighbor); - lfsr_addopened(&lfs, &right_neighbor); - - // cause middle mdir to drop - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, RM, -1, NULL()))) => 0; - - // we should now have 2 mdirs - assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); - - // assert that our neighbors were updated correctly - assert(left_neighbor.mdir.mid == 0*lfsr_mweight(&lfs)+0); - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(memcmp(&left_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - - assert(right_neighbor.mdir.mid == 1*lfsr_mweight(&lfs)+0); - lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(memcmp(&right_neighbor.mdir.rbyd, &mdir.rbyd, - sizeof(mdir.rbyd)) == 0); - - lfsr_removeopened(&lfs, &left_neighbor); - lfsr_removeopened(&lfs, &right_neighbor); + lfsr_removeopened(&lfs, &left); + lfsr_removeopened(&lfs, &right); lfsr_unmount(&lfs) => 0; ''' @@ -3000,17 +2889,12 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // insert a new entry, this should update our neighbors - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF("a", 1)))) => 0; - - // assert that our entry is still in the mtree - assert(lfs.mroot.rbyd.weight == 1); - - uint8_t buffer[1]; - lfsr_mdir_get(&lfs, &lfs.mroot, 0, LFSR_TAG_BOOKMARK, - buffer, 1) => 1; - assert(memcmp(buffer, "a", 1) == 0); + // insert at least one entry + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; // test that we can traverse the tree, keeping track of all blocks we see uint8_t *seen = malloc((BLOCK_COUNT+7)/8); @@ -3071,19 +2955,31 @@ code = ''' // and the tree should still work // assert that our entry is still in the mtree - assert(lfs.mroot.rbyd.weight == 1); + uint8_t buffer[256]; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 2; + assert(memcmp(buffer, "\0a", 2) == 0); - lfsr_mdir_get(&lfs, &lfs.mroot, 0, LFSR_TAG_BOOKMARK, - buffer, 1) => 1; - assert(memcmp(buffer, "a", 1) == 0); + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert that our entry is still in the mtree + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 2; + assert(memcmp(buffer, "\0a", 2) == 0); lfsr_unmount(&lfs) => 0; ''' [cases.test_mtree_traversal_uninline] +defines.VALIDATE = [false, true] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' -defines.VALIDATE = [false, true] in = 'lfs.c' code = ''' lfs_t lfs; @@ -3091,40 +2987,27 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with a large attr so the next entry can not fit + // create a 2 large attrs that needs to be uninlined uint8_t buffer[SIZE]; memset(buffer, 'a', SIZE); lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - // create a large entry that needs to be uninlined (but not split!) memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(UATTR(1), 0, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - // assert mdir was unininlined correctly + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - // assert that our entry is still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - // test that we can traverse the tree, keeping track of all blocks we see uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); @@ -3183,31 +3066,49 @@ code = ''' // and the tree should still work - // assert mdir was unininlined correctly + // assert mdirs were unininlined assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot + // assert that our attrs are still in the mroot/mtree lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[0] == 'a'); - // assert that our entry is still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; assert(mdir.rbyd.weight == 1); - - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[0] == 'b'); + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert mdirs were unininlined + assert(lfsr_mtree_weight(&lfs) == 1*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our attrs are still in the mroot/mtree + lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + buffer, SIZE) => SIZE; + assert(buffer[0] == 'a'); + + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_UATTR(1), + buffer, SIZE) => SIZE; + assert(buffer[0] == 'b'); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_traversal_split] +[cases.test_mtree_traversal_uninline_split] +defines.VALIDATE = [false, true] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' -defines.VALIDATE = [false, true] in = 'lfs.c' code = ''' lfs_t lfs; @@ -3217,13 +3118,19 @@ code = ''' // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(0, BOOKMARK, 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(1, REG, +1, BUF(buffer, SIZE)))) => 0; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact lfs.mroot.rbyd.eoff = -1; @@ -3234,22 +3141,6 @@ code = ''' // assert mroot now has no entries assert(lfs.mroot.rbyd.weight == 0); - // assert that our entries are still in the mtree - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); - - lfsr_mdir_t msibling; - lfsr_mtree_lookup(&lfs, - 1*lfsr_mweight(&lfs)+0, &msibling) => 0; - assert(msibling.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &msibling, msibling.mid, LFSR_TAG_REG, - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); - // test that we can traverse the tree, keeping track of all blocks we see uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); @@ -3314,28 +3205,47 @@ code = ''' assert(lfs.mroot.rbyd.weight == 0); // assert that our entries are still in the mtree - lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+0, &mdir) => 0; - assert(mdir.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "a", 1) == 0); + assert(buffer[1] == 'a'); - lfsr_mtree_lookup(&lfs, - 1*lfsr_mweight(&lfs)+0, &msibling) => 0; - assert(msibling.rbyd.weight == 1); - lfsr_mdir_get(&lfs, &msibling, msibling.mid, LFSR_TAG_REG, + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'b'); + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'a'); + + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'b'); lfsr_unmount(&lfs) => 0; ''' -[cases.test_mtree_traversal_extend] +[cases.test_mtree_traversal_split] +defines.VALIDATE = [false, true] # this should be set so only one entry can fit in a metadata block defines.SIZE = 'BLOCK_SIZE / 4' -# make it so blocks relocate every two compacts -defines.BLOCK_CYCLES = 2 -defines.VALIDATE = [false, true] in = 'lfs.c' code = ''' lfs_t lfs; @@ -3343,29 +3253,46 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with an attr + // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mroot to compact twice, this should extend the mroot - lfsr_mdir_t old_mroot = lfs.mroot; + memset(buffer+1, 'b', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + // force mroot to compact lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - // assert we relocated - assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); + // assert mdirs were unininlined and split + assert(lfsr_mtree_weight(&lfs) == 2*lfsr_mweight(&lfs)); + // assert mroot now has no entries + assert(lfs.mroot.rbyd.weight == 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + // now add another large entry to an mdir, forcing a split + memset(buffer+1, 'c', SIZE-1); + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; + + // force mdir to compact + mdir.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &mdir, NULL, 0) => 0; + + // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); // test that we can traverse the tree, keeping track of all blocks we see uint8_t *seen = malloc((BLOCK_COUNT+7)/8); @@ -3425,13 +3352,164 @@ code = ''' // and the tree should still work + // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'a'); + + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'b'); + + lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert mdir was split correctly + assert(lfsr_mtree_weight(&lfs) == 3*lfsr_mweight(&lfs)); + // assert mroot still has no entries + assert(lfs.mroot.rbyd.weight == 0); + + // assert that our entries are still in the mtree + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'a'); + + lfsr_mtree_lookup(&lfs, 1*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'b'); + + lfsr_mtree_lookup(&lfs, 2*lfsr_mweight(&lfs)+0, &mdir) => 0; + assert(mdir.rbyd.weight == 1); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, SIZE) => SIZE; + assert(buffer[1] == 'c'); + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_mtree_traversal_extend] +defines.VALIDATE = [false, true] +# make it so blocks relocate every two compacts +defines.BLOCK_CYCLES = 2 +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfs_alloc_ckpoint(&lfs); + + // insert at least one entry + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF("\0a", 2)))) => 0; + + // force mroot to compact twice, this should extend the mroot + lfsr_mdir_t old_mroot = lfs.mroot; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), - buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + // test that we can traverse the tree, keeping track of all blocks we see + uint8_t *seen = malloc((BLOCK_COUNT+7)/8); + memset(seen, 0, (BLOCK_COUNT+7)/8); + + lfsr_traversal_t traversal = LFSR_TRAVERSAL( + (VALIDATE) ? LFSR_TRAVERSAL_VALIDATE : 0); + for (lfs_block_t i = 0;; i++) { + // a bit hacky, but this catches infinite loops + assert(i < 2*BLOCK_COUNT); + + lfsr_tinfo_t tinfo; + int err = lfsr_traversal_read(&lfs, &traversal, &tinfo); + assert(!err || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + break; + } + + if (tinfo.tag == LFSR_TAG_MDIR) { + printf("traversal: 0x%x mdir 0x{%x,%x}\n", + tinfo.tag, + tinfo.u.mdir.rbyd.blocks[0], + tinfo.u.mdir.rbyd.blocks[1]); + + // keep track of seen blocks + seen[tinfo.u.mdir.rbyd.blocks[1] / 8] + |= 1 << (tinfo.u.mdir.rbyd.blocks[1] % 8); + seen[tinfo.u.mdir.rbyd.blocks[0] / 8] + |= 1 << (tinfo.u.mdir.rbyd.blocks[0] % 8); + + } else if (tinfo.tag == LFSR_TAG_BRANCH) { + printf("traversal: 0x%x btree 0x%x.%x\n", + tinfo.tag, + tinfo.u.rbyd.blocks[0], tinfo.u.rbyd.trunk); + + // keep track of seen blocks + seen[tinfo.u.rbyd.blocks[0] / 8] + |= 1 << (tinfo.u.rbyd.blocks[0] % 8); + + } else { + // this shouldn't happen + printf("traversal: 0x%x\n", tinfo.tag); + assert(false); + } + } + + // if traversal worked, we should be able to clobber all other blocks + uint8_t clobber_buf[BLOCK_SIZE]; + memset(clobber_buf, 0xcc, BLOCK_SIZE); + for (lfs_block_t block = 0; block < BLOCK_COUNT; block++) { + if (!(seen[block / 8] & (1 << (block % 8)))) { + CFG->erase(CFG, block) => 0; + CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0; + } + } + free(seen); + + // and the tree should still work + + // assert that our entry is still in the mtree + uint8_t buffer[256]; + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 2; + assert(memcmp(buffer, "\0a", 2) == 0); + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // assert that our entry is still in the mtree + lfsr_mtree_namelookup(&lfs, 0, "a", 1, + &mdir, NULL, NULL) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 2; + assert(memcmp(buffer, "\0a", 2) == 0); lfsr_unmount(&lfs) => 0; ''' @@ -3443,64 +3521,33 @@ defines.VALIDATE = [false, true] defines.FORCE_COMPACTION = [false, true] in = 'lfs.c' code = ''' - const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); // create entries - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, - lfs_smax32( - lfsr_mtree_weight(&lfs) - lfsr_mweight(&lfs), - 0), - &mdir) => 0; - mdir.mid += 1; for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; // force a compaction? if (FORCE_COMPACTION) { - mdir.rbyd.eoff = -1; lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; } - lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, - BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(REG, +1, BUF(name, 4)))) => 0; - uint8_t buffer[4]; + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - - mdir.mid += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } - // try looking up each entry - lfs_size_t i = 0; - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - i += 1; - } - } - assert(i == N); - // test that we can traverse the tree, keeping track of all blocks we see uint8_t *seen = malloc((BLOCK_COUNT+7)/8); memset(seen, 0, (BLOCK_COUNT+7)/8); @@ -3560,29 +3607,38 @@ code = ''' // and the tree should still work // try looking up each entry - i = 0; - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - i += 1; - } + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + uint8_t buffer[256]; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + lfsr_mtree_seek(&lfs, &mdir, +1) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } + + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // try looking up each entry + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + lfsr_mtree_seek(&lfs, &mdir, +1) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } - assert(i == N); lfsr_unmount(&lfs) => 0; ''' @@ -3594,80 +3650,50 @@ defines.FORCE_COMPACTION = [false, true] defines.SEED = 'range(100)' in = 'lfs.c' code = ''' - const char *alphas = "abcdefghijklmnopqrstuvwxyz"; - lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // at least keep track of the number of entries we expect - lfs_size_t count = 0; + bool sim[N]; + for (lfs_size_t i = 0; i < N; i++) { + sim[i] = false; + } uint32_t prng = SEED; for (lfs_size_t i = 0; i < N; i++) { - // choose a pseudo-random mid - lfs_ssize_t mid = TEST_PRNG(&prng) % lfs_max32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - // fetch mdir - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - // limit our mid to our mdir's weight - mdir.mid = lfs_max32( - lfsr_mid_bid(&lfs, mdir.mid)-(lfsr_mweight(&lfs)-1) - + (mdir.mid % (mdir.rbyd.weight+1)), - 1); + // choose a pseudo-random name + lfs_size_t x = TEST_PRNG(&prng) % N; + // update sim + sim[x] = true; + + // update mtree + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", x); + lfsr_mdir_t mdir; + int err = lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL); + assert(!err || err == LFS_ERR_NOENT); + if (!err) { + continue; + } // force a compaction? if (FORCE_COMPACTION) { - mdir.rbyd.eoff = -1; lfs.mroot.rbyd.eoff = -1; + mdir.rbyd.eoff = -1; } - - // add to rbyd, potentially splitting the mdir lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid, REG, +1, - BUF(&alphas[i % 26], 1)))) => 0; + LFSR_ATTR(REG, +1, BUF(name, 4)))) => 0; - // make sure we can look up the new entry - uint8_t buffer[4]; + // double check + uint8_t buffer[256]; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); - - count += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); } - // try looking up each entry - lfs_size_t count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; - lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; - } - } - - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); - // test that we can traverse the tree, keeping track of all blocks // we see uint8_t *seen = malloc((BLOCK_COUNT+7)/8); @@ -3728,33 +3754,50 @@ code = ''' // and the tree should still work // try looking up each entry - count_ = 0; - - for (lfs_ssize_t mid = 0; - mid < lfs_smax32( - lfsr_mtree_weight(&lfs), - lfsr_mweight(&lfs)); - mid += lfsr_mweight(&lfs)) { - lfsr_mdir_t mdir; - lfsr_mtree_lookup(&lfs, mid, &mdir) => 0; - for (; lfsr_mid_rid(&lfs, mdir.mid) < mdir.rbyd.weight; - mdir.mid += 1) { - // skip the root bookmark - if (mdir.mid == 0) { - continue; - } - - uint8_t buffer[4]; + lfsr_mdir_t mdir; + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + uint8_t buffer[256]; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, - buffer, 4) => 1; - - count_ += 1; + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; } } - // the mtree is a bit difficult to simulate, but we can at least test - // we ended up with the right number of entries - assert(count_ == count); + // check things stay sane after remount + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // try looking up each entry + lfsr_mtree_lookup(&lfs, 0, &mdir) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_BOOKMARK, + buffer, sizeof(buffer)) => 1; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + name[0] = '\0'; + sprintf(name+1, "%03x", i); + if (sim[i]) { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => 0; + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, + buffer, sizeof(buffer)) => 4; + assert(memcmp(buffer, name, 4) == 0); + } else { + lfsr_mtree_namelookup(&lfs, 0, (const char*)name+1, 3, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + } + } lfsr_unmount(&lfs) => 0; ''' @@ -3773,8 +3816,9 @@ code = ''' uint8_t buf[LFSR_MPTR_DSIZE]; lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, - MROOT, 0, FROMMPTR(&LFSR_MPTR_MROOTANCHOR(), buf)))) => 0; + LFSR_ATTR( + MROOT, 0, + FROMMPTR(&LFSR_MPTR_MROOTANCHOR(), buf)))) => 0; // technically, cycle detection only needs to work when we're validating lfsr_traversal_t traversal = LFSR_TRAVERSAL(LFSR_TRAVERSAL_VALIDATE); @@ -3815,8 +3859,6 @@ code = ''' # make sure our magic string ("littlefs") shows up in the same place (off=8) [cases.test_mtree_magic] -# this should be set so only one entry can fit in a metadata block -defines.SIZE = 'BLOCK_SIZE / 4' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -3845,33 +3887,34 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with an attr + // prepare mroot with an entry uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; // force mroot to compact twice, this should extend the mroot lfsr_mdir_t old_mroot = lfs.mroot; - lfs.mroot.rbyd.eoff = -1; lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; - + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); - // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + // assert that our entry is still in the mroot + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; - // check our magic string // // note if we lose power we may not have the magic string in both blocks! @@ -3899,35 +3942,48 @@ code = ''' lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ckpoint(&lfs); - // prepare mroot with an attr + // prepare mroot with an entry uint8_t buffer[SIZE]; - memset(buffer, 'a', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + buffer[0] = '\0'; + memset(buffer+1, 'a', SIZE-1); + lfsr_mdir_t mdir; + lfsr_mtree_namelookup(&lfs, 0, (const char*)buffer+1, SIZE-1, + &mdir, NULL, NULL) => LFS_ERR_NOENT; + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(REG, +1, BUF(buffer, SIZE)))) => 0; - // force mroot to compact 2x2 times, this should extend the mroot twice + // force mroot to compact twice, this should extend the mroot lfsr_mdir_t old_mroot = lfs.mroot; - - for (int i = 0; i < 4; i++) { - lfs.mroot.rbyd.eoff = -1; - lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; - } lfs.mroot.rbyd.eoff = -1; - memset(buffer, 'b', SIZE); - lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( - LFSR_ATTR(-1, UATTR(1), 0, BUF(buffer, SIZE)))) => 0; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + // assert we relocated + assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); + // force mroot to compact four times, this should relocate the mroot + // twice, forcing a second mroot extension + old_mroot = lfs.mroot; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; + lfs.mroot.rbyd.eoff = -1; + lfsr_mdir_commit(&lfs, &lfs.mroot, NULL, 0) => 0; // assert we relocated assert(lfsr_mdir_cmp(&old_mroot, &lfs.mroot) != 0); // assert that our attr is still in the mroot - lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1), + lfsr_mtree_lookup(&lfs, 0*lfsr_mweight(&lfs)+1, &mdir) => 0; + assert(mdir.rbyd.weight == 2); + lfsr_mdir_get(&lfs, &mdir, mdir.mid, LFSR_TAG_REG, buffer, SIZE) => SIZE; - assert(memcmp(buffer, "b", 1) == 0); + assert(buffer[1] == 'a'); lfsr_unmount(&lfs) => 0; - // check our magic string // // note if we lose power we may not have the magic string in both blocks!