diff --git a/bd/lfs_emubd.c b/bd/lfs_emubd.c index c14b2c59..c7d1b515 100644 --- a/bd/lfs_emubd.c +++ b/bd/lfs_emubd.c @@ -1232,6 +1232,30 @@ int lfs_emubd_markbadbit(const struct lfs_config *cfg, return 0; } +int lfs_emubd_flipbit(const struct lfs_config *cfg, + lfs_block_t block, lfs_size_t bit) { + LFS_EMUBD_TRACE("lfs_emubd_flipbit(%p, %"PRIu32", %"PRIu32")", + (void*)cfg, block, bit); + lfs_emubd_t *bd = cfg->context; + + // check if block is valid + LFS_ASSERT(block < cfg->block_count); + + // mutate the block + lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, bd->blocks[block]); + if (!b) { + LFS_EMUBD_TRACE("lfs_emubd_flipbit -> %d", LFS_ERR_NOMEM); + return LFS_ERR_NOMEM; + } + bd->blocks[block] = b; + + // flip the bit + b->data[bit/8] ^= 1 << (bit%8); + + LFS_EMUBD_TRACE("lfs_emubd_flipbit -> %d", 0); + return 0; +} + lfs_emubd_spowercycles_t lfs_emubd_powercycles( const struct lfs_config *cfg) { LFS_EMUBD_TRACE("lfs_emubd_powercycles(%p)", (void*)cfg); diff --git a/bd/lfs_emubd.h b/bd/lfs_emubd.h index d0812480..326590d5 100644 --- a/bd/lfs_emubd.h +++ b/bd/lfs_emubd.h @@ -241,6 +241,10 @@ int lfs_emubd_randomizebadbit(const struct lfs_config *cfg, int lfs_emubd_markbadbit(const struct lfs_config *cfg, lfs_block_t block, lfs_size_t bit); +// Flip a bit in a given block, intended for emulating bit errors +int lfs_emubd_flipbit(const struct lfs_config *cfg, + lfs_block_t block, lfs_size_t bit); + // Get the remaining power-cycles lfs_emubd_spowercycles_t lfs_emubd_powercycles( const struct lfs_config *cfg); diff --git a/lfs.c b/lfs.c index 1d0ee6ba..debec286 100644 --- a/lfs.c +++ b/lfs.c @@ -317,7 +317,7 @@ static int lfsr_bd_prog_(lfs_t *lfs, lfs_block_t block, lfs_size_t off, } #ifdef LFS_CKPROGS - // check progs? + // checking progs? if (lfsr_m_isckprogs(lfs->flags)) { // pcache should have been dropped at this point LFS_ASSERT(lfs->pcache.size == 0); @@ -4794,6 +4794,11 @@ static int lfsr_data_readbtree(lfs_t *lfs, lfsr_data_t *data, // core btree operations +// needed in lfsr_btree_lookupnext_ +#ifdef LFS_CKFETCHES +static inline bool lfsr_m_isckfetches(uint32_t flags); +#endif + static int lfsr_btree_lookupnext_(lfs_t *lfs, const lfsr_btree_t *btree, lfsr_bid_t bid, lfsr_bid_t *bid_, lfsr_rbyd_t *rbyd_, lfsr_srid_t *rid_, @@ -4833,6 +4838,18 @@ static int lfsr_btree_lookupnext_(lfs_t *lfs, const lfsr_btree_t *btree, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &branch, + branch.blocks[0], lfsr_rbyd_trunk(&branch), + branch.cksum); + if (err) { + return err; + } + } + #endif + // found our bid } else { // TODO how many of these should be conditional? @@ -4946,6 +4963,18 @@ static int lfsr_btree_parent(lfs_t *lfs, const lfsr_btree_t *btree, return 0; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &branch_, + branch_.blocks[0], lfsr_rbyd_trunk(&branch_), + branch_.cksum); + if (err) { + return err; + } + } + #endif + branch = branch_; } } @@ -5117,6 +5146,18 @@ static int lfsr_btree_commit__(lfs_t *lfs, lfsr_btree_t *btree, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &sibling, + sibling.blocks[0], lfsr_rbyd_trunk(&sibling), + sibling.cksum); + if (err) { + return err; + } + } + #endif + // estimate if our sibling will fit lfs_ssize_t sibling_estimate = lfsr_rbyd_estimate(lfs, &sibling, -1, -1, @@ -5165,6 +5206,18 @@ static int lfsr_btree_commit__(lfs_t *lfs, lfsr_btree_t *btree, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &sibling, + sibling.blocks[0], lfsr_rbyd_trunk(&sibling), + sibling.cksum); + if (err) { + return err; + } + } + #endif + // estimate if our sibling will fit lfs_ssize_t sibling_estimate = lfsr_rbyd_estimate(lfs, &sibling, -1, -1, @@ -5607,6 +5660,18 @@ static lfs_scmp_t lfsr_btree_namelookup(lfs_t *lfs, const lfsr_btree_t *btree, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &branch, + branch.blocks[0], lfsr_rbyd_trunk(&branch), + branch.cksum); + if (err < 0) { + return err; + } + } + #endif + // found our rid } else { // TODO how many of these should be conditional? @@ -5703,6 +5768,19 @@ static int lfsr_btree_traverse(lfs_t *lfs, const lfsr_btree_t *btree, if (err) { return err; } + + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &bt->rbyd, + bt->rbyd.blocks[0], lfsr_rbyd_trunk(&bt->rbyd), + bt->rbyd.cksum); + if (err) { + return err; + } + } + #endif + LFS_ASSERT((lfsr_bid_t)bt->rbyd.weight == weight__); bt->branch = &bt->rbyd; @@ -6697,6 +6775,12 @@ static inline bool lfsr_m_isckreads(uint32_t flags) { } #endif +#ifdef LFS_CKFETCHES +static inline bool lfsr_m_isckfetches(uint32_t flags) { + return flags & LFS_M_CKFETCHES; +} +#endif + static inline bool lfsr_m_isflush(uint32_t flags) { return flags & LFS_M_FLUSH; } @@ -9185,6 +9269,19 @@ static int lfsr_mtree_traverse_(lfs_t *lfs, lfsr_traversal_t *t, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &t->o.bshrub.u.btree, + t->o.bshrub.u.btree.blocks[0], + lfsr_rbyd_trunk(&t->o.bshrub.u.btree), + t->o.bshrub.u.btree.cksum); + if (err) { + return err; + } + } + #endif + // transition to traversing the mtree t->u.bt = LFSR_BTRAVERSAL(); t->o.o.flags = lfsr_t_settstate(t->o.o.flags, @@ -9266,6 +9363,19 @@ static int lfsr_mtree_traverse_(lfs_t *lfs, lfsr_traversal_t *t, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &t->o.bshrub.u.btree, + t->o.bshrub.u.btree.blocks[0], + lfsr_rbyd_trunk(&t->o.bshrub.u.btree), + t->o.bshrub.u.btree.cksum); + if (err) { + return err; + } + } + #endif + // no? next we need to check any opened files } else { t->ot = lfs->omdirs; @@ -10726,6 +10836,19 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, if (err) { return err; } + + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags)) { + err = lfsr_rbyd_fetchck(lfs, &file->o.bshrub.u.btree, + file->o.bshrub.u.btree.blocks[0], + lfsr_rbyd_trunk(&file->o.bshrub.u.btree), + file->o.bshrub.u.btree.cksum); + if (err) { + return err; + } + } + #endif } } } @@ -10847,6 +10970,17 @@ static lfs_ssize_t lfsr_file_readnext(lfs_t *lfs, const lfsr_file_t *file, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags) + && tag == LFSR_TAG_BLOCK) { + err = lfsr_bptr_ck(lfs, &bptr); + if (err) { + return err; + } + } + #endif + // any data on disk? if (pos_ < bid-(weight-1) + lfsr_data_size(bptr.data)) { // note one important side-effect here is a strict @@ -11089,6 +11223,17 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags) + && tag_ == LFSR_TAG_BLOCK) { + err = lfsr_bptr_ck(lfs, &bptr_); + if (err) { + return err; + } + } + #endif + // note, an entry can be both a left and right sibling lfsr_data_t left_slice_ = lfsr_data_slice(bptr_.data, -1, @@ -11556,6 +11701,17 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags) + && tag_ == LFSR_TAG_BLOCK) { + err = lfsr_bptr_ck(lfs, &bptr_); + if (err) { + return err; + } + } + #endif + // make sure to include all of our crystal, or else this // loop may never terminate if (bid_-(weight_-1) >= crystal_end @@ -11718,6 +11874,17 @@ fragment:; return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags) + && tag == LFSR_TAG_BLOCK) { + err = lfsr_bptr_ck(lfs, &bptr); + if (err) { + return err; + } + } + #endif + // can we coalesce? if (bid-(weight-1) + lfsr_data_size(bptr.data) >= fragment_start && fragment_end - (bid-(weight-1)) @@ -11756,6 +11923,17 @@ fragment:; return err; } + #ifdef LFS_CKFETCHES + // checking fetches? + if (lfsr_m_isckfetches(lfs->flags) + && tag == LFSR_TAG_BLOCK) { + err = lfsr_bptr_ck(lfs, &bptr); + if (err) { + return err; + } + } + #endif + // can we coalesce? if (fragment_end < bid-(weight-1) + lfsr_data_size(bptr.data) && bid-(weight-1) + lfsr_data_size(bptr.data) @@ -13353,6 +13531,7 @@ int lfsr_mount(lfs_t *lfs, uint32_t flags, | LFS_M_RDONLY | LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, 0) | LFS_IFDEF_CKREADS(LFS_M_CKREADS, 0) + | LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, 0) | LFS_M_FLUSH | LFS_M_SYNC | LFS_M_MTREEONLY @@ -13509,6 +13688,7 @@ int lfsr_format(lfs_t *lfs, uint32_t flags, LFS_F_RDWR | LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, 0) | LFS_IFDEF_CKREADS(LFS_F_CKREADS, 0) + | LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, 0) | LFS_F_MTREEONLY | LFS_F_COMPACT | LFS_F_CKMETA @@ -13574,6 +13754,7 @@ int lfsr_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { LFS_I_RDONLY | LFS_IFDEF_CKPROGS(LFS_I_CKPROGS, 0) | LFS_IFDEF_CKREADS(LFS_I_CKREADS, 0) + | LFS_IFDEF_CKFETCHES(LFS_I_CKFETCHES, 0) | LFS_I_FLUSH | LFS_I_SYNC | LFS_I_UNCOMPACTED); diff --git a/lfs.h b/lfs.h index 79229eac..ac3fc341 100644 --- a/lfs.h +++ b/lfs.h @@ -159,6 +159,9 @@ enum lfs_type { #ifdef LFS_CKREADS #define LFS_F_CKREADS 0x00200000 // Check reads via parity bits/checksums #endif +#ifdef LFS_CKFETCHES +#define LFS_F_CKFETCHES 0x00400000 // Check checksums before reads +#endif #define LFS_F_MTREEONLY 0x00000800 // Only traverse the mtree #define LFS_F_COMPACT 0x00008000 // Compact metadata logs @@ -176,6 +179,9 @@ enum lfs_type { #ifdef LFS_CKREADS #define LFS_M_CKREADS 0x00200000 // Check reads via parity bits/checksums #endif +#ifdef LFS_CKFETCHES +#define LFS_M_CKFETCHES 0x00400000 // Check checksums before reads +#endif #define LFS_M_MTREEONLY 0x00000800 // Only traverse the mtree #define LFS_M_MKCONSISTENT \ @@ -195,6 +201,9 @@ enum lfs_type { #ifdef LFS_CKREADS #define LFS_I_CKREADS 0x00200000 // Filesystem mounted with LFS_M_CKREADS #endif +#ifdef LFS_CKFETCHES +#define LFS_I_CKFETCHES 0x00400000 // Filesystem mounted with LFS_M_CKFETCHES +#endif #define LFS_I_INCONSISTENT \ 0x01000000 // Filesystem needs mkconsistent to write diff --git a/lfs_util.h b/lfs_util.h index 4d99d618..dc56e1cd 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -150,6 +150,12 @@ extern "C" #define LFS_IFDEF_CKREADS(a, b) (b) #endif +#ifdef LFS_CKFETCHES +#define LFS_IFDEF_CKFETCHES(a, b) (a) +#else +#define LFS_IFDEF_CKFETCHES(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 59b5385a..561b5253 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -770,8 +770,6 @@ code = ''' # Some simple ckread tests -# -# We test these much more aggressively in test_badblocks # These tests were originally intended to test all single-bit # metastability errors with ckreads, however they quickly found that @@ -883,7 +881,7 @@ code = ''' err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); assert(!err || err == LFS_ERR_CORRUPT - // metastability can also cause our fs state to "rollback", + // 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); @@ -1015,7 +1013,7 @@ code = ''' err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); assert(!err || err == LFS_ERR_CORRUPT - // metastability can also cause our fs state to "rollback", + // 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); @@ -1151,7 +1149,7 @@ code = ''' err = lfsr_file_open(&lfs, &file, "bathykorus", LFS_O_RDONLY); assert(!err || err == LFS_ERR_CORRUPT - // metastability can also cause our fs state to "rollback", + // 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); @@ -1177,3 +1175,307 @@ code = ''' lfs_emubd_markgood(CFG, badblock) => 0; } ''' + + + +# Some simple ckfetches tests + +# test every single-bit error in block 0/1 +[cases.test_ck_ckfetches_mroot] +defines.BADBLOCK = [0, 1] +defines.BADBIT = -1 +# this should stay inlined +defines.SIZE = 'BLOCK_SIZE/16' +ifdef = 'LFS_CKFETCHES' +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; + + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + (lfs_size_t)BADBLOCK, badbit/8, badbit, badbit/8, badbit%8); + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKFETCHES, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKFETCHES, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "tripedalia", + 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; + + // try to read our file + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + + // flip our badbit + lfs_emubd_flipbit(CFG, BADBLOCK, badbit) => 0; + + int err = lfsr_mount(&lfs, + LFS_M_RDWR | LFS_M_CKFETCHES, CFG); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + goto corrupt; + } + } + + // yes reads can fail here + int err = lfsr_file_open(&lfs, &file, + "tripedalia", LFS_O_RDONLY); + assert(!err + // bit errors can also cause our fs state to "rollback", + // which is not great but we can't solve this with + // ckfetches alone + || err == LFS_ERR_NOENT); + if (err == LFS_ERR_NOENT) { + goto corrupt_mounted; + } + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + 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_ckfetches_data] +defines.BADBIT = -1 +# this should create a single block file +defines.SIZE = 'BLOCK_SIZE' +ifdef = 'LFS_CKFETCHES' +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_CKFETCHES, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKFETCHES, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "tripedalia", + 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; + + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKFETCHES, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKFETCHES, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "tripedalia", + 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; + + // flip our badbit + lfs_emubd_flipbit(CFG, badblock, badbit) => 0; + + // 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_CKFETCHES, CFG) => 0; + } + + // yes reads can fail here + int err = lfsr_file_open(&lfs, &file, + "tripedalia", LFS_O_RDONLY); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + 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_ckfetches_btree] +defines.BADBIT = -1 +# 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_CKFETCHES' +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_CKFETCHES, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKFETCHES, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "tripedalia", + 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; + + printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", + badblock, badbit/8, badbit, badbit/8, badbit%8); + + // format + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR | LFS_F_CKFETCHES, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR | LFS_M_CKFETCHES, CFG) => 0; + + { + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "tripedalia", + 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; + + // flip our badbit + lfs_emubd_flipbit(CFG, badblock, badbit) => 0; + + // 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_CKFETCHES, CFG) => 0; + } + + // yes reads can fail here + int err = lfsr_file_open(&lfs, &file, + "tripedalia", LFS_O_RDONLY); + assert(!err || err == LFS_ERR_CORRUPT); + if (err == LFS_ERR_CORRUPT) { + 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; + } +''' diff --git a/tests/test_mount.toml b/tests/test_mount.toml index 0772758b..ad13bab7 100644 --- a/tests/test_mount.toml +++ b/tests/test_mount.toml @@ -17,6 +17,7 @@ code = ''' defines.RDONLY = [false, true] defines.CKPROGS = [false, true] defines.CKREADS = [false, true] +defines.CKFETCHES = [false, true] defines.FLUSH = [false, true] defines.SYNC = [false, true] defines.MTREEONLY = [false, true] @@ -28,6 +29,7 @@ defines.CKDATA = [false, true] if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', '!RDONLY || !MKCONSISTENT', '!RDONLY || !LOOKAHEAD', '!RDONLY || !COMPACT', @@ -41,6 +43,7 @@ code = ''' ((RDONLY) ? LFS_M_RDONLY : LFS_M_RDWR) | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_M_CKPROGS, -1) : 0) | ((CKREADS) ? LFS_IFDEF_CKREADS(LFS_M_CKREADS, -1) : 0) + | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_M_CKFETCHES, -1) : 0) | ((FLUSH) ? LFS_M_FLUSH : 0) | ((SYNC) ? LFS_M_SYNC : 0) | ((MTREEONLY) ? LFS_M_MTREEONLY : 0) @@ -58,6 +61,7 @@ code = ''' ((RDONLY) ? LFS_I_RDONLY : 0) | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_I_CKPROGS, -1) : 0) | ((CKREADS) ? LFS_IFDEF_CKREADS(LFS_I_CKREADS, -1) : 0) + | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_I_CKFETCHES, -1) : 0) | ((FLUSH) ? LFS_I_FLUSH : 0) | ((SYNC) ? LFS_I_SYNC : 0) | ((!LOOKAHEAD) ? LFS_I_CANLOOKAHEAD : 0) @@ -72,6 +76,7 @@ code = ''' [cases.test_mount_format_flags] defines.CKPROGS = [false, true] defines.CKREADS = [false, true] +defines.CKFETCHES = [false, true] defines.MTREEONLY = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] @@ -79,6 +84,7 @@ defines.CKDATA = [false, true] if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', '!MTREEONLY || !CKDATA', ] code = ''' @@ -87,6 +93,7 @@ code = ''' LFS_F_RDWR | ((CKPROGS) ? LFS_IFDEF_CKPROGS(LFS_F_CKPROGS, -1) : 0) | ((CKREADS) ? LFS_IFDEF_CKREADS(LFS_F_CKREADS, -1) : 0) + | ((CKFETCHES) ? LFS_IFDEF_CKFETCHES(LFS_F_CKFETCHES, -1) : 0) | ((MTREEONLY) ? LFS_M_MTREEONLY : 0) | ((COMPACT) ? LFS_M_COMPACT : 0) | ((CKMETA) ? LFS_M_CKMETA : 0)