Skip to content

Commit

Permalink
Support for LittleFS v2.8 (#57)
Browse files Browse the repository at this point in the history
* Update littlefs to v2.8.0
* Add block_count and block_size to LFSFSInfo
* lfs.format cannot autodetect block_count
* support lfs_fs_grow
  • Loading branch information
BrianPugh authored Sep 25, 2023
1 parent ebd4987 commit 6123c35
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion src/littlefs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -34,23 +33,41 @@ 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"""
return self.cfg.user_context

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)
Expand Down
5 changes: 4 additions & 1 deletion src/littlefs/lfs.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ cdef extern from "lfs.h":


cdef struct lfs:
pass
lfs_size_t block_count

ctypedef lfs lfs_t

Expand All @@ -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
Expand Down Expand Up @@ -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);
16 changes: 15 additions & 1 deletion src/littlefs/lfs.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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: ...

Expand All @@ -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: ...
Expand Down
35 changes: 33 additions & 2 deletions src/littlefs/lfs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -250,7 +262,14 @@ def fs_stat(LFSFilesystem fs):
cdef lfs_fsinfo * info = <lfs_fsinfo *>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)

Expand Down Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions test/test_block_count.py
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions test/test_grow.py
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion test/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit 6123c35

Please sign in to comment.