Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Haster <geky@geky.net>2022-11-13 22:09:21 +0300
committerChristopher Haster <geky@geky.net>2023-09-12 09:32:09 +0300
commit23505fa9fa4f2d863f046c0bfad8db2ca63cfb90 (patch)
tree64deb43357a9ce95ff86dfc3cc44ad24e8eec83a
parent2c222af17d8c3066e663b3adca422bf8ea50c243 (diff)
Added lfs_fs_grow for growing the filesystem to a different block_countfs-grow
The initial implementation for this was provided by kaetemi, originally as a mount flag. However, it has been modified here to be self-contained in an explicit runtime function that can be called after mount. The reasons for an explicit function: 1. lfs_mount stays a strictly readonly operation, and avoids pulling in all of the write machinery. 2. filesystem-wide operations such as lfs_fs_grow can be a bit risky, and irreversable. The action of growing the filesystem should be very intentional. --- One concern with this change is that this will be the first function that changes metadata in the superblock. This might break tools that expect the first valid superblock entry to contain the most recent metadata, since only the last superblock in the superblock chain will contain the updated metadata.
-rw-r--r--lfs.c54
-rw-r--r--lfs.h10
-rw-r--r--tests/test_superblocks.toml105
3 files changed, 169 insertions, 0 deletions
diff --git a/lfs.c b/lfs.c
index 99550fe..85660ac 100644
--- a/lfs.c
+++ b/lfs.c
@@ -5033,6 +5033,44 @@ static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) {
return size;
}
+#ifndef LFS_READONLY
+int lfs_fs_rawgrow(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;
+
+ // 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;
+}
+#endif
#ifdef LFS_MIGRATE
////// Migration from littelfs v1 below this //////
@@ -6216,6 +6254,22 @@ int lfs_fs_mkconsistent(lfs_t *lfs) {
}
#endif
+#ifndef LFS_READONLY
+int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) {
+ int err = LFS_LOCK(lfs->cfg);
+ if (err) {
+ return err;
+ }
+ LFS_TRACE("lfs_fs_grow(%p, %"PRIu32")", (void*)lfs, block_count);
+
+ err = lfs_fs_rawgrow(lfs, block_count);
+
+ LFS_TRACE("lfs_fs_grow -> %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 6f40c55..291dbb5 100644
--- a/lfs.h
+++ b/lfs.h
@@ -725,6 +725,16 @@ int lfs_fs_mkconsistent(lfs_t *lfs);
#endif
#ifndef LFS_READONLY
+// Grows the filesystem to a new size, updating the superblock with the new
+// block count.
+//
+// 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
#ifdef LFS_MIGRATE
// Attempts to migrate a previous version of littlefs
//
diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml
index a99a5a4..6d4a3e4 100644
--- a/tests/test_superblocks.toml
+++ b/tests/test_superblocks.toml
@@ -359,3 +359,108 @@ code = '''
cfg->block_count = BLOCK_COUNT;
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
+
+# mount and grow the filesystem
+[cases.test_superblocks_grow]
+defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2']
+defines.BLOCK_COUNT_2 = 'ERASE_COUNT'
+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_grow(&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_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);
+ 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_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);
+ 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;
+'''