From 4fa2864f3074eb1202669653779d28de86f9d297 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 13 Aug 2024 22:05:54 -0500 Subject: [PATCH] Replaced test_ck_every_* with more interesting error-spam tests Instead of testing every block (which test_badblocks_every already does) with a single random bit-error, the new test_ck_spam tests continuously throw bit-errors at the filesystem until it fails. This should reveal much more interesting failures than flipping a single bit in the entire device, while also taking less testing time. And we still have test_badblocks_every to make sure no specific problem blocks (except the mrootanchor) are missed. This makes test_ck_spam more similar to test_exhaustion than test_badblocks_every. All this being said, these tests are still sort of in stasis until rollback protection gets sorted out. So we're not actually testing anything interesting yet... I've also reverted the test_badblocks -> test_ck dependency, since we want to keep the longer-running tests near the end of the queue. --- tests/test_badblocks.toml | 1 + tests/test_ck.toml | 3473 ++++++++++++++++++------------------- 2 files changed, 1652 insertions(+), 1822 deletions(-) diff --git a/tests/test_badblocks.toml b/tests/test_badblocks.toml index bbcf97ae..ba3d70b5 100644 --- a/tests/test_badblocks.toml +++ b/tests/test_badblocks.toml @@ -7,6 +7,7 @@ after = [ 'test_traversal', 'test_gc', 'test_mount', + 'test_ck', 'test_compat', ] diff --git a/tests/test_ck.toml b/tests/test_ck.toml index fc2ffd7d..cb8dcfdb 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -1,10 +1,5 @@ # Test checksum validation things -after = [ - 'test_traversal', - 'test_gc', - 'test_mount', - 'test_badblocks', -] +after = ['test_traversal', 'test_gc', 'test_mount'] @@ -1482,185 +1477,30 @@ code = ''' -## High-level tests with every possible single badblock +## High-level error spam tests # -# these are basically the same as our test_badblock tests, except we -# accept LFS_ERR_CORRUPT here +# we basically just throw errors at filesystem operations until they +# error with either LFS_ERR_CORRUPT or LFS_ERR_NOSPC +# +# TODO this is all basically in stasis until rollback is solved -# badblocks with dirs -[cases.test_ck_every_dir_many] -defines.BADBLOCK = -1 +# fuzz errors with fuzz dirs +[cases.test_ck_spam_dir_fuzz] # 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.PERIOD = 10 +defines.PROTECTED_MROOTANCHOR = [false, true] +# TODO rollback is even a problem for ckprogs, change this +# to PROGFLIP when rollback protection is in place +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGNOOP' 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 +defines.SEED = 'range(10)' fuzz = 'SEED' if = [ 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', @@ -1668,1183 +1508,481 @@ if = [ '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); + // seed our block device with our seed so we have different error + // bit patterns + uint32_t prng = SEED; + lfs_emubd_seed(CFG, TEST_PRNG(&prng)); - // reset the bd prng every run for reproducibility - lfs_emubd_seed(CFG, 42); + // create a permutation of blocks to test against + // + // precalculating the permutation avoids issues around running out + // of blocks to randomly select + uint32_t badblocks[BLOCK_COUNT]; + TEST_PERMUTATION(TEST_PRNG(&prng), badblocks, BLOCK_COUNT); - // ckprogs? ckreads? - if (METHOD == 0 || METHOD == 1) { - // mark our badblock as bad - lfs_emubd_markbad(CFG, badblock) => 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; - // 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; + // set up a simulation to compare against + lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); + lfs_size_t sim_size = 0; + + // keep adding block errors until we either run out of blocks or + // our errors + lfs_size_t i = 0; + for (; i < PERIOD*BLOCK_COUNT; i++) { + if (i % PERIOD == 0 + && !(PROTECTED_MROOTANCHOR + && badblocks[i/PERIOD] < 2)) { + lfs_block_t badblock = badblocks[i/PERIOD]; + printf("badblock: 0x%x\n", badblock); + + // our different error-detection methods detect different + // types of errors, so we implement errors for each one a + // bit differently + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckfetches? + } else if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*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; + // keep testing... - // set up a simulation to compare against - lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); - lfs_size_t sim_size = 0; + // choose a pseudo-random op, either mkdir, remove, or rename + uint8_t op = TEST_PRNG(&prng) % 3; - 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; + 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++) { + // create a directory here 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); + sprintf(name, "dir%03x", x); + int err = lfsr_mkdir(&lfs, name); + assert(!err || err == LFS_ERR_EXIST || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; } - 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; - } + } 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; - 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; + // remove this directory + char name[256]; + sprintf(name, "dir%03x", x); + int err = lfsr_remove(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; } - // 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; + // 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; } - - 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; + // rename this directory + char old_name[256]; + sprintf(old_name, "dir%03x", x); + char new_name[256]; + sprintf(new_name, "dir%03x", y); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } } - - 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; - } + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 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; } - // 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; + // grm should be zero here + assert(lfs.grm_p[0] == 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 + // test that our directories match our simulation for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; - sprintf(name, "batman%03x", sim[j]); + 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; + } + +corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // clean up sim + free(sim); + // how many errors did we survive? + printf("survived %d block errors!\n", (int)(i/PERIOD)); +''' + +# fuzz errors with fuzz files +[cases.test_ck_spam_file_fuzz] +# TODO enable other methods once rollback protection is in place +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.PERIOD = 10 +defines.PROTECTED_MROOTANCHOR = [false, true] +# TODO rollback is even a problem for ckprogs, change this +# to PROGFLIP when rollback protection is in place +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGNOOP' +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', +] +defines.SEED = 'range(10)' +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + '(SIZE*N)/BLOCK_SIZE <= 16', +] +code = ''' + // seed our block device with our seed so we have different error + // bit patterns + uint32_t prng = SEED; + lfs_emubd_seed(CFG, TEST_PRNG(&prng)); + + // create a permutation of blocks to test against + // + // precalculating the permutation avoids issues around running out + // of blocks to randomly select + uint32_t badblocks[BLOCK_COUNT]; + TEST_PERMUTATION(TEST_PRNG(&prng), badblocks, BLOCK_COUNT); + + // 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; + + // keep adding block errors until we either run out of blocks or + // our errors + lfs_size_t i = 0; + for (; i < PERIOD*BLOCK_COUNT; i++) { + if (i % PERIOD == 0 + && !(PROTECTED_MROOTANCHOR + && badblocks[i/PERIOD] < 2)) { + lfs_block_t badblock = badblocks[i/PERIOD]; + printf("badblock: 0x%x\n", badblock); + + // our different error-detection methods detect different + // types of errors, so we implement errors for each one a + // bit differently + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckfetches? + } else if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + } + + // keep testing... + + // 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; + int err = lfsr_file_open(&lfs, &file, name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC); + assert(!err || err == LFS_ERR_NOSPC); + if (err) { + goto corrupt_mounted; + } + lfs_ssize_t d = lfsr_file_write(&lfs, &file, wbuf, SIZE); + assert(d == SIZE || d == LFS_ERR_NOSPC); + if (d == LFS_ERR_NOSPC) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + err = lfsr_file_close(&lfs, &file); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_remove(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + } + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 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); @@ -2865,7 +2003,7 @@ code = ''' assert(info.size == 0); for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; - sprintf(name, "batman%03x", sim[j]); + 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); @@ -2874,7 +2012,1174 @@ code = ''' 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; + } + } + +corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // clean up sim + free(sim); + free(sim_prngs); + // how many errors did we survive? + printf("survived %d block errors!\n", (int)(i/PERIOD)); +''' + +# fuzz errors with more complex file writes +[cases.test_ck_spam_fwrite_fuzz] +# TODO enable other methods once rollback protection is in place +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.PERIOD = 10 +defines.PROTECTED_MROOTANCHOR = [false, true] +# TODO rollback is even a problem for ckprogs, change this +# to PROGFLIP when rollback protection is in place +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGNOOP' +defines.CKPROGS = 'METHOD == 0' +defines.CKREADS = 'METHOD == 1' +defines.CKFETCHES = 'METHOD == 2' +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 = 'range(10)' +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 = ''' + // seed our block device with our seed so we have different error + // bit patterns + uint32_t prng = SEED; + lfs_emubd_seed(CFG, TEST_PRNG(&prng)); + + // create a permutation of blocks to test against + // + // precalculating the permutation avoids issues around running out + // of blocks to randomly select + uint32_t badblocks[BLOCK_COUNT]; + TEST_PERMUTATION(TEST_PRNG(&prng), badblocks, BLOCK_COUNT); + + // 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; + 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; + } + + // keep adding block errors until we either run out of blocks or + // our errors + lfs_size_t i = 0; + for (; i < PERIOD*BLOCK_COUNT; i++) { + if (i % PERIOD == 0 + && !(PROTECTED_MROOTANCHOR + && badblocks[i/PERIOD] < 2)) { + lfs_block_t badblock = badblocks[i/PERIOD]; + printf("badblock: 0x%x\n", badblock); + + // our different error-detection methods detect different + // types of errors, so we implement errors for each one a + // bit differently + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckfetches? + } else if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + } + + // keep testing... + + // 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; + lfs_ssize_t d = lfsr_file_write(&lfs, &file, &sim[off], chunk); + assert(d == (lfs_ssize_t)chunk || d == LFS_ERR_NOSPC); + if (d == LFS_ERR_NOSPC) { + goto corrupt_open; + } + + // sync? + if (SYNC) { + int err = lfsr_file_sync(&lfs, &file); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_open; + } + } + } + + int err = lfsr_file_close(&lfs, &file); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + for (int remount = 0; remount < 2; remount++) { + // remount? + if (remount) { + lfsr_unmount(&lfs) => 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; + } + + goto corrupt_mounted; +corrupt_open:; + lfsr_file_close(&lfs, &file) => 0; +corrupt_mounted:; + lfsr_unmount(&lfs) => 0; + + // how many errors did we survive? + printf("survived %d block errors! (%d)\n", (int)(i/PERIOD), i); +''' + +# fuzz errors with orphans, zombies, etc +[cases.test_ck_spam_orphanzombie_fuzz] +defines.BADBLOCK = -1 +# TODO enable other methods once rollback protection is in place +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.PERIOD = 10 +defines.PROTECTED_MROOTANCHOR = [false, true] +# TODO rollback is even a problem for ckprogs, change this +# to PROGFLIP when rollback protection is in place +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGNOOP' +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', +] +defines.SEED = 'range(10)' +fuzz = 'SEED' +if = [ + 'LFS_IFDEF_CKPROGS(true, !CKPROGS)', + 'LFS_IFDEF_CKREADS(true, !CKREADS)', + 'LFS_IFDEF_CKFETCHES(true, !CKFETCHES)', + '(SIZE*N)/BLOCK_SIZE <= 16', +] +code = ''' + // seed our block device with our seed so we have different error + // bit patterns + uint32_t prng = SEED; + lfs_emubd_seed(CFG, TEST_PRNG(&prng)); + + // create a permutation of blocks to test against + // + // precalculating the permutation avoids issues around running out + // of blocks to randomly select + uint32_t badblocks[BLOCK_COUNT]; + TEST_PERMUTATION(TEST_PRNG(&prng), badblocks, BLOCK_COUNT); + + // 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; + + // keep adding block errors until we either run out of blocks or + // our errors + lfs_size_t i = 0; + for (; i < PERIOD*BLOCK_COUNT; i++) { + if (i % PERIOD == 0 + && !(PROTECTED_MROOTANCHOR + && badblocks[i/PERIOD] < 2)) { + lfs_block_t badblock = badblocks[i/PERIOD]; + printf("badblock: 0x%x\n", badblock); + + // our different error-detection methods detect different + // types of errors, so we implement errors for each one a + // bit differently + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckfetches? + } else if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + } + + // keep testing... + + 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; + + // open the actual file + char name[256]; + sprintf(name, "batman%03x", x); + int err = lfsr_file_open(&lfs, &sim_files[j]->file, name, + LFS_O_RDWR | LFS_O_CREAT); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + sim_file_count++; + + // 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); + } + lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, + wbuf, SIZE); + assert(d == SIZE || d == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + } + + // 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); + } + lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, + wbuf, SIZE); + assert(d == SIZE || d == LFS_ERR_NOSPC); + if (d == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + int err = lfsr_file_sync(&lfs, &sim_files[j]->file); + assert(err == ((!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT) + || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_remove(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + } + } + + // 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); + } + +corrupt_mounted:; + // clean up sim/lfs + free(sim); + free(sim_prngs); + for (lfs_size_t j = 0; j < sim_file_count; j++) { + lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); + } + free(sim_files); + lfsr_unmount(&lfs) => 0; + + // how many errors did we survive? + printf("survived %d block errors!\n", (int)(i/PERIOD)); +''' + +# fuzz errors with orphans, zombies, dirs, etc +[cases.test_ck_spam_orphanzombiedir_fuzz] +# TODO enable other methods once rollback protection is in place +# METHOD=0 => ckprogs +# METHOD=1 => ckreads +# METHOD=2 => ckfetches +defines.METHOD = [0] +defines.PERIOD = 10 +defines.PROTECTED_MROOTANCHOR = [false, true] +# TODO rollback is even a problem for ckprogs, change this +# to PROGFLIP when rollback protection is in place +defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_PROGNOOP' +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', +] +defines.SEED = 'range(10)' +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 = ''' + // seed our block device with our seed so we have different error + // bit patterns + uint32_t prng = SEED; + lfs_emubd_seed(CFG, TEST_PRNG(&prng)); + + // create a permutation of blocks to test against + // + // precalculating the permutation avoids issues around running out + // of blocks to randomly select + uint32_t badblocks[BLOCK_COUNT]; + TEST_PERMUTATION(TEST_PRNG(&prng), badblocks, BLOCK_COUNT); + + // 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; + + // keep adding block errors until we either run out of blocks or + // our errors + lfs_size_t i = 0; + for (; i < PERIOD*BLOCK_COUNT; i++) { + if (i % PERIOD == 0 + && !(PROTECTED_MROOTANCHOR + && badblocks[i/PERIOD] < 2)) { + lfs_block_t badblock = badblocks[i/PERIOD]; + printf("badblock: 0x%x\n", badblock); + + // our different error-detection methods detect different + // types of errors, so we implement errors for each one a + // bit differently + + // ckprogs? ckreads? + if (METHOD == 0 || METHOD == 1) { + // mark our badblock as bad + lfs_emubd_markbad(CFG, badblock) => 0; + + // ckfetches? + } else if (METHOD == 2) { + // flip a bit + lfs_emubd_flipbit(CFG, badblock, + lfs_emubd_prng(CFG) % (BLOCK_SIZE*8)) => 0; + } + } + + // keep testing... + + 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; + + // open the actual file + char name[256]; + sprintf(name, "batman%03x", x); + int err = lfsr_file_open(&lfs, &sim_files[j]->file, name, + LFS_O_RDWR | LFS_O_CREAT); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + sim_file_count++; + + // 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); + } + lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, + wbuf, SIZE); + assert(d == SIZE || d == LFS_ERR_NOSPC); + if (d == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + } + + // 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); + } + lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, + wbuf, SIZE); + assert(d == SIZE || d == LFS_ERR_NOSPC); + if (d == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + int err = lfsr_file_sync(&lfs, &sim_files[j]->file); + assert(err == ((!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT) + || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_remove(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + + // 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); + int err = lfsr_mkdir(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + } + } + + // 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; @@ -2891,509 +3196,33 @@ code = ''' 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; - } + // 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); } - // 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; + 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); } + +corrupt_mounted:; + // clean up sim/lfs + free(sim); + free(sim_prngs); + for (lfs_size_t j = 0; j < sim_file_count; j++) { + lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); + } + free(sim_files); + lfsr_unmount(&lfs) => 0; + // how many errors did we survive? + printf("survived %d block errors!\n", (int)(i/PERIOD)); '''