From 2105e502c526ebd4d0cc7e2dca63e6586f1e884f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 16 Apr 2025 17:46:34 +0200 Subject: [PATCH 1/5] Add support for shrinking a filesystem This PR adds a new `lfs_fs_shrink`, which functions similarly to `lfs_fs_grow`, but supports reducing the block count. This functions first checks that none of the removed block are in use. If it is the case, it will fail. --- lfs.c | 89 ++++++++++++++++++++++-------- lfs.h | 11 ++++ tests/test_shrink.toml | 104 +++++++++++++++++++++++++++++++++++ tests/test_superblocks.toml | 106 ++++++++++++++++++++++++++++++++++++ 4 files changed, 288 insertions(+), 22 deletions(-) create mode 100644 tests/test_shrink.toml diff --git a/lfs.c b/lfs.c index 28a632b6..874715be 100644 --- a/lfs.c +++ b/lfs.c @@ -5233,38 +5233,67 @@ static int lfs_fs_gc_(lfs_t *lfs) { #endif #ifndef LFS_READONLY +static int lfs_fs_rewrite_block_count(lfs_t *lfs, lfs_size_t block_count) { + lfs->block_count = block_count; + + // fetch the root + lfs_mdir_t root; + int err = lfs_dir_fetch(lfs, &root, lfs->root); + if (err) { + return err; + } + + // update the superblock + lfs_superblock_t superblock; + lfs_stag_t tag = lfs_dir_get(lfs, &root, LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + return tag; + } + lfs_superblock_fromle32(&superblock); + + superblock.block_count = lfs->block_count; + + lfs_superblock_tole32(&superblock); + err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( + {tag, &superblock})); + if (err) { + return err; + } + return 0; +} + static int lfs_fs_grow_(lfs_t *lfs, lfs_size_t block_count) { // shrinking is not supported LFS_ASSERT(block_count >= lfs->block_count); if (block_count > lfs->block_count) { - lfs->block_count = block_count; + return lfs_fs_rewrite_block_count(lfs, block_count); + } - // fetch the root - lfs_mdir_t root; - int err = lfs_dir_fetch(lfs, &root, lfs->root); + return 0; +} + +static int lfs_shrink_check_block(void * data, lfs_block_t block) { + lfs_size_t threshold = *((lfs_size_t *) data); + if (block >= threshold) { + return LFS_ERR_NOTEMPTY; + } + return 0; +} + +static int lfs_fs_shrink_(lfs_t *lfs, lfs_size_t block_count) { + if (block_count != lfs->block_count) { + + lfs_block_t threshold = block_count; + + int err = lfs_fs_traverse_(lfs, lfs_shrink_check_block, &threshold, true); if (err) { return err; } - // update the superblock - lfs_superblock_t superblock; - lfs_stag_t tag = lfs_dir_get(lfs, &root, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - return tag; - } - lfs_superblock_fromle32(&superblock); - - superblock.block_count = lfs->block_count; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( - {tag, &superblock})); - if (err) { - return err; - } + return lfs_fs_rewrite_block_count(lfs, block_count); } return 0; @@ -6485,6 +6514,22 @@ int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) { } #endif +#ifndef LFS_READONLY +int lfs_fs_shrink(lfs_t *lfs, lfs_size_t block_count) { + int err = LFS_LOCK(lfs->cfg); + if (err) { + return err; + } + LFS_TRACE("lfs_fs_shrink(%p, %"PRIu32")", (void*)lfs, block_count); + + err = lfs_fs_shrink_(lfs, block_count); + + LFS_TRACE("lfs_fs_shrink -> %d", err); + LFS_UNLOCK(lfs->cfg); + return err; +} +#endif + #ifdef LFS_MIGRATE int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { int err = LFS_LOCK(cfg); diff --git a/lfs.h b/lfs.h index 45315603..072b290b 100644 --- a/lfs.h +++ b/lfs.h @@ -772,6 +772,17 @@ int lfs_fs_gc(lfs_t *lfs); int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count); #endif +#ifndef LFS_READONLY +// Shrinks the filesystem to a new size, updating the superblock with the new +// block count. +// +// Note: This first checks that none of the blocks that are being removed are in use +// and will fail if it is the case +// +// Returns a negative error code on failure. +int lfs_fs_shrink(lfs_t *lfs, lfs_size_t block_count); +#endif + #ifndef LFS_READONLY #ifdef LFS_MIGRATE // Attempts to migrate a previous version of littlefs diff --git a/tests/test_shrink.toml b/tests/test_shrink.toml new file mode 100644 index 00000000..0c317d66 --- /dev/null +++ b/tests/test_shrink.toml @@ -0,0 +1,104 @@ +# simple shrink +[cases.test_shrink_simple] +defines.BLOCK_COUNT = [10, 15, 20] +defines.AFTER_BLOCK_COUNT = [5, 10, 15, 19] +if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT" +code = ''' + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + lfs_fs_shrink(&lfs, AFTER_BLOCK_COUNT) => 0; + lfs_unmount(&lfs); + if (BLOCK_COUNT != AFTER_BLOCK_COUNT) { + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + } + lfs_t lfs2 = lfs; + struct lfs_config cfg2 = *cfg; + cfg2.block_count = AFTER_BLOCK_COUNT; + lfs2.cfg = &cfg2; + lfs_mount(&lfs2, &cfg2) => 0; + lfs_unmount(&lfs2) => 0; +''' + +# shrinking full +[cases.test_shrink_full] +defines.BLOCK_COUNT = [10, 15, 20] +defines.AFTER_BLOCK_COUNT = [5, 7, 10, 12, 15, 17, 20] +defines.FILES_COUNT = [7, 8, 9, 10] +if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT && FILES_COUNT + 2 < BLOCK_COUNT" +code = ''' + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + // create FILES_COUNT files of BLOCK_SIZE - 50 bytes (to avoid inlining) + lfs_mount(&lfs, cfg) => 0; + for (int i = 0; i < FILES_COUNT + 1; i++) { + lfs_file_t file; + char path[1024]; + sprintf(path, "file_%03d", i); + lfs_file_open(&lfs, &file, path, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + char wbuffer[BLOCK_SIZE]; + memset(wbuffer, 'b', BLOCK_SIZE); + // Ensure one block is taken per file, but that files are not inlined. + lfs_size_t size = BLOCK_SIZE - 0x40; + sprintf(wbuffer, "Hi %03d", i); + lfs_file_write(&lfs, &file, wbuffer, size) => size; + lfs_file_close(&lfs, &file) => 0; + } + + int err = lfs_fs_shrink(&lfs, AFTER_BLOCK_COUNT); + if (err == 0) { + for (int i = 0; i < FILES_COUNT + 1; i++) { + lfs_file_t file; + char path[1024]; + sprintf(path, "file_%03d", i); + lfs_file_open(&lfs, &file, path, + LFS_O_RDONLY ) => 0; + lfs_size_t size = BLOCK_SIZE - 0x40; + char wbuffer[size]; + char wbuffer_ref[size]; + // Ensure one block is taken per file, but that files are not inlined. + memset(wbuffer_ref, 'b', size); + sprintf(wbuffer_ref, "Hi %03d", i); + lfs_file_read(&lfs, &file, wbuffer, BLOCK_SIZE) => size; + lfs_file_close(&lfs, &file) => 0; + for (lfs_size_t j = 0; j < size; j++) { + wbuffer[j] => wbuffer_ref[j]; + } + } + } else { + assert(err == LFS_ERR_NOTEMPTY); + } + + lfs_unmount(&lfs) => 0; + if (err == 0 ) { + if ( AFTER_BLOCK_COUNT != BLOCK_COUNT ) { + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + } + + lfs_t lfs2 = lfs; + struct lfs_config cfg2 = *cfg; + cfg2.block_count = AFTER_BLOCK_COUNT; + lfs2.cfg = &cfg2; + lfs_mount(&lfs2, &cfg2) => 0; + for (int i = 0; i < FILES_COUNT + 1; i++) { + lfs_file_t file; + char path[1024]; + sprintf(path, "file_%03d", i); + lfs_file_open(&lfs2, &file, path, + LFS_O_RDONLY ) => 0; + lfs_size_t size = BLOCK_SIZE - 0x40; + char wbuffer[size]; + char wbuffer_ref[size]; + // Ensure one block is taken per file, but that files are not inlined. + memset(wbuffer_ref, 'b', size); + sprintf(wbuffer_ref, "Hi %03d", i); + lfs_file_read(&lfs2, &file, wbuffer, BLOCK_SIZE) => size; + lfs_file_close(&lfs2, &file) => 0; + for (lfs_size_t j = 0; j < size; j++) { + wbuffer[j] => wbuffer_ref[j]; + } + } + lfs_unmount(&lfs2); + } +''' diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 5911c9b0..07961751 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -524,6 +524,112 @@ code = ''' lfs_unmount(&lfs) => 0; ''' + +# mount and grow the filesystem +[cases.test_superblocks_shrink] +defines.BLOCK_COUNT = 'ERASE_COUNT' +defines.BLOCK_COUNT_2 = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2'] +defines.KNOWN_BLOCK_COUNT = [true, false] +code = ''' + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + + if (KNOWN_BLOCK_COUNT) { + cfg->block_count = BLOCK_COUNT; + } else { + cfg->block_count = 0; + } + + // mount with block_size < erase_size + 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; + + // same size is a noop + lfs_mount(&lfs, cfg) => 0; + lfs_fs_shrink(&lfs, BLOCK_COUNT) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT); + lfs_unmount(&lfs) => 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; + + // grow to new size + lfs_mount(&lfs, cfg) => 0; + lfs_fs_shrink(&lfs, BLOCK_COUNT_2) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT_2); + lfs_unmount(&lfs) => 0; + + if (KNOWN_BLOCK_COUNT) { + cfg->block_count = BLOCK_COUNT_2; + } else { + 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_2); + lfs_unmount(&lfs) => 0; + + // mounting with the previous size should fail + cfg->block_count = BLOCK_COUNT; + lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; + + if (KNOWN_BLOCK_COUNT) { + cfg->block_count = BLOCK_COUNT_2; + } else { + cfg->block_count = 0; + } + + // same size is a noop + lfs_mount(&lfs, cfg) => 0; + lfs_fs_shrink(&lfs, BLOCK_COUNT_2) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT_2); + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT_2); + 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_2); + lfs_file_t file; + lfs_file_open(&lfs, &file, "test", + LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; + lfs_file_write(&lfs, &file, "hello!", 6) => 6; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, cfg) => 0; + lfs_fs_stat(&lfs, &fsinfo) => 0; + assert(fsinfo.block_size == BLOCK_SIZE); + assert(fsinfo.block_count == BLOCK_COUNT_2); + lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; + uint8_t buffer[256]; + lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; + lfs_file_close(&lfs, &file) => 0; + assert(memcmp(buffer, "hello!", 6) == 0); + lfs_unmount(&lfs) => 0; +''' + # test that metadata_max does not cause problems for superblock compaction [cases.test_superblocks_metadata_max] defines.METADATA_MAX = [ From 9b8f802b4357e7cb8d8d6e6dc5c768ad2178c1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 5 May 2025 11:37:39 +0200 Subject: [PATCH 2/5] fixup! Add support for shrinking a filesystem --- lfs.c | 84 ++++++++++++++----------------------- lfs.h | 15 ++----- tests/test_shrink.toml | 4 +- tests/test_superblocks.toml | 6 +-- 4 files changed, 40 insertions(+), 69 deletions(-) diff --git a/lfs.c b/lfs.c index 874715be..3d128ff7 100644 --- a/lfs.c +++ b/lfs.c @@ -5233,12 +5233,41 @@ static int lfs_fs_gc_(lfs_t *lfs) { #endif #ifndef LFS_READONLY -static int lfs_fs_rewrite_block_count(lfs_t *lfs, lfs_size_t block_count) { +#ifdef LFS_SHRINKIFCHEAP +static int lfs_shrink_checkblock(void * data, lfs_block_t block) { + lfs_size_t threshold = *((lfs_size_t *) data); + if (block >= threshold) { + return LFS_ERR_NOTEMPTY; + } + return 0; +} +#endif + +static int lfs_fs_grow_(lfs_t *lfs, lfs_size_t block_count) { + int err; + + if (block_count == lfs->block_count) { + return 0; + } + + +#ifndef LFS_SHRINKIFCHEAP + // shrinking is not supported + LFS_ASSERT(block_count >= lfs->block_count); +#endif +#ifdef LFS_SHRINKIFCHEAP + lfs_block_t threshold = block_count; + err = lfs_fs_traverse_(lfs, lfs_shrink_checkblock, &threshold, true); + if (err) { + return err; + } +#endif + lfs->block_count = block_count; // fetch the root lfs_mdir_t root; - int err = lfs_dir_fetch(lfs, &root, lfs->root); + err = lfs_dir_fetch(lfs, &root, lfs->root); if (err) { return err; } @@ -5263,41 +5292,6 @@ static int lfs_fs_rewrite_block_count(lfs_t *lfs, lfs_size_t block_count) { } return 0; } - -static int lfs_fs_grow_(lfs_t *lfs, lfs_size_t block_count) { - // shrinking is not supported - LFS_ASSERT(block_count >= lfs->block_count); - - if (block_count > lfs->block_count) { - return lfs_fs_rewrite_block_count(lfs, block_count); - } - - return 0; -} - -static int lfs_shrink_check_block(void * data, lfs_block_t block) { - lfs_size_t threshold = *((lfs_size_t *) data); - if (block >= threshold) { - return LFS_ERR_NOTEMPTY; - } - return 0; -} - -static int lfs_fs_shrink_(lfs_t *lfs, lfs_size_t block_count) { - if (block_count != lfs->block_count) { - - lfs_block_t threshold = block_count; - - int err = lfs_fs_traverse_(lfs, lfs_shrink_check_block, &threshold, true); - if (err) { - return err; - } - - return lfs_fs_rewrite_block_count(lfs, block_count); - } - - return 0; -} #endif #ifdef LFS_MIGRATE @@ -6514,22 +6508,6 @@ int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) { } #endif -#ifndef LFS_READONLY -int lfs_fs_shrink(lfs_t *lfs, lfs_size_t block_count) { - int err = LFS_LOCK(lfs->cfg); - if (err) { - return err; - } - LFS_TRACE("lfs_fs_shrink(%p, %"PRIu32")", (void*)lfs, block_count); - - err = lfs_fs_shrink_(lfs, block_count); - - LFS_TRACE("lfs_fs_shrink -> %d", err); - LFS_UNLOCK(lfs->cfg); - return err; -} -#endif - #ifdef LFS_MIGRATE int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { int err = LFS_LOCK(cfg); diff --git a/lfs.h b/lfs.h index 072b290b..be98b54b 100644 --- a/lfs.h +++ b/lfs.h @@ -766,23 +766,16 @@ int lfs_fs_gc(lfs_t *lfs); // Grows the filesystem to a new size, updating the superblock with the new // block count. // +// if LFS_SHRINKIFCHEAP is defined, this function will also accept +// block_counts smaller than the current configuration, after checking +// that none of the blocks that are being removed are in use. +// // Note: This is irreversible. // // Returns a negative error code on failure. int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count); #endif -#ifndef LFS_READONLY -// Shrinks the filesystem to a new size, updating the superblock with the new -// block count. -// -// Note: This first checks that none of the blocks that are being removed are in use -// and will fail if it is the case -// -// Returns a negative error code on failure. -int lfs_fs_shrink(lfs_t *lfs, lfs_size_t block_count); -#endif - #ifndef LFS_READONLY #ifdef LFS_MIGRATE // Attempts to migrate a previous version of littlefs diff --git a/tests/test_shrink.toml b/tests/test_shrink.toml index 0c317d66..10c00ae8 100644 --- a/tests/test_shrink.toml +++ b/tests/test_shrink.toml @@ -7,7 +7,7 @@ code = ''' lfs_t lfs; lfs_format(&lfs, cfg) => 0; lfs_mount(&lfs, cfg) => 0; - lfs_fs_shrink(&lfs, AFTER_BLOCK_COUNT) => 0; + lfs_fs_grow(&lfs, AFTER_BLOCK_COUNT) => 0; lfs_unmount(&lfs); if (BLOCK_COUNT != AFTER_BLOCK_COUNT) { lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; @@ -46,7 +46,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; } - int err = lfs_fs_shrink(&lfs, AFTER_BLOCK_COUNT); + int err = lfs_fs_grow(&lfs, AFTER_BLOCK_COUNT); if (err == 0) { for (int i = 0; i < FILES_COUNT + 1; i++) { lfs_file_t file; diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 07961751..4daf764b 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -550,7 +550,7 @@ code = ''' // same size is a noop lfs_mount(&lfs, cfg) => 0; - lfs_fs_shrink(&lfs, BLOCK_COUNT) => 0; + lfs_fs_grow(&lfs, BLOCK_COUNT) => 0; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT); @@ -564,7 +564,7 @@ code = ''' // grow to new size lfs_mount(&lfs, cfg) => 0; - lfs_fs_shrink(&lfs, BLOCK_COUNT_2) => 0; + lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT_2); @@ -594,7 +594,7 @@ code = ''' // same size is a noop lfs_mount(&lfs, cfg) => 0; - lfs_fs_shrink(&lfs, BLOCK_COUNT_2) => 0; + lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0; lfs_fs_stat(&lfs, &fsinfo) => 0; assert(fsinfo.block_size == BLOCK_SIZE); assert(fsinfo.block_count == BLOCK_COUNT_2); From 7782d3dfa306b3dad917afb3a646cc1e72bba4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 6 May 2025 10:59:32 +0200 Subject: [PATCH 3/5] Mention that shrinking is unlikely to work --- .github/workflows/test.yml | 16 ++++++++++++++++ lfs.h | 6 +++--- tests/test_shrink.toml | 5 +++++ tests/test_superblocks.toml | 2 ++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f3100bee..0815867c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -374,6 +374,22 @@ jobs: run: | CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test + test-shrink: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - 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-no-intrinsics + run: | + CFLAGS="$CFLAGS -DLFS_SHRINKIFCHEAP" make test + # run with all trace options enabled to at least make sure these # all compile test-yes-trace: diff --git a/lfs.h b/lfs.h index be98b54b..a8e35eed 100644 --- a/lfs.h +++ b/lfs.h @@ -766,11 +766,11 @@ int lfs_fs_gc(lfs_t *lfs); // Grows the filesystem to a new size, updating the superblock with the new // block count. // -// if LFS_SHRINKIFCHEAP is defined, this function will also accept +// If LFS_SHRINKIFCHEAP is defined, this function will also accept // block_counts smaller than the current configuration, after checking // that none of the blocks that are being removed are in use. -// -// Note: This is irreversible. +// Note that littlefs's pseudorandom block allocation means that +// this is very unlikely to work in the general case. // // Returns a negative error code on failure. int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count); diff --git a/tests/test_shrink.toml b/tests/test_shrink.toml index 10c00ae8..47610485 100644 --- a/tests/test_shrink.toml +++ b/tests/test_shrink.toml @@ -2,8 +2,10 @@ [cases.test_shrink_simple] defines.BLOCK_COUNT = [10, 15, 20] defines.AFTER_BLOCK_COUNT = [5, 10, 15, 19] + if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT" code = ''' +#ifdef LFS_SHRINKIFCHEAP lfs_t lfs; lfs_format(&lfs, cfg) => 0; lfs_mount(&lfs, cfg) => 0; @@ -18,6 +20,7 @@ code = ''' lfs2.cfg = &cfg2; lfs_mount(&lfs2, &cfg2) => 0; lfs_unmount(&lfs2) => 0; +#endif ''' # shrinking full @@ -27,6 +30,7 @@ defines.AFTER_BLOCK_COUNT = [5, 7, 10, 12, 15, 17, 20] defines.FILES_COUNT = [7, 8, 9, 10] if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT && FILES_COUNT + 2 < BLOCK_COUNT" code = ''' +#ifdef LFS_SHRINKIFCHEAP lfs_t lfs; lfs_format(&lfs, cfg) => 0; // create FILES_COUNT files of BLOCK_SIZE - 50 bytes (to avoid inlining) @@ -101,4 +105,5 @@ code = ''' } lfs_unmount(&lfs2); } +#endif ''' diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 4daf764b..188e4339 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -531,6 +531,7 @@ defines.BLOCK_COUNT = 'ERASE_COUNT' defines.BLOCK_COUNT_2 = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2'] defines.KNOWN_BLOCK_COUNT = [true, false] code = ''' +#ifdef LFS_SHRINKIFCHEAP lfs_t lfs; lfs_format(&lfs, cfg) => 0; @@ -628,6 +629,7 @@ code = ''' lfs_file_close(&lfs, &file) => 0; assert(memcmp(buffer, "hello!", 6) == 0); lfs_unmount(&lfs) => 0; +#endif ''' # test that metadata_max does not cause problems for superblock compaction From 7d79423972049b195ba26822e58e34d2d4cf5f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 7 May 2025 10:32:03 +0200 Subject: [PATCH 4/5] Rename SHRINKIFCHEAP to SHRINKNONRELOCATING --- .github/workflows/test.yml | 2 +- lfs.c | 6 +++--- lfs.h | 2 +- tests/test_shrink.toml | 4 ++-- tests/test_superblocks.toml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0815867c..a1af9049 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -388,7 +388,7 @@ jobs: python3 --version - name: test-no-intrinsics run: | - CFLAGS="$CFLAGS -DLFS_SHRINKIFCHEAP" make test + CFLAGS="$CFLAGS -DLFS_SHRINKNONRELOCATING" make test # run with all trace options enabled to at least make sure these # all compile diff --git a/lfs.c b/lfs.c index 3d128ff7..14b44001 100644 --- a/lfs.c +++ b/lfs.c @@ -5233,7 +5233,7 @@ static int lfs_fs_gc_(lfs_t *lfs) { #endif #ifndef LFS_READONLY -#ifdef LFS_SHRINKIFCHEAP +#ifdef LFS_SHRINKNONRELOCATING static int lfs_shrink_checkblock(void * data, lfs_block_t block) { lfs_size_t threshold = *((lfs_size_t *) data); if (block >= threshold) { @@ -5251,11 +5251,11 @@ static int lfs_fs_grow_(lfs_t *lfs, lfs_size_t block_count) { } -#ifndef LFS_SHRINKIFCHEAP +#ifndef LFS_SHRINKNONRELOCATING // shrinking is not supported LFS_ASSERT(block_count >= lfs->block_count); #endif -#ifdef LFS_SHRINKIFCHEAP +#ifdef LFS_SHRINKNONRELOCATING lfs_block_t threshold = block_count; err = lfs_fs_traverse_(lfs, lfs_shrink_checkblock, &threshold, true); if (err) { diff --git a/lfs.h b/lfs.h index a8e35eed..3968b5d7 100644 --- a/lfs.h +++ b/lfs.h @@ -766,7 +766,7 @@ int lfs_fs_gc(lfs_t *lfs); // Grows the filesystem to a new size, updating the superblock with the new // block count. // -// If LFS_SHRINKIFCHEAP is defined, this function will also accept +// If LFS_SHRINKNONRELOCATING is defined, this function will also accept // block_counts smaller than the current configuration, after checking // that none of the blocks that are being removed are in use. // Note that littlefs's pseudorandom block allocation means that diff --git a/tests/test_shrink.toml b/tests/test_shrink.toml index 47610485..6efa012c 100644 --- a/tests/test_shrink.toml +++ b/tests/test_shrink.toml @@ -5,7 +5,7 @@ defines.AFTER_BLOCK_COUNT = [5, 10, 15, 19] if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT" code = ''' -#ifdef LFS_SHRINKIFCHEAP +#ifdef LFS_SHRINKNONRELOCATING lfs_t lfs; lfs_format(&lfs, cfg) => 0; lfs_mount(&lfs, cfg) => 0; @@ -30,7 +30,7 @@ defines.AFTER_BLOCK_COUNT = [5, 7, 10, 12, 15, 17, 20] defines.FILES_COUNT = [7, 8, 9, 10] if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT && FILES_COUNT + 2 < BLOCK_COUNT" code = ''' -#ifdef LFS_SHRINKIFCHEAP +#ifdef LFS_SHRINKNONRELOCATING lfs_t lfs; lfs_format(&lfs, cfg) => 0; // create FILES_COUNT files of BLOCK_SIZE - 50 bytes (to avoid inlining) diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 188e4339..78050f13 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -531,7 +531,7 @@ defines.BLOCK_COUNT = 'ERASE_COUNT' defines.BLOCK_COUNT_2 = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2'] defines.KNOWN_BLOCK_COUNT = [true, false] code = ''' -#ifdef LFS_SHRINKIFCHEAP +#ifdef LFS_SHRINKNONRELOCATING lfs_t lfs; lfs_format(&lfs, cfg) => 0; From edaaaf88ead8d0828fa79f45b9c0d55662504da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 7 May 2025 10:38:43 +0200 Subject: [PATCH 5/5] Apply review comments --- lfs.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lfs.c b/lfs.c index 14b44001..fcb20c77 100644 --- a/lfs.c +++ b/lfs.c @@ -5234,8 +5234,8 @@ static int lfs_fs_gc_(lfs_t *lfs) { #ifndef LFS_READONLY #ifdef LFS_SHRINKNONRELOCATING -static int lfs_shrink_checkblock(void * data, lfs_block_t block) { - lfs_size_t threshold = *((lfs_size_t *) data); +static int lfs_shrink_checkblock(void *data, lfs_block_t block) { + lfs_size_t threshold = *((lfs_size_t*)data); if (block >= threshold) { return LFS_ERR_NOTEMPTY; } @@ -5256,10 +5256,11 @@ static int lfs_fs_grow_(lfs_t *lfs, lfs_size_t block_count) { LFS_ASSERT(block_count >= lfs->block_count); #endif #ifdef LFS_SHRINKNONRELOCATING - lfs_block_t threshold = block_count; - err = lfs_fs_traverse_(lfs, lfs_shrink_checkblock, &threshold, true); - if (err) { - return err; + if (block_count < lfs->block_count) { + err = lfs_fs_traverse_(lfs, lfs_shrink_checkblock, &block_count, true); + if (err) { + return err; + } } #endif