From 2450e833b67b8efec29f1116018f5ac000245759 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sat, 12 Nov 2022 14:31:52 -0600 Subject: [PATCH] Added more block_size/count inference tests and fixed a couple bugs - Need to drop cache during block_size search. - Removed "Corrupted" messages when root is not found to avoid too much debug output. This also gets rid of a long-term point of confusion arround "Corrupted" messages on a mount that is intended to fail. --- lfs.c | 10 +- tests/test_superblocks.toml | 331 ++++++++++++++++++++++++++++++++++-- 2 files changed, 327 insertions(+), 14 deletions(-) diff --git a/lfs.c b/lfs.c index 38d09273..8dfbedcc 100644 --- a/lfs.c +++ b/lfs.c @@ -1299,8 +1299,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, dir->rev = revs[(r+1)%2]; } - LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", - dir->pair[0], dir->pair[1]); + if (!lfs_pair_isnull(lfs->root)) { + LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", + dir->pair[0], dir->pair[1]); + } return LFS_ERR_CORRUPT; } @@ -4231,6 +4233,10 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { lfs->block_count /= lfs->block_size/lfs->erase_size; } + // make sure cached data from a different block_size doesn't cause + // problems, we should never visit the same mdir twice here anyways + lfs_cache_drop(lfs, &lfs->rcache); + // scan directory blocks for superblock and any global updates lfs_mdir_t dir = {.tail = {0, 1}}; lfs_block_t cycle = 0; diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 1e34b3f2..67d13f6f 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -150,20 +150,54 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -# mount with unknown block_size -[cases.test_superblocks_unknown_block_size] +# mount with unknown block_size/block_count +[cases.test_superblocks_unknowns] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; + // known block_size/block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => 0; + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_size cfg->block_size = 0; + cfg->block_count = ERASE_COUNT; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + // unknown block_count/block_size + cfg->block_size = 0; + cfg->block_count = 0; lfs_mount(&lfs, cfg) => 0; - struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + // do some work + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); lfs_file_t file; lfs_file_open(&lfs, &file, "test", LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; @@ -175,7 +209,6 @@ code = ''' lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); - lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[256]; lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; @@ -184,20 +217,63 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -# mount with unknown block_count -[cases.test_superblocks_unknown_block_count] +# mount with blocks larger than the erase_size +[cases.test_superblocks_larger_blocks] +defines.BLOCK_SIZE = [ + '2*ERASE_SIZE', + '4*ERASE_SIZE', + '(ERASE_COUNT/2)*ERASE_SIZE'] code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; + // known block_size/block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => 0; + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // incorrect block_size + cfg->block_size = ERASE_SIZE; cfg->block_count = 0; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + // unknown block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_size + cfg->block_size = 0; + cfg->block_count = ERASE_COUNT; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_count/block_size + cfg->block_size = 0; + cfg->block_count = 0; lfs_mount(&lfs, cfg) => 0; - struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + // do some work + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); lfs_file_t file; lfs_file_open(&lfs, &file, "test", LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; @@ -209,7 +285,6 @@ code = ''' lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); - lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[256]; lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; @@ -218,21 +293,119 @@ code = ''' lfs_unmount(&lfs) => 0; ''' -# mount with unknown block_size+block_count -[cases.test_superblocks_unknown_block_size_count] +# mount with blocks smaller than the erase_size +[cases.test_superblocks_smaller_blocks] +defines.FORMAT_BLOCK_SIZE = 'ERASE_SIZE/2' +defines.FORMAT_BLOCK_COUNT = 'BLOCK_COUNT * (BLOCK_SIZE/FORMAT_BLOCK_SIZE)' +in = 'lfs.c' code = ''' lfs_t lfs; - lfs_format(&lfs, cfg) => 0; + lfs_init(&lfs, cfg) => 0; + lfs.block_size = BLOCK_SIZE; + lfs.block_count = BLOCK_COUNT; + + lfs_mdir_t root = { + .pair = {0, 0}, // make sure this goes into block 0 + .rev = 0, + .off = sizeof(uint32_t), + .etag = 0xffffffff, + .count = 0, + .tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}, + .erased = false, + .split = false, + }; + + lfs_superblock_t superblock = { + .version = LFS_DISK_VERSION, + .block_size = FORMAT_BLOCK_SIZE, + .block_count = FORMAT_BLOCK_COUNT, + .name_max = LFS_NAME_MAX, + .file_max = LFS_FILE_MAX, + .attr_max = LFS_ATTR_MAX, + }; + + lfs_superblock_tole32(&superblock); + lfs_dir_commit(&lfs, &root, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, + {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, + {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})) => 0; + + // known block_size/block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + // unknown block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + + // unknown block_size + cfg->block_size = 0; + cfg->block_count = ERASE_COUNT; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + + // unknown block_count/block_size cfg->block_size = 0; cfg->block_count = 0; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; +''' +# mount with blocks fewer than the erase_count +[cases.test_superblocks_fewer_blocks] +defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2'] +code = ''' + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + + // known block_size/block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = BLOCK_COUNT; lfs_mount(&lfs, cfg) => 0; struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // incorrect block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = ERASE_COUNT; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + + // unknown block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_size + cfg->block_size = 0; + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + // unknown block_count/block_size + cfg->block_size = 0; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // do some work + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); lfs_file_t file; lfs_file_open(&lfs, &file, "test", LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; @@ -244,7 +417,6 @@ code = ''' lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); - lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[256]; lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; @@ -252,3 +424,138 @@ code = ''' assert(memcmp(buffer, "hello!", 6) == 0); lfs_unmount(&lfs) => 0; ''' + +# mount with more blocks than the erase_count +[cases.test_superblocks_more_blocks] +defines.FORMAT_BLOCK_COUNT = '2*ERASE_COUNT' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + lfs.block_size = BLOCK_SIZE; + lfs.block_count = BLOCK_COUNT; + + lfs_mdir_t root = { + .pair = {0, 0}, // make sure this goes into block 0 + .rev = 0, + .off = sizeof(uint32_t), + .etag = 0xffffffff, + .count = 0, + .tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}, + .erased = false, + .split = false, + }; + + lfs_superblock_t superblock = { + .version = LFS_DISK_VERSION, + .block_size = BLOCK_SIZE, + .block_count = FORMAT_BLOCK_COUNT, + .name_max = LFS_NAME_MAX, + .file_max = LFS_FILE_MAX, + .attr_max = LFS_ATTR_MAX, + }; + + lfs_superblock_tole32(&superblock); + lfs_dir_commit(&lfs, &root, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, + {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, + {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})) => 0; + + // known block_size/block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + + // unknown block_size + cfg->block_size = 0; + cfg->block_count = ERASE_COUNT; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; +''' + +# the real challenge is finding superblock 1 when block_size is unknown, +# test for this explicitly +[cases.test_superblocks_unknown_superblock1] +in = 'lfs.c' +defines.BLOCK_SIZE = [ + 'ERASE_SIZE', + '2*ERASE_SIZE', + '4*ERASE_SIZE', + '(ERASE_COUNT/2)*ERASE_SIZE'] +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + lfs.block_size = BLOCK_SIZE; + lfs.block_count = BLOCK_COUNT; + + uint8_t buffer[lfs_max(READ_SIZE, sizeof(uint32_t))]; + memset(buffer, 0, lfs_max(READ_SIZE, sizeof(uint32_t))); + cfg->read(cfg, 0, 0, buffer, lfs_max(READ_SIZE, sizeof(uint32_t))) => 0; + uint32_t rev; + memcpy(&rev, buffer, sizeof(uint32_t)); + rev = lfs_fromle32(rev); + + lfs_mdir_t root = { + .pair = {1, 1}, // make sure this goes into block 1 + .rev = rev, + .off = sizeof(uint32_t), + .etag = 0xffffffff, + .count = 0, + .tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}, + .erased = false, + .split = false, + }; + + lfs_superblock_t superblock = { + .version = LFS_DISK_VERSION, + .block_size = BLOCK_SIZE, + .block_count = BLOCK_COUNT, + .name_max = LFS_NAME_MAX, + .file_max = LFS_FILE_MAX, + .attr_max = LFS_ATTR_MAX, + }; + + lfs_superblock_tole32(&superblock); + lfs_dir_commit(&lfs, &root, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, + {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, + {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})) => 0; + + // known block_size/block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => 0; + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_count + cfg->block_size = BLOCK_SIZE; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_size + cfg->block_size = 0; + cfg->block_count = ERASE_COUNT; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; + + // unknown block_count/block_size + cfg->block_size = 0; + cfg->block_count = 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 0; +'''