t: Moved open mdir/mroot cksum checks into lfsr_mtree_traverse

This changes lfsr_mtree_traverse + CKMETA to check on-disk mdir
checksums against all open mdirs and the mroot (which is always "open").

This is basically free (a bit more CPU work, but no disk accesses), and
makes lfsr_mtree_traverse a bit more robust against rollback issues. It
also lets us drop the obshrub-specific mdir cksum check.

This adds a bit of code, but it's probably worth it to catch rollback
issues earlier:

           code          stack          ctx
  before: 38572           2640          644
  after:  38664 (+0.2%)   2648 (+0.3%)  644 (+0.0%)
This commit is contained in:
Christopher Haster
2025-01-17 15:00:40 -06:00
parent 5aada6f54a
commit 8414f18d1b
+51 -19
View File
@@ -9477,20 +9477,6 @@ static int lfsr_mtree_traverse_(lfs_t *lfs, lfsr_traversal_t *t,
continue;
}
// hold on, does our mdir cksum match? if not we found some
// sort of error
if (t->ot->mdir.rbyd.cksum != t->o.o.mdir.rbyd.cksum) {
LFS_DEBUG("Found mdir cksum mismatch %"PRId32" "
"0x{%"PRIx32",%"PRIx32"}, "
"cksum %08"PRIx32" (!= %08"PRIx32")",
t->o.o.mdir.mid >> lfs->mdir_bits,
t->o.o.mdir.rbyd.blocks[0],
t->o.o.mdir.rbyd.blocks[1],
t->o.o.mdir.rbyd.cksum,
t->ot->mdir.rbyd.cksum);
return LFS_ERR_CORRUPT;
}
// start traversing the file
const lfsr_file_t *file = (const lfsr_file_t*)t->ot;
t->o.bshrub = file->o.bshrub;
@@ -9585,15 +9571,55 @@ static int lfsr_mtree_traverse(lfs_t *lfs, lfsr_traversal_t *t,
return err;
}
// recalculate gcksum?
// validate mdirs? mdir checksums are already validated in
// lfsr_mdir_fetch, but this doesn't prevent rollback issues, where
// the most recent commit is corrupted but a previous outdated
// commit appears valid
//
// this is where the gcksum comes in, which we can recalculate to
// check if the filesystem state on-disk is as expected
//
// we also compare mdir checksums with any open mdirs to try to
// avoid traversing any outdated bshrubs/btrees
if ((lfsr_t_isckmeta(t->o.o.flags)
|| lfsr_t_isckdata(t->o.o.flags))
&& tag == LFSR_TAG_MDIR) {
lfsr_mdir_t *mdir = (lfsr_mdir_t*)bptr.data.u.buffer;
// check cksum matches our mroot
if (lfsr_mdir_cmp(mdir, &lfs->mroot) == 0
&& mdir->rbyd.cksum != lfs->mroot.rbyd.cksum) {
LFS_DEBUG("Found mroot cksum mismatch "
"0x{%"PRIx32",%"PRIx32"}, "
"cksum %08"PRIx32" (!= %08"PRIx32")",
mdir->rbyd.blocks[0],
mdir->rbyd.blocks[1],
mdir->rbyd.cksum,
lfs->mroot.rbyd.cksum);
return LFS_ERR_CORRUPT;
}
// check cksum matches any open mdirs
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
if (lfsr_mdir_cmp(&o->mdir, mdir) == 0
&& o->mdir.rbyd.cksum != mdir->rbyd.cksum) {
LFS_DEBUG("Found mdir cksum mismatch %"PRId32" "
"0x{%"PRIx32",%"PRIx32"}, "
"cksum %08"PRIx32" (!= %08"PRIx32")",
mdir->mid >> lfs->mdir_bits,
mdir->rbyd.blocks[0],
mdir->rbyd.blocks[1],
mdir->rbyd.cksum,
o->mdir.rbyd.cksum);
return LFS_ERR_CORRUPT;
}
}
// recalculate gcksum
t->gcksum ^= mdir->rbyd.cksum;
}
// validate btree nodes? note mdirs are already validated
// validate btree nodes?
if ((lfsr_t_isckmeta(t->o.o.flags)
|| lfsr_t_isckdata(t->o.o.flags))
&& tag == LFSR_TAG_BRANCH
@@ -13900,14 +13926,20 @@ static int lfsr_mountmroot(lfs_t *lfs, const lfsr_mdir_t *mroot) {
}
static int lfsr_mountinited(lfs_t *lfs) {
// zero gdeltas, we'll read these from our mdirs
lfs->gcksum = 0;
lfsr_fs_flushgdelta(lfs);
// mark mroot as invalid to prevent lfsr_mtree_traverse from getting
// confused
lfs->mroot.mid = -1;
lfs->mroot.rbyd.blocks[0] = -1;
lfs->mroot.rbyd.blocks[1] = -1;
// default to no mtree, this is allowed and implies all files are inlined
// in the mroot
lfsr_mtree_init(&lfs->mtree);
// zero gcksum/gdeltas, we'll read these from our mdirs
lfs->gcksum = 0;
lfsr_fs_flushgdelta(lfs);
// traverse the mtree rooted at mroot 0x{1,0}
//
// we do validate btree inner nodes here, how can we trust our