Эх сурвалжийг харах

Added lfs_fs_mkconsistent

lfs_fs_mkconsistent allows running the internal consistency operations
(desuperblock/deorphan/demove) on demand and without any other
filesystem changes.

This can be useful for front-loading and persisting consistency operations
when you don't want to pay for this cost on the first write to the
filesystem.

Conveniently, this also offers a way to force the on-disk minor version
to bump, if that is wanted behavior.

Idea from kasper0
Christopher Haster 2 жил өмнө
parent
commit
259535ee73
3 өөрчлөгдсөн 135 нэмэгдсэн , 0 устгасан
  1. 46 0
      lfs.c
  2. 12 0
      lfs.h
  3. 77 0
      tests/test_orphans.toml

+ 46 - 0
lfs.c

@@ -4887,6 +4887,36 @@ static int lfs_fs_forceconsistency(lfs_t *lfs) {
 }
 #endif
 
+#ifndef LFS_READONLY
+int lfs_fs_rawmkconsistent(lfs_t *lfs) {
+    // lfs_fs_forceconsistency does most of the work here
+    int err = lfs_fs_forceconsistency(lfs);
+    if (err) {
+        return err;
+    }
+
+    // do we have any pending gstate?
+    lfs_gstate_t delta = {0};
+    lfs_gstate_xor(&delta, &lfs->gdisk);
+    lfs_gstate_xor(&delta, &lfs->gstate);
+    if (!lfs_gstate_iszero(&delta)) {
+        // lfs_dir_commit will implicitly write out any pending gstate
+        lfs_mdir_t root;
+        err = lfs_dir_fetch(lfs, &root, lfs->root);
+        if (err) {
+            return err;
+        }
+
+        err = lfs_dir_commit(lfs, &root, NULL, 0);
+        if (err) {
+            return err;
+        }
+    }
+
+    return 0;
+}
+#endif
+
 static int lfs_fs_size_count(void *p, lfs_block_t block) {
     (void)block;
     lfs_size_t *size = p;
@@ -6052,6 +6082,22 @@ int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) {
     return err;
 }
 
+#ifndef LFS_READONLY
+int lfs_fs_mkconsistent(lfs_t *lfs) {
+    int err = LFS_LOCK(lfs->cfg);
+    if (err) {
+        return err;
+    }
+    LFS_TRACE("lfs_fs_mkconsistent(%p)", (void*)lfs);
+
+    err = lfs_fs_rawmkconsistent(lfs);
+
+    LFS_TRACE("lfs_fs_mkconsistent -> %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);

+ 12 - 0
lfs.h

@@ -676,6 +676,18 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs);
 // Returns a negative error code on failure.
 int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
 
+#ifndef LFS_READONLY
+// Attempt to make the filesystem consistent and ready for writing
+//
+// Calling this function is not required, consistency will be implicitly
+// enforced on the first operation that writes to the filesystem, but this
+// function allows the work to be performed earlier and without other
+// filesystem changes.
+//
+// Returns a negative error code on failure.
+int lfs_fs_mkconsistent(lfs_t *lfs);
+#endif
+
 #ifndef LFS_READONLY
 #ifdef LFS_MIGRATE
 // Attempts to migrate a previous version of littlefs

+ 77 - 0
tests/test_orphans.toml

@@ -126,6 +126,83 @@ code = '''
     lfs_unmount(&lfs) => 0;
 '''
 
+# test that we can persist gstate with lfs_fs_mkconsistent
+[cases.test_orphans_mkconsistent_no_orphans]
+in = 'lfs.c'
+code = '''
+    lfs_t lfs;
+    lfs_format(&lfs, cfg) => 0;
+
+    lfs_mount(&lfs, cfg) => 0;
+    // mark the filesystem as having orphans
+    lfs_fs_preporphans(&lfs, +1) => 0;
+    lfs_mdir_t mdir;
+    lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
+    lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0;
+
+    // we should have orphans at this state
+    assert(lfs_gstate_hasorphans(&lfs.gstate));
+    lfs_unmount(&lfs) => 0;
+
+    // mount
+    lfs_mount(&lfs, cfg) => 0;
+    // we should detect orphans
+    assert(lfs_gstate_hasorphans(&lfs.gstate));
+    // force consistency
+    lfs_fs_mkconsistent(&lfs) => 0;
+    // we should no longer have orphans
+    assert(!lfs_gstate_hasorphans(&lfs.gstate));
+
+    // remount
+    lfs_unmount(&lfs) => 0;
+    lfs_mount(&lfs, cfg) => 0;
+    // we should still have no orphans
+    assert(!lfs_gstate_hasorphans(&lfs.gstate));
+    lfs_unmount(&lfs) => 0;
+'''
+
+[cases.test_orphans_mkconsistent_one_orphan]
+in = 'lfs.c'
+code = '''
+    lfs_t lfs;
+    lfs_format(&lfs, cfg) => 0;
+
+    lfs_mount(&lfs, cfg) => 0;
+    // create an orphan
+    lfs_mdir_t orphan;
+    lfs_alloc_ack(&lfs);
+    lfs_dir_alloc(&lfs, &orphan) => 0;
+    lfs_dir_commit(&lfs, &orphan, NULL, 0) => 0;
+
+    // append our orphan and mark the filesystem as having orphans
+    lfs_fs_preporphans(&lfs, +1) => 0;
+    lfs_mdir_t mdir;
+    lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
+    lfs_pair_tole32(orphan.pair);
+    lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
+            {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), orphan.pair})) => 0;
+
+    // we should have orphans at this state
+    assert(lfs_gstate_hasorphans(&lfs.gstate));
+    lfs_unmount(&lfs) => 0;
+
+    // mount
+    lfs_mount(&lfs, cfg) => 0;
+    // we should detect orphans
+    assert(lfs_gstate_hasorphans(&lfs.gstate));
+    // force consistency
+    lfs_fs_mkconsistent(&lfs) => 0;
+    // we should no longer have orphans
+    assert(!lfs_gstate_hasorphans(&lfs.gstate));
+
+    // remount
+    lfs_unmount(&lfs) => 0;
+    lfs_mount(&lfs, cfg) => 0;
+    // we should still have no orphans
+    assert(!lfs_gstate_hasorphans(&lfs.gstate));
+    lfs_unmount(&lfs) => 0;
+'''
+
 # reentrant testing for orphans, basically just spam mkdir/remove
 [cases.test_orphans_reentrant]
 reentrant = true