diff --git a/bd/lfs_emubd.c b/bd/lfs_emubd.c index c7d1b515..1fdb4235 100644 --- a/bd/lfs_emubd.c +++ b/bd/lfs_emubd.c @@ -92,7 +92,7 @@ static lfs_emubd_block_t *lfs_emubd_mutblock( // prng used for some emulation things -static uint32_t lfs_emubd_prng(uint32_t *state) { +static uint32_t lfs_emubd_prng_(uint32_t *state) { // A simple xorshift32 generator, easily reproducible. Keep in mind // determinism is much more important than actual randomness here. uint32_t x = *state; @@ -331,7 +331,7 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, lfs_size_t bit = b->bad_bit & 0x7fffffff; if (bit/8 >= off && bit/8 < off+size - && (lfs_emubd_prng(&bd->prng) & 1)) { + && (lfs_emubd_prng_(&bd->prng) & 1)) { ((uint8_t*)buffer)[(bit/8) - off] ^= 1 << (bit%8); } } @@ -401,7 +401,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, bd->blocks[block] = b; // flip bit - lfs_size_t bit = lfs_emubd_prng(&bd->prng) + lfs_size_t bit = lfs_emubd_prng_(&bd->prng) % (cfg->prog_size*8); b->data[off + (bit/8)] ^= 1 << (bit%8); @@ -441,7 +441,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, memcpy(&b->data[off], buffer, size); // flip bit - lfs_size_t bit = lfs_emubd_prng(&bd->prng) + lfs_size_t bit = lfs_emubd_prng_(&bd->prng) % (cfg->prog_size*8); b->data[off + (bit/8)] ^= 1 << (bit%8); @@ -519,7 +519,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, // choose a new bad bit unless overridden if (!(0x80000000 & b->bad_bit)) { - b->bad_bit = lfs_emubd_prng(&bd->prng) + b->bad_bit = lfs_emubd_prng_(&bd->prng) % (cfg->block_size*8); } @@ -696,7 +696,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { bd->blocks[block] = b; // flip bit - lfs_size_t bit = lfs_emubd_prng(&bd->prng) + lfs_size_t bit = lfs_emubd_prng_(&bd->prng) % (cfg->block_size*8); b->data[(bit/8)] ^= 1 << (bit%8); @@ -739,7 +739,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { } // flip bit - lfs_size_t bit = lfs_emubd_prng(&bd->prng) + lfs_size_t bit = lfs_emubd_prng_(&bd->prng) % (cfg->block_size*8); b->data[(bit/8)] ^= 1 << (bit%8); @@ -817,7 +817,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { // choose a new bad bit unless overridden if (!(0x80000000 & b->bad_bit)) { - b->bad_bit = lfs_emubd_prng(&bd->prng) + b->bad_bit = lfs_emubd_prng_(&bd->prng) % (cfg->block_size*8); } @@ -917,7 +917,7 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) { || bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READFLIP) { if (!(0x80000000 & b->bad_bit)) { - b->bad_bit = lfs_emubd_prng(&bd->prng) + b->bad_bit = lfs_emubd_prng_(&bd->prng) % (cfg->block_size*8); } } @@ -988,15 +988,24 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { /// Additional extended API for driving test features /// -int lfs_emubd_seed(const struct lfs_config *cfg, uint32_t seed) { +void lfs_emubd_seed(const struct lfs_config *cfg, uint32_t seed) { LFS_EMUBD_TRACE("lfs_emubd_seed(%p, 0x%08"PRIx32")", (void*)cfg, seed); lfs_emubd_t *bd = cfg->context; bd->prng = seed; - LFS_EMUBD_TRACE("lfs_emubd_seed -> %d", 0); - return 0; + LFS_EMUBD_TRACE("lfs_emubd_seed -> _"); +} + +uint32_t lfs_emubd_prng(const struct lfs_config *cfg) { + LFS_EMUBD_TRACE("lfs_emubd_prng(%p)", (void*)cfg); + lfs_emubd_t *bd = cfg->context; + + uint32_t x = lfs_emubd_prng_(&bd->prng); + + LFS_EMUBD_TRACE("lfs_emubd_prng -> 0x%08"PRIx32, x); + return x; } lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg) { diff --git a/bd/lfs_emubd.h b/bd/lfs_emubd.h index 326590d5..c6a319ed 100644 --- a/bd/lfs_emubd.h +++ b/bd/lfs_emubd.h @@ -191,7 +191,10 @@ int lfs_emubd_sync(const struct lfs_config *cfg); /// Additional extended API for driving test features /// // Set the current prng state -int lfs_emubd_seed(const struct lfs_config *cfg, uint32_t seed); +void lfs_emubd_seed(const struct lfs_config *cfg, uint32_t seed); + +// Get a pseudo-random number from emubd's internal prng +uint32_t lfs_emubd_prng(const struct lfs_config *cfg); // Get total amount of bytes read lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg); diff --git a/tests/test_badblocks.toml b/tests/test_badblocks.toml index 6678a982..bbcf97ae 100644 --- a/tests/test_badblocks.toml +++ b/tests/test_badblocks.toml @@ -7,18 +7,17 @@ after = [ 'test_traversal', 'test_gc', 'test_mount', - 'test_ck', 'test_compat', ] -## Single badblock tests +## Single-block badblock tests # # first test with every possible single badblock # B-tree's ridiculous branching factor is great for performance, but it makes # them a bit of a pain to test, here we test them explicitly -[cases.test_badblocks_single_btree_many] +[cases.test_badblocks_every_btree_many] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -26,7 +25,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -43,9 +41,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test creating a btree lfs_t lfs; @@ -129,8 +127,8 @@ code = ''' } ''' -# with dirs -[cases.test_badblocks_single_dir_many] +# badblocks with dirs +[cases.test_badblocks_every_dir_many] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -138,7 +136,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -150,9 +147,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test creating directories lfs_t lfs; @@ -245,8 +242,8 @@ code = ''' } ''' -# fuzz dirs -[cases.test_badblocks_single_dir_fuzz] +# badblocks with fuzz dirs +[cases.test_badblocks_every_dir_fuzz] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -254,7 +251,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -269,9 +265,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test fuzz with dirs lfs_t lfs; @@ -432,8 +428,8 @@ code = ''' } ''' -# with files -[cases.test_badblocks_single_file_many] +# badblocks with files +[cases.test_badblocks_every_file_many] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -441,7 +437,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -465,9 +460,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test creating files lfs_t lfs; @@ -544,8 +539,8 @@ code = ''' } ''' -# fuzz files -[cases.test_badblocks_single_file_fuzz] +# badblocks with fuzz files +[cases.test_badblocks_every_file_fuzz] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -553,7 +548,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -580,9 +574,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test fuzz with files lfs_t lfs; @@ -795,8 +789,8 @@ code = ''' } ''' -# with more complex file writes -[cases.test_badblocks_single_fwrite_fuzz] +# badblocks with more complex file writes +[cases.test_badblocks_every_fwrite_fuzz] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -804,7 +798,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -838,9 +831,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test with complex file writes lfs_t lfs; @@ -966,8 +959,8 @@ code = ''' } ''' -# with orphans, zombies, etc -[cases.test_badblocks_single_orphanzombie_fuzz] +# badblocks with orphans, zombies, etc +[cases.test_badblocks_every_orphanzombie_fuzz] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -975,7 +968,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1002,9 +994,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test with orphans, zombies, etc lfs_t lfs; @@ -1338,8 +1330,8 @@ code = ''' } ''' -# with orphans, zombies, dirs, etc -[cases.test_badblocks_single_orphanzombiedir_fuzz] +# badblocks with orphans, zombies, dirs, etc +[cases.test_badblocks_every_orphanzombiedir_fuzz] defines.BADBLOCK = -1 defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -1347,7 +1339,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1374,9 +1365,9 @@ code = ''' i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); i++) { lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); // mark our badblock as bad lfs_emubd_markbad(CFG, badblock) => 0; - printf("--- badblock: 0x%x ---\n", badblock); // test with orphans, zombies, dirs, etc lfs_t lfs; @@ -1806,7 +1797,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -1907,7 +1897,7 @@ code = ''' lfs_deinit(&lfs) => 0; ''' -# with dirs +# badblocks with dirs [cases.test_badblocks_region_dir_many] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -1915,7 +1905,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2024,7 +2013,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# fuzz dirs +# badblocks with fuzz dirs [cases.test_badblocks_region_dir_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -2032,7 +2021,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2212,7 +2200,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with files +# badblocks with files [cases.test_badblocks_region_file_many] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -2220,7 +2208,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2325,7 +2312,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# fuzz files +# badblocks with fuzz files [cases.test_badblocks_region_file_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -2333,7 +2320,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2577,7 +2563,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with more complex file writes +# badblocks with more complex file writes [cases.test_badblocks_region_fwrite_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -2585,7 +2571,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -2749,7 +2734,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with orphans, zombies, etc +# badblocks with orphans, zombies, etc [cases.test_badblocks_region_orphanzombie_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -2757,7 +2742,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3122,7 +3106,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with orphans, zombies, dirs, etc +# badblocks with orphans, zombies, dirs, etc [cases.test_badblocks_region_orphanzombiedir_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -3130,7 +3114,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3588,7 +3571,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3689,7 +3671,7 @@ code = ''' lfs_deinit(&lfs) => 0; ''' -# with dirs +# badblocks with dirs [cases.test_badblocks_alternating_dir_many] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -3697,7 +3679,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3806,7 +3787,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# fuzz dirs +# badblocks with fuzz dirs [cases.test_badblocks_alternating_dir_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -3814,7 +3795,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -3994,7 +3974,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with files +# badblocks with files [cases.test_badblocks_alternating_file_many] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -4002,7 +3982,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4107,7 +4086,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# fuzz files +# badblocks with fuzz files [cases.test_badblocks_alternating_file_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -4115,7 +4094,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4359,7 +4337,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with more complex file writes +# badblocks with more complex file writes [cases.test_badblocks_alternating_fwrite_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -4367,7 +4345,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4529,7 +4506,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with orphans, zombies, etc +# badblocks with orphans, zombies, etc [cases.test_badblocks_alternating_orphanzombie_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -4537,7 +4514,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' @@ -4902,7 +4878,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# with orphans, zombies, dirs, etc +# badblocks with orphans, zombies, dirs, etc [cases.test_badblocks_alternating_orphanzombiedir_fuzz] defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_PROGERROR', @@ -4910,7 +4886,6 @@ defines.BADBLOCK_BEHAVIOR = [ 'LFS_EMUBD_BADBLOCK_READERROR', 'LFS_EMUBD_BADBLOCK_PROGNOOP', 'LFS_EMUBD_BADBLOCK_ERASENOOP', - 'LFS_EMUBD_BADBLOCK_PROGFLIP', ] # we need prog checking to detect read errors defines.CKPROGS = 'BADBLOCK_BEHAVIOR >= LFS_EMUBD_BADBLOCK_READERROR' diff --git a/tests/test_ck.toml b/tests/test_ck.toml index 303349da..fc2ffd7d 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -1,5 +1,10 @@ # Test checksum validation things -after = ['test_traversal', 'test_gc', 'test_mount'] +after = [ + 'test_traversal', + 'test_gc', + 'test_mount', + 'test_badblocks', +] @@ -467,8 +472,6 @@ done:; # Some simple ckprog tests -# -# We test these much more aggressively in test_badblocks # test every single-bit error in block 0/1 [cases.test_ck_ckprogs_mroot] @@ -484,11 +487,11 @@ code = ''' 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); // mark our badbit as bad lfs_emubd_markbadbit(CFG, BADBLOCK, badbit) => 0; - printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", - (lfs_size_t)BADBLOCK, badbit/8, badbit, badbit/8, badbit%8); // formatting the filesystem may already find the bit error lfs_t lfs; @@ -600,11 +603,11 @@ code = ''' 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); // mark our badbit as bad lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; - printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", - badblock, badbit/8, badbit, badbit/8, badbit%8); // format lfs_t lfs; @@ -710,11 +713,11 @@ code = ''' 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); // mark our badbit as bad lfs_emubd_markbadbit(CFG, badblock, badbit) => 0; - printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", - badblock, badbit/8, badbit, badbit/8, badbit%8); // format lfs_t lfs; @@ -817,12 +820,12 @@ code = ''' lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; // reset the bd prng every run for reproducibility - lfs_emubd_seed(CFG, 42) => 0; + 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; - printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", - (lfs_size_t)BADBLOCK, badbit/8, badbit, badbit/8, badbit%8); // With metastability, basically any filesystem operation can // return LFS_ERR_CORRUPT. This is ok, what we're really testing @@ -964,12 +967,12 @@ code = ''' lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; // reset the bd prng every run for reproducibility - lfs_emubd_seed(CFG, 42) => 0; + 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; - printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", - badblock, badbit/8, badbit, badbit/8, badbit%8); // With metastability, basically any filesystem operation can // return LFS_ERR_CORRUPT. This is ok, what we're really testing @@ -1100,12 +1103,12 @@ code = ''' lfs_size_t badbit = (BADBIT == -1) ? i : BADBIT; // reset the bd prng every run for reproducibility - lfs_emubd_seed(CFG, 42) => 0; + 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; - printf("--- badblock: 0x%x.%x, badbit: 0x%x (0x%x+%x) ---\n", - badblock, badbit/8, badbit, badbit/8, badbit%8); // With metastability, basically any filesystem operation can // return LFS_ERR_CORRUPT. This is ok, what we're really testing @@ -1193,7 +1196,6 @@ code = ''' 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); @@ -1308,7 +1310,6 @@ code = ''' 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); @@ -1420,7 +1421,6 @@ code = ''' 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); @@ -1479,3 +1479,1921 @@ code = ''' lfs_emubd_markgood(CFG, badblock) => 0; } ''' + + + +## High-level tests with every possible single badblock +# +# these are basically the same as our test_badblock tests, except we +# accept LFS_ERR_CORRUPT here + +# badblocks with dirs +[cases.test_ck_every_dir_many] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test creating directories + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // make this many directories + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + sprintf(name, "dir%03x", i); + int err = lfsr_mkdir(&lfs, name); + assert(!err || (TEST_PLS && err == LFS_ERR_EXIST)); + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + + // ckfetches? + if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + + lfsr_mount(&lfs, + 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), + CFG) => 0; + } + + // grm should be zero here + assert(lfs.grm_p[0] == 0); + + // check that our mkdir worked + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + sprintf(name, "dir%03x", i); + struct lfs_info info; + lfsr_stat(&lfs, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } + + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + sprintf(name, "dir%03x", i); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + sprintf(name, "dir%03x", i); + lfsr_dir_open(&lfs, &dir, name) => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + } + } + + lfsr_unmount(&lfs) => 0; + + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# badblocks with fuzz dirs +[cases.test_ck_every_dir_fuzz] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256] +defines.OPS = '2*N' +defines.SEED = 42 +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test fuzz with dirs + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // set up a simulation to compare against + lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); + lfs_size_t sim_size = 0; + + uint32_t prng = SEED; + for (lfs_size_t i = 0; i < OPS; i++) { + // choose a pseudo-random op, either mkdir, remove, or rename + uint8_t op = TEST_PRNG(&prng) % 3; + + if (op == 0 || sim_size == 0) { + // choose a pseudo-random number, truncate to 3 hexadecimals + lfs_size_t x = TEST_PRNG(&prng) % N; + // insert into our sim + for (lfs_size_t j = 0;; j++) { + if (j >= sim_size || sim[j] >= x) { + // already seen? + if (j < sim_size && sim[j] == x) { + // do nothing + } else { + // insert + memmove(&sim[j+1], &sim[j], + (sim_size-j)*sizeof(lfs_size_t)); + sim_size += 1; + sim[j] = x; + } + break; + } + } + + // create a directory here + char name[256]; + sprintf(name, "dir%03x", x); + int err = lfsr_mkdir(&lfs, name); + assert(!err || err == LFS_ERR_EXIST); + + } else if (op == 1) { + // choose a pseudo-random entry to delete + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + // delete from our sim + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + sim_size -= 1; + + // remove this directory + char name[256]; + sprintf(name, "dir%03x", x); + lfsr_remove(&lfs, name) => 0; + + } else { + // choose a pseudo-random entry to rename, and a pseudo-random + // number to rename to + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + lfs_size_t y = TEST_PRNG(&prng) % N; + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // already seen and not a noop? + if (k < sim_size && sim[k] == y && x != y) { + // just delete the original entry + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + sim_size -= 1; + } else { + // first delete + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + if (k > j) { + k -= 1; + } + // then insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + sim[k] = y; + } + break; + } + } + + // rename this directory + char old_name[256]; + sprintf(old_name, "dir%03x", x); + char new_name[256]; + sprintf(new_name, "dir%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + + // ckfetches? + if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + + lfsr_mount(&lfs, + 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), + CFG) => 0; + } + + // grm should be zero here + assert(lfs.grm_p[0] == 0); + + // test that our directories match our simulation + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "dir%03x", sim[j]); + struct lfs_info info; + lfsr_stat(&lfs, name, &info) => 0; + char name2[256]; + sprintf(name2, "dir%03x", sim[j]); + assert(strcmp(info.name, name2) == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } + + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "dir%03x", sim[j]); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + } + + // clean up sim/lfs + free(sim); + lfsr_unmount(&lfs) => 0; + + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# badblocks with files +[cases.test_ck_every_file_many] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.N = [1, 2, 4, 8, 16, 32, 64] +defines.SIZE = [ + '0', + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + '(SIZE*N)/BLOCK_SIZE <= 32', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test creating files + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // create this many files + uint32_t prng = 42; + for (lfs_size_t i = 0; i < N; i++) { + char name[256]; + sprintf(name, "amethyst%03x", i); + + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + + // ckfetches? + if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + + lfsr_mount(&lfs, + 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), + CFG) => 0; + } + + // check that our writes worked + prng = 42; + for (lfs_size_t i = 0; i < N; i++) { + // check with stat + char name[256]; + sprintf(name, "amethyst%03x", i); + struct lfs_info info; + lfsr_stat(&lfs, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // try reading the file, note we reset prng above + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_t file; + uint8_t rbuf[SIZE]; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + lfsr_unmount(&lfs) => 0; + + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# badblocks with fuzz files +[cases.test_ck_every_file_fuzz] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.N = [1, 2, 4, 8, 16, 32, 64] +defines.OPS = '2*N' +defines.SIZE = [ + '0', + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SEED = 42 +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + '(SIZE*N)/BLOCK_SIZE <= 16', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test fuzz with files + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // set up a simulation to compare against + lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); + uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + lfs_size_t sim_size = 0; + + uint32_t prng = SEED; + for (lfs_size_t i = 0; i < OPS; i++) { + // choose which operation to do + uint8_t op = TEST_PRNG(&prng) % 3; + + // creating a new file? + if (op == 0 || sim_size == 0) { + // choose a pseudo-random number + lfs_size_t x = TEST_PRNG(&prng) % N; + // associate each file with a prng that generates its contents + uint32_t wprng = TEST_PRNG(&prng); + + // insert into our sim + for (lfs_size_t j = 0;; j++) { + if (j >= sim_size || sim[j] >= x) { + // already seen? + if (j < sim_size && sim[j] == x) { + // new prng + sim_prngs[j] = wprng; + } else { + // insert + memmove(&sim[j+1], &sim[j], + (sim_size-j)*sizeof(lfs_size_t)); + memmove(&sim_prngs[j+1], &sim_prngs[j], + (sim_size-j)*sizeof(uint32_t)); + sim_size += 1; + sim[j] = x; + sim_prngs[j] = wprng; + } + break; + } + } + + // create a file here + char name[256]; + sprintf(name, "amethyst%03x", x); + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); + } + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // deleting a file? + } else if (op == 1) { + // choose a random file to delete + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + // delete from our sim + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + sim_size -= 1; + + // delete this file + char name[256]; + sprintf(name, "amethyst%03x", x); + lfsr_remove(&lfs, name) => 0; + + // renaming a file? + } else { + // choose a random file to rename, and a random number to + // rename to + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + lfs_size_t y = TEST_PRNG(&prng) % N; + uint32_t wprng = sim_prngs[j]; + + // update our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // delete the original entry + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + sim_size -= 1; + if (k > j) { + k -= 1; + } + // update the prng + sim_prngs[k] = wprng; + // just renaming + } else { + // first delete + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + if (k > j) { + k -= 1; + } + // then insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + sim[k] = y; + sim_prngs[k] = wprng; + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "amethyst%03x", x); + char new_name[256]; + sprintf(new_name, "amethyst%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + + // ckfetches? + if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + + lfsr_mount(&lfs, + 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), + CFG) => 0; + } + + // check that our files match our simulation + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "amethyst%03x", sim[j]); + struct lfs_info info; + lfsr_stat(&lfs, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } + + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "amethyst%03x", sim[j]); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // check the file contents + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "amethyst%03x", sim[j]); + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; + + uint32_t wprng = sim_prngs[j]; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); + } + + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + // clean up sim/lfs + free(sim); + free(sim_prngs); + lfsr_unmount(&lfs) => 0; + + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# badblocks with more complex file writes +[cases.test_ck_every_fwrite_fuzz] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.OPS = 20 +defines.SIZE = [ + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# chunk is more an upper limit here +defines.CHUNK = 64 +# INIT=0 => no init +# INIT=1 => fill with data +# INIT=2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +defines.SEED = 42 +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + 'CHUNK <= SIZE', + # this just saves testing time + 'SIZE <= 4*1024*FRAGMENT_SIZE', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test with complex file writes + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = SEED; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + for (lfs_size_t i = 0; i < OPS; i++) { + // choose a random location + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + + // update sim + for (lfs_size_t j = 0; j < chunk; j++) { + sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + if (chunk != 0) { + size = lfs_max(size, off+chunk); + } + + // update file + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + } + + lfsr_file_close(&lfs, &file) => 0; + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 0; + + // ckfetches? + if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + + lfsr_mount(&lfs, + 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), + CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + + lfsr_unmount(&lfs) => 0; + + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# badblocks with orphans, zombies, etc +[cases.test_ck_every_orphanzombie_fuzz] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.N = [1, 2, 4, 8, 16, 32, 64] +defines.OPS = '2*N' +defines.SIZE = [ + '0', + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SEED = 42 +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + '(SIZE*N)/BLOCK_SIZE <= 16', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test with orphans, zombies, etc + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // set up a simulation to compare against + lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); + uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + lfs_size_t sim_size = 0; + + typedef struct sim_file { + lfs_size_t x; + bool orphan; + bool zombie; + uint32_t prng; + lfsr_file_t file; + } sim_file_t; + sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); + lfs_size_t sim_file_count = 0; + + uint32_t prng = SEED; + for (lfs_size_t i = 0; i < OPS; i++) { + nonsense:; + // choose which operation to do + uint8_t op = TEST_PRNG(&prng) % 5; + + // open a new file? + if (op == 0) { + if (sim_file_count >= N) { + goto nonsense; + } + // choose a pseudo-random number + lfs_size_t x = TEST_PRNG(&prng) % N; + + // already exists? + bool orphan = true; + uint32_t wprng = 0; + for (lfs_size_t j = 0; j < sim_size; j++) { + if (sim[j] == x) { + orphan = false; + wprng = sim_prngs[j]; + break; + } + } + // choose a random seed if we don't exist + if (orphan) { + wprng = TEST_PRNG(&prng); + } + + // open in our sim + lfs_size_t j = sim_file_count; + sim_files[j] = malloc(sizeof(sim_file_t)); + sim_files[j]->x = x; + sim_files[j]->orphan = orphan; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // open the actual file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_file_open(&lfs, &sim_files[j]->file, name, + LFS_O_RDWR | LFS_O_CREAT) => 0; + + // write some initial data if we don't exist + if (orphan) { + uint8_t wbuf[SIZE]; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // write/rewrite a file? + } else if (op == 1) { + if (sim_file_count == 0) { + goto nonsense; + } + // choose a random file handle + lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + // choose a random seed + uint32_t wprng = TEST_PRNG(&prng); + + // update sim + sim_files[j]->prng = wprng; + if (!sim_files[j]->zombie) { + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + } + break; + } + } + + // update related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + sim_files[k]->orphan = false; + sim_files[k]->prng = wprng; + } + } + } + + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + + // close a file? + } else if (op == 2) { + if (sim_file_count == 0) { + goto nonsense; + } + // choose a random file handle + lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + + // this doesn't really test anything, but if we don't close + // files eventually everything will end up zombies + + // close the file without affected disk + lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + + // remove from list + free(sim_files[j]); + sim_files[j] = sim_files[sim_file_count-1]; + sim_file_count -= 1; + + // remove a file? + } else if (op == 3) { + if (sim_size == 0) { + goto nonsense; + } + // choose a random file to delete + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + + // delete from our sim + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + sim_size -= 1; + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } + + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + + // rename a file? + } else if (op == 4) { + if (sim_size == 0) { + goto nonsense; + } + // choose a random file to rename, and a random number to + // rename to + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + lfs_size_t y = TEST_PRNG(&prng) % N; + uint32_t wprng = sim_prngs[j]; + + // update our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // delete the original entry + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + sim_size -= 1; + if (k > j) { + k -= 1; + } + // update the prng + sim_prngs[k] = wprng; + // just renaming + } else { + // first delete + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + if (k > j) { + k -= 1; + } + // then insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + sim[k] = y; + sim_prngs[k] = wprng; + } + break; + } + } + + // update any related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + // move source files + if (sim_files[k]->x == x) { + sim_files[k]->x = y; + + // mark target files as zombied + } else if (sim_files[k]->x == y) { + sim_files[k]->zombie = true; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; + } + } + + // check that disk matches our simulation + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + struct lfs_info info; + lfsr_stat(&lfs, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } + + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, name) == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; + + uint32_t wprng = sim_prngs[j]; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); + } + + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + + // check that our file handles match our simulation + for (lfs_size_t j = 0; j < sim_file_count; j++) { + uint32_t wprng = sim_files[j]->prng; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); + } + + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } + + // clean up sim/lfs + free(sim); + free(sim_prngs); + for (lfs_size_t j = 0; j < sim_file_count; j++) { + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); + } + free(sim_files); + lfsr_unmount(&lfs) => 0; + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +''' + +# badblocks with orphans, zombies, dirs, etc +[cases.test_ck_every_orphanzombiedir_fuzz] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# our different methods detect different types of errors, so we implement +# errors for each one a bit differently +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGFLIP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +defines.N = [1, 2, 4, 8, 16, 32, 64] +defines.OPS = '2*N' +defines.SIZE = [ + '0', + 'FILE_BUFFER_SIZE/2', + '2*FILE_BUFFER_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SEED = 42 +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + '(SIZE*N)/BLOCK_SIZE <= 16', +] +code = ''' + // test all possible bad blocks + for (lfs_size_t i = 0; + i < ((BADBLOCK == -1) ? BLOCK_COUNT : 1); + i++) { + lfs_size_t badblock = (BADBLOCK == -1) ? i : BADBLOCK; + printf("--- badblock: 0x%x ---\n", badblock); + + // reset the bd prng every run for reproducibility + lfs_emubd_seed(CFG, 42); + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckreads? + if (METHOD == 1) { + // limit to first 4-bytes to avoid escaping parity bits + lfs_emubd_setbadbit(CFG, badblock, + lfs_emubd_prng(CFG) % (4*8)) => 0; + } + } + + // test with orphans, zombies, dirs, etc + lfs_t lfs; + lfsr_format(&lfs, + 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), + CFG) => 0; + lfsr_mount(&lfs, + 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), + CFG) => 0; + + // set up a simulation to compare against + lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); + uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isdirs = malloc(N*sizeof(bool)); + lfs_size_t sim_size = 0; + + typedef struct sim_file { + lfs_size_t x; + bool orphan; + bool zombie; + uint32_t prng; + lfsr_file_t file; + } sim_file_t; + sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); + lfs_size_t sim_file_count = 0; + + uint32_t prng = SEED; + for (lfs_size_t i = 0; i < OPS; i++) { + nonsense:; + // choose which operation to do + uint8_t op = TEST_PRNG(&prng) % 8; + + // open a new file? + if (op == 0) { + if (sim_file_count >= N) { + goto nonsense; + } + // choose a pseudo-random number + lfs_size_t x = TEST_PRNG(&prng) % N; + + // already exists? + bool orphan = true; + uint32_t wprng = 0; + for (lfs_size_t j = 0; j < sim_size; j++) { + if (sim[j] == x) { + if (sim_isdirs[j]) { + goto nonsense; + } + orphan = false; + wprng = sim_prngs[j]; + break; + } + } + // choose a random seed if we don't exist + if (orphan) { + wprng = TEST_PRNG(&prng); + } + + // open in our sim + lfs_size_t j = sim_file_count; + sim_files[j] = malloc(sizeof(sim_file_t)); + sim_files[j]->x = x; + sim_files[j]->orphan = orphan; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // open the actual file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_file_open(&lfs, &sim_files[j]->file, name, + LFS_O_RDWR | LFS_O_CREAT) => 0; + + // write some initial data if we don't exist + if (orphan) { + uint8_t wbuf[SIZE]; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // write/rewrite a file? + } else if (op == 1) { + if (sim_file_count == 0) { + goto nonsense; + } + // choose a random file handle + lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + // choose a random seed + uint32_t wprng = TEST_PRNG(&prng); + + // update sim + sim_files[j]->prng = wprng; + if (!sim_files[j]->zombie) { + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isdirs[k] = false; + } + break; + } + } + + // update related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + sim_files[k]->orphan = false; + sim_files[k]->prng = wprng; + } + } + } + + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + + // close a file? + } else if (op == 2) { + if (sim_file_count == 0) { + goto nonsense; + } + // choose a random file handle + lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + + // this doesn't really test anything, but if we don't close + // files eventually everything will end up zombies + + // close the file without affected disk + lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + + // remove from list + free(sim_files[j]); + sim_files[j] = sim_files[sim_file_count-1]; + sim_file_count -= 1; + + // remove a file? + } else if (op == 3) { + if (sim_size == 0) { + goto nonsense; + } + // choose a random file to delete + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + + // delete from our sim + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isdirs[j], &sim_isdirs[j+1], + (sim_size-(j+1))*sizeof(bool)); + sim_size -= 1; + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } + + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + + // rename a file? + } else if (op == 4) { + if (sim_size == 0) { + goto nonsense; + } + // choose a random file to rename, and a random number to + // rename to + lfs_size_t j = TEST_PRNG(&prng) % sim_size; + lfs_size_t x = sim[j]; + lfs_size_t y = TEST_PRNG(&prng) % N; + uint32_t wprng = sim_prngs[j]; + bool isdir = sim_isdirs[j]; + + // update our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != isdir) { + goto nonsense; + } + + // delete the original entry + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isdirs[j], &sim_isdirs[j+1], + (sim_size-(j+1))*sizeof(bool)); + sim_size -= 1; + if (k > j) { + k -= 1; + } + // update the prng + sim_prngs[k] = wprng; + // just renaming + } else { + // first delete + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isdirs[j], &sim_isdirs[j+1], + (sim_size-(j+1))*sizeof(bool)); + if (k > j) { + k -= 1; + } + // then insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim[k] = y; + sim_prngs[k] = wprng; + sim_isdirs[k] = isdir; + } + break; + } + } + + // update any related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + // move source files + if (sim_files[k]->x == x) { + sim_files[k]->x = y; + + // mark target files as zombied + } else if (sim_files[k]->x == y) { + sim_files[k]->zombie = true; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; + + // toss a directory into the mix + } else if (op == 5) { + // choose a pseudo-random number + lfs_size_t x = TEST_PRNG(&prng) % N; + + // insert into our sim, use negative numbers for dirs + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + goto nonsense; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + } + break; + } + } + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } + + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + } + } + + // check that disk matches our simulation + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + struct lfs_info info; + lfsr_stat(&lfs, name, &info) => 0; + assert(strcmp(info.name, name) == 0); + if (sim_isdirs[j]) { + assert(info.type == LFS_TYPE_DIR); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } + } + + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + struct lfs_info info; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + for (lfs_size_t j = 0; j < sim_size; j++) { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, name) == 0); + if (sim_isdirs[j]) { + assert(info.type == LFS_TYPE_DIR); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + for (lfs_size_t j = 0; j < sim_size; j++) { + if (sim_isdirs[j]) { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; + + } else { + char name[256]; + sprintf(name, "batman%03x", sim[j]); + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; + + uint32_t wprng = sim_prngs[j]; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); + } + + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + } + } + + // check that our file handles match our simulation + for (lfs_size_t j = 0; j < sim_file_count; j++) { + uint32_t wprng = sim_files[j]->prng; + uint8_t wbuf[SIZE]; + for (lfs_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); + } + + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } + + // clean up sim/lfs + free(sim); + free(sim_prngs); + for (lfs_size_t j = 0; j < sim_file_count; j++) { + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); + } + free(sim_files); + lfsr_unmount(&lfs) => 0; + + // reset badblock + lfs_emubd_markgood(CFG, badblock) => 0; + } +'''