Merge pull request #959 from littlefs-project/fix-expanded-magic

Duplicate the superblock entry during superblock expansion, fix missing magic
This commit is contained in:
Christopher Haster
2024-04-29 14:26:38 -05:00
committed by GitHub
3 changed files with 66 additions and 9 deletions
+4 -3
View File
@@ -441,9 +441,10 @@ Superblock fields:
7. **Attr max (32-bits)** - Maximum size of file attributes in bytes. 7. **Attr max (32-bits)** - Maximum size of file attributes in bytes.
The superblock must always be the first entry (id 0) in a metadata pair as well The superblock must always be the first entry (id 0) in the metadata pair, and
as be the first entry written to the block. This means that the superblock the name tag must always be the first tag in the metadata pair. This makes it
entry can be read from a device using offsets alone. so that the magic string "littlefs" will always reside at offset=8 in a valid
littlefs superblock.
--- ---
#### `0x2xx` LFS_TYPE_STRUCT #### `0x2xx` LFS_TYPE_STRUCT
+10 -5
View File
@@ -2191,7 +2191,8 @@ static int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir,
// we can do, we'll error later if we've become frozen // we can do, we'll error later if we've become frozen
LFS_WARN("Unable to expand superblock"); LFS_WARN("Unable to expand superblock");
} else { } else {
end = begin; // duplicate the superblock entry into the new superblock
end = 1;
} }
} }
} }
@@ -2358,7 +2359,9 @@ fixmlist:;
while (d->id >= d->m.count && d->m.split) { while (d->id >= d->m.count && d->m.split) {
// we split and id is on tail now // we split and id is on tail now
d->id -= d->m.count; if (lfs_pair_cmp(d->m.tail, lfs->root) != 0) {
d->id -= d->m.count;
}
int err = lfs_dir_fetch(lfs, &d->m, d->m.tail); int err = lfs_dir_fetch(lfs, &d->m, d->m.tail);
if (err) { if (err) {
return err; return err;
@@ -4466,6 +4469,7 @@ static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) {
// found older minor version? set an in-device only bit in the // found older minor version? set an in-device only bit in the
// gstate so we know we need to rewrite the superblock before // gstate so we know we need to rewrite the superblock before
// the first write // the first write
bool needssuperblock = false;
if (minor_version < lfs_fs_disk_version_minor(lfs)) { if (minor_version < lfs_fs_disk_version_minor(lfs)) {
LFS_DEBUG("Found older minor version " LFS_DEBUG("Found older minor version "
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
@@ -4473,10 +4477,11 @@ static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) {
minor_version, minor_version,
lfs_fs_disk_version_major(lfs), lfs_fs_disk_version_major(lfs),
lfs_fs_disk_version_minor(lfs)); lfs_fs_disk_version_minor(lfs));
// note this bit is reserved on disk, so fetching more gstate needssuperblock = true;
// will not interfere here
lfs_fs_prepsuperblock(lfs, true);
} }
// note this bit is reserved on disk, so fetching more gstate
// will not interfere here
lfs_fs_prepsuperblock(lfs, needssuperblock);
// check superblock configuration // check superblock configuration
if (superblock.name_max) { if (superblock.name_max) {
+52 -1
View File
@@ -14,6 +14,24 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
# make sure the magic string "littlefs" is always at offset=8
[cases.test_superblocks_magic]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
// check our magic string
//
// note if we lose power we may not have the magic string in both blocks!
// but we don't lose power in this test so we can assert the magic string
// is present in both
uint8_t magic[lfs_max(16, READ_SIZE)];
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
'''
# mount/unmount from interpretting a previous superblock block_count # mount/unmount from interpretting a previous superblock block_count
[cases.test_superblocks_mount_unknown_block_count] [cases.test_superblocks_mount_unknown_block_count]
code = ''' code = '''
@@ -28,7 +46,6 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
# reentrant format # reentrant format
[cases.test_superblocks_reentrant_format] [cases.test_superblocks_reentrant_format]
reentrant = true reentrant = true
@@ -135,6 +152,39 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
# make sure the magic string "littlefs" is always at offset=8
[cases.test_superblocks_magic_expand]
defines.BLOCK_CYCLES = [32, 33, 1]
defines.N = [10, 100, 1000]
code = '''
lfs_t lfs;
lfs_format(&lfs, cfg) => 0;
lfs_mount(&lfs, cfg) => 0;
for (int i = 0; i < N; i++) {
lfs_file_t file;
lfs_file_open(&lfs, &file, "dummy",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfs_file_close(&lfs, &file) => 0;
struct lfs_info info;
lfs_stat(&lfs, "dummy", &info) => 0;
assert(strcmp(info.name, "dummy") == 0);
assert(info.type == LFS_TYPE_REG);
lfs_remove(&lfs, "dummy") => 0;
}
lfs_unmount(&lfs) => 0;
// check our magic string
//
// note if we lose power we may not have the magic string in both blocks!
// but we don't lose power in this test so we can assert the magic string
// is present in both
uint8_t magic[lfs_max(16, READ_SIZE)];
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
assert(memcmp(&magic[8], "littlefs", 8) == 0);
'''
# expanding superblock with power cycle # expanding superblock with power cycle
[cases.test_superblocks_expand_power_cycle] [cases.test_superblocks_expand_power_cycle]
defines.BLOCK_CYCLES = [32, 33, 1] defines.BLOCK_CYCLES = [32, 33, 1]
@@ -221,6 +271,7 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
''' '''
# mount with unknown block_count # mount with unknown block_count
[cases.test_superblocks_unknown_blocks] [cases.test_superblocks_unknown_blocks]
code = ''' code = '''