Added useful handling of LFS_M_RDONLY

It was a bit tricky to figure out what this should look like.
Traditionally, filesystems tend to fallback to readonly if they detect
unsupported wcompat (ro_compat) flags or similar config mismatch.

We could do something similar in littlefs, but since we default to
asserting on writes to readonly objects for smaller code size, this
would be really weird and hard to use from a users perspective...

Instead, lfsr_mount returns LFS_ERR_NOTSUP on encountering wcompat-
mismatch in RDWR mode, but _not_ RDONLY mode. This allows the common
rdonly-fallback pattern to be implemented on the user's side of things,
similar to the common format-fallback pattern:

  int err = lfsr_mount(&lfs, LFS_M_RDWR, &cfg);
  if (err && err != LFS_ERR_NOTSUP) {
      return err;
  }
  if (err == LFS_ERR_NOTSUP) {
      err = lfsr_mount(&lfs, LFS_M_RDONLY, &cfg);
      if (err) {
          return err;
      }
  }

Note that lfsr_mount may still return LFS_ERR_NOTSUP if it encounters
rcompat-flags, even with RDONLY. Detecting this state will likely need
two lfsr_mount calls with the current API, but I don't think that will
be a big deal.

The main benefit of this scheme is that it is quite cheap thanks to
pushing the fallback logic on the user:

           code          stack
  before: 36356           2664
  after:  36396 (+0.1%)   2664 (+0.0%)

One missing puzzle piece here is how do you upgrade the filesystem? But I
think the lesson from the on-disk v2.0 -> v2.1 version bump is that this
should really be an explicit function (lfsr_fs_upgrade?). If explicit
and stand-alone, like lfsr_format, we shouldn't need a weird pseudo-
rdonly mode at all.
This commit is contained in:
Christopher Haster
2024-07-27 00:11:36 -05:00
parent 36eabb1c68
commit 0ab0406d53
2 changed files with 114 additions and 54 deletions
+27
View File
@@ -452,6 +452,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_CORRUPT;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_CORRUPT;
'''
# test that we fail if we find bad magic
@@ -475,6 +476,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_CORRUPT;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_CORRUPT;
'''
# test that we fail to mount after a major version bump
@@ -500,6 +502,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after a minor version bump
@@ -525,6 +528,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount incompatible rcompat flags
@@ -551,6 +555,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount incompatible wcompat flags
@@ -577,6 +582,10 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
// but we _can_ mount readonly
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test that an incompatible ocompat flag is a noop
@@ -604,6 +613,8 @@ code = '''
// mount should _not_ fail, ocompat should always be ignored
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''
# these are just a bit harder to detect
@@ -628,6 +639,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
[cases.test_mount_incompat_wcompat_overflow]
@@ -651,6 +663,10 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
// but we _can_ mount readonly
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_ocompat_overflow]
@@ -675,6 +691,8 @@ code = '''
// mount should _not_ fail, ocompat should always be ignored
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test that we fail to mount incompatible block sizes
@@ -701,6 +719,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after incompatible block counts
@@ -727,6 +746,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after incompatible name limit
@@ -751,6 +771,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test that we fail to mount after incompatible file limit
@@ -779,6 +800,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test what happens if we find an unknown config
@@ -799,6 +821,7 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test what happens if we find an unknown file type
@@ -846,4 +869,8 @@ code = '''
// mount should now fail
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => LFS_ERR_NOTSUP;
// but we _can_ mount readonly
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
lfsr_unmount(&lfs) => 0;
'''