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!