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.
This commit is contained in:
Sosthène Guédon
2025-04-16 17:46:34 +02:00
parent 8ed63b27be
commit 2105e502c5
4 changed files with 288 additions and 22 deletions
+67 -22
View File
@@ -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);