diff --git a/README.md b/README.md index 3cad7512..a1286b83 100644 --- a/README.md +++ b/README.md @@ -231,11 +231,25 @@ License Identifiers that are here available: http://spdx.org/licenses/ to use littlefs in a Rust-friendly API, reaping the benefits of Rust's memory safety and other guarantees. +- [nim-littlefs] - A Nim wrapper and API for littlefs. Includes a fuse + implementation based on [littlefs-fuse] + +- [chamelon] - A pure-OCaml implementation of (most of) littlefs, designed for + use with the MirageOS library operating system project. It is interoperable + with the reference implementation, with some caveats. + - [littlefs-disk-img-viewer] - A memory-efficient web application for viewing littlefs disk images in your web browser. -- [mklfs] - A command line tool built by the [Lua RTOS] guys for making - littlefs images from a host PC. Supports Windows, Mac OS, and Linux. +- [mklfs] - A command line tool for creating littlefs images. Used in the Lua + RTOS ecosystem. + +- [mklittlefs] - A command line tool for creating littlefs images. Used in the + ESP8266 and RP2040 ecosystem. + +- [pico-littlefs-usb] - An interface for littlefs that emulates a FAT12 + filesystem over USB. Allows mounting littlefs on a host PC without additional + drivers. - [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed which already has block device drivers for most forms of embedded storage. @@ -254,27 +268,21 @@ License Identifiers that are here available: http://spdx.org/licenses/ for microcontroller-scale devices. Due to limitations of FAT it can't provide power-loss resilience, but it does allow easy interop with PCs. -- [chamelon] - A pure-OCaml implementation of (most of) littlefs, designed for - use with the MirageOS library operating system project. It is interoperable - with the reference implementation, with some caveats. - -- [nim-littlefs] - A Nim wrapper and API for littlefs. Includes a fuse - implementation based on [littlefs-fuse] - [BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html -[littlefs-disk-img-viewer]: https://github.com/tniessen/littlefs-disk-img-viewer [littlefs-fuse]: https://github.com/geky/littlefs-fuse [FUSE]: https://github.com/libfuse/libfuse [littlefs-js]: https://github.com/geky/littlefs-js [littlefs-js-demo]:http://littlefs.geky.net/demo.html +[littlefs-python]: https://pypi.org/project/littlefs-python/ +[littlefs2-rust]: https://crates.io/crates/littlefs2 +[nim-littlefs]: https://github.com/Graveflo/nim-littlefs +[chamelon]: https://github.com/yomimono/chamelon +[littlefs-disk-img-viewer]: https://github.com/tniessen/littlefs-disk-img-viewer [mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src -[Lua RTOS]: https://github.com/whitecatboard/Lua-RTOS-ESP32 +[mklittlefs]: https://github.com/earlephilhower/mklittlefs +[pico-littlefs-usb]: https://github.com/oyama/pico-littlefs-usb [Mbed OS]: https://github.com/armmbed/mbed-os [LittleFileSystem]: https://os.mbed.com/docs/mbed-os/latest/apis/littlefilesystem.html [SPIFFS]: https://github.com/pellepl/spiffs [Dhara]: https://github.com/dlbeer/dhara [ChaN's FatFs]: http://elm-chan.org/fsw/ff/00index_e.html -[littlefs-python]: https://pypi.org/project/littlefs-python/ -[littlefs2-rust]: https://crates.io/crates/littlefs2 -[chamelon]: https://github.com/yomimono/chamelon -[nim-littlefs]: https://github.com/Graveflo/nim-littlefs diff --git a/SPEC.md b/SPEC.md index d213677e..eff56522 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` LFS2_TYPE_STRUCT diff --git a/lfs2.c b/lfs2.c index 3c0bb6b8..517b1c74 100644 --- a/lfs2.c +++ b/lfs2.c @@ -2191,7 +2191,8 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, // we can do, we'll error later if we've become frozen LFS2_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 (lfs2_pair_cmp(d->m.tail, lfs2->root) != 0) { + d->id -= d->m.count; + } int err = lfs2_dir_fetch(lfs2, &d->m, d->m.tail); if (err) { return err; @@ -4466,6 +4469,7 @@ static int lfs2_mount_(lfs2_t *lfs2, const struct lfs2_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 < lfs2_fs_disk_version_minor(lfs2)) { LFS2_DEBUG("Found older minor version " "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, @@ -4473,10 +4477,11 @@ static int lfs2_mount_(lfs2_t *lfs2, const struct lfs2_config *cfg) { minor_version, lfs2_fs_disk_version_major(lfs2), lfs2_fs_disk_version_minor(lfs2)); - // note this bit is reserved on disk, so fetching more gstate - // will not interfere here - lfs2_fs_prepsuperblock(lfs2, true); + needssuperblock = true; } + // note this bit is reserved on disk, so fetching more gstate + // will not interfere here + lfs2_fs_prepsuperblock(lfs2, needssuperblock); // check superblock configuration if (superblock.name_max) { diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 2c96cba6..b947c460 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -14,6 +14,24 @@ code = ''' lfs2_unmount(&lfs2) => 0; ''' +# make sure the magic string "littlefs" is always at offset=8 +[cases.test_superblocks_magic] +code = ''' + lfs2_t lfs2; + lfs2_format(&lfs2, 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[lfs2_max(16, READ_SIZE)]; + cfg->read(cfg, 0, 0, magic, lfs2_max(16, READ_SIZE)) => 0; + assert(memcmp(&magic[8], "littlefs", 8) == 0); + cfg->read(cfg, 1, 0, magic, lfs2_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 = ''' lfs2_unmount(&lfs2) => 0; ''' - # reentrant format [cases.test_superblocks_reentrant_format] reentrant = true @@ -135,6 +152,39 @@ code = ''' lfs2_unmount(&lfs2) => 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 = ''' + lfs2_t lfs2; + lfs2_format(&lfs2, cfg) => 0; + lfs2_mount(&lfs2, cfg) => 0; + for (int i = 0; i < N; i++) { + lfs2_file_t file; + lfs2_file_open(&lfs2, &file, "dummy", + LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_EXCL) => 0; + lfs2_file_close(&lfs2, &file) => 0; + struct lfs2_info info; + lfs2_stat(&lfs2, "dummy", &info) => 0; + assert(strcmp(info.name, "dummy") == 0); + assert(info.type == LFS2_TYPE_REG); + lfs2_remove(&lfs2, "dummy") => 0; + } + lfs2_unmount(&lfs2) => 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[lfs2_max(16, READ_SIZE)]; + cfg->read(cfg, 0, 0, magic, lfs2_max(16, READ_SIZE)) => 0; + assert(memcmp(&magic[8], "littlefs", 8) == 0); + cfg->read(cfg, 1, 0, magic, lfs2_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 = ''' lfs2_unmount(&lfs2) => 0; ''' + # mount with unknown block_count [cases.test_superblocks_unknown_blocks] code = '''