diff options
author | Christopher Haster <geky@geky.net> | 2023-05-01 07:26:04 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-01 07:26:04 +0300 |
commit | 405f33214aefb718e16a115c990541f219eb63b6 (patch) | |
tree | dc1570c26643bcc7f9fbd21f479df39cf6b29c5b | |
parent | 3dca02911ffa9d66e16c4386de7d5cd414dd29a9 (diff) | |
parent | 259535ee73a792556bb16eebfbd076f467c5bdfa (diff) |
Merge pull request #812 from littlefs-project/mkconsistent
Add lfs_fs_mkconsistent
-rw-r--r-- | lfs.c | 46 | ||||
-rw-r--r-- | lfs.h | 12 | ||||
-rw-r--r-- | tests/test_orphans.toml | 77 |
3 files changed, 135 insertions, 0 deletions
@@ -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); @@ -677,6 +677,18 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs); 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 // diff --git a/tests/test_orphans.toml b/tests/test_orphans.toml index 1cce2fb..2c8405a 100644 --- a/tests/test_orphans.toml +++ b/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 |