From 87bbf1d37458b134e994e4cf0e355184a9a1af4b Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 6 Jun 2023 12:20:47 -0500 Subject: [PATCH 01/12] Added lfs_fs_stat for access to filesystem status/configuration Currently this includes: - minor_version - on-disk minor version - block_usage - estimated number of in-use blocks - name_max - configurable name limit - file_max - configurable file limit - attr_max - configurable attr limit These are currently the only configuration operations that need to be written to disk. Other configuration is either needed to mount, such as block_size, or does not change the on-disk representation, such as read/prog_size. This also includes the current block usage, which is common in other filesystems, though a more expensive to find in littlefs. I figure it's not unreasonable to make lfs_fs_stat no worse than block allocation, hopefully this isn't a mistake. It may be worth caching the current usage after the most recent lookahead scan. More configuration may be added to this struct in the future. --- lfs.c | 60 +++++++++++++++++- lfs.h | 27 ++++++++ tests/test_compat.toml | 122 ++++++++++++++++++++++++++++++------ tests/test_superblocks.toml | 48 ++++++++++++++ 4 files changed, 237 insertions(+), 20 deletions(-) diff --git a/lfs.c b/lfs.c index f0daf540..a215ba8f 100644 --- a/lfs.c +++ b/lfs.c @@ -4324,11 +4324,9 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, major_version, minor_version, LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR); - #ifndef LFS_READONLY // note this bit is reserved on disk, so fetching more gstate // will not interfere here lfs_fs_prepsuperblock(lfs, true); - #endif } // check superblock configuration @@ -4421,6 +4419,49 @@ static int lfs_rawunmount(lfs_t *lfs) { /// Filesystem filesystem operations /// +static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { + // if the superblock is up-to-date, we must be on the most recent + // minor version of littlefs + if (!lfs_gstate_needssuperblock(&lfs->gstate)) { + fsinfo->minor_version = LFS_DISK_VERSION_MINOR; + + // otherwise we need to read the minor version on disk + } else { + // fetch the superblock + lfs_mdir_t dir; + int err = lfs_dir_fetch(lfs, &dir, lfs->root); + if (err) { + return err; + } + + lfs_superblock_t superblock; + lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + return tag; + } + lfs_superblock_fromle32(&superblock); + + // read the minor version + fsinfo->minor_version = (0xffff & (superblock.version >> 0)); + } + + // find the current block usage + lfs_ssize_t usage = lfs_fs_rawsize(lfs); + if (usage < 0) { + return usage; + } + fsinfo->block_usage = usage; + + // other on-disk configuration, we cache all of these for internal use + fsinfo->name_max = lfs->name_max; + fsinfo->file_max = lfs->file_max; + fsinfo->attr_max = lfs->attr_max; + + return 0; +} + int lfs_fs_rawtraverse(lfs_t *lfs, int (*cb)(void *data, lfs_block_t block), void *data, bool includeorphans) { @@ -4934,6 +4975,7 @@ static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) { return size; } + #ifdef LFS_MIGRATE ////// Migration from littelfs v1 below this ////// @@ -6053,6 +6095,20 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { return err; } +int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { + int err = LFS_LOCK(lfs->cfg); + if (err) { + return err; + } + LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo); + + err = lfs_fs_rawstat(lfs, fsinfo); + + LFS_TRACE("lfs_fs_stat -> %d", err); + LFS_UNLOCK(lfs->cfg); + return err; +} + lfs_ssize_t lfs_fs_size(lfs_t *lfs) { int err = LFS_LOCK(lfs->cfg); if (err) { diff --git a/lfs.h b/lfs.h index eb5c355d..25bd03c8 100644 --- a/lfs.h +++ b/lfs.h @@ -280,6 +280,27 @@ struct lfs_info { char name[LFS_NAME_MAX+1]; }; +// Filesystem info structure +struct lfs_fsinfo { + // On-disk minor version. + uint16_t minor_version; + + // Number of blocks in use, this is the same as lfs_fs_size. + // + // Note: block_usage is best effort. If files share COW structures, the + // calculated block_usage may be larger than the actual contents on-disk. + lfs_size_t block_usage; + + // Upper limit on the length of file names in bytes. + lfs_size_t name_max; + + // Upper limit on the size of files in bytes. + lfs_size_t file_max; + + // Upper limit on the size of custom attributes in bytes. + lfs_size_t attr_max; +}; + // Custom attribute structure, used to describe custom attributes // committed atomically during file writes. struct lfs_attr { @@ -659,6 +680,12 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); /// Filesystem-level filesystem operations +// Find on-disk info about the filesystem +// +// Fills out the fsinfo structure based on the filesystem found on-disk. +// Returns a negative error code on failure. +int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo); + // Finds the current size of the filesystem // // Note: Result is best effort. If files share COW structures, the returned diff --git a/tests/test_compat.toml b/tests/test_compat.toml index a36c38a4..e458ab01 100644 --- a/tests/test_compat.toml +++ b/tests/test_compat.toml @@ -25,11 +25,16 @@ code = ''' #define LFSP_VERSION LFS_VERSION #define LFSP_VERSION_MAJOR LFS_VERSION_MAJOR #define LFSP_VERSION_MINOR LFS_VERSION_MINOR +#define LFSP_DISK_VERSION LFS_DISK_VERSION +#define LFSP_DISK_VERSION_MAJOR LFS_DISK_VERSION_MAJOR +#define LFSP_DISK_VERSION_MINOR LFS_DISK_VERSION_MINOR #define lfsp_t lfs_t #define lfsp_config lfs_config #define lfsp_format lfs_format #define lfsp_mount lfs_mount #define lfsp_unmount lfs_unmount +#define lfsp_fsinfo lfs_fsinfo +#define lfsp_fs_stat lfs_fs_stat #define lfsp_dir_t lfs_dir_t #define lfsp_info lfs_info #define LFSP_TYPE_REG LFS_TYPE_REG @@ -74,6 +79,12 @@ code = ''' // now test the new mount lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + lfs_unmount(&lfs) => 0; ''' @@ -102,6 +113,11 @@ code = ''' lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + // can we list the directories? lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; @@ -166,6 +182,11 @@ code = ''' lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + // can we list the files? lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; @@ -251,6 +272,11 @@ code = ''' lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + // can we list the directories? lfs_dir_t dir; lfs_dir_open(&lfs, &dir, "/") => 0; @@ -343,6 +369,11 @@ code = ''' lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + // write another COUNT/2 dirs for (lfs_size_t i = COUNT/2; i < COUNT; i++) { char name[8]; @@ -420,6 +451,11 @@ code = ''' lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + // write half COUNT files prng = 42; for (lfs_size_t i = 0; i < COUNT; i++) { @@ -537,6 +573,11 @@ code = ''' lfs_t lfs; lfs_mount(&lfs, cfg) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + // write half COUNT files prng = 42; for (lfs_size_t i = 0; i < COUNT; i++) { @@ -651,6 +692,12 @@ code = ''' memcpy(&cfgp, cfg, sizeof(cfgp)); lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + + // we should be able to read the version using lfs_fs_stat + struct lfsp_fsinfo fsinfo; + lfsp_fs_stat(&lfsp, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + lfsp_unmount(&lfsp) => 0; ''' @@ -679,6 +726,11 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + // can we list the directories? lfsp_dir_t dir; lfsp_dir_open(&lfsp, &dir, "/") => 0; @@ -743,6 +795,11 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfsp_fsinfo fsinfo; + lfsp_fs_stat(&lfsp, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + // can we list the files? lfsp_dir_t dir; lfsp_dir_open(&lfsp, &dir, "/") => 0; @@ -828,6 +885,11 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfsp_fsinfo fsinfo; + lfsp_fs_stat(&lfsp, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + // can we list the directories? lfsp_dir_t dir; lfsp_dir_open(&lfsp, &dir, "/") => 0; @@ -920,6 +982,11 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfsp_fsinfo fsinfo; + lfsp_fs_stat(&lfsp, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + // write another COUNT/2 dirs for (lfs_size_t i = COUNT/2; i < COUNT; i++) { char name[8]; @@ -997,6 +1064,11 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfsp_fsinfo fsinfo; + lfsp_fs_stat(&lfsp, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + // write half COUNT files prng = 42; for (lfs_size_t i = 0; i < COUNT; i++) { @@ -1114,6 +1186,11 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; + // we should be able to read the version using lfs_fs_stat + struct lfsp_fsinfo fsinfo; + lfsp_fs_stat(&lfsp, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + // write half COUNT files prng = 42; for (lfs_size_t i = 0; i < COUNT; i++) { @@ -1316,45 +1393,54 @@ code = ''' // mount should still work lfs_mount(&lfs, cfg) => 0; + + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR-1); + lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[8]; lfs_file_read(&lfs, &file, buffer, 8) => 8; assert(memcmp(buffer, "testtest", 8) == 0); lfs_file_close(&lfs, &file) => 0; + + // minor version should be unchanged + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR-1); + lfs_unmount(&lfs) => 0; // if we write, we need to bump the minor version lfs_mount(&lfs, cfg) => 0; + + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR-1); + lfs_file_open(&lfs, &file, "test", LFS_O_WRONLY | LFS_O_TRUNC) => 0; lfs_file_write(&lfs, &file, "teeeeest", 8) => 8; lfs_file_close(&lfs, &file) => 0; - // minor version should have changed - lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; - lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock) - => LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)); - lfs_superblock_fromle32(&superblock); - assert((superblock.version >> 16) & 0xffff == LFS_DISK_VERSION_MAJOR); - assert((superblock.version >> 0) & 0xffff == LFS_DISK_VERSION_MINOR); + // minor version should be changed + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + lfs_unmount(&lfs) => 0; // and of course mount should still work lfs_mount(&lfs, cfg) => 0; + + // minor version should have changed + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; lfs_file_read(&lfs, &file, buffer, 8) => 8; assert(memcmp(buffer, "teeeeest", 8) == 0); lfs_file_close(&lfs, &file) => 0; - // minor version should have changed - lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; - lfs_dir_get(&lfs, &mdir, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock) - => LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)); - lfs_superblock_fromle32(&superblock); - assert((superblock.version >> 16) & 0xffff == LFS_DISK_VERSION_MAJOR); - assert((superblock.version >> 0) & 0xffff == LFS_DISK_VERSION_MINOR); + // yep, still changed + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + lfs_unmount(&lfs) => 0; ''' diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 689bbcd2..3001fb41 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -34,6 +34,54 @@ code = ''' lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; ''' +# test we can read superblock info through lfs_fs_stat +[cases.test_superblocks_stat] +code = ''' + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + + // test we can mount and read fsinfo + lfs_mount(&lfs, cfg) => 0; + + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.block_usage > 0 && fsinfo.block_usage < BLOCK_COUNT); + assert(fsinfo.name_max == LFS_NAME_MAX); + assert(fsinfo.file_max == LFS_FILE_MAX); + assert(fsinfo.attr_max == LFS_ATTR_MAX); + + lfs_unmount(&lfs) => 0; +''' + +[cases.test_superblocks_stat_tweaked] +defines.TWEAKED_NAME_MAX = 63 +defines.TWEAKED_FILE_MAX = '(1 << 16)-1' +defines.TWEAKED_ATTR_MAX = 512 +code = ''' + // create filesystem with tweaked params + struct lfs_config tweaked_cfg = *cfg; + tweaked_cfg.name_max = TWEAKED_NAME_MAX; + tweaked_cfg.file_max = TWEAKED_FILE_MAX; + tweaked_cfg.attr_max = TWEAKED_ATTR_MAX; + + lfs_t lfs; + lfs_format(&lfs, &tweaked_cfg) => 0; + + // test we can mount and read these params with the original config + lfs_mount(&lfs, cfg) => 0; + + struct lfs_fsinfo fsinfo; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.block_usage > 0 && fsinfo.block_usage < BLOCK_COUNT); + assert(fsinfo.name_max == TWEAKED_NAME_MAX); + assert(fsinfo.file_max == TWEAKED_FILE_MAX); + assert(fsinfo.attr_max == TWEAKED_ATTR_MAX); + + lfs_unmount(&lfs) => 0; +''' + # expanding superblock [cases.test_superblocks_expand] defines.BLOCK_CYCLES = [32, 33, 1] From fdee127f742caa34618c68c1328bb68f451da82f Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 6 Jun 2023 14:55:22 -0500 Subject: [PATCH 02/12] Removed use of LFS_VERSION in test_compat LFS_VERSION -> LFS_DISK_VERSION These tests shouldn't depend on LFS_VERSION. It's a bit subtle, but LFS_VERSION versions the API, and LFS_DISK_VERSION versions the on-disk format, which is what test_compat should be testing. --- tests/test_compat.toml | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/test_compat.toml b/tests/test_compat.toml index e458ab01..61bf2aa9 100644 --- a/tests/test_compat.toml +++ b/tests/test_compat.toml @@ -22,9 +22,6 @@ code = ''' #define STRINGIZE_(x) #x #include STRINGIZE(LFSP) #else -#define LFSP_VERSION LFS_VERSION -#define LFSP_VERSION_MAJOR LFS_VERSION_MAJOR -#define LFSP_VERSION_MINOR LFS_VERSION_MINOR #define LFSP_DISK_VERSION LFS_DISK_VERSION #define LFSP_DISK_VERSION_MAJOR LFS_DISK_VERSION_MAJOR #define LFSP_DISK_VERSION_MINOR LFS_DISK_VERSION_MINOR @@ -63,7 +60,7 @@ code = ''' # test we can mount in a new version [cases.test_compat_forward_mount] -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -91,7 +88,7 @@ code = ''' # test we can read dirs in a new version [cases.test_compat_forward_read_dirs] defines.COUNT = 5 -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -148,7 +145,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -235,7 +232,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -347,7 +344,7 @@ code = ''' # test we can write dirs in a new version [cases.test_compat_forward_write_dirs] defines.COUNT = 10 -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -411,7 +408,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -530,7 +527,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_VERSION_MAJOR == LFSP_VERSION_MAJOR' +if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' code = ''' // create the previous version struct lfsp_config cfgp; @@ -677,7 +674,7 @@ code = ''' # test we can mount in an old version [cases.test_compat_backward_mount] -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the new version lfs_t lfs; @@ -704,7 +701,7 @@ code = ''' # test we can read dirs in an old version [cases.test_compat_backward_read_dirs] defines.COUNT = 5 -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the new version lfs_t lfs; @@ -761,7 +758,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the new version lfs_t lfs; @@ -848,7 +845,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the new version lfs_t lfs; @@ -960,7 +957,7 @@ code = ''' # test we can write dirs in an old version [cases.test_compat_backward_write_dirs] defines.COUNT = 10 -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the new version lfs_t lfs; @@ -1024,7 +1021,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the previous version lfs_t lfs; @@ -1143,7 +1140,7 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_VERSION == LFSP_VERSION' +if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' code = ''' // create the previous version lfs_t lfs; From a7ccc1df596134bc240163d4bfa7e652a3c42817 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 6 Jun 2023 15:59:45 -0500 Subject: [PATCH 03/12] Promoted lfs_gstate_needssuperblock to be available in readonly builds Needed for minor version reporting in lfs_fs_stat to work correctly. --- lfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lfs.c b/lfs.c index a215ba8f..09fd71d9 100644 --- a/lfs.c +++ b/lfs.c @@ -415,11 +415,11 @@ static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) { static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) { return lfs_tag_type1(a->tag); } +#endif static inline bool lfs_gstate_needssuperblock(const lfs_gstate_t *a) { return lfs_tag_size(a->tag) >> 9; } -#endif static inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a, const lfs_block_t *pair) { @@ -535,7 +535,6 @@ static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file); static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss); -static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock); static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); static void lfs_fs_prepmove(lfs_t *lfs, uint16_t id, const lfs_block_t pair[2]); @@ -546,6 +545,8 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], static int lfs_fs_forceconsistency(lfs_t *lfs); #endif +static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock); + #ifdef LFS_MIGRATE static int lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); @@ -4672,12 +4673,10 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], } #endif -#ifndef LFS_READONLY static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock) { lfs->gstate.tag = (lfs->gstate.tag & ~LFS_MKTAG(0, 0, 0x200)) | (uint32_t)needssuperblock << 9; } -#endif #ifndef LFS_READONLY static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { From a51be18765ca466f360179492d2bd5066358f87f Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 6 Jun 2023 16:09:31 -0500 Subject: [PATCH 04/12] Removed previous-version lfsp_fs_stat checks in test_compat This function naturally doesn't exist in the previous version. We should eventually add these calls when we can expect the previous version to support this function, though it's a bit unclear when that should happen. Or maybe not! Maybe this is testing more of the previous version than we really care about. --- tests/test_compat.toml | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/tests/test_compat.toml b/tests/test_compat.toml index 61bf2aa9..4bb45d09 100644 --- a/tests/test_compat.toml +++ b/tests/test_compat.toml @@ -690,11 +690,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfsp_fsinfo fsinfo; - lfsp_fs_stat(&lfsp, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - lfsp_unmount(&lfsp) => 0; ''' @@ -723,11 +718,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfs_fsinfo fsinfo; - lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - // can we list the directories? lfsp_dir_t dir; lfsp_dir_open(&lfsp, &dir, "/") => 0; @@ -792,11 +782,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfsp_fsinfo fsinfo; - lfsp_fs_stat(&lfsp, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - // can we list the files? lfsp_dir_t dir; lfsp_dir_open(&lfsp, &dir, "/") => 0; @@ -882,11 +867,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfsp_fsinfo fsinfo; - lfsp_fs_stat(&lfsp, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - // can we list the directories? lfsp_dir_t dir; lfsp_dir_open(&lfsp, &dir, "/") => 0; @@ -979,11 +959,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfsp_fsinfo fsinfo; - lfsp_fs_stat(&lfsp, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - // write another COUNT/2 dirs for (lfs_size_t i = COUNT/2; i < COUNT; i++) { char name[8]; @@ -1061,11 +1036,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfsp_fsinfo fsinfo; - lfsp_fs_stat(&lfsp, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - // write half COUNT files prng = 42; for (lfs_size_t i = 0; i < COUNT; i++) { @@ -1183,11 +1153,6 @@ code = ''' lfsp_t lfsp; lfsp_mount(&lfsp, &cfgp) => 0; - // we should be able to read the version using lfs_fs_stat - struct lfsp_fsinfo fsinfo; - lfsp_fs_stat(&lfsp, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); - // write half COUNT files prng = 42; for (lfs_size_t i = 0; i < COUNT; i++) { From 8610f7c36b6554d65b7502222c76c286e4c8d0e2 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 6 Jun 2023 21:31:50 -0500 Subject: [PATCH 05/12] Increased context on failures for Valgrind in CI Valgrind output is very verbose but useful, with a default limit of 5 lines the output usually doesn't contain much useful info. --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cee3528..0d54b20a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -371,7 +371,8 @@ jobs: # on one geometry - name: test-valgrind run: | - TESTFLAGS="$TESTFLAGS --valgrind -Gdefault -Pnone" make test + TESTFLAGS="$TESTFLAGS --valgrind --context=1024 -Gdefault -Pnone" \ + make test # test that compilation is warning free under clang # run with Clang, mostly to check for Clang-specific warnings From c5fb3f181beff64de9cb2a7079594dfae29c982a Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 6 Jun 2023 21:52:04 -0500 Subject: [PATCH 06/12] Changed fsinfo.minor_version -> fsinfo.disk_version Version are now returned with major/minor packed into 32-bits, so 0x00020001 is the current disk version, for example. 1. This needed to change to use a disk_* prefix for consistency with the defines that already exist for LFS_VERSION/LFS_DISK_VERSION. 2. Encoding the version this way has the nice side effect of making 0 an invalid value. This is useful for adding a similar config option that needs to have reasonable default behavior for backwards compatibility. In theory this uses more space, but in practice most other config/status is 32-bits in littlefs. We would be wasting this space for alignment anyways. --- lfs.c | 6 +++--- lfs.h | 4 ++-- tests/test_compat.toml | 26 +++++++++++++------------- tests/test_superblocks.toml | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lfs.c b/lfs.c index 09fd71d9..c930adc5 100644 --- a/lfs.c +++ b/lfs.c @@ -4424,7 +4424,7 @@ static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { // if the superblock is up-to-date, we must be on the most recent // minor version of littlefs if (!lfs_gstate_needssuperblock(&lfs->gstate)) { - fsinfo->minor_version = LFS_DISK_VERSION_MINOR; + fsinfo->disk_version = LFS_DISK_VERSION; // otherwise we need to read the minor version on disk } else { @@ -4444,8 +4444,8 @@ static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { } lfs_superblock_fromle32(&superblock); - // read the minor version - fsinfo->minor_version = (0xffff & (superblock.version >> 0)); + // read the on-disk version + fsinfo->disk_version = superblock.version; } // find the current block usage diff --git a/lfs.h b/lfs.h index 25bd03c8..d477cdd4 100644 --- a/lfs.h +++ b/lfs.h @@ -282,8 +282,8 @@ struct lfs_info { // Filesystem info structure struct lfs_fsinfo { - // On-disk minor version. - uint16_t minor_version; + // On-disk version. + uint32_t disk_version; // Number of blocks in use, this is the same as lfs_fs_size. // diff --git a/tests/test_compat.toml b/tests/test_compat.toml index 4bb45d09..d84e68ea 100644 --- a/tests/test_compat.toml +++ b/tests/test_compat.toml @@ -80,7 +80,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); lfs_unmount(&lfs) => 0; ''' @@ -113,7 +113,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); // can we list the directories? lfs_dir_t dir; @@ -182,7 +182,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); // can we list the files? lfs_dir_t dir; @@ -272,7 +272,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); // can we list the directories? lfs_dir_t dir; @@ -369,7 +369,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); // write another COUNT/2 dirs for (lfs_size_t i = COUNT/2; i < COUNT; i++) { @@ -451,7 +451,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); // write half COUNT files prng = 42; @@ -573,7 +573,7 @@ code = ''' // we should be able to read the version using lfs_fs_stat struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFSP_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFSP_DISK_VERSION); // write half COUNT files prng = 42; @@ -1358,7 +1358,7 @@ code = ''' struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR-1); + assert(fsinfo.disk_version == LFS_DISK_VERSION-1); lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; uint8_t buffer[8]; @@ -1368,7 +1368,7 @@ code = ''' // minor version should be unchanged lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR-1); + assert(fsinfo.disk_version == LFS_DISK_VERSION-1); lfs_unmount(&lfs) => 0; @@ -1376,7 +1376,7 @@ code = ''' lfs_mount(&lfs, cfg) => 0; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR-1); + assert(fsinfo.disk_version == LFS_DISK_VERSION-1); lfs_file_open(&lfs, &file, "test", LFS_O_WRONLY | LFS_O_TRUNC) => 0; lfs_file_write(&lfs, &file, "teeeeest", 8) => 8; @@ -1384,7 +1384,7 @@ code = ''' // minor version should be changed lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFS_DISK_VERSION); lfs_unmount(&lfs) => 0; @@ -1393,7 +1393,7 @@ code = ''' // minor version should have changed lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFS_DISK_VERSION); lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; lfs_file_read(&lfs, &file, buffer, 8) => 8; @@ -1402,7 +1402,7 @@ code = ''' // yep, still changed lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFS_DISK_VERSION); lfs_unmount(&lfs) => 0; ''' diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 3001fb41..6ce148ba 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -45,7 +45,7 @@ code = ''' struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFS_DISK_VERSION); assert(fsinfo.block_usage > 0 && fsinfo.block_usage < BLOCK_COUNT); assert(fsinfo.name_max == LFS_NAME_MAX); assert(fsinfo.file_max == LFS_FILE_MAX); @@ -73,7 +73,7 @@ code = ''' struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; - assert(fsinfo.minor_version == LFS_DISK_VERSION_MINOR); + assert(fsinfo.disk_version == LFS_DISK_VERSION); assert(fsinfo.block_usage > 0 && fsinfo.block_usage < BLOCK_COUNT); assert(fsinfo.name_max == TWEAKED_NAME_MAX); assert(fsinfo.file_max == TWEAKED_FILE_MAX); From 08a132e048c862770e727f7762ebbd4e3730e2dd Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 26 Jun 2023 15:37:32 -0500 Subject: [PATCH 07/12] Added a link to ChaN's FatFS implementation ChaN's FAT implementation definitely deserves a mention here, since it was one of the first open-source microcontroller-oriented filesystem implementations that I'm aware of, and has a lot of good ideas at the implementation level. Honestly I didn't realize this wasn't already linked to from here. If you're using FAT on a microcontroller, it's most likely this library. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3afddfdd..df7ee003 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,10 @@ License Identifiers that are here available: http://spdx.org/licenses/ MCUs. It offers static wear-leveling and power-resilience with only a fixed _O(|address|)_ pointer structure stored on each block and in RAM. +- [ChaN's FatFs] - A lightweight reimplementation of the infamous FAT filesystem + for microcontroller-scale devices. Due to limitations of FAT it can't provide + power-loss resilience, but it does allow easy interop with PCs. + - [chamelon] - A pure-OCaml implementation of (most of) littlefs, designed for use with the MirageOS library operating system project. It is interoperable with the reference implementation, with some caveats. @@ -266,6 +270,7 @@ License Identifiers that are here available: http://spdx.org/licenses/ [LittleFileSystem]: https://os.mbed.com/docs/mbed-os/latest/apis/littlefilesystem.html [SPIFFS]: https://github.com/pellepl/spiffs [Dhara]: https://github.com/dlbeer/dhara +[ChaN's FatFs]: http://elm-chan.org/fsw/ff/00index_e.html [littlefs-python]: https://pypi.org/project/littlefs-python/ [littlefs2-rust]: https://crates.io/crates/littlefs2 [chamelon]: https://github.com/yomimono/chamelon From 265692e709a1e379019d596fddaff8bfb4efb5e9 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 29 Jun 2023 12:23:33 -0500 Subject: [PATCH 08/12] Removed fsinfo.block_usage for now In terms of ease-of-use, a user familiar with other filesystems expects block_usage in fsinfo. But in terms of practicality, block_usage can be expensive to find in littlefs, so if it's not needed in the resulting fsinfo, that operation is wasteful. It's not clear to me what the best course of action is, but since block_usage can always be added to fsinfo later, but not removed without breaking backwards compatibility, I'm leaving this out for now. Block usage can still be found by explicitly calling lfs_fs_size. --- lfs.c | 7 ------- lfs.h | 6 ------ tests/test_superblocks.toml | 2 -- 3 files changed, 15 deletions(-) diff --git a/lfs.c b/lfs.c index c930adc5..9736c18e 100644 --- a/lfs.c +++ b/lfs.c @@ -4448,13 +4448,6 @@ static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { fsinfo->disk_version = superblock.version; } - // find the current block usage - lfs_ssize_t usage = lfs_fs_rawsize(lfs); - if (usage < 0) { - return usage; - } - fsinfo->block_usage = usage; - // other on-disk configuration, we cache all of these for internal use fsinfo->name_max = lfs->name_max; fsinfo->file_max = lfs->file_max; diff --git a/lfs.h b/lfs.h index d477cdd4..596ba5ed 100644 --- a/lfs.h +++ b/lfs.h @@ -285,12 +285,6 @@ struct lfs_fsinfo { // On-disk version. uint32_t disk_version; - // Number of blocks in use, this is the same as lfs_fs_size. - // - // Note: block_usage is best effort. If files share COW structures, the - // calculated block_usage may be larger than the actual contents on-disk. - lfs_size_t block_usage; - // Upper limit on the length of file names in bytes. lfs_size_t name_max; diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 6ce148ba..f2fe452a 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -46,7 +46,6 @@ code = ''' struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.disk_version == LFS_DISK_VERSION); - assert(fsinfo.block_usage > 0 && fsinfo.block_usage < BLOCK_COUNT); assert(fsinfo.name_max == LFS_NAME_MAX); assert(fsinfo.file_max == LFS_FILE_MAX); assert(fsinfo.attr_max == LFS_ATTR_MAX); @@ -74,7 +73,6 @@ code = ''' struct lfs_fsinfo fsinfo; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.disk_version == LFS_DISK_VERSION); - assert(fsinfo.block_usage > 0 && fsinfo.block_usage < BLOCK_COUNT); assert(fsinfo.name_max == TWEAKED_NAME_MAX); assert(fsinfo.file_max == TWEAKED_FILE_MAX); assert(fsinfo.attr_max == TWEAKED_ATTR_MAX); From b72c96d44079efc332f9fe41debc4f64d90a2d67 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 7 Jun 2023 02:03:31 -0500 Subject: [PATCH 09/12] Added support for writing on-disk version lfs2.0 The intention is to help interop with older minor versions of littlefs. Unfortunately, since lfs2.0 drivers cannot mount lfs2.1 images, there are situations where it would be useful to write to write strictly lfs2.0 compatible images. The solution here adds a "disk_version" configuration option which determines the behavior of lfs2.1 dependent features. Normally you would expect this to only change write behavior. But since the main change in lfs2.1 increased validation of erased data, we also need to skip this extra validation (fcrc) or see terrible slowdowns when writing. --- lfs.c | 130 ++++++++++++++++++++++++------------ lfs.h | 6 ++ runners/test_runner.c | 5 ++ runners/test_runner.h | 7 +- tests/test_compat.toml | 75 ++++++++++++++++----- tests/test_powerloss.toml | 5 +- tests/test_superblocks.toml | 2 + 7 files changed, 169 insertions(+), 61 deletions(-) diff --git a/lfs.c b/lfs.c index 9736c18e..b0c52cff 100644 --- a/lfs.c +++ b/lfs.c @@ -518,6 +518,24 @@ static void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) { lfs->mlist = mlist; } +// some other filesystem operations +static uint32_t lfs_fs_disk_version(lfs_t *lfs) { + if (lfs->cfg->disk_version) { + return lfs->cfg->disk_version; + } else { + return LFS_DISK_VERSION; + } +} + +static uint16_t lfs_fs_disk_version_major(lfs_t *lfs) { + return 0xffff & (lfs_fs_disk_version(lfs) >> 16); + +} + +static uint16_t lfs_fs_disk_version_minor(lfs_t *lfs) { + return 0xffff & (lfs_fs_disk_version(lfs) >> 0); +} + /// Internal operations predeclared here /// #ifndef LFS_READONLY @@ -1111,7 +1129,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, // next commit not yet programmed? if (!lfs_tag_isvalid(tag)) { - maybeerased = true; + // we only might be erased if the last tag was a crc + maybeerased = (lfs_tag_type2(ptag) == LFS_TYPE_CCRC); break; // out of range? } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { @@ -1156,14 +1175,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, dir->tail[1] = temptail[1]; dir->split = tempsplit; - // reset crc + // reset crc, hasfcrc crc = 0xffffffff; continue; } - // fcrc is only valid when last tag was a crc - hasfcrc = false; - // crc the entry first, hopefully leaving it in the cache err = lfs_bd_crc(lfs, NULL, &lfs->rcache, lfs->cfg->block_size, @@ -1257,20 +1273,30 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, // did we end on a valid commit? we may have an erased block dir->erased = false; - if (maybeerased && hasfcrc && dir->off % lfs->cfg->prog_size == 0) { - // check for an fcrc matching the next prog's erased state, if - // this failed most likely a previous prog was interrupted, we - // need a new erase - uint32_t fcrc_ = 0xffffffff; - int err = lfs_bd_crc(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], dir->off, fcrc.size, &fcrc_); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } + if (maybeerased && dir->off % lfs->cfg->prog_size == 0) { + // note versions < lfs2.1 did not have fcrc tags, if + // we're < lfs2.1 treat missing fcrc as erased data + // + // we don't strictly need to do this, but otherwise writing + // to lfs2.0 disks becomes very inefficient + if (lfs_fs_disk_version(lfs) < 0x00020001) { + dir->erased = true; - // found beginning of erased part? - dir->erased = (fcrc_ == fcrc.crc); + } else if (hasfcrc) { + // check for an fcrc matching the next prog's erased state, if + // this failed most likely a previous prog was interrupted, we + // need a new erase + uint32_t fcrc_ = 0xffffffff; + int err = lfs_bd_crc(lfs, + NULL, &lfs->rcache, lfs->cfg->block_size, + dir->pair[0], dir->off, fcrc.size, &fcrc_); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } + + // found beginning of erased part? + dir->erased = (fcrc_ == fcrc.crc); + } } // synthetic move @@ -1606,22 +1632,29 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { return err; } - // find the expected fcrc, don't bother avoiding a reread - // of the eperturb, it should still be in our cache - struct lfs_fcrc fcrc = {.size=lfs->cfg->prog_size, .crc=0xffffffff}; - err = lfs_bd_crc(lfs, - NULL, &lfs->rcache, lfs->cfg->prog_size, - commit->block, noff, fcrc.size, &fcrc.crc); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } + // unfortunately fcrcs break mdir fetching < lfs2.1, so only write + // these if we're a >= lfs2.1 filesystem + if (lfs_fs_disk_version(lfs) >= 0x00020001) { + // find the expected fcrc, don't bother avoiding a reread + // of the eperturb, it should still be in our cache + struct lfs_fcrc fcrc = { + .size = lfs->cfg->prog_size, + .crc = 0xffffffff + }; + err = lfs_bd_crc(lfs, + NULL, &lfs->rcache, lfs->cfg->prog_size, + commit->block, noff, fcrc.size, &fcrc.crc); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } - lfs_fcrc_tole32(&fcrc); - err = lfs_dir_commitattr(lfs, commit, - LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)), - &fcrc); - if (err) { - return err; + lfs_fcrc_tole32(&fcrc); + err = lfs_dir_commitattr(lfs, commit, + LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)), + &fcrc); + if (err) { + return err; + } } } @@ -4052,6 +4085,13 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->cfg = cfg; int err = 0; + // this driver only supports minor version < current minor version + LFS_ASSERT(!lfs->cfg->disk_version || ( + (0xffff & (lfs->cfg->disk_version >> 16)) + == LFS_DISK_VERSION_MAJOR + && (0xffff & (lfs->cfg->disk_version >> 0)) + <= LFS_DISK_VERSION_MINOR)); + // check that bool is a truthy-preserving type // // note the most common reason for this failure is a before-c99 compiler, @@ -4209,7 +4249,7 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) { // write one superblock lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, + .version = lfs_fs_disk_version(lfs), .block_size = lfs->cfg->block_size, .block_count = lfs->cfg->block_count, .name_max = lfs->name_max, @@ -4307,12 +4347,14 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { // check version uint16_t major_version = (0xffff & (superblock.version >> 16)); uint16_t minor_version = (0xffff & (superblock.version >> 0)); - if ((major_version != LFS_DISK_VERSION_MAJOR || - minor_version > LFS_DISK_VERSION_MINOR)) { + if (major_version != lfs_fs_disk_version_major(lfs) + || minor_version > lfs_fs_disk_version_minor(lfs)) { LFS_ERROR("Invalid version " "v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16, - major_version, minor_version, - LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR); + major_version, + minor_version, + lfs_fs_disk_version_major(lfs), + lfs_fs_disk_version_minor(lfs)); err = LFS_ERR_INVAL; goto cleanup; } @@ -4320,11 +4362,13 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { // found older minor version? set an in-device only bit in the // gstate so we know we need to rewrite the superblock before // the first write - if (minor_version < LFS_DISK_VERSION_MINOR) { + if (minor_version < lfs_fs_disk_version_minor(lfs)) { LFS_DEBUG("Found older minor version " "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, - major_version, minor_version, - LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR); + major_version, + minor_version, + lfs_fs_disk_version_major(lfs), + lfs_fs_disk_version_minor(lfs)); // note this bit is reserved on disk, so fetching more gstate // will not interfere here lfs_fs_prepsuperblock(lfs, true); @@ -4424,7 +4468,7 @@ static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { // if the superblock is up-to-date, we must be on the most recent // minor version of littlefs if (!lfs_gstate_needssuperblock(&lfs->gstate)) { - fsinfo->disk_version = LFS_DISK_VERSION; + fsinfo->disk_version = lfs_fs_disk_version(lfs); // otherwise we need to read the minor version on disk } else { @@ -4711,7 +4755,7 @@ static int lfs_fs_desuperblock(lfs_t *lfs) { // write a new superblock lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, + .version = lfs_fs_disk_version(lfs), .block_size = lfs->cfg->block_size, .block_count = lfs->cfg->block_count, .name_max = lfs->name_max, diff --git a/lfs.h b/lfs.h index 596ba5ed..b8dfe5db 100644 --- a/lfs.h +++ b/lfs.h @@ -263,6 +263,12 @@ struct lfs_config { // can help bound the metadata compaction time. Must be <= block_size. // Defaults to block_size when zero. lfs_size_t metadata_max; + + // On-disk version to use when writing in the form of 16-bit major version + // + 16-bit minor version. This limiting metadata to what is supported by + // older minor versions. Note that some features will be lost. Defaults to + // to the most recent minor version when zero. + uint32_t disk_version; }; // File info structure diff --git a/runners/test_runner.c b/runners/test_runner.c index abc867c2..00f34f04 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -1346,6 +1346,7 @@ static void run_powerloss_none( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .disk_version = DISK_VERSION, }; struct lfs_emubd_config bdcfg = { @@ -1415,6 +1416,7 @@ static void run_powerloss_linear( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .disk_version = DISK_VERSION, }; struct lfs_emubd_config bdcfg = { @@ -1501,6 +1503,7 @@ static void run_powerloss_log( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .disk_version = DISK_VERSION, }; struct lfs_emubd_config bdcfg = { @@ -1585,6 +1588,7 @@ static void run_powerloss_cycles( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .disk_version = DISK_VERSION, }; struct lfs_emubd_config bdcfg = { @@ -1767,6 +1771,7 @@ static void run_powerloss_exhaustive( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + .disk_version = DISK_VERSION, }; struct lfs_emubd_config bdcfg = { diff --git a/runners/test_runner.h b/runners/test_runner.h index 9ff1f790..e30d4928 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -91,6 +91,7 @@ intmax_t test_define(size_t define); #define ERASE_CYCLES_i 8 #define BADBLOCK_BEHAVIOR_i 9 #define POWERLOSS_BEHAVIOR_i 10 +#define DISK_VERSION_i 11 #define READ_SIZE TEST_DEFINE(READ_SIZE_i) #define PROG_SIZE TEST_DEFINE(PROG_SIZE_i) @@ -103,6 +104,7 @@ intmax_t test_define(size_t define); #define ERASE_CYCLES TEST_DEFINE(ERASE_CYCLES_i) #define BADBLOCK_BEHAVIOR TEST_DEFINE(BADBLOCK_BEHAVIOR_i) #define POWERLOSS_BEHAVIOR TEST_DEFINE(POWERLOSS_BEHAVIOR_i) +#define DISK_VERSION TEST_DEFINE(DISK_VERSION_i) #define TEST_IMPLICIT_DEFINES \ TEST_DEF(READ_SIZE, PROG_SIZE) \ @@ -115,9 +117,10 @@ intmax_t test_define(size_t define); TEST_DEF(ERASE_VALUE, 0xff) \ TEST_DEF(ERASE_CYCLES, 0) \ TEST_DEF(BADBLOCK_BEHAVIOR, LFS_EMUBD_BADBLOCK_PROGERROR) \ - TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) + TEST_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP) \ + TEST_DEF(DISK_VERSION, 0) -#define TEST_IMPLICIT_DEFINE_COUNT 11 +#define TEST_IMPLICIT_DEFINE_COUNT 12 #define TEST_GEOMETRY_DEFINE_COUNT 4 diff --git a/tests/test_compat.toml b/tests/test_compat.toml index d84e68ea..ba447142 100644 --- a/tests/test_compat.toml +++ b/tests/test_compat.toml @@ -60,7 +60,10 @@ code = ''' # test we can mount in a new version [cases.test_compat_forward_mount] -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -88,7 +91,10 @@ code = ''' # test we can read dirs in a new version [cases.test_compat_forward_read_dirs] defines.COUNT = 5 -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -145,7 +151,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -232,7 +241,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -344,7 +356,10 @@ code = ''' # test we can write dirs in a new version [cases.test_compat_forward_write_dirs] defines.COUNT = 10 -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -408,7 +423,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -527,7 +545,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR' +if = ''' + LFS_DISK_VERSION_MAJOR == LFSP_DISK_VERSION_MAJOR + && DISK_VERSION == 0 +''' code = ''' // create the previous version struct lfsp_config cfgp; @@ -674,7 +695,10 @@ code = ''' # test we can mount in an old version [cases.test_compat_backward_mount] -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the new version lfs_t lfs; @@ -696,7 +720,10 @@ code = ''' # test we can read dirs in an old version [cases.test_compat_backward_read_dirs] defines.COUNT = 5 -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the new version lfs_t lfs; @@ -748,7 +775,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the new version lfs_t lfs; @@ -830,7 +860,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 4 -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the new version lfs_t lfs; @@ -937,7 +970,10 @@ code = ''' # test we can write dirs in an old version [cases.test_compat_backward_write_dirs] defines.COUNT = 10 -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the new version lfs_t lfs; @@ -996,7 +1032,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the previous version lfs_t lfs; @@ -1110,7 +1149,10 @@ code = ''' defines.COUNT = 5 defines.SIZE = [4, 32, 512, 8192] defines.CHUNK = 2 -if = 'LFS_DISK_VERSION == LFSP_DISK_VERSION' +if = ''' + LFS_DISK_VERSION == LFSP_DISK_VERSION + && DISK_VERSION == 0 +''' code = ''' // create the previous version lfs_t lfs; @@ -1319,7 +1361,10 @@ code = ''' # test that we correctly bump the minor version [cases.test_compat_minor_bump] in = 'lfs.c' -if = 'LFS_DISK_VERSION_MINOR > 0' +if = ''' + LFS_DISK_VERSION_MINOR > 0 + && DISK_VERSION == 0 +''' code = ''' // create a superblock lfs_t lfs; diff --git a/tests/test_powerloss.toml b/tests/test_powerloss.toml index 06f8661d..92c323b3 100644 --- a/tests/test_powerloss.toml +++ b/tests/test_powerloss.toml @@ -90,7 +90,10 @@ code = ''' # partial prog, may not be byte in order! [cases.test_powerloss_partial_prog] -if = "PROG_SIZE < BLOCK_SIZE" +if = ''' + PROG_SIZE < BLOCK_SIZE + && (DISK_VERSION == 0 || DISK_VERSION >= 0x00020001) +''' defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"] defines.BYTE_VALUE = [0x33, 0xcc] in = "lfs.c" diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index f2fe452a..0aff84ba 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -36,6 +36,7 @@ code = ''' # test we can read superblock info through lfs_fs_stat [cases.test_superblocks_stat] +if = 'DISK_VERSION == 0' code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -54,6 +55,7 @@ code = ''' ''' [cases.test_superblocks_stat_tweaked] +if = 'DISK_VERSION == 0' defines.TWEAKED_NAME_MAX = 63 defines.TWEAKED_FILE_MAX = '(1 << 16)-1' defines.TWEAKED_ATTR_MAX = 512 From eb9af7abe5a56d20574ee127ced018b810e405c2 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 14 Jun 2023 15:46:36 -0500 Subject: [PATCH 10/12] Added LFS_MULTIVERSION, made lfs2.0 support a compile-time option The code-cost wasn't that bad: 16556 B -> 16754 B (+1.2%) But moving write support of older versions behind a compile-time flag allows us to be a bit more liberal with what gets added to support older versions, since the cost won't hit most users. --- lfs.c | 20 +++++++++++++++++--- lfs.h | 2 ++ runners/test_runner.c | 10 ++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lfs.c b/lfs.c index b0c52cff..38b825dd 100644 --- a/lfs.c +++ b/lfs.c @@ -520,9 +520,13 @@ static void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) { // some other filesystem operations static uint32_t lfs_fs_disk_version(lfs_t *lfs) { + (void)lfs; +#ifdef LFS_MULTIVERSION if (lfs->cfg->disk_version) { return lfs->cfg->disk_version; - } else { + } else +#endif + { return LFS_DISK_VERSION; } } @@ -1274,6 +1278,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, // did we end on a valid commit? we may have an erased block dir->erased = false; if (maybeerased && dir->off % lfs->cfg->prog_size == 0) { + #ifdef LFS_MULTIVERSION // note versions < lfs2.1 did not have fcrc tags, if // we're < lfs2.1 treat missing fcrc as erased data // @@ -1282,7 +1287,9 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, if (lfs_fs_disk_version(lfs) < 0x00020001) { dir->erased = true; - } else if (hasfcrc) { + } else + #endif + if (hasfcrc) { // check for an fcrc matching the next prog's erased state, if // this failed most likely a previous prog was interrupted, we // need a new erase @@ -1632,9 +1639,14 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { return err; } + #ifdef LFS_MULTIVERSION // unfortunately fcrcs break mdir fetching < lfs2.1, so only write // these if we're a >= lfs2.1 filesystem - if (lfs_fs_disk_version(lfs) >= 0x00020001) { + if (lfs_fs_disk_version(lfs) <= 0x00020000) { + // don't write fcrc + } else + #endif + { // find the expected fcrc, don't bother avoiding a reread // of the eperturb, it should still be in our cache struct lfs_fcrc fcrc = { @@ -4085,12 +4097,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->cfg = cfg; int err = 0; +#ifdef LFS_MULTIVERSION // this driver only supports minor version < current minor version LFS_ASSERT(!lfs->cfg->disk_version || ( (0xffff & (lfs->cfg->disk_version >> 16)) == LFS_DISK_VERSION_MAJOR && (0xffff & (lfs->cfg->disk_version >> 0)) <= LFS_DISK_VERSION_MINOR)); +#endif // check that bool is a truthy-preserving type // diff --git a/lfs.h b/lfs.h index b8dfe5db..33af474a 100644 --- a/lfs.h +++ b/lfs.h @@ -264,11 +264,13 @@ struct lfs_config { // Defaults to block_size when zero. lfs_size_t metadata_max; +#ifdef LFS_MULTIVERSION // On-disk version to use when writing in the form of 16-bit major version // + 16-bit minor version. This limiting metadata to what is supported by // older minor versions. Note that some features will be lost. Defaults to // to the most recent minor version when zero. uint32_t disk_version; +#endif }; // File info structure diff --git a/runners/test_runner.c b/runners/test_runner.c index 00f34f04..27f85249 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -1346,7 +1346,9 @@ static void run_powerloss_none( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, + #endif }; struct lfs_emubd_config bdcfg = { @@ -1416,7 +1418,9 @@ static void run_powerloss_linear( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, + #endif }; struct lfs_emubd_config bdcfg = { @@ -1503,7 +1507,9 @@ static void run_powerloss_log( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, + #endif }; struct lfs_emubd_config bdcfg = { @@ -1588,7 +1594,9 @@ static void run_powerloss_cycles( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, + #endif }; struct lfs_emubd_config bdcfg = { @@ -1771,7 +1779,9 @@ static void run_powerloss_exhaustive( .block_cycles = BLOCK_CYCLES, .cache_size = CACHE_SIZE, .lookahead_size = LOOKAHEAD_SIZE, + #ifdef LFS_MULTIVERSION .disk_version = DISK_VERSION, + #endif }; struct lfs_emubd_config bdcfg = { From 79cc75d18f216286641b90cb32fba53e4e836ff6 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 14 Jun 2023 16:02:34 -0500 Subject: [PATCH 11/12] Added LFS_MULTIVERSION and testing of lfs2.0 to CI - Added test-multiversion test job - Added test-lfs2_0 test job - Added mutliversion size measurement --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 59 ++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2ead2e4..c0d446df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,7 +102,7 @@ jobs: # sizes table i=0 j=0 - for c in "" readonly threadsafe migrate error-asserts + for c in "" readonly threadsafe multiversion migrate error-asserts do # per-config results c_or_default=${c:-default} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d54b20a..ccb08fea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -170,6 +170,27 @@ jobs: cp lfs.data.csv sizes/${{matrix.arch}}-threadsafe.data.csv cp lfs.stack.csv sizes/${{matrix.arch}}-threadsafe.stack.csv cp lfs.structs.csv sizes/${{matrix.arch}}-threadsafe.structs.csv + - name: sizes-multiversion + run: | + make clean + CFLAGS="$CFLAGS \ + -DLFS_NO_ASSERT \ + -DLFS_NO_DEBUG \ + -DLFS_NO_WARN \ + -DLFS_NO_ERROR \ + -DLFS_MULTIVERSION" \ + make lfs.code.csv lfs.data.csv lfs.stack.csv lfs.structs.csv + ./scripts/structs.py -u lfs.structs.csv + ./scripts/summary.py lfs.code.csv lfs.data.csv lfs.stack.csv \ + -bfunction \ + -fcode=code_size \ + -fdata=data_size \ + -fstack=stack_limit --max=stack_limit + mkdir -p sizes + cp lfs.code.csv sizes/${{matrix.arch}}-multiversion.code.csv + cp lfs.data.csv sizes/${{matrix.arch}}-multiversion.data.csv + cp lfs.stack.csv sizes/${{matrix.arch}}-multiversion.stack.csv + cp lfs.structs.csv sizes/${{matrix.arch}}-multiversion.structs.csv - name: sizes-migrate run: | make clean @@ -353,6 +374,42 @@ jobs: run: | CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test + # run LFS_MULTIVERSION tests + test-multiversion: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: install + run: | + # need a few things + sudo apt-get update -qq + sudo apt-get install -qq gcc python3 python3-pip + pip3 install toml + gcc --version + python3 --version + - name: test-multiversion + run: | + CFLAGS="$CFLAGS -DLFS_MULTIVERSION" make test + + # run tests on the older version lfs2.0 + test-lfs2_0: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: install + run: | + # need a few things + sudo apt-get update -qq + sudo apt-get install -qq gcc python3 python3-pip + pip3 install toml + gcc --version + python3 --version + - name: test-lfs2_0 + run: | + CFLAGS="$CFLAGS -DLFS_MULTIVERSION" \ + TESTFLAGS="$TESTFLAGS -DDISK_VERSION=0x00020000" \ + make test + # run under Valgrind to check for memory errors test-valgrind: runs-on: ubuntu-22.04 @@ -685,7 +742,7 @@ jobs: # sizes table i=0 j=0 - for c in "" readonly threadsafe migrate error-asserts + for c in "" readonly threadsafe multiversion migrate error-asserts do # per-config results c_or_default=${c:-default} From a942cdba664dd9f7b48dd7494807c29ef6fe313c Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 30 Jun 2023 00:28:10 -0500 Subject: [PATCH 12/12] Bumped minor version to v2.7 --- lfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfs.h b/lfs.h index 33af474a..1081735f 100644 --- a/lfs.h +++ b/lfs.h @@ -21,7 +21,7 @@ extern "C" // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS_VERSION 0x00020006 +#define LFS_VERSION 0x00020007 #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))