Skip to content

Commit

Permalink
Merge pull request #959 from littlefs-project/fix-expanded-magic
Browse files Browse the repository at this point in the history
Duplicate the superblock entry during superblock expansion, fix missing magic
  • Loading branch information
geky authored Apr 29, 2024
2 parents 68d28b5 + 11b036c commit 6e52140
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 9 deletions.
7 changes: 4 additions & 3 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,10 @@ Superblock fields:

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
as be the first entry written to the block. This means that the superblock
entry can be read from a device using offsets alone.
The superblock must always be the first entry (id 0) in the metadata pair, and
the name tag must always be the first tag in the metadata pair. This makes it
so that the magic string "littlefs" will always reside at offset=8 in a valid
littlefs superblock.

---
#### `0x2xx` LFS_TYPE_STRUCT
Expand Down
15 changes: 10 additions & 5 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
LFS_WARN("Unable to expand superblock");
} else {
end = begin;
// duplicate the superblock entry into the new superblock
end = 1;
}
}
}
Expand Down Expand Up @@ -2358,7 +2359,9 @@ fixmlist:;

while (d->id >= d->m.count && d->m.split) {
// 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);
if (err) {
return err;
Expand Down Expand Up @@ -4466,17 +4469,19 @@ static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) {
// 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
bool needssuperblock = false;
if (minor_version < lfs_fs_disk_version_minor(lfs)) {
LFS_DEBUG("Found older minor version "
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
major_version,
minor_version,
lfs_fs_disk_version_major(lfs),
lfs_fs_disk_version_minor(lfs));
// note this bit is reserved on disk, so fetching more gstate
// will not interfere here
lfs_fs_prepsuperblock(lfs, true);
needssuperblock = true;
}
// note this bit is reserved on disk, so fetching more gstate
// will not interfere here
lfs_fs_prepsuperblock(lfs, needssuperblock);

// check superblock configuration
if (superblock.name_max) {
Expand Down
53 changes: 52 additions & 1 deletion tests/test_superblocks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ code = '''
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
[cases.test_superblocks_mount_unknown_block_count]
code = '''
Expand All @@ -28,7 +46,6 @@ code = '''
lfs_unmount(&lfs) => 0;
'''


# reentrant format
[cases.test_superblocks_reentrant_format]
reentrant = true
Expand Down Expand Up @@ -135,6 +152,39 @@ code = '''
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
[cases.test_superblocks_expand_power_cycle]
defines.BLOCK_CYCLES = [32, 33, 1]
Expand Down Expand Up @@ -221,6 +271,7 @@ code = '''
lfs_unmount(&lfs) => 0;
'''


# mount with unknown block_count
[cases.test_superblocks_unknown_blocks]
code = '''
Expand Down

0 comments on commit 6e52140

Please sign in to comment.