Adopted mtree traversal in lfsr_mountinited

This is a nice bit of deduplication as long as the mtree traversal can
handle both:

1. Cycle detection
2. Btree node validation

Eventually we'll also collect gstate here, which mtree traversal should
make quite easy.

The only catch is if we eventually need a non-fetching way to read the
mroot config, such as if we need to infer the csum type or block-size,
but that's a future problem.
This commit is contained in:
Christopher Haster
2023-05-27 17:30:30 -05:00
parent 1631ca8d78
commit 773278eb26
+43 -83
View File
@@ -6766,41 +6766,39 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg);
static int lfs_deinit(lfs_t *lfs);
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;
// traverse the mtree rooted at mroot 0x{1,0}
//
// note that lfsr_mtree_traversal_next will update our mroot/mtree
// based on what mroots it finds
//
// we do validate btree inner nodes here, how can we trust our
// mdirs are valid if we haven't checked the btree inner nodes at
// least once?
lfsr_mtree_traversal_t traversal = LFSR_MTREE_TRAVERSAL_INIT(
LFSR_MTREE_TRAVERSAL_VALIDATE);
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, -1, 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;
}
lfsr_tag_t tag;
lfsr_data_t data;
int err = lfsr_mtree_traversal_next(lfs, &traversal,
NULL, &tag, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
break;
}
// we only care about mdirs here
if (tag != LFSR_TAG_MDIR) {
continue;
}
lfsr_mdir_t *mdir = (lfsr_mdir_t*)data.buf.buffer;
// found an mroot?
if (mdir->mid == -1) {
// has magic string?
lfsr_data_t data;
err = lfsr_mdir_lookup(lfs, &mdir, -1, LFSR_TAG_MAGIC, &data);
err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_MAGIC, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
@@ -6824,7 +6822,7 @@ static int lfsr_mountinited(lfs_t *lfs) {
}
// lookup the superconfig
err = lfsr_mdir_lookup(lfs, &mdir, -1, LFSR_TAG_CONFIG, &data);
err = lfsr_mdir_lookup(lfs, mdir, -1, LFSR_TAG_CONFIG, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
@@ -6835,7 +6833,8 @@ static int lfsr_mountinited(lfs_t *lfs) {
uint32_t minor_version;
lfs_size_t d = 0;
lfs_ssize_t d_ = lfsr_data_readleb128(lfs, data, d, &major_version);
lfs_ssize_t d_ = lfsr_data_readleb128(lfs, data, d,
&major_version);
// treat any leb128 overflows as out-of-range values
if (d_ < 0 && d_ != LFS_ERR_CORRUPT) {
return d_;
@@ -6858,8 +6857,12 @@ static int lfsr_mountinited(lfs_t *lfs) {
|| minor_version > LFS_DISK_VERSION_MINOR) {
LFS_ERROR("Incompatible version v%"PRIu32".%"PRIu32
" (!= v%"PRIu32".%"PRIu32")",
(d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : major_version),
(d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : minor_version),
(d_ == LFS_ERR_CORRUPT
? (uint32_t)-1
: major_version),
(d_ == LFS_ERR_CORRUPT
? (uint32_t)-1
: minor_version),
LFS_DISK_VERSION_MAJOR,
LFS_DISK_VERSION_MINOR);
return LFS_ERR_INVAL;
@@ -6915,7 +6918,8 @@ static int lfsr_mountinited(lfs_t *lfs) {
d += d_;
}
if (d_ == LFS_ERR_CORRUPT || block_size != lfs->cfg->block_size) {
if (d_ == LFS_ERR_CORRUPT
|| block_size != lfs->cfg->block_size) {
LFS_ERROR("Incompatible block size 0x%"PRIx32
" (!= 0x%"PRIx32")",
(d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : block_size),
@@ -6935,10 +6939,13 @@ static int lfsr_mountinited(lfs_t *lfs) {
d += d_;
}
if (d_ == LFS_ERR_CORRUPT || block_count != lfs->cfg->block_count) {
if (d_ == LFS_ERR_CORRUPT
|| block_count != lfs->cfg->block_count) {
LFS_ERROR("Incompatible block count 0x%"PRIx32
" (!= 0x%"PRIx32")",
(d_ == LFS_ERR_CORRUPT ? (uint32_t)-1 : block_count),
(d_ == LFS_ERR_CORRUPT
? (uint32_t)-1
: block_count),
lfs->cfg->block_count);
return LFS_ERR_INVAL;
}
@@ -7023,56 +7030,9 @@ static int lfsr_mountinited(lfs_t *lfs) {
return LFS_ERR_INVAL;
}
}
// lookup mroot
//
// if we have a mroot, this is actually a fake superblock and
// we need to parse the next superblock in the chain
err = lfsr_mdir_lookup(lfs, &mdir, -1, LFSR_TAG_MROOT, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
// no more mroots means we found our real superblock
if (err == LFS_ERR_NOENT) {
break;
}
lfs_ssize_t d = lfsr_mpair_fromdisk(lfs, &mpair, data);
if (d < 0) {
return d;
}
}
// do we have an mtree? this could be either a single mdir or a btree
// of mdirs
lfs_ssize_t id;
lfsr_tag_t tag;
lfsr_data_t data;
int err = lfsr_mdir_lookupnext(lfs, &mdir, -1, LFSR_TAG_STRUCT,
&id, &tag, NULL, &data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err != LFS_ERR_NOENT
&& id == -1
&& lfsr_tag_suptype(tag) == LFSR_TAG_STRUCT) {
if (tag != LFSR_TAG_MDIR && tag != LFSR_TAG_BTREE) {
LFS_ERROR("Weird superstruct? 0x%"PRIx32, tag);
return LFS_ERR_CORRUPT;
}
lfs_ssize_t d = lfsr_btree_fromdisk(lfs, &lfs->mtree, tag, 1, data);
if (d < 0) {
return d;
}
} else {
// TODO null?
lfs->mtree = LFSR_BTREE_NULL;
}
lfs->mroot = mdir;
return 0;
}