From 2f11fa71f4978dcbce10e1f06c2d5677f39e6650 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 19 Aug 2024 15:12:22 -0500 Subject: [PATCH] Implemented ckcksums Since we already need all the machinery to track ck info for ckparity, I figured we might as well implement a full ckcksums option as well. Ckcksums closes the checksum-read-hole by reading enough data to check a relevant checksum on ever read, even if this ends up being significantly more data than the initial request. This should always detect detectable bit-errors, even if they occur between consecutive reads. If this sounds naive, that's because it is. Performance will be awful. To be clear, ckcksums should probably never be used in production. I can't think of a use case that isn't better handled by either ECC in the block device or the future-planned ckredund feature. Just look at the runtime complexities: small-reads rbyd-lookup rbyd-compaction ckcksums: O(b^2) O(b log b) O(b^2 log b) ckredund*: O(log_b(n) + xb) O(log b) O(b log b) eccbd*: O(b) O(log b) O(b log b) * theoretical We've already seen that O(b^2) compactions turns a performance problem into a tractability problem, so I think O(b^2 log b) compactions will be a bit too much for most applications. We can already seen this in our test_ck_ckcksums_* tests (which do pass by the way!). Compare to test_ck_ckprogs_*, which is basically the same set of tests: test_ck_ckprogs_*: 6.08s test_ck_ckcksums_*: 64.88s Or consider test_rbyd with/without ckcksums: test_rbyd: 12.21s test_rbyd+ckcksums: 389.94s Still, ckcksums is an interesting proof-of-concept, and does manage to close the checksum-read-hole. --- Like ckprogs/ckfetches/ckparity/etc, ckcksums is an opt-in feature, requiring both 1. defining LFS_CKCKSUMS and 2. passing LFS_M_CKCKSUMS at mount time. Like ckparity, ckcksums requires a significant code and stack increase to track ck info in lfsr_data_t: code stack before: 36416 2616 yes-ckcksums: 38872 (+6.7%) 3176 (+21.4%) no-ckcksums: 36416 (+0.0%) 2616 (+0.0%) It's interesting to note how this compares to all of the current ck-modes, though each has their own set of tradeoffs: code stack default: 36416 2616 ckprogs: 36468 (+0.1%) 2616 (+0.0%) ckfetches: 36666 (+0.7%) 2648 (+1.2%) ckparity: 37996 (+4.3%) 3040 (+16.2%) ckcksums: 38872 (+6.7%) 3176 (+21.4%) --- Note that even though ckcksums is opt-in, it may still be worth removing from the codebase in the future, for a couple reasons: - Every feature, even if unused, adds developer/maintenance burden. - Ck info is particularly messy with how it interacts with all lfsr_data_t APIs. Though getting rid of ck info would also require getting rid of ckparity. - It's possible for a user to see ckcksums in the codebase, misunderstand its tradeoffs, enable it, and get the impression that littlefs itself is just unusably slow. --- lfs.c | 654 ++++++++++++++++++++++++++++++++++-------- lfs.h | 19 +- lfs_util.h | 6 + tests/test_ck.toml | 458 +++++++++++++++++++++++++++-- tests/test_mount.toml | 7 + 5 files changed, 1001 insertions(+), 143 deletions(-) diff --git a/lfs.c b/lfs.c index d178950a..9ac5bb8c 100644 --- a/lfs.c +++ b/lfs.c @@ -704,20 +704,20 @@ static int lfsr_bd_set(lfs_t *lfs, lfs_block_t block, lfs_size_t off, /// lfsr_ck_t stuff /// -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) // yes, these are slightly different things #define LFSR_CK_ISPARITY 0x80000000 #define LFSR_CK_PARITY 0x80000000 #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_CK_CKSUM(_cksize, _cksum) \ ((lfsr_ck_t){ \ .cksize=_cksize, \ .u.cksum=_cksum}) #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_CK_PARITY_(_ckoff, _cksize, _parity) \ ((lfsr_ck_t){ \ .cksize=LFSR_CK_ISPARITY | (_cksize), \ @@ -727,31 +727,31 @@ static int lfsr_bd_set(lfs_t *lfs, lfs_block_t block, lfs_size_t off, #endif // ck helpers -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static inline bool lfsr_ck_isparity(lfsr_ck_t ck) { return ck.cksize & LFSR_CK_ISPARITY; } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static inline bool lfsr_ck_iscksum(lfsr_ck_t ck) { return !(ck.cksize & LFSR_CK_ISPARITY); } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static inline lfs_size_t lfsr_ck_cksize(lfsr_ck_t ck) { return ck.cksize & ~LFSR_CK_ISPARITY; } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static inline bool lfsr_ck_parity(lfsr_ck_t ck) { return ck.u.ckoff & LFSR_CK_PARITY; } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static inline lfs_size_t lfsr_ck_ckoff(lfsr_ck_t ck) { if (lfsr_ck_isparity(ck)) { return ck.u.ckoff & ~LFSR_CK_PARITY; @@ -767,23 +767,11 @@ static inline lfs_size_t lfsr_ck_ckoff(lfsr_ck_t ck) { // tailck tracks the most recent trunk's parity so we can parity-check // if it hasn't been written to disk yet -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_TAILCK_PARITY 0x80000000 #endif -#ifdef LFS_CKPARITY -static inline bool lfsr_tailck_parity(const lfsr_tailck_t *tailck) { - return tailck->ckoff & LFSR_TAILCK_PARITY; -} -#endif - -#ifdef LFS_CKPARITY -static inline lfs_size_t lfsr_tailck_ckoff(const lfsr_tailck_t *tailck) { - return tailck->ckoff & ~LFSR_TAILCK_PARITY; -} -#endif - -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) // read the pesky parity byte from disk static lfs_sbool_t lfsr_bd_readparity(lfs_t *lfs, lfs_block_t block, lfs_size_t off, lfs_size_t hint) { @@ -795,15 +783,14 @@ static lfs_sbool_t lfsr_bd_readparity(lfs_t *lfs, // // unless we're in the middle of building a commit, where things get // tricky... to avoid problems with not-yet-written parity bits - // tailck tracks the most recent trunk's parity + // tailck tracks the most recent trunk's cksum // // parity in in tailck? - if (block == lfs->tailck.ckblock - && off == lfsr_tailck_ckoff(&lfs->tailck)) { - return lfsr_tailck_parity(&lfs->tailck); + if (block == lfs->tailck.ckblock && off == lfs->tailck.ckoff) { + return lfs_parity(lfs->tailck.cksum); - // parity on disk + // parity on disk? } else { uint8_t p; int err = lfsr_bd_read(lfs, block, off, hint, @@ -818,21 +805,43 @@ static lfs_sbool_t lfsr_bd_readparity(lfs_t *lfs, #endif -// checked read functions +// checked read helpers -#ifdef LFS_CKPARITY -static lfs_ssize_t lfsr_bd_ckprefix(lfs_t *lfs, +// needed in lfsr_bd_ckprefix +#ifdef LFS_CKCKSUMS +static inline bool lfsr_m_isckcksums(uint32_t flags); +static int lfsr_bd_ckrbydprefix(lfs_t *lfs, + lfs_block_t block, lfs_size_t off, + lfsr_ck_t ck, + lfs_size_t *hint_, + uint32_t *cksum); +static int lfsr_bd_ckrbydsuffix(lfs_t *lfs, + lfs_block_t block, lfs_size_t off, + lfsr_ck_t ck, + uint32_t cksum); +#endif + +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) +static int lfsr_bd_ckprefix(lfs_t *lfs, lfs_block_t block, lfs_size_t off, lfs_size_t hint, lfsr_ck_t ck, + lfs_size_t *hint_, uint32_t *cksum) { // must be in-bounds LFS_ASSERT(block < lfs->block_count); LFS_ASSERT(lfsr_ck_ckoff(ck)+lfsr_ck_cksize(ck) <= lfs->cfg->block_size); - // only parity checking is currently supported - LFS_ASSERT(lfsr_ck_isparity(ck)); + + #ifdef LFS_CKCKSUMS + // in rbyd? checking checksums? this gets a bit more complicated + if (lfsr_m_isckcksums(lfs->flags) && lfsr_ck_isparity(ck)) { + return lfsr_bd_ckrbydprefix(lfs, block, off, ck, + hint_, + cksum); + } + #endif // make sure hint includes our prefix/suffix - lfs_size_t hint_ = lfs_max( + lfs_size_t hint__ = lfs_max( // watch out for overflow when hint=-1! (off-lfsr_ck_ckoff(ck)) + lfs_min( hint, @@ -841,7 +850,7 @@ static lfs_ssize_t lfsr_bd_ckprefix(lfs_t *lfs, // checksum any prefixed data int err = lfsr_bd_cksum(lfs, - block, lfsr_ck_ckoff(ck), hint_, + block, lfsr_ck_ckoff(ck), hint__, off-lfsr_ck_ckoff(ck), cksum); if (err) { @@ -850,11 +859,12 @@ static lfs_ssize_t lfsr_bd_ckprefix(lfs_t *lfs, // return adjusted hint, note we clamped this to a positive range // earlier, otherwise we'd have real problems with hint=-1! - return hint_ - (off-lfsr_ck_ckoff(ck)); + *hint_ = hint__ - (off-lfsr_ck_ckoff(ck)); + return 0; } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static int lfsr_bd_cksuffix(lfs_t *lfs, lfs_block_t block, lfs_size_t off, lfs_size_t hint, lfsr_ck_t ck, @@ -862,8 +872,21 @@ static int lfsr_bd_cksuffix(lfs_t *lfs, // must be in-bounds LFS_ASSERT(block < lfs->block_count); LFS_ASSERT(lfsr_ck_ckoff(ck)+lfsr_ck_cksize(ck) <= lfs->cfg->block_size); - // only parity checking is currently supported - LFS_ASSERT(lfsr_ck_isparity(ck)); + + #ifdef LFS_CKCKSUMS + // in rbyd? checking checksums? this gets a bit more complicated + if (lfsr_m_isckcksums(lfs->flags) && lfsr_ck_isparity(ck)) { + int err = lfsr_bd_ckrbydsuffix(lfs, block, off, ck, + cksum); + if (err) { + LFS_ERROR("Found ckcksums rbyd mismatch " + "0x%"PRIx32".%"PRIx32" %"PRId32, + block, lfsr_ck_ckoff(ck), lfsr_ck_cksize(ck)); + return err; + } + return 0; + } + #endif // checksum any suffixed data int err = lfsr_bd_cksum(lfs, @@ -874,21 +897,41 @@ static int lfsr_bd_cksuffix(lfs_t *lfs, return err; } - // does parity match? - if (lfs_parity(cksum) != lfsr_ck_parity(ck)) { - LFS_ERROR("Found ckparity mismatch " - "0x%"PRIx32".%"PRIx32" %"PRId32", " - "parity %01"PRIx32" (!= %01"PRIx32")", - block, lfsr_ck_ckoff(ck), lfsr_ck_cksize(ck), - lfs_parity(cksum), lfsr_ck_parity(ck)); - return LFS_ERR_CORRUPT; + if (lfsr_ck_isparity(ck)) { + #ifdef LFS_CKPARITY + // does parity match? + if (lfs_parity(cksum) != lfsr_ck_parity(ck)) { + LFS_ERROR("Found ckparity mismatch " + "0x%"PRIx32".%"PRIx32" %"PRId32", " + "parity %01"PRIx32" (!= %01"PRIx32")", + block, lfsr_ck_ckoff(ck), lfsr_ck_cksize(ck), + lfs_parity(cksum), lfsr_ck_parity(ck)); + return LFS_ERR_CORRUPT; + } + #endif + + } else { + #ifdef LFS_CKCKSUMS + // do checksums match? + if (cksum != ck.u.cksum) { + LFS_ERROR("Found ckcksums mismatch " + "0x%"PRIx32".%"PRIx32" %"PRId32", " + "cksum %08"PRIx32" (!= %08"PRIx32")", + block, lfsr_ck_ckoff(ck), lfsr_ck_cksize(ck), + cksum, ck.u.cksum); + return LFS_ERR_CORRUPT; + } + #endif } return 0; } #endif -#ifdef LFS_CKPARITY + +// checked read functions + +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) // caching read with parity/checksum checks // // the main downside of checking reads is we need to read all data that @@ -908,14 +951,16 @@ static int lfsr_bd_readck_(lfs_t *lfs, // checksum any prefixed data uint32_t cksum = 0; - lfs_ssize_t hint_ = lfsr_bd_ckprefix(lfs, block, off, hint, ck, + lfs_size_t hint_; + int err = lfsr_bd_ckprefix(lfs, block, off, hint, ck, + &hint_, &cksum); - if (hint_ < 0) { - return hint_; + if (err) { + return err; } // read and checksum the data we're interested in - int err = lfsr_bd_read(lfs, + err = lfsr_bd_read(lfs, block, off, hint_, buffer, size); if (err) { @@ -935,17 +980,24 @@ static int lfsr_bd_readck_(lfs_t *lfs, } #endif -#ifdef LFS_CKPARITY // needed in lfsr_bd_readck +#ifdef LFS_CKPARITY static inline bool lfsr_m_isckparity(uint32_t flags); +#endif +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static int lfsr_bd_readck(lfs_t *lfs, lfs_block_t block, lfs_size_t off, lfs_size_t hint, void *buffer, lfs_size_t size, lfsr_ck_t ck) { // check this read? - if (lfsr_m_isckparity(lfs->flags) - && lfsr_ck_isparity(ck)) { + if (LFS_IFDEF_CKPARITY( + lfsr_m_isckparity(lfs->flags) + && lfsr_ck_isparity(ck), + false) + || LFS_IFDEF_CKCKSUMS( + lfsr_m_isckcksums(lfs->flags), + false)) { return lfsr_bd_readck_(lfs, block, off, hint, buffer, size, @@ -959,7 +1011,7 @@ static int lfsr_bd_readck(lfs_t *lfs, #endif // redirect to lfsr_bd_read if not checking reads -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_BD_READCK(_lfs, _block, _off, _hint, _buffer, _size, _ck) \ lfsr_bd_readck(_lfs, _block, _off, _hint, _buffer, _size, _ck) #else @@ -973,7 +1025,7 @@ static int lfsr_bd_readck(lfs_t *lfs, // // we'd also need to worry about early termination in lfsr_bd_cmp/cmpck -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static lfs_scmp_t lfsr_bd_cmpck_(lfs_t *lfs, lfs_block_t block, lfs_size_t off, lfs_size_t hint, const void *buffer, lfs_size_t size, @@ -987,10 +1039,12 @@ static lfs_scmp_t lfsr_bd_cmpck_(lfs_t *lfs, // checksum any prefixed data uint32_t cksum = 0; - lfs_ssize_t hint_ = lfsr_bd_ckprefix(lfs, block, off, hint, ck, + lfs_size_t hint_; + int err = lfsr_bd_ckprefix(lfs, block, off, hint, ck, + &hint_, &cksum); - if (hint_ < 0) { - return hint_; + if (err) { + return err; } // compare the data while simultaneously updating the checksum @@ -1002,7 +1056,7 @@ static lfs_scmp_t lfsr_bd_cmpck_(lfs_t *lfs, while (size_ > 0) { const uint8_t *buffer__; lfs_size_t size__; - int err = lfsr_bd_readnext(lfs, block, off_, hint__, size_, + err = lfsr_bd_readnext(lfs, block, off_, hint__, size_, &buffer__, &size__); if (err) { return err; @@ -1024,7 +1078,7 @@ static lfs_scmp_t lfsr_bd_cmpck_(lfs_t *lfs, } // checksum any suffixed data and validate - int err = lfsr_bd_cksuffix(lfs, block, off+size, hint_-size, ck, + err = lfsr_bd_cksuffix(lfs, block, off+size, hint_-size, ck, cksum); if (err) { return err; @@ -1034,14 +1088,19 @@ static lfs_scmp_t lfsr_bd_cmpck_(lfs_t *lfs, } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static lfs_scmp_t lfsr_bd_cmpck(lfs_t *lfs, lfs_block_t block, lfs_size_t off, lfs_size_t hint, const void *buffer, lfs_size_t size, lfsr_ck_t ck) { // check this read? - if (lfsr_m_isckparity(lfs->flags) - && lfsr_ck_isparity(ck)) { + if (LFS_IFDEF_CKPARITY( + lfsr_m_isckparity(lfs->flags) + && lfsr_ck_isparity(ck), + false) + || LFS_IFDEF_CKCKSUMS( + lfsr_m_isckcksums(lfs->flags), + false)) { return lfsr_bd_cmpck_(lfs, block, off, hint, buffer, size, @@ -1055,7 +1114,7 @@ static lfs_scmp_t lfsr_bd_cmpck(lfs_t *lfs, #endif // redirect to lfsr_bd_cmp if not checking reads -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_BD_CMPCK(_lfs, _block, _off, _hint, _buffer, _size, _ck) \ lfsr_bd_cmpck(_lfs, _block, _off, _hint, _buffer, _size, _ck) #else @@ -1063,7 +1122,7 @@ static lfs_scmp_t lfsr_bd_cmpck(lfs_t *lfs, lfsr_bd_cmp(_lfs, _block, _off, _hint, _buffer, _size) #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static int lfsr_bd_cpyck_(lfs_t *lfs, lfs_block_t dst_block, lfs_size_t dst_off, lfs_block_t src_block, lfs_size_t src_off, lfs_size_t hint, @@ -1081,10 +1140,12 @@ static int lfsr_bd_cpyck_(lfs_t *lfs, // checksum any prefixed data uint32_t src_cksum = 0; - lfs_ssize_t hint_ = lfsr_bd_ckprefix(lfs, src_block, src_off, hint, ck, + lfs_size_t hint_; + int err = lfsr_bd_ckprefix(lfs, src_block, src_off, hint, ck, + &hint_, &src_cksum); - if (hint_ < 0) { - return hint_; + if (err) { + return err; } // copy the data while simultaneously updating our checksum @@ -1098,7 +1159,7 @@ static int lfsr_bd_cpyck_(lfs_t *lfs, // clobbering the rcache at all uint8_t *buffer__; lfs_size_t size__; - int err = lfsr_bd_prognext(lfs, dst_block, dst_off_, size_, + err = lfsr_bd_prognext(lfs, dst_block, dst_off_, size_, &buffer__, &size__, cksum, align); if (err) { @@ -1125,7 +1186,7 @@ static int lfsr_bd_cpyck_(lfs_t *lfs, } // checksum any suffixed data and validate - int err = lfsr_bd_cksuffix(lfs, src_block, src_off+size, hint_-size, ck, + err = lfsr_bd_cksuffix(lfs, src_block, src_off+size, hint_-size, ck, src_cksum); if (err) { return err; @@ -1135,7 +1196,7 @@ static int lfsr_bd_cpyck_(lfs_t *lfs, } #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) static int lfsr_bd_cpyck(lfs_t *lfs, lfs_block_t dst_block, lfs_size_t dst_off, lfs_block_t src_block, lfs_size_t src_off, lfs_size_t hint, @@ -1143,8 +1204,13 @@ static int lfsr_bd_cpyck(lfs_t *lfs, lfsr_ck_t ck, uint32_t *cksum, bool align) { // check this read? - if (lfsr_m_isckparity(lfs->flags) - && lfsr_ck_isparity(ck)) { + if (LFS_IFDEF_CKPARITY( + lfsr_m_isckparity(lfs->flags) + && lfsr_ck_isparity(ck), + false) + || LFS_IFDEF_CKCKSUMS( + lfsr_m_isckcksums(lfs->flags), + false)) { return lfsr_bd_cpyck_(lfs, dst_block, dst_off, src_block, src_off, hint, @@ -1162,7 +1228,7 @@ static int lfsr_bd_cpyck(lfs_t *lfs, #endif // redirect to lfsr_bd_cpy if not checking reads -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_BD_CPYCK(_lfs, \ _dst_block, _dst_off, \ _src_block, _src_off, _hint, \ @@ -1646,6 +1712,23 @@ static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs, bool *parity_, uint32_t *cksum) { (void)parity_; + + #ifdef LFS_CKCKSUMS + // if we're checking checksums and not already calculating a + // checksum, we first need to checksum any prefixed data + uint32_t cksum__ = 0; + if (lfsr_m_isckcksums(lfs->flags) && !cksum) { + int err = lfsr_bd_ckprefix(lfs, block, off, hint, + // the actual parity doesn't really matter here + LFSR_CK_PARITY_(off, 0, 0), + &hint, + &cksum__); + if (err) { + return err; + } + } + #endif + // read the largest possible tag size uint8_t tag_buf[LFSR_TAG_DSIZE]; lfs_size_t tag_dsize = lfs_min(LFSR_TAG_DSIZE, lfs->cfg->block_size-off); @@ -1653,8 +1736,7 @@ static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs, return LFS_ERR_CORRUPT; } - int err = lfsr_bd_read(lfs, - block, off, hint, + int err = lfsr_bd_read(lfs, block, off, hint, tag_buf, tag_dsize); if (err < 0) { return err; @@ -1767,6 +1849,29 @@ static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs, } #endif + #ifdef LFS_CKCKSUMS + // if we're checking checksums and not already calculating a + // checksum, checksum any suffixed data + // + // this uses the rbyd's checksum to check things + if (lfsr_m_isckcksums(lfs->flags) && !cksum) { + // exclude valid bit from checksum + cksum__ ^= tag_buf[0] & 0x00000080; + // calculate checksum + cksum__ = lfs_crc32c(cksum__, tag_buf, d); + + err = lfsr_bd_cksuffix(lfs, block, off+d, hint, + // the actual parity doesn't really matter here + LFSR_CK_PARITY_(off, + d + ((!lfsr_tag_isalt(tag)) ? size : 0), + 0), + cksum__); + if (err) { + return err; + } + } + #endif + // save what we found, clearing the valid bit, we don't need it // anymore *tag_ = tag & 0x7fff; @@ -1837,7 +1942,7 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs, .size=_size, \ .u.buffer=(const void*)(_buffer)}) -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_DATA_DISKCKSUM(_block, _off, _size, _cksize, _cksum) \ ((lfsr_data_t){ \ .size=LFSR_DATA_ONDISK | (_size), \ @@ -1852,7 +1957,7 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs, .u.disk.off=_off}) #endif -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) #define LFSR_DATA_DISKPARITY(_block, _off, _size, _ckoff, _cksize, _parity) \ ((lfsr_data_t){ \ .size=LFSR_DATA_ONDISK | (_size), \ @@ -2514,7 +2619,7 @@ static int lfsr_data_readecksum(lfs_t *lfs, lfsr_data_t *data, // unnecessarily duplicate, long story short accessing ck info gets // annoyingly messy... static inline lfs_size_t lfsr_bptr_cksize(const lfsr_bptr_t *bptr) { - #ifdef LFS_CKPARITY + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) return bptr->data.u.disk.ck.cksize; #else return bptr->cksize; @@ -2522,7 +2627,7 @@ static inline lfs_size_t lfsr_bptr_cksize(const lfsr_bptr_t *bptr) { } static inline uint32_t lfsr_bptr_cksum(const lfsr_bptr_t *bptr) { - #ifdef LFS_CKPARITY + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) return bptr->data.u.disk.ck.u.cksum; #else return bptr->cksum; @@ -2595,7 +2700,9 @@ static int lfsr_data_readbptr(lfs_t *lfs, lfsr_data_t *data, err = lfsr_data_readlleb128(lfs, data, LFS_IFDEF_CKPARITY( &bptr->data.u.disk.ck.cksize, - &bptr->cksize)); + LFS_IFDEF_CKCKSUMS( + &bptr->data.u.disk.ck.cksize, + &bptr->cksize))); if (err) { return err; } @@ -2603,7 +2710,9 @@ static int lfsr_data_readbptr(lfs_t *lfs, lfsr_data_t *data, err = lfsr_data_readle32(lfs, data, LFS_IFDEF_CKPARITY( &bptr->data.u.disk.ck.u.cksum, - &bptr->cksum)); + LFS_IFDEF_CKCKSUMS( + &bptr->data.u.disk.ck.u.cksum, + &bptr->cksum))); if (err) { return err; } @@ -3158,7 +3267,10 @@ static int lfsr_rbyd_lookupnext(lfs_t *lfs, const lfsr_rbyd_t *rbyd, if (data_) { *data_ = LFSR_DATA_DISKPARITY( rbyd->blocks[0], branch + d, jump, - branch, d + jump, parity); + branch, d + jump, + // we don't really care about the parity if + // we're not checking it + LFS_IFDEF_CKPARITY(parity, false)); } return 0; } @@ -3234,6 +3346,282 @@ static int lfsr_rbyd_suplookup(lfs_t *lfs, const lfsr_rbyd_t *rbyd, } +// checked read rbyd helpers + +#ifdef LFS_CKCKSUMS +// a weaker lfsr_bd_readtag that is just used for ckcksums +// +// we can't call lfsr_bd_readtag in lfsr_bd_ckrbydprefix/suffix or else +// things would end up recursive +static int lfsr_bd_ckrbydtag(lfs_t *lfs, + lfs_block_t block, lfs_size_t off, + lfsr_tag_t *tag_, lfsr_rid_t *weight_, lfs_size_t *size_, + uint32_t *cksum) { + // read the largest possible tag size + uint8_t tag_buf[LFSR_TAG_DSIZE]; + lfs_size_t tag_dsize = lfs_min(LFSR_TAG_DSIZE, lfs->cfg->block_size-off); + if (tag_dsize < 4) { + return LFS_ERR_CORRUPT; + } + + int err = lfsr_bd_read(lfs, block, off, -1, + tag_buf, tag_dsize); + if (err < 0) { + return err; + } + + // check the valid bit + if ((tag_buf[0] >> 7) != lfs_parity(*cksum)) { + return LFS_ERR_CORRUPT; + } + + lfsr_tag_t tag + = ((lfsr_tag_t)tag_buf[0] << 8) + | ((lfsr_tag_t)tag_buf[1] << 0); + lfs_ssize_t d = 2; + + lfsr_rid_t weight; + lfs_ssize_t d_ = lfs_fromleb128(&weight, &tag_buf[d], tag_dsize-d); + if (d_ < 0) { + return d_; + } + // weights should be limited to 31-bits + if (weight > 0x7fffffff) { + return LFS_ERR_CORRUPT; + } + d += d_; + + lfs_size_t size; + d_ = lfs_fromleb128(&size, &tag_buf[d], tag_dsize-d); + if (d_ < 0) { + return d_; + } + // sizes should be limited to 28-bits + if (size > 0x0fffffff) { + return LFS_ERR_CORRUPT; + } + d += d_; + + // check our tag does not go out of bounds + if (!lfsr_tag_isalt(tag) && off+d + size > lfs->cfg->block_size) { + return LFS_ERR_CORRUPT; + } + + // exclude valid bit from checksum + *cksum ^= tag_buf[0] & 0x00000080; + // calculate checksum + *cksum = lfs_crc32c(*cksum, tag_buf, d); + + // save what we found, clearing the valid bit, we don't need it + // anymore + *tag_ = tag & 0x7fff; + *weight_ = weight; + *size_ = size; + return d; +} +#endif + +#ifdef LFS_CKCKSUMS +static int lfsr_bd_ckrbydprefix(lfs_t *lfs, + lfs_block_t block, lfs_size_t off, + lfsr_ck_t ck, + lfs_size_t *hint_, + uint32_t *cksum) { + // must be in-bounds + LFS_ASSERT(block < lfs->block_count); + LFS_ASSERT(lfsr_ck_ckoff(ck)+lfsr_ck_cksize(ck) <= lfs->cfg->block_size); + // doesn't make sense to be here if not checking parity + LFS_ASSERT(lfsr_ck_isparity(ck)); + + // this is basically just a small rbyd parser + + // checksum the revision count + uint32_t cksum_ = *cksum; + int err = lfsr_bd_cksum(lfs, block, 0, -1, sizeof(uint32_t), + &cksum_); + if (err) { + return err; + } + + uint32_t cksum__ = cksum_; + bool perturb = false; + lfs_size_t off_ = sizeof(uint32_t); + while (off_ < off) { + // read next tag + lfsr_tag_t tag; + lfsr_rid_t weight; + lfs_size_t size; + lfs_ssize_t d = lfsr_bd_ckrbydtag(lfs, block, off_, + &tag, &weight, &size, + &cksum__); + if (d < 0) { + return d; + } + lfs_size_t off__ = off_ + d; + + // ckrbydtag should already check we're in-bounds + LFS_ASSERT(lfsr_tag_isalt(tag) + || off__ + size <= lfs->cfg->block_size); + + // take care of cksum + if (!lfsr_tag_isalt(tag)) { + // not an end-of-commit cksum + if (lfsr_tag_suptype(tag) != LFSR_TAG_CKSUM) { + // checksum the entry + err = lfsr_bd_cksum(lfs, block, off__, -1, + // stop early if we hit our data + lfs_min(size, off-off__), + &cksum__); + if (err) { + return err; + } + + // is an end-of-commit cksum + } else { + // check checksum + uint32_t cksum___ = 0; + err = lfsr_bd_read(lfs, block, off__, -1, + &cksum___, sizeof(uint32_t)); + if (err) { + return err; + } + cksum___ = lfs_fromle32_(&cksum___); + + if (cksum__ != cksum___) { + // uh oh, checksums don't match + return LFS_ERR_CORRUPT; + } + + // revert to canonical checksum and perturb if necessary + perturb = lfsr_tag_p(tag); + cksum__ = cksum_ ^ ((perturb) + ? LFS_CRC32C_ODDZERO + : LFS_CRC32C_EVENZERO); + } + } + + // found a trunk? + if (lfsr_tag_istrunk(tag)) { + // update canonical checksum, xoring out any perturb + // state + cksum_ = cksum__ ^ ((perturb) + ? LFS_CRC32C_ODDZERO + : LFS_CRC32C_EVENZERO); + } + + // skip data + if (!lfsr_tag_isalt(tag)) { + off__ += size; + } + + off_ = off__; + } + + *hint_ = -1; + *cksum = cksum__; + return 0; +} +#endif + +#ifdef LFS_CKCKSUMS +static int lfsr_bd_ckrbydsuffix(lfs_t *lfs, + lfs_block_t block, lfs_size_t off, + lfsr_ck_t ck, + uint32_t cksum) { + // must be in-bounds + LFS_ASSERT(block < lfs->block_count); + LFS_ASSERT(lfsr_ck_ckoff(ck)+lfsr_ck_cksize(ck) <= lfs->cfg->block_size); + // doesn't make sense to be here if not checking parity + LFS_ASSERT(lfsr_ck_isparity(ck)); + + // checksum any suffixed data + int err = lfsr_bd_cksum(lfs, block, off, -1, + (lfsr_ck_ckoff(ck)+lfsr_ck_cksize(ck)) - off, + &cksum); + if (err) { + return err; + } + + lfs_size_t off_ = lfsr_ck_ckoff(ck)+lfsr_ck_cksize(ck); + while (off_ < lfs->cfg->block_size) { + // wait did we hit our tailck? tailck provides a temporary + // cksum if we're in the middle of building a commit + if (block == lfs->tailck.ckblock && off_ == lfs->tailck.ckoff) { + if (cksum != lfs->tailck.cksum) { + // uh oh, checksums don't match + return LFS_ERR_CORRUPT; + } + + // if we get here we've checked the commit containing + // our data + return 0; + } + + // read next tag + lfsr_tag_t tag; + lfsr_rid_t weight; + lfs_size_t size; + lfs_ssize_t d = lfsr_bd_ckrbydtag(lfs, block, off_, + &tag, &weight, &size, + &cksum); + if (d < 0) { + return d; + } + lfs_size_t off__ = off_ + d; + + // ckrbydtag should already check we're in-bounds + LFS_ASSERT(lfsr_tag_isalt(tag) + || off__ + size <= lfs->cfg->block_size); + + // take care of cksum + if (!lfsr_tag_isalt(tag)) { + // not an end-of-commit cksum + if (lfsr_tag_suptype(tag) != LFSR_TAG_CKSUM) { + // checksum the entry + err = lfsr_bd_cksum(lfs, block, off__, -1, size, + &cksum); + if (err) { + return err; + } + + // is an end-of-commit cksum + } else { + // check checksum + uint32_t cksum_ = 0; + err = lfsr_bd_read(lfs, block, off__, -1, + &cksum_, sizeof(uint32_t)); + if (err) { + return err; + } + cksum_ = lfs_fromle32_(&cksum_); + + if (cksum != cksum_) { + // uh oh, checksums don't match + return LFS_ERR_CORRUPT; + } + + // if we get here we've checked the commit containing + // our data + return 0; + } + } + + // skip data + if (!lfsr_tag_isalt(tag)) { + off__ += size; + } + + off_ = off__; + } + + // didn't find a checksum tag? that's weird + return LFS_ERR_CORRUPT; +} +#endif + + + +// rbyd append operations // append a revision count // @@ -3279,14 +3667,13 @@ static int lfsr_rbyd_appendtag(lfs_t *lfs, lfsr_rbyd_t *rbyd, rbyd->eoff += d; - #ifdef LFS_CKPARITY - // keep track of most recent parity + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) + // keep track of most recent cksum lfs->tailck.ckblock = rbyd->blocks[0]; - lfs->tailck.ckoff - = ((lfs_size_t)( - lfs_parity(rbyd->cksum) ^ lfsr_rbyd_isperturb(rbyd) - ) << (8*sizeof(lfs_size_t)-1)) - | lfsr_rbyd_eoff(rbyd); + lfs->tailck.ckoff = lfsr_rbyd_eoff(rbyd); + lfs->tailck.cksum = rbyd->cksum ^ ((lfsr_rbyd_isperturb(rbyd)) + ? LFS_CRC32C_ODDZERO + : LFS_CRC32C_EVENZERO); #endif return 0; @@ -3310,14 +3697,13 @@ static int lfsr_rbyd_appendcat(lfs_t *lfs, lfsr_rbyd_t *rbyd, rbyd->eoff += lfsr_cat_size(cat, count); - #ifdef LFS_CKPARITY + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) // keep track of most recent parity lfs->tailck.ckblock = rbyd->blocks[0]; - lfs->tailck.ckoff - = ((lfs_size_t)( - lfs_parity(rbyd->cksum) ^ lfsr_rbyd_isperturb(rbyd) - ) << (8*sizeof(lfs_size_t)-1)) - | lfsr_rbyd_eoff(rbyd); + lfs->tailck.ckoff = lfsr_rbyd_eoff(rbyd); + lfs->tailck.cksum = rbyd->cksum ^ ((lfsr_rbyd_isperturb(rbyd)) + ? LFS_CRC32C_ODDZERO + : LFS_CRC32C_EVENZERO); #endif return 0; @@ -6809,6 +7195,12 @@ static inline bool lfsr_m_isckparity(uint32_t flags) { } #endif +#ifdef LFS_CKCKSUMS +static inline bool lfsr_m_isckcksums(uint32_t flags) { + return flags & LFS_M_CKCKSUMS; +} +#endif + #ifdef LFS_CKFETCHES static inline bool lfsr_m_isckfetches(uint32_t flags) { return flags & LFS_M_CKFETCHES; @@ -11330,10 +11722,13 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, LFS_IFDEF_CKPARITY( (&(lfsr_bptr_t){ .data=left_slice_}), - (&(lfsr_bptr_t){ - .data=left_slice_, - .cksize=bptr_.cksize, - .cksum=bptr_.cksum})), + LFS_IFDEF_CKCKSUMS( + (&(lfsr_bptr_t){ + .data=left_slice_}), + (&(lfsr_bptr_t){ + .data=left_slice_, + .cksize=bptr_.cksize, + .cksum=bptr_.cksum}))), left.buf)); } else { @@ -11388,10 +11783,13 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, LFS_IFDEF_CKPARITY( (&(lfsr_bptr_t){ .data=right_slice_}), - (&(lfsr_bptr_t){ - .data=right_slice_, - .cksize=bptr_.cksize, - .cksum=bptr_.cksum})), + LFS_IFDEF_CKCKSUMS( + (&(lfsr_bptr_t){ + .data=right_slice_}), + (&(lfsr_bptr_t){ + .data=right_slice_, + .cksize=bptr_.cksize, + .cksum=bptr_.cksum}))), right.buf)); } else { @@ -11633,10 +12031,14 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, bptr.data = LFSR_DATA_DISKCKSUM(block, 0, 0, 0, 0); LFS_IFDEF_CKPARITY( bptr.data.u.disk.ck.cksize, - bptr.cksize) = 0; + LFS_IFDEF_CKCKSUMS( + bptr.data.u.disk.ck.cksize, + bptr.cksize)) = 0; LFS_IFDEF_CKPARITY( bptr.data.u.disk.ck.u.cksum, - bptr.cksum) = 0; + LFS_IFDEF_CKCKSUMS( + bptr.data.u.disk.ck.u.cksum, + bptr.cksum)) = 0; compact:; // compact data into our block @@ -11669,7 +12071,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, &buffer[pos_ - pos], d_, LFS_IFDEF_CKPARITY( &bptr.data.u.disk.ck.u.cksum, - &bptr.cksum), true); + LFS_IFDEF_CKCKSUMS( + &bptr.data.u.disk.ck.u.cksum, + &bptr.cksum)), true); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); // bad prog? try another block @@ -11682,7 +12086,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, pos_ += d_; LFS_IFDEF_CKPARITY( bptr.data.u.disk.ck.cksize, - bptr.cksize) += d_; + LFS_IFDEF_CKCKSUMS( + bptr.data.u.disk.ck.cksize, + bptr.cksize)) += d_; d -= d_; } @@ -11745,7 +12151,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, d_), LFS_IFDEF_CKPARITY( &bptr.data.u.disk.ck.u.cksum, - &bptr.cksum), true); + LFS_IFDEF_CKCKSUMS( + &bptr.data.u.disk.ck.u.cksum, + &bptr.cksum)), true); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); // bad prog? try another block @@ -11758,7 +12166,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, pos_ += d_; LFS_IFDEF_CKPARITY( bptr.data.u.disk.ck.cksize, - bptr.cksize) += d_; + LFS_IFDEF_CKCKSUMS( + bptr.data.u.disk.ck.cksize, + bptr.cksize)) += d_; d -= d_; } @@ -11772,7 +12182,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, 0, d, LFS_IFDEF_CKPARITY( &bptr.data.u.disk.ck.u.cksum, - &bptr.cksum), true); + LFS_IFDEF_CKCKSUMS( + &bptr.data.u.disk.ck.u.cksum, + &bptr.cksum)), true); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); // bad prog? try another block @@ -11785,7 +12197,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, pos_ += d; LFS_IFDEF_CKPARITY( bptr.data.u.disk.ck.cksize, - bptr.cksize) += d; + LFS_IFDEF_CKCKSUMS( + bptr.data.u.disk.ck.cksize, + bptr.cksize)) += d; } // A bit of a hack here, we need to truncate our block to prog_size @@ -11796,12 +12210,16 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, lfs->pcache.size -= d; LFS_IFDEF_CKPARITY( bptr.data.u.disk.ck.cksize, - bptr.cksize) -= d; + LFS_IFDEF_CKCKSUMS( + bptr.data.u.disk.ck.cksize, + bptr.cksize)) -= d; // finalize our write int err = lfsr_bd_flush(lfs, LFS_IFDEF_CKPARITY( &bptr.data.u.disk.ck.u.cksum, - &bptr.cksum), true); + LFS_IFDEF_CKCKSUMS( + &bptr.data.u.disk.ck.u.cksum, + &bptr.cksum)), true); if (err) { // bad prog? try another block if (err == LFS_ERR_CORRUPT) { @@ -12827,10 +13245,11 @@ static int lfs_init(lfs_t *lfs, uint32_t flags, } } - #ifdef LFS_CKPARITY + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) // setup tailck, nothing should actually check off=0 lfs->tailck.ckblock = 0; lfs->tailck.ckoff = 0; + lfs->tailck.cksum = 0; #endif // setup lookahead buffer, note mount finishes initializing this after @@ -13542,6 +13961,7 @@ int lfsr_mount(lfs_t *lfs, uint32_t flags, | LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, 0) | LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, 0) | LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, 0) + | LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, 0) | LFS_M_MTREEONLY | LFS_M_MKCONSISTENT | LFS_M_LOOKAHEAD @@ -13697,6 +14117,7 @@ int lfsr_format(lfs_t *lfs, uint32_t flags, | LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, 0) | LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, 0) | LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, 0) + | LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, 0) | LFS_F_MTREEONLY | LFS_F_COMPACT | LFS_F_CKMETA @@ -13765,6 +14186,7 @@ int lfsr_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { | LFS_IFDEF_CKPROGS(LFS_I_CKPROGS, 0) | LFS_IFDEF_CKFETCHES(LFS_I_CKFETCHES, 0) | LFS_IFDEF_CKPARITY(LFS_I_CKPARITY, 0) + | LFS_IFDEF_CKCKSUMS(LFS_I_CKCKSUMS, 0) | LFS_I_UNCOMPACTED); // some flags we calculate on demand fsinfo->flags |= (lfsr_fs_isinconsistent(lfs)) ? LFS_I_INCONSISTENT : 0; diff --git a/lfs.h b/lfs.h index 2aacfa84..597dd678 100644 --- a/lfs.h +++ b/lfs.h @@ -162,6 +162,9 @@ enum lfs_type { #ifdef LFS_CKPARITY #define LFS_F_CKPARITY 0x00400000 // Check tag parity bits on reads #endif +#ifdef LFS_CKCKSUMS +#define LFS_F_CKCKSUMS 0x00800000 // Check checksums on reads (expensive!) +#endif #define LFS_F_MTREEONLY 0x00000800 // Only traverse the mtree #define LFS_F_COMPACT 0x00008000 // Compact metadata logs @@ -182,6 +185,9 @@ enum lfs_type { #ifdef LFS_CKPARITY #define LFS_M_CKPARITY 0x00400000 // Check tag parity bits on reads #endif +#ifdef LFS_CKCKSUMS +#define LFS_M_CKCKSUMS 0x00800000 // Check checksums on reads (expensive!) +#endif #define LFS_M_MTREEONLY 0x00000800 // Only traverse the mtree #define LFS_M_MKCONSISTENT \ @@ -204,6 +210,9 @@ enum lfs_type { #ifdef LFS_CKPARITY #define LFS_I_CKPARITY 0x00400000 // Filesystem mounted with LFS_M_CKPARITY #endif +#ifdef LFS_CKCKSUMS +#define LFS_I_CKCKSUMS 0x00800000 // Filesystem mounted with LFS_M_CKCKSUMS +#endif #define LFS_I_INCONSISTENT \ 0x01000000 // Filesystem needs mkconsistent to write @@ -559,7 +568,7 @@ typedef struct lfsr_omdir { // lfs_block_t tail[2]; //} lfs_mdir_t; -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) // context for validating data typedef struct lfsr_ck { // sign(cksize)=0 => cksum check @@ -583,7 +592,7 @@ typedef struct lfsr_data { struct { lfs_block_t block; lfs_size_t off; - #ifdef LFS_CKPARITY + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) lfsr_ck_t ck; #endif } disk; @@ -758,11 +767,11 @@ typedef struct lfsr_grm { lfsr_smid_t mids[2]; } lfsr_grm_t; -#ifdef LFS_CKPARITY +#if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) typedef struct lfsr_tailck { lfs_block_t ckblock; - // sign(ckoff) => tail parity lfs_size_t ckoff; + uint32_t cksum; } lfsr_tailck_t; #endif @@ -798,7 +807,7 @@ typedef struct lfs { uint8_t *buffer; } pcache; - #ifdef LFS_CKPARITY + #if defined(LFS_CKPARITY) || defined(LFS_CKCKSUMS) lfsr_tailck_t tailck; #endif diff --git a/lfs_util.h b/lfs_util.h index c0af33d9..88e468e4 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -156,6 +156,12 @@ extern "C" #define LFS_IFDEF_CKPARITY(a, b) (b) #endif +#ifdef LFS_CKCKSUMS +#define LFS_IFDEF_CKCKSUMS(a, b) (a) +#else +#define LFS_IFDEF_CKCKSUMS(a, b) (b) +#endif + // Builtin functions, these may be replaced by more efficient // toolchain-specific implementations. LFS_NO_BUILTINS falls back to a more diff --git a/tests/test_ck.toml b/tests/test_ck.toml index a4bd3d7a..10cb329e 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -1095,7 +1095,7 @@ code = ''' # will make them more useful. # -# test every single-bit error in block 0/1 +# test some single-bit errors in block 0/1 [cases.test_ck_ckparity_mroot] defines.BADBLOCK = [0, 1] defines.BADBIT = -1 @@ -1208,7 +1208,7 @@ code = ''' } ''' -# test every single-bit error in a file's btree node +# test some single-bit errors in a file's btree node [cases.test_ck_ckparity_btree] defines.BADBIT = -1 defines.BADBLOCK_BEHAVIOR = [ @@ -1346,6 +1346,386 @@ code = ''' +# Some simple ckcksums tests + +# test every single-bit error in block 0/1 +[cases.test_ck_ckcksums_mroot] +defines.BADBLOCK = [0, 1] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = [ + 'LFS_EMUBD_BADBLOCK_PROGFLIP', + 'LFS_EMUBD_BADBLOCK_READFLIP', +] +# this should stay inlined +defines.SIZE = 'BLOCK_SIZE/16' +ifdef = 'LFS_CKCKSUMS' +code = ''' + // test all bad bits in the mroot + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + (lfs_size_t)BADBLOCK, badbit/8, badbit, badbit/8, badbit%8); + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, BADBLOCK, badbit) => 0; + + // With metastability, basically any filesystem operation can + // return LFS_ERR_CORRUPT. This is ok, what we're really testing + // for is no internal/external asserts failing. + + // format + lfs_t lfs; + int err = lfsr_format(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + err = lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + + { + // create a file + lfsr_file_t file; + err = lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + err = lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + } + + // yes reads can fail here + err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); + assert(!err + || err == LFS_ERR_CORRUPT + // bit errors can also cause our fs state to "rollback", + // which is not great but we can't solve this with ckreads + // alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_CORRUPT || err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfs_ssize_t res = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + corrupt:; + // reset badbit + lfs_emubd_markgood(CFG, BADBLOCK) => 0; + } +''' + +# test every single-bit error in a file's data block +[cases.test_ck_ckcksums_data] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = [ + 'LFS_EMUBD_BADBLOCK_PROGFLIP', + 'LFS_EMUBD_BADBLOCK_READFLIP', +] +# this should create a single block file +defines.SIZE = 'BLOCK_SIZE' +ifdef = 'LFS_CKCKSUMS' +code = ''' + // first we need to figure out where the data block will actually + // end up, fortunately our block randomization is intentionally + // consistent + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKCKSUMS, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // find the data block + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, 0) => 0; + lfs_block_t badblock; + while (true) { + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + if (tinfo.btype == LFS_BTYPE_DATA) { + badblock = tinfo.block; + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + lfsr_unmount(&lfs) => 0; + + // now test all bad bits in the data block + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; + + // With metastability, basically any filesystem operation can + // return LFS_ERR_CORRUPT. This is ok, what we're really testing + // for is no internal/external asserts failing. + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKCKSUMS, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + int err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG) => 0; + } + + // yes reads can fail here + err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); + assert(!err + || err == LFS_ERR_CORRUPT + // bit errors can also cause our fs state to "rollback", + // which is not great but we can't solve this with ckreads + // alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_CORRUPT || err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfs_ssize_t res = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // reset badbit + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# test every single-bit error in a file's btree node +[cases.test_ck_ckcksums_btree] +defines.BADBIT = -1 +defines.BADBLOCK_BEHAVIOR = [ + 'LFS_EMUBD_BADBLOCK_PROGFLIP', + 'LFS_EMUBD_BADBLOCK_READFLIP', +] +# force the file to create a btree +defines.INLINE_SIZE = 0 +defines.CRYSTAL_THRESH = -1 +defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8' +defines.SIZE = '2*FRAGMENT_SIZE' +ifdef = 'LFS_CKCKSUMS' +code = ''' + // first we need to figure out where the btree block will actually + // end up, fortunately our block randomization is intentionally + // consistent + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKCKSUMS, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // find the btree block + lfsr_traversal_t t; + lfsr_traversal_open(&lfs, &t, 0) => 0; + lfs_block_t badblock; + while (true) { + struct lfs_tinfo tinfo; + lfsr_traversal_read(&lfs, &t, &tinfo) => 0; + if (tinfo.btype == LFS_BTYPE_BTREE) { + badblock = tinfo.block; + break; + } + } + lfsr_traversal_close(&lfs, &t) => 0; + + lfsr_unmount(&lfs) => 0; + + // now test all bad bits in the btree block + for (lfs_size_t i = 0; + i < ((BADBIT == -1) ? 8*BLOCK_SIZE : 1); + i++) { + lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // mark our badbit as bad + lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; + + // With metastability, basically any filesystem operation can + // return LFS_ERR_CORRUPT. This is ok, what we're really testing + // for is no internal/external asserts failing. + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKCKSUMS, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "bathykorus", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + uint32_t prng = 42; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfs_ssize_t res = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + int err = lfsr_file_close(&lfs, &file); + if (err == LFS_ERR_CORRUPT) { + goto corrupt_mounted; + } + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKCKSUMS, CFG) => 0; + } + + // yes reads can fail here + err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); + assert(!err + || err == LFS_ERR_CORRUPT + // bit errors can also cause our fs state to "rollback", + // which is not great but we can't solve this with ckreads + // alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_CORRUPT || err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfs_ssize_t res = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(res == SIZE || res == LFS_ERR_CORRUPT); + if (res == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // reset badbit + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + + + ## High-level error spam tests # @@ -1360,6 +1740,7 @@ code = ''' # METHOD=0 => ckprogs # METHOD=1 => ckfetches # METHOD=2 => ckparity +# METHOD=3 => ckcksums defines.METHOD = [0] defines.PERIOD = 10 defines.PROTECTED_MROOTANCHOR = [false, true] @@ -1367,6 +1748,7 @@ defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' defines.CKPROGS = 'METHOD == 0' defines.CKFETCHES = 'METHOD == 1' defines.CKPARITY = 'METHOD == 2' +defines.CKCKSUMS = 'METHOD == 3' defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256] defines.SEED = 'range(10)' fuzz = 'SEED' @@ -1374,6 +1756,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', ] code = ''' // seed our block device with our seed so we have different error @@ -1394,13 +1777,15 @@ code = ''' LFS_F_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, -1) : 0), CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; // set up a simulation to compare against @@ -1421,8 +1806,8 @@ code = ''' // types of errors, so we implement errors for each one a // bit differently - // ckprogs? ckparity? - if (METHOD == 0 || METHOD == 2) { + // ckprogs? ckparity? ckcksums? + if (METHOD == 0 || METHOD == 2 || METHOD == 3) { // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; @@ -1543,6 +1928,9 @@ code = ''' : 0) | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) + : 0) + | ((CKCKSUMS) + ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; } @@ -1601,6 +1989,7 @@ corrupt_mounted:; # METHOD=0 => ckprogs # METHOD=1 => ckfetches # METHOD=2 => ckparity +# METHOD=3 => ckcksums defines.METHOD = [0] defines.PERIOD = 10 defines.PROTECTED_MROOTANCHOR = [false, true] @@ -1608,6 +1997,7 @@ defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' defines.CKPROGS = 'METHOD == 0' defines.CKFETCHES = 'METHOD == 1' defines.CKPARITY = 'METHOD == 2' +defines.CKCKSUMS = 'METHOD == 3' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', @@ -1624,6 +2014,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', '(SIZE*N)/BLOCK_SIZE <= 16', ] code = ''' @@ -1645,13 +2036,15 @@ code = ''' LFS_F_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, -1) : 0), CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; // set up a simulation to compare against @@ -1673,8 +2066,8 @@ code = ''' // types of errors, so we implement errors for each one a // bit differently - // ckprogs? ckparity? - if (METHOD == 0 || METHOD == 2) { + // ckprogs? ckparity? ckcksums? + if (METHOD == 0 || METHOD == 2 || METHOD == 3) { // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; @@ -1841,6 +2234,9 @@ code = ''' : 0) | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) + : 0) + | ((CKCKSUMS) + ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; } @@ -1914,6 +2310,7 @@ corrupt_mounted:; # METHOD=0 => ckprogs # METHOD=1 => ckfetches # METHOD=2 => ckparity +# METHOD=3 => ckcksums defines.METHOD = [0] defines.PERIOD = 10 defines.PROTECTED_MROOTANCHOR = [false, true] @@ -1921,6 +2318,7 @@ defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' defines.CKPROGS = 'METHOD == 0' defines.CKFETCHES = 'METHOD == 1' defines.CKPARITY = 'METHOD == 2' +defines.CKCKSUMS = 'METHOD == 3' defines.SIZE = [ 'FILE_BUFFER_SIZE/2', '2*FILE_BUFFER_SIZE', @@ -1942,6 +2340,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', 'CHUNK <= SIZE', # this just saves testing time 'SIZE <= 4*1024*FRAGMENT_SIZE', @@ -1965,13 +2364,15 @@ code = ''' LFS_F_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, -1) : 0), CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; // create a file @@ -2015,8 +2416,8 @@ code = ''' // types of errors, so we implement errors for each one a // bit differently - // ckprogs? ckparity? - if (METHOD == 0 || METHOD == 2) { + // ckprogs? ckparity? ckcksums? + if (METHOD == 0 || METHOD == 2 || METHOD == 3) { // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; @@ -2083,6 +2484,9 @@ code = ''' : 0) | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) + : 0) + | ((CKCKSUMS) + ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; } @@ -2142,6 +2546,7 @@ defines.BADBLOCK = -1 # METHOD=0 => ckprogs # METHOD=1 => ckfetches # METHOD=2 => ckparity +# METHOD=3 => ckcksums defines.METHOD = [0] defines.PERIOD = 10 defines.PROTECTED_MROOTANCHOR = [false, true] @@ -2149,6 +2554,7 @@ defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' defines.CKPROGS = 'METHOD == 0' defines.CKFETCHES = 'METHOD == 1' defines.CKPARITY = 'METHOD == 2' +defines.CKCKSUMS = 'METHOD == 3' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', @@ -2165,6 +2571,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', '(SIZE*N)/BLOCK_SIZE <= 16', ] code = ''' @@ -2186,13 +2593,15 @@ code = ''' LFS_F_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, -1) : 0), CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; // set up a simulation to compare against @@ -2224,8 +2633,8 @@ code = ''' // types of errors, so we implement errors for each one a // bit differently - // ckprogs? ckparity? - if (METHOD == 0 || METHOD == 2) { + // ckprogs? ckparity? ckcksums? + if (METHOD == 0 || METHOD == 2 || METHOD == 3) { // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; @@ -2576,6 +2985,7 @@ corrupt_mounted:; # METHOD=0 => ckprogs # METHOD=1 => ckfetches # METHOD=2 => ckparity +# METHOD=3 => ckcksums defines.METHOD = [0] defines.PERIOD = 10 defines.PROTECTED_MROOTANCHOR = [false, true] @@ -2583,6 +2993,7 @@ defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' defines.CKPROGS = 'METHOD == 0' defines.CKFETCHES = 'METHOD == 1' defines.CKPARITY = 'METHOD == 2' +defines.CKCKSUMS = 'METHOD == 3' defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', @@ -2599,6 +3010,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', '(SIZE*N)/BLOCK_SIZE <= 16', ] code = ''' @@ -2620,13 +3032,15 @@ code = ''' LFS_F_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, -1) : 0), CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) - | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0), + | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0), CFG) => 0; // set up a simulation to compare against @@ -2659,8 +3073,8 @@ code = ''' // types of errors, so we implement errors for each one a // bit differently - // ckprogs? ckparity? - if (METHOD == 0 || METHOD == 2) { + // ckprogs? ckparity? ckcksums? + if (METHOD == 0 || METHOD == 2 || METHOD == 3) { // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; diff --git a/tests/test_mount.toml b/tests/test_mount.toml index 706b2e5d..d16f1c39 100644 --- a/tests/test_mount.toml +++ b/tests/test_mount.toml @@ -20,6 +20,7 @@ defines.SYNC = [false, true] defines.CKPROGS = [false, true] defines.CKFETCHES = [false, true] defines.CKPARITY = [false, true] +defines.CKCKSUMS = [false, true] defines.MTREEONLY = [false, true] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] @@ -30,6 +31,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', '!RDONLY || !MKCONSISTENT', '!RDONLY || !LOOKAHEAD', '!RDONLY || !COMPACT', @@ -46,6 +48,7 @@ code = ''' | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_M_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_M_CKCKSUMS, -1) : 0) | ((MTREEONLY) ? LFS_M_MTREEONLY : 0) | ((MKCONSISTENT) ? LFS_M_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS_M_LOOKAHEAD : 0) @@ -64,6 +67,7 @@ code = ''' | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_I_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_I_CKFETCHES, -1) : 0) | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_I_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_I_CKCKSUMS, -1) : 0) | ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0) | ((!COMPACT) ? LFS_I_UNCOMPACTED : 0))); @@ -77,6 +81,7 @@ code = ''' defines.CKPROGS = [false, true] defines.CKFETCHES = [false, true] defines.CKPARITY = [false, true] +defines.CKCKSUMS = [false, true] defines.MTREEONLY = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] @@ -85,6 +90,7 @@ if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS_IFDEF_CKPARITY(true, !CKPARITY)', + 'LFS_IFDEF_CKCKSUMS(true, !CKCKSUMS)', '!MTREEONLY || !CKDATA', ] code = ''' @@ -94,6 +100,7 @@ code = ''' | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) | ((CKPARITY) ? LFS_IFDEF_CKPARITY(LFS_F_CKPARITY, -1) : 0) + | ((CKCKSUMS) ? LFS_IFDEF_CKCKSUMS(LFS_F_CKCKSUMS, -1) : 0) | ((MTREEONLY) ? LFS_M_MTREEONLY : 0) | ((COMPACT) ? LFS_M_COMPACT : 0) | ((CKMETA) ? LFS_M_CKMETA : 0)