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)); '''