Skip to content

Commit 6e52140

Browse files
authored
Merge pull request #959 from littlefs-project/fix-expanded-magic
Duplicate the superblock entry during superblock expansion, fix missing magic
2 parents 68d28b5 + 11b036c commit 6e52140

File tree

3 files changed

+66
-9
lines changed

3 files changed

+66
-9
lines changed

SPEC.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,10 @@ Superblock fields:
441441

442442
7. **Attr max (32-bits)** - Maximum size of file attributes in bytes.
443443

444-
The superblock must always be the first entry (id 0) in a metadata pair as well
445-
as be the first entry written to the block. This means that the superblock
446-
entry can be read from a device using offsets alone.
444+
The superblock must always be the first entry (id 0) in the metadata pair, and
445+
the name tag must always be the first tag in the metadata pair. This makes it
446+
so that the magic string "littlefs" will always reside at offset=8 in a valid
447+
littlefs superblock.
447448

448449
---
449450
#### `0x2xx` LFS_TYPE_STRUCT

lfs.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,7 +2191,8 @@ static int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir,
21912191
// we can do, we'll error later if we've become frozen
21922192
LFS_WARN("Unable to expand superblock");
21932193
} else {
2194-
end = begin;
2194+
// duplicate the superblock entry into the new superblock
2195+
end = 1;
21952196
}
21962197
}
21972198
}
@@ -2358,7 +2359,9 @@ fixmlist:;
23582359

23592360
while (d->id >= d->m.count && d->m.split) {
23602361
// we split and id is on tail now
2361-
d->id -= d->m.count;
2362+
if (lfs_pair_cmp(d->m.tail, lfs->root) != 0) {
2363+
d->id -= d->m.count;
2364+
}
23622365
int err = lfs_dir_fetch(lfs, &d->m, d->m.tail);
23632366
if (err) {
23642367
return err;
@@ -4466,17 +4469,19 @@ static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) {
44664469
// found older minor version? set an in-device only bit in the
44674470
// gstate so we know we need to rewrite the superblock before
44684471
// the first write
4472+
bool needssuperblock = false;
44694473
if (minor_version < lfs_fs_disk_version_minor(lfs)) {
44704474
LFS_DEBUG("Found older minor version "
44714475
"v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
44724476
major_version,
44734477
minor_version,
44744478
lfs_fs_disk_version_major(lfs),
44754479
lfs_fs_disk_version_minor(lfs));
4476-
// note this bit is reserved on disk, so fetching more gstate
4477-
// will not interfere here
4478-
lfs_fs_prepsuperblock(lfs, true);
4480+
needssuperblock = true;
44794481
}
4482+
// note this bit is reserved on disk, so fetching more gstate
4483+
// will not interfere here
4484+
lfs_fs_prepsuperblock(lfs, needssuperblock);
44804485

44814486
// check superblock configuration
44824487
if (superblock.name_max) {

tests/test_superblocks.toml

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ code = '''
1414
lfs_unmount(&lfs) => 0;
1515
'''
1616

17+
# make sure the magic string "littlefs" is always at offset=8
18+
[cases.test_superblocks_magic]
19+
code = '''
20+
lfs_t lfs;
21+
lfs_format(&lfs, cfg) => 0;
22+
23+
// check our magic string
24+
//
25+
// note if we lose power we may not have the magic string in both blocks!
26+
// but we don't lose power in this test so we can assert the magic string
27+
// is present in both
28+
uint8_t magic[lfs_max(16, READ_SIZE)];
29+
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
30+
assert(memcmp(&magic[8], "littlefs", 8) == 0);
31+
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
32+
assert(memcmp(&magic[8], "littlefs", 8) == 0);
33+
'''
34+
1735
# mount/unmount from interpretting a previous superblock block_count
1836
[cases.test_superblocks_mount_unknown_block_count]
1937
code = '''
@@ -28,7 +46,6 @@ code = '''
2846
lfs_unmount(&lfs) => 0;
2947
'''
3048

31-
3249
# reentrant format
3350
[cases.test_superblocks_reentrant_format]
3451
reentrant = true
@@ -135,6 +152,39 @@ code = '''
135152
lfs_unmount(&lfs) => 0;
136153
'''
137154

155+
# make sure the magic string "littlefs" is always at offset=8
156+
[cases.test_superblocks_magic_expand]
157+
defines.BLOCK_CYCLES = [32, 33, 1]
158+
defines.N = [10, 100, 1000]
159+
code = '''
160+
lfs_t lfs;
161+
lfs_format(&lfs, cfg) => 0;
162+
lfs_mount(&lfs, cfg) => 0;
163+
for (int i = 0; i < N; i++) {
164+
lfs_file_t file;
165+
lfs_file_open(&lfs, &file, "dummy",
166+
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
167+
lfs_file_close(&lfs, &file) => 0;
168+
struct lfs_info info;
169+
lfs_stat(&lfs, "dummy", &info) => 0;
170+
assert(strcmp(info.name, "dummy") == 0);
171+
assert(info.type == LFS_TYPE_REG);
172+
lfs_remove(&lfs, "dummy") => 0;
173+
}
174+
lfs_unmount(&lfs) => 0;
175+
176+
// check our magic string
177+
//
178+
// note if we lose power we may not have the magic string in both blocks!
179+
// but we don't lose power in this test so we can assert the magic string
180+
// is present in both
181+
uint8_t magic[lfs_max(16, READ_SIZE)];
182+
cfg->read(cfg, 0, 0, magic, lfs_max(16, READ_SIZE)) => 0;
183+
assert(memcmp(&magic[8], "littlefs", 8) == 0);
184+
cfg->read(cfg, 1, 0, magic, lfs_max(16, READ_SIZE)) => 0;
185+
assert(memcmp(&magic[8], "littlefs", 8) == 0);
186+
'''
187+
138188
# expanding superblock with power cycle
139189
[cases.test_superblocks_expand_power_cycle]
140190
defines.BLOCK_CYCLES = [32, 33, 1]
@@ -221,6 +271,7 @@ code = '''
221271
lfs_unmount(&lfs) => 0;
222272
'''
223273

274+
224275
# mount with unknown block_count
225276
[cases.test_superblocks_unknown_blocks]
226277
code = '''

0 commit comments

Comments
 (0)