From 6123c35710c4dbec10e99e5d16ebdff046a25cd3 Mon Sep 17 00:00:00 2001 From: Brian Pugh Date: Mon, 25 Sep 2023 15:05:30 -0700 Subject: [PATCH] Support for LittleFS v2.8 (#57) * Update littlefs to v2.8.0 * Add block_count and block_size to LFSFSInfo * lfs.format cannot autodetect block_count * support lfs_fs_grow --- README.rst | 1 + littlefs | 2 +- src/littlefs/__init__.py | 19 ++++++++++++++- src/littlefs/lfs.pxd | 5 +++- src/littlefs/lfs.pyi | 16 ++++++++++++- src/littlefs/lfs.pyx | 35 +++++++++++++++++++++++++-- test/test_block_count.py | 52 ++++++++++++++++++++++++++++++++++++++++ test/test_grow.py | 7 ++++++ test/test_version.py | 2 +- 9 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 test/test_block_count.py create mode 100644 test/test_grow.py diff --git a/README.rst b/README.rst index 37afa4e..4031ed9 100644 --- a/README.rst +++ b/README.rst @@ -71,6 +71,7 @@ Installation .. csv-table:: :header: "Package Version", "LittleFS Version", "LittleFS File System Version" + 0.8.X, 2.8.0, 2.0 / 2.1 [#f1]_ 0.7.X, 2.7.0, 2.0 / 2.1 [#f1]_ 0.6.X, 2.7.0, 2.0 / 2.1 [#f1]_ 0.5.0, 2.6.1, 2.1 diff --git a/littlefs b/littlefs index 611c9b2..f77214d 160000 --- a/littlefs +++ b/littlefs @@ -1 +1 @@ -Subproject commit 611c9b20db2b99faee261daa7cc9bbe175d3eaca +Subproject commit f77214d1f0a8ccd7ddc7e1204fedd25ee5a41534 diff --git a/src/littlefs/__init__.py b/src/littlefs/__init__.py index 43cb563..cdcb107 100644 --- a/src/littlefs/__init__.py +++ b/src/littlefs/__init__.py @@ -23,7 +23,6 @@ class LittleFS: """Littlefs file system""" def __init__(self, context:Optional['UserContext']=None, mount=True, **kwargs) -> None: - self.cfg = lfs.LFSConfig(context=context, **kwargs) self.fs = lfs.LFSFilesystem() @@ -34,6 +33,10 @@ def __init__(self, context:Optional['UserContext']=None, mount=True, **kwargs) - self.format() self.mount() + @property + def block_count(self) -> int: + return self.fs.block_count + @property def context(self) -> 'UserContext': """User context of the file system""" @@ -41,16 +44,30 @@ def context(self) -> 'UserContext': def format(self) -> int: """Format the underlying buffer""" + if self.cfg.block_count == 0: + # ``lfs.format`` looks at cfg's block_count. + # Cannot autodetect size when formatting. + raise LittleFSError(LittleFSError.Error.LFS_ERR_INVAL) return lfs.format(self.fs, self.cfg) def mount(self) -> int: """Mount the underlying buffer""" return lfs.mount(self.fs, self.cfg) + def unmount(self) -> int: + """Unmount the underlying buffer""" + return lfs.unmount(self.fs) + def fs_mkconsistent(self) -> int: """Attempt to make the filesystem consistent and ready for writing""" return lfs.fs_mkconsistent(self.fs) + def fs_grow(self, block_count: int) -> int: + if block_count < self.block_count: + raise ValueError(f"Supplied {block_count=} cannot be smaller than current block_count {self.block_count}") + + return lfs.fs_grow(self.fs, block_count) + def fs_stat(self) -> 'LFSFSStat': """Get the status of the filesystem""" return lfs.fs_stat(self.fs) diff --git a/src/littlefs/lfs.pxd b/src/littlefs/lfs.pxd index 6d5f2c1..ef31adc 100644 --- a/src/littlefs/lfs.pxd +++ b/src/littlefs/lfs.pxd @@ -57,7 +57,7 @@ cdef extern from "lfs.h": cdef struct lfs: - pass + lfs_size_t block_count ctypedef lfs lfs_t @@ -71,6 +71,8 @@ cdef extern from "lfs.h": lfs_size_t name_max lfs_size_t file_max lfs_size_t attr_max + lfs_size_t block_count + lfs_size_t block_size cdef struct lfs_dir: pass @@ -161,3 +163,4 @@ cdef extern from "lfs.h": lfs_ssize_t lfs_fs_size(lfs_t *lfs) int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) int lfs_fs_mkconsistent(lfs_t *lfs) + int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count); diff --git a/src/littlefs/lfs.pyi b/src/littlefs/lfs.pyi index f7b0675..bb30e24 100644 --- a/src/littlefs/lfs.pyi +++ b/src/littlefs/lfs.pyi @@ -12,6 +12,16 @@ LFSStat = NamedTuple('LFSStat', [ ('name', str) ]) +LFSFSStat = NamedTuple('LFSFSStat', [ + 'disk_version', + 'name_max', + 'file_max', + 'attr_max', + 'block_count', + 'block_size', +]) + + class LFSFileFlag(enum.IntFlag): ... class LFSConfig: @@ -62,8 +72,11 @@ class LFSConfig: @property def attr_max(self) -> int: ... +class LFSFilesystem: + @property + def block_count(self) -> int: ... + # The following classes are opaque wrappers around the actual handles -class LFSFilesystem: ... class LFSFile: ... class LFSDirectory: ... @@ -74,6 +87,7 @@ def format(fs: LFSFilesystem, cfg: LFSConfig) -> int: ... def mount(fs: LFSFilesystem, cfg: LFSConfig) -> int: ... def unmount(fs: LFSFilesystem) -> int: ... def fs_mkconsistent(fs: LFSFilesystem) -> int: ... +def fs_grow(fs: LFSFilesystem, block_count) -> int: ... def remove(fs: LFSFilesystem, path: str) -> int: ... def rename(fs: LFSFilesystem, oldpath: str, newpath: str) -> int: ... diff --git a/src/littlefs/lfs.pyx b/src/littlefs/lfs.pyx index dbacac3..3401fdc 100644 --- a/src/littlefs/lfs.pyx +++ b/src/littlefs/lfs.pyx @@ -16,7 +16,14 @@ LFSStat.__doc__ = """\ Littlefs File / Directory status """ -LFSFSStat = namedtuple('LFSFSStat', ['disk_version', 'name_max', 'file_max', 'attr_max']) +LFSFSStat = namedtuple('LFSFSStat', [ + 'disk_version', + 'name_max', + 'file_max', + 'attr_max', + 'block_count', + 'block_size', +]) LFSFSStat.__doc__ = """\ Littlefs filesystem status """ @@ -107,6 +114,7 @@ cdef class LFSConfig: Defaults to 128. block_count : int Number of blocks in the filesystem. + If set to 0, attempt to autodetect ``block_count`` from filesystem. Defaults to 64. read_size: int Minimum size of a block read in bytes. All read operations will be a @@ -231,6 +239,10 @@ cdef class LFSConfig: cdef class LFSFilesystem: cdef lfs_t _impl + @property + def block_count(self) -> lfs_size_t: + return self._impl.block_count + cdef class LFSFile: cdef lfs_file_t _impl @@ -250,7 +262,14 @@ def fs_stat(LFSFilesystem fs): cdef lfs_fsinfo * info = malloc(sizeof(lfs_fsinfo)) try: _raise_on_error(lfs_fs_stat(&fs._impl, info)) - return LFSFSStat(info.disk_version, info.name_max, info.file_max, info.attr_max) + return LFSFSStat( + info.disk_version, + info.name_max, + info.file_max, + info.attr_max, + info.block_count, + info.block_size, + ) finally: free(info) @@ -282,6 +301,18 @@ def fs_mkconsistent(LFSFilesystem fs): return _raise_on_error(lfs_fs_mkconsistent(&fs._impl)) +def fs_grow(LFSFilesystem fs, block_count) -> int: + """Irreversibly grows the filesystem to a new size. + + Parameters + ---------- + fs: LFSFilesystem + block_count: int + Number of blocks in the new filesystem. + """ + return _raise_on_error(lfs_fs_grow(&fs._impl, block_count)) + + def remove(LFSFilesystem fs, path): """Remove a file or directory diff --git a/test/test_block_count.py b/test/test_block_count.py new file mode 100644 index 0000000..e0c8881 --- /dev/null +++ b/test/test_block_count.py @@ -0,0 +1,52 @@ +import pytest +from littlefs import LittleFS, LittleFSError +from littlefs.context import UserContext + + +def test_block_count_autodetect(): + block_size = 256 + block_count = 57 + context = UserContext(block_size * block_count) + + # Create the filesystem with 57 blocks + fs = LittleFS(context=context, + block_size=block_size, + block_count=block_count, + ) + assert fs.block_count == 57 + + fs = LittleFS(context=context, + block_size=block_size, + block_count=0, # infer from superblock + ) + + assert fs.block_count == 57 + + +def test_block_count_autodetect_format_fail(): + with pytest.raises(LittleFSError) as e: + fs = LittleFS(block_count=0) + assert e.value.code == LittleFSError.Error.LFS_ERR_INVAL + + +def test_fs_stat_block_count_autodetect(): + block_size = 256 + block_count = 57 + context = UserContext(block_size * block_count) + + # Create the filesystem with 57 blocks + fs = LittleFS(context=context, + block_size=block_size, + block_count=block_count, + ) + assert fs.block_count == 57 + + fs = LittleFS(context=context, + block_size=block_size, + block_count=0, # infer from superblock + ) + + # Note: filesystem has to be mounted for fs_stat to work. + info = fs.fs_stat() + assert info.block_count == 57 + assert info.block_size == 256 diff --git a/test/test_grow.py b/test/test_grow.py new file mode 100644 index 0000000..6a23c23 --- /dev/null +++ b/test/test_grow.py @@ -0,0 +1,7 @@ +from littlefs import LittleFS, LittleFSError + + +def test_fs_grow_basic(): + fs = LittleFS(block_count=32) + fs.fs_grow(40) + assert fs.block_count == 40 diff --git a/test/test_version.py b/test/test_version.py index 0ee3c45..5b26c56 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -2,5 +2,5 @@ def test_version(): """Test if the versions of littlefs can be imported""" - assert littlefs.__LFS_VERSION__ == (2, 7) + assert littlefs.__LFS_VERSION__ == (2, 8) assert littlefs.__LFS_DISK_VERSION__ == (2, 1)