diff --git a/SPEC.md b/SPEC.md index 2370ea6d..6682c749 100644 --- a/SPEC.md +++ b/SPEC.md @@ -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 diff --git a/lfs.c b/lfs.c index c14efaac..d35d5d6d 100644 --- a/lfs.c +++ b/lfs.c @@ -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; } } } @@ -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; @@ -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 // 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, @@ -4473,10 +4477,11 @@ static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) { 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) { diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index a7c2aff3..e93f02eb 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -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 = ''' @@ -28,7 +46,6 @@ code = ''' lfs_unmount(&lfs) => 0; ''' - # reentrant format [cases.test_superblocks_reentrant_format] reentrant = true @@ -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] @@ -221,6 +271,7 @@ code = ''' lfs_unmount(&lfs) => 0; ''' + # mount with unknown block_count [cases.test_superblocks_unknown_blocks] code = '''