jrast/littlefs-python

LFS_NAME_MAX should be <= 1022

Closed this issue · 3 comments

In #55 LFS_NAME_MAX was increased to 32767. That isn't supported and is causing weird behaviour.

See the comment at lfs.h line 47-52:

// Maximum name size in bytes, may be redefined to reduce the size of the
// info struct. Limited to <= 1022. Stored in superblock and must be
// respected by other littlefs drivers.
#ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 255
#endif

Reproduction snippet:

from littlefs import LittleFS

fs = LittleFS(
    block_size=4096,
    block_count=32,
    read_size=4096,
    prog_size=4096,
    name_max=0, # equivalent to LFS_NAME_MAX
    disk_version=0x00020001,
)
fs.format()
fs.mount()

# Assert the FS looks empty
try:
    result = fs.listdir('/')
except Exception as exc:
    result = exc
print(f'Listing /\n\tExpected:  []\n\tActual  :  {result}')

# Assert my directory doesn't yet exist
try:
    result = fs.stat('/mydir')
except Exception as exc:
    result = exc
print(f'Stat /mydir\n\tExpected:  LittleFSError -2: LFS_ERR_NOENT\n\tActual  :  {result}')

# Create my directory
try:
    result = fs.mkdir('/mydir')
except Exception as exc:
    result = exc
print(f'Create /mydir\n\tExpected:  0\n\tActual  :  {result}')

# Assert the FS contains my directory
try:
    result = fs.listdir('/')
except Exception as exc:
    result = exc
print(f'List /\n\tExpected:  [\'mydir\']\n\tActual  :  {result}')

# Assert my directory now exists
try:
    result = fs.stat('/mydir')
except Exception as exc:
    result = exc
print(f'Stat /mydir\n\tExpected:  LFSStat(...)\n\tActual  :  {result}')

# Creating the directory a second time should fail
try:
    result = fs.mkdir('/mydir')
except Exception as exc:
    result = exc
print(f'Create /mydir\n\tExpected:  [LittleFSError -17] Cannot create a file when that file already exists: \'/mydir\'.\n\tActual  :  {result}')

Output:

$ pip3.11 install littlefs-python==0.7.0 &>/dev/null && python3.11 reproduction.py
Listing /
        Expected:  []
        Actual  :  []
Stat /mydir
        Expected:  LittleFSError -2: LFS_ERR_NOENT
        Actual  :  LittleFSError -2: LFS_ERR_NOENT
Create /mydir
        Expected:  0
        Actual  :  0
List /
        Expected:  ['mydir']
        Actual  :  ['mydir']
Stat /mydir
        Expected:  LFSStat(...)
        Actual  :  LFSStat(type=2, size=3758096384, name='mydir')
Create /mydir
        Expected:  [LittleFSError -17] Cannot create a file when that file already exists: '/mydir'.
        Actual  :  [LittleFSError -17] Cannot create a file when that file already exists: '/mydir'.
$ pip3.11 install littlefs-python==0.7.1 &>/dev/null && python3.11 reproduction.py
Listing /
        Expected:  []
        Actual  :  []
Stat /mydir
        Expected:  LittleFSError -2: LFS_ERR_NOENT
        Actual  :  LittleFSError -2: LFS_ERR_NOENT
Create /mydir
        Expected:  0
        Actual  :  0
List /
        Expected:  ['mydir']
        Actual  :  []
Stat /mydir
        Expected:  LFSStat(...)
        Actual  :  LittleFSError -2: LFS_ERR_NOENT
Create /mydir
        Expected:  [LittleFSError -17] Cannot create a file when that file already exists: '/mydir'.
        Actual  :  [LittleFSError -17] Cannot create a file when that file already exists: '/mydir'.

(LittleFS should probably also have an assert for this instead of only mentioning it in a comment so this mistake can be caught automatically at compile-time.)

EDIT: Updated reproduction to show additional inconsistency: LFS claims the directory is not there, but a second mkdir fails.

ack, this is definitely a bug; i'll open a PR.

To be clear, this only happens when setting name_max=0 (Or I guess any value above 1022, as well), correct? The solution here being to change the arbitrarily large 32768 to 1022.

To be clear, this only happens when setting name_max=0, correct? The solution here being to change the arbitrarily large 32768 to 1022.

Yes, indeed. 0 is interpreted as "set it to LFS_NAME_MAX". But the issue occurs as well when setting it to 32768 directly.

(Or I guess any value above 1022, as well)

I didn't test all values, but given the comment I suspect it will break in some ways (might be subtle depending on which bits overflow the maximum etc.)

EDIT: And I now see I didn't even include where the failure got more interesting: Despite claiming the directory was not there, an second mkdir would still fail. Updated issue with that reproduction.

v0.9.0 has been released and should resolve this issue.