From 71ea438561b737de7d4bb6bc60412a7e409cf298 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Jan 2021 13:09:44 +1100 Subject: [PATCH] extmod/vfs: Check block 0 and 1 when auto-detecting littlefs. The superblock for littlefs is in block 0 and 1, but block 0 may be erased or partially written, so block 1 must be checked if block 0 does not have a valid littlefs superblock in it. Prior to this commit, the mount of a block device which auto-detected the filysystem type would fail for littlefs if block 0 did not contain a valid superblock. That is now fixed. Signed-off-by: Damien George --- extmod/vfs.c | 37 ++++++++++---------- tests/extmod/vfs_lfs_superblock.py | 47 ++++++++++++++++++++++++++ tests/extmod/vfs_lfs_superblock.py.exp | 2 ++ 3 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 tests/extmod/vfs_lfs_superblock.py create mode 100644 tests/extmod/vfs_lfs_superblock.py.exp diff --git a/extmod/vfs.c b/extmod/vfs.c index 7dca59d351..6dcdcfa22d 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -160,27 +160,30 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_obj_t vfs = MP_OBJ_NULL; + // The superblock for littlefs is in both block 0 and 1, but block 0 may be erased + // or partially written, so search both blocks 0 and 1 for the littlefs signature. mp_vfs_blockdev_t blockdev; mp_vfs_blockdev_init(&blockdev, bdev_obj); uint8_t buf[44]; - mp_vfs_blockdev_read_ext(&blockdev, 0, 8, sizeof(buf), buf); - #if MICROPY_VFS_LFS1 - if (memcmp(&buf[32], "littlefs", 8) == 0) { - // LFS1 - vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); - nlr_pop(); - return vfs; + for (size_t block_num = 0; block_num <= 1; ++block_num) { + mp_vfs_blockdev_read_ext(&blockdev, block_num, 8, sizeof(buf), buf); + #if MICROPY_VFS_LFS1 + if (memcmp(&buf[32], "littlefs", 8) == 0) { + // LFS1 + mp_obj_t vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); + nlr_pop(); + return vfs; + } + #endif + #if MICROPY_VFS_LFS2 + if (memcmp(&buf[0], "littlefs", 8) == 0) { + // LFS2 + mp_obj_t vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); + nlr_pop(); + return vfs; + } + #endif } - #endif - #if MICROPY_VFS_LFS2 - if (memcmp(&buf[0], "littlefs", 8) == 0) { - // LFS2 - vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); - nlr_pop(); - return vfs; - } - #endif nlr_pop(); } else { // Ignore exception (eg block device doesn't support extended readblocks) diff --git a/tests/extmod/vfs_lfs_superblock.py b/tests/extmod/vfs_lfs_superblock.py new file mode 100644 index 0000000000..1ac5675554 --- /dev/null +++ b/tests/extmod/vfs_lfs_superblock.py @@ -0,0 +1,47 @@ +# Test for VfsLfs using a RAM device, when the first superblock does not exist + +try: + import uos + + uos.VfsLfs2 +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + def __init__(self, block_size, data): + self.block_size = block_size + self.data = data + + def readblocks(self, block, buf, off): + addr = block * self.block_size + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.block_size + if op == 5: # block size + return self.block_size + if op == 6: # erase block + return 0 + + +# This is a valid littlefs2 filesystem with a block size of 64 bytes. +# The first block (where the first superblock is stored) is fully erased. +lfs2_data = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x02\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x00\x00\x02\x00@\x00\x00\x00\x04\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00p\x1f\xfc\x08\x1b\xb4\x14\xa7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\xff\xef\xff\xf7test.txt \x00\x00\x08p\x1f\xfc\x08\x83\xf1u\xba\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + +# Create the block device from the static data (it will be read-only). +bdev = RAMBlockDevice(64, lfs2_data) + +# Create the VFS explicitly, no auto-detection is needed for this. +vfs = uos.VfsLfs2(bdev) +print(list(vfs.ilistdir())) + +# Mount the block device directly; this relies on auto-detection. +uos.mount(bdev, "/userfs") +print(uos.listdir("/userfs")) + +# Clean up. +uos.umount("/userfs") diff --git a/tests/extmod/vfs_lfs_superblock.py.exp b/tests/extmod/vfs_lfs_superblock.py.exp new file mode 100644 index 0000000000..c71bf50e82 --- /dev/null +++ b/tests/extmod/vfs_lfs_superblock.py.exp @@ -0,0 +1,2 @@ +[] +[]