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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user