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:
-rw-r--r--lfs.c83
-rw-r--r--tests/test_compat.toml82
2 files changed, 160 insertions, 5 deletions
diff --git a/lfs.c b/lfs.c
index 7a4b41a..5ad8a1f 100644
--- a/lfs.c
+++ b/lfs.c
@@ -411,12 +411,16 @@ static inline bool lfs_gstate_hasorphans(const lfs_gstate_t *a) {
}
static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) {
- return lfs_tag_size(a->tag);
+ return lfs_tag_size(a->tag) & 0x1ff;
}
static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) {
return lfs_tag_type1(a->tag);
}
+
+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,
@@ -533,6 +537,7 @@ 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]);
@@ -4258,12 +4263,29 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
uint16_t minor_version = (0xffff & (superblock.version >> 0));
if ((major_version != LFS_DISK_VERSION_MAJOR ||
minor_version > LFS_DISK_VERSION_MINOR)) {
- LFS_ERROR("Invalid version v%"PRIu16".%"PRIu16,
- major_version, minor_version);
+ LFS_ERROR("Invalid version "
+ "v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16,
+ major_version, minor_version,
+ LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
err = LFS_ERR_INVAL;
goto cleanup;
}
+ // 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) {
+ LFS_DEBUG("Found older minor version "
+ "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
if (superblock.name_max) {
if (superblock.name_max > lfs->name_max) {
@@ -4538,9 +4560,16 @@ 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) {
LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0);
- LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) < 0x3ff || orphans <= 0);
+ LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) < 0x1ff || orphans <= 0);
lfs->gstate.tag += orphans;
lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x800, 0, 0)) |
((uint32_t)lfs_gstate_hasorphans(&lfs->gstate) << 31));
@@ -4560,6 +4589,45 @@ static void lfs_fs_prepmove(lfs_t *lfs,
#endif
#ifndef LFS_READONLY
+static int lfs_fs_desuperblock(lfs_t *lfs) {
+ if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
+ return 0;
+ }
+
+ LFS_DEBUG("Rewriting superblock {0x%"PRIx32", 0x%"PRIx32"}",
+ lfs->root[0],
+ lfs->root[1]);
+
+ lfs_mdir_t root;
+ int err = lfs_dir_fetch(lfs, &root, lfs->root);
+ if (err) {
+ return err;
+ }
+
+ // write a new superblock
+ lfs_superblock_t superblock = {
+ .version = LFS_DISK_VERSION,
+ .block_size = lfs->cfg->block_size,
+ .block_count = lfs->cfg->block_count,
+ .name_max = lfs->name_max,
+ .file_max = lfs->file_max,
+ .attr_max = lfs->attr_max,
+ };
+
+ lfs_superblock_tole32(&superblock);
+ err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
+ {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
+ &superblock}));
+ if (err) {
+ return err;
+ }
+
+ lfs_fs_prepsuperblock(lfs, false);
+ return 0;
+}
+#endif
+
+#ifndef LFS_READONLY
static int lfs_fs_demove(lfs_t *lfs) {
if (!lfs_gstate_hasmove(&lfs->gdisk)) {
return 0;
@@ -4736,7 +4804,12 @@ static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
#ifndef LFS_READONLY
static int lfs_fs_forceconsistency(lfs_t *lfs) {
- int err = lfs_fs_demove(lfs);
+ int err = lfs_fs_desuperblock(lfs);
+ if (err) {
+ return err;
+ }
+
+ err = lfs_fs_demove(lfs);
if (err) {
return err;
}
diff --git a/tests/test_compat.toml b/tests/test_compat.toml
index 7af487f..a36c38a 100644
--- a/tests/test_compat.toml
+++ b/tests/test_compat.toml
@@ -1276,3 +1276,85 @@ code = '''
// mount should now fail
lfs_mount(&lfs, cfg) => LFS_ERR_INVAL;
'''
+
+# test that we correctly bump the minor version
+[cases.test_compat_minor_bump]
+in = 'lfs.c'
+if = 'LFS_DISK_VERSION_MINOR > 0'
+code = '''
+ // create a superblock
+ lfs_t lfs;
+ lfs_format(&lfs, cfg) => 0;
+ lfs_mount(&lfs, cfg) => 0;
+ lfs_file_t file;
+ lfs_file_open(&lfs, &file, "test",
+ LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
+ lfs_file_write(&lfs, &file, "testtest", 8) => 8;
+ lfs_file_close(&lfs, &file) => 0;
+ lfs_unmount(&lfs) => 0;
+
+ // write an old minor version
+ //
+ // note we're messing around with internals to do this! this
+ // is not a user API
+ lfs_mount(&lfs, cfg) => 0;
+ lfs_mdir_t mdir;
+ lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
+ lfs_superblock_t superblock = {
+ .version = LFS_DISK_VERSION - 0x00000001,
+ .block_size = lfs.cfg->block_size,
+ .block_count = lfs.cfg->block_count,
+ .name_max = lfs.name_max,
+ .file_max = lfs.file_max,
+ .attr_max = lfs.attr_max,
+ };
+ lfs_superblock_tole32(&superblock);
+ lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
+ {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
+ &superblock})) => 0;
+ lfs_unmount(&lfs) => 0;
+
+ // mount should still work
+ lfs_mount(&lfs, cfg) => 0;
+ 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;
+ lfs_unmount(&lfs) => 0;
+
+ // if we write, we need to bump the minor version
+ lfs_mount(&lfs, cfg) => 0;
+ 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);
+ lfs_unmount(&lfs) => 0;
+
+ // and of course mount should still work
+ lfs_mount(&lfs, cfg) => 0;
+ 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);
+ lfs_unmount(&lfs) => 0;
+'''