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
This commit is contained in:
parent
94d9e097a6
commit
259535ee73
46
lfs.c
46
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
lfs.h
12
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue