Check gcksum during traversals, harder ckmeta/ckdata tests
This adds a check that the on-disk gcksum matches the in-RAM gcksum
in lfsr_mtree_traverse, so ckmeta/ckdata scans should now be able to
at least detect global-rollback issues that occur while mounted.
This also moves the LFS_I_CKMETA/CKDATA flag clearing logic from
lfsr_mtree_gc -> lfsr_mtree_traverse. There's no reason to not clear
these flags if we've made a successful traversal. We weren't actually
calling lfsr_mtree_traverse with the right flags for this to matter, but
it does let us drop an explicit flag clear in lfsr_fs_ck.
---
These changes were a part of adding the harder versions of our ckmeta/
ckdata tests, where we flip individual bits instead of clobbering the
entire block. These are more realistic errors and stress our gcksum
system.
Recalculating the gcksum required another gcksum copy in
lfsr_traversal_t, which adds a bit of code and ctx to our incremental-gc
build:
code stack ctx
default before: 38428 2640 644
default after: 38560 (+0.3%) 2640 (+0.0%) 644 (+0.0%)
gc before: 38484 2640 788
gc after: 38616 (+0.3%) 2640 (+0.0%) 792 (+0.5%)
Unfortunately we can't easily abuse the copies in lfs_t since
multiple traversals may be open at once.
This commit is contained in:
@@ -9236,6 +9236,7 @@ static void lfsr_traversal_init(lfsr_traversal_t *t, uint32_t flags) {
|
||||
t->u.mtortoise.blocks[1] = -1;
|
||||
t->u.mtortoise.step = 0;
|
||||
t->u.mtortoise.power = 0;
|
||||
t->gcksum = 0;
|
||||
}
|
||||
|
||||
// low-level traversal _only_ finds blocks
|
||||
@@ -9563,17 +9564,29 @@ static int lfsr_mtree_traverse(lfs_t *lfs, lfsr_traversal_t *t,
|
||||
int err = lfsr_mtree_traverse_(lfs, t,
|
||||
&tag, &bptr);
|
||||
if (err) {
|
||||
// end of traversal?
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
goto eot;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// recalculate gcksum?
|
||||
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;
|
||||
t->gcksum ^= mdir->rbyd.cksum;
|
||||
}
|
||||
|
||||
// validate btree nodes? note mdirs are already validated
|
||||
if ((lfsr_t_isckmeta(t->o.o.flags)
|
||||
|| lfsr_t_isckdata(t->o.o.flags))
|
||||
&& tag == LFSR_TAG_BRANCH
|
||||
// note ckfetches already validates btree nodes
|
||||
&& LFS_IFDEF_CKFETCHES(
|
||||
!lfsr_m_isckfetches(lfs->flags),
|
||||
true)
|
||||
&& tag == LFSR_TAG_BRANCH) {
|
||||
true)) {
|
||||
lfsr_rbyd_t *rbyd = (lfsr_rbyd_t*)bptr.data.u.buffer;
|
||||
err = lfsr_rbyd_fetchck(lfs, rbyd,
|
||||
rbyd->blocks[0], rbyd->trunk,
|
||||
@@ -9599,6 +9612,35 @@ static int lfsr_mtree_traverse(lfs_t *lfs, lfsr_traversal_t *t,
|
||||
*bptr_ = bptr;
|
||||
}
|
||||
return 0;
|
||||
|
||||
eot:;
|
||||
// compare gcksum with in-RAM gcksum
|
||||
if ((lfsr_t_isckmeta(t->o.o.flags)
|
||||
|| lfsr_t_isckdata(t->o.o.flags))
|
||||
&& t->gcksum != lfs->gcksum) {
|
||||
LFS_ERROR("Found gcksum mismatch, cksum %08"PRIx32" (!= %08"PRIx32")",
|
||||
t->gcksum,
|
||||
lfs->gcksum);
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
// was ckmeta/ckdata successful? we only consider our filesystem
|
||||
// checked if we weren't mutated
|
||||
if (lfsr_t_isckmeta(t->o.o.flags)
|
||||
&& !lfsr_t_ismtreeonly(t->o.o.flags)
|
||||
&& !lfsr_t_isdirty(t->o.o.flags)
|
||||
&& !lfsr_t_ismutated(t->o.o.flags)) {
|
||||
lfs->flags &= ~LFS_I_CKMETA;
|
||||
}
|
||||
if (lfsr_t_isckdata(t->o.o.flags)
|
||||
&& !lfsr_t_ismtreeonly(t->o.o.flags)
|
||||
&& !lfsr_t_isdirty(t->o.o.flags)
|
||||
&& !lfsr_t_ismutated(t->o.o.flags)) {
|
||||
// note ckdata implies ckmeta
|
||||
lfs->flags &= ~LFS_I_CKDATA & ~LFS_I_CKMETA;
|
||||
}
|
||||
|
||||
return LFS_ERR_NOENT;
|
||||
}
|
||||
|
||||
// needed in lfsr_mtree_gc
|
||||
@@ -9693,11 +9735,6 @@ dropped:;
|
||||
}
|
||||
return 0;
|
||||
|
||||
failed:;
|
||||
// swap back dirty/mutated flags
|
||||
t->o.o.flags = lfsr_t_swapdirty(t->o.o.flags);
|
||||
return err;
|
||||
|
||||
eot:;
|
||||
// was lookahead scan successful?
|
||||
if (lfsr_t_islookahead(t->o.o.flags)
|
||||
@@ -9721,23 +9758,12 @@ eot:;
|
||||
lfs->flags &= ~LFS_I_COMPACT;
|
||||
}
|
||||
|
||||
// was ckmeta/ckdata successful? we only consider our filesystem
|
||||
// checked if we weren't mutated
|
||||
if (lfsr_t_isckmeta(t->o.o.flags)
|
||||
&& !lfsr_t_ismtreeonly(t->o.o.flags)
|
||||
&& !lfsr_t_isdirty(t->o.o.flags)
|
||||
&& !lfsr_t_ismutated(t->o.o.flags)) {
|
||||
lfs->flags &= ~LFS_I_CKMETA;
|
||||
}
|
||||
if (lfsr_t_isckdata(t->o.o.flags)
|
||||
&& !lfsr_t_ismtreeonly(t->o.o.flags)
|
||||
&& !lfsr_t_isdirty(t->o.o.flags)
|
||||
&& !lfsr_t_ismutated(t->o.o.flags)) {
|
||||
// note ckdata implies ckmeta
|
||||
lfs->flags &= ~LFS_I_CKDATA & ~LFS_I_CKMETA;
|
||||
}
|
||||
|
||||
return LFS_ERR_NOENT;
|
||||
|
||||
failed:;
|
||||
// swap back dirty/mutated flags
|
||||
t->o.o.flags = lfsr_t_swapdirty(t->o.o.flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -11109,7 +11135,9 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
|
||||
| LFS_O_APPEND
|
||||
| LFS_O_FLUSH
|
||||
| LFS_O_SYNC
|
||||
| LFS_O_DESYNC)) == 0);
|
||||
| LFS_O_DESYNC
|
||||
| LFS_O_CKMETA
|
||||
| LFS_O_CKDATA)) == 0);
|
||||
// writeable files require a writeable filesystem
|
||||
LFS_ASSERT(!lfsr_m_isrdonly(lfs->flags) || lfsr_o_isrdonly(flags));
|
||||
// these flags require a writable file
|
||||
@@ -13857,6 +13885,7 @@ 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);
|
||||
|
||||
// default to no mtree, this is allowed and implies all files are inlined
|
||||
@@ -13940,7 +13969,7 @@ static int lfsr_mountinited(lfs_t *lfs) {
|
||||
lfs->seed ^= mdir->rbyd.cksum;
|
||||
|
||||
// build gcksum out of mdir cksums
|
||||
lfs->gcksum_p ^= mdir->rbyd.cksum;
|
||||
lfs->gcksum ^= mdir->rbyd.cksum;
|
||||
|
||||
// collect any gdeltas from this mdir
|
||||
err = lfsr_fs_consumegdelta(lfs, mdir);
|
||||
@@ -13961,9 +13990,6 @@ static int lfsr_mountinited(lfs_t *lfs) {
|
||||
}
|
||||
}
|
||||
|
||||
// keep track of the current gcksum
|
||||
lfs->gcksum = lfs->gcksum_p;
|
||||
|
||||
// validate gcksum by comparing its cube against the gcksumdeltas
|
||||
//
|
||||
// The use of cksum^3 here is important to avoid trivial
|
||||
@@ -13997,6 +14023,9 @@ static int lfsr_mountinited(lfs_t *lfs) {
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
// keep track of the current gcksum
|
||||
lfs->gcksum_p = lfs->gcksum;
|
||||
|
||||
// once we've mounted and derived a pseudo-random seed, initialize our
|
||||
// block allocator
|
||||
//
|
||||
@@ -14505,7 +14534,7 @@ int lfsr_fs_mkconsistent(lfs_t *lfs) {
|
||||
static int lfsr_fs_ck(lfs_t *lfs, uint32_t flags) {
|
||||
// we leave this up to lfsr_mtree_traverse
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_init(&t, flags);;
|
||||
lfsr_traversal_init(&t, flags);
|
||||
while (true) {
|
||||
int err = lfsr_mtree_traverse(lfs, &t,
|
||||
NULL, NULL);
|
||||
@@ -14517,8 +14546,6 @@ static int lfsr_fs_ck(lfs_t *lfs, uint32_t flags) {
|
||||
}
|
||||
}
|
||||
|
||||
// clear relevant ck flags
|
||||
lfs->flags &= ~flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user