Reintroduced Brent's algorithm for cycle detection in lfsr_mount

This commit is contained in:
Christopher Haster
2023-04-27 00:41:24 -05:00
parent 6236f460a4
commit 85ebdd0881
3 changed files with 69 additions and 3 deletions
+27 -2
View File
@@ -4108,6 +4108,12 @@ typedef struct lfsr_mpair {
#define LFSR_MPAIR(block0, block1) ((lfsr_mpair_t){.blocks={block0, block1}})
static inline bool lfsr_mpair_eq(lfsr_mpair_t a, lfsr_mpair_t b) {
// allow either order
return (a.blocks[0] == b.blocks[0] && a.blocks[1] == b.blocks[1])
|| (a.blocks[0] == b.blocks[1] && a.blocks[1] == b.blocks[0]);
}
// 2 leb128 => 10 bytes (worst case)
#define LFSR_MPAIR_DSIZE (5+5)
@@ -4378,13 +4384,32 @@ static int lfsr_mountinited(lfs_t *lfs) {
// scan for the first non-fake superblock
lfsr_mpair_t mpair = LFSR_MPAIR(0, 1);
lfsr_mdir_t mdir;
// detect cycles using Brent's algorithm
lfsr_mpair_t tortoise = LFSR_MPAIR(-1, -1);
lfs_size_t tortoise_i = 1;
lfs_size_t tortoise_period = 1;
while (true) {
// TODO detect cycles with Brent's algorithm
// found a cycle?
if (lfsr_mpair_eq(mpair, tortoise)) {
LFS_WARN("Cycle detected in superblocks");
return LFS_ERR_CORRUPT;
}
if (tortoise_i == tortoise_period) {
tortoise = mpair;
tortoise_i = 0;
tortoise_period *= 2;
}
tortoise_i += 1;
// fetch next possible superblock
int err = lfsr_mdir_fetch(lfs, &mdir, mpair, NULL);
if (err) {
LFS_ERROR("No littlefs superblock found");
// treat corrupt errors as invalid littlefs images
if (err == LFS_ERR_CORRUPT) {
return LFS_ERR_INVAL;
}
return err;
}
@@ -4667,7 +4692,7 @@ static int lfsr_formatinited(lfs_t *lfs) {
for (int i = 0; i < 2; i++) {
// write superblock to both rbyds in the root supermdir to hopefully
// avoid mounting an older filesystem on disk
lfsr_rbyd_t rbyd = {.block=i, .rev=1, .off=0, .trunk=0};
lfsr_rbyd_t rbyd = {.block=i, .rev=i+1, .off=0, .trunk=0};
int err = lfsr_bd_erase(lfs, rbyd.block);
if (err) {
@@ -8288,7 +8313,7 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
while (!lfs_pair_isnull(dir.tail)) {
// detect cycles with Brent's algorithm
if (lfs_pair_issync(dir.tail, tortoise)) {
LFS_WARN("Cycle detected in tail list");
LFS_ERROR("Cycle detected in tail list");
err = LFS_ERR_CORRUPT;
goto cleanup;
}
+1 -1
View File
@@ -878,7 +878,7 @@ def main(disk, block1=0, block2=None, *,
weights.append(weight)
# compare with sequence arithmetic
if off and ((rev - revs[i]) & 0x80000000):
if off and not ((rev - revs[i]) & 0x80000000):
i = len(revs)-1
# print contents of the winning metadata block
+41
View File
@@ -14,6 +14,47 @@ code = '''
lfsr_unmount(&lfs) => 0;
'''
# reentrant format
[cases.test_superblocks_reentrant_format]
reentrant = true
code = '''
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# invalid mount
[cases.test_superblocks_invalid]
code = '''
lfs_t lfs;
lfsr_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
# superblock cycle detection
[cases.test_superblocks_cycle]
in = 'lfs.c'
code = '''
// create a cycle
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
lfsr_mdir_t mdir;
lfsr_mdir_fetch(&lfs, &mdir, LFSR_MPAIR(0, 1), NULL) => 0;
uint8_t buf[LFSR_MPAIR_DSIZE];
lfs_ssize_t d = lfsr_mpair_todisk(&lfs, LFSR_MPAIR(0, 1), buf);
assert(d >= 0);
lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS(
LFSR_ATTR(-1, SUPERMDIR, 0, buf, d))) => 0;
lfsr_unmount(&lfs) => 0;
// now detect the cycle
lfsr_mount(&lfs, cfg) => LFS_ERR_CORRUPT;
'''