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
path: root/lfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'lfs.c')
-rw-r--r--lfs.c83
1 files changed, 78 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;
}