Skip to content

Merge tag 'v2.11.3'#33

Merged
Rbb666 merged 12 commits into
RT-Thread-packages:masterfrom
wdfk-prog:master
May 4, 2026
Merged

Merge tag 'v2.11.3'#33
Rbb666 merged 12 commits into
RT-Thread-packages:masterfrom
wdfk-prog:master

Conversation

@wdfk-prog
Copy link
Copy Markdown
Collaborator

@wdfk-prog wdfk-prog commented May 4, 2026

v2.11.3

 Latest
@geky-bot geky-bot released this Mar 25

littlefs3 status: in-progress, unstable (update, now with plots!).
littlefs2 status: feature freeze.

Notable fix: This includes a fix for a bug found by @Ictogan1, where multiple open write handles could lead to data corruption (#1194).

  | Code | Stack | Structs |   | Coverage -- | -- | -- | -- | -- | -- Default | 17200 B (+0.4%) | 1448 B (+0.0%) | 812 B (+0.0%) | Lines | 2449/2610 lines (+0.0%) Readonly | 6234 B (+0.0%) | 448 B (+0.0%) | 812 B (+0.0%) | Branches | 1301/1638 branches (+0.1%) Threadsafe | 18056 B (+0.4%) | 1448 B (+0.0%) | 820 B (+0.0%) |   | Benchmarks Multiversion | 17272 B (+0.4%) | 1448 B (+0.0%) | 816 B (+0.0%) | Readed | 29000746676 B (+0.0%) Migrate | 18864 B (+0.4%) | 1752 B (+0.0%) | 816 B (+0.0%) | Proged | 1482895246 B (+0.0%) Error-asserts | 18036 B (+0.5%) | 1440 B (+0.0%) | 812 B (+0.0%) | Erased | 1568921600 B (+0.0%)

488e84bb Fixed data corruption with multiple write handles
fd5e7f62 Using LFS_ASSERT instead of an runtime check.
2311cd78 Guard null callbacks in lfs_dir_fetchmatch
74f32c83 Fix broken link on README.md for emu device
dbe96d03 Fixes for 'Implicit conversion loses integer precision' warnings.
b062c88a Fix comment typo and -> an

A special thanks to littlefs's sponsors: @micropython, @gurgalof

v2.11.3 [Latest](https://github.com/littlefs-project/littlefs/releases/latest) @[geky-bot](https://github.com/geky-bot) geky-bot released this Mar 25 [ v2.11.3](https://github.com/littlefs-project/littlefs/tree/v2.11.3) https://github.com/littlefs-project/littlefs/commit/6cb4e86540eca0d9ba62500a298385c9d863c8be littlefs3 status: in-progress, unstable (https://github.com/littlefs-project/issues/1114#issuecomment-3932058541). littlefs2 status: feature freeze.

Notable fix: This includes a fix for a bug found by @Ictogan1, where multiple open write handles could lead to data corruption (littlefs-project#1194).

Code Stack Structs Coverage
Default 17200 B (+0.4%) 1448 B (+0.0%) 812 B (+0.0%) Lines 2449/2610 lines (+0.0%)
Readonly 6234 B (+0.0%) 448 B (+0.0%) 812 B (+0.0%) Branches 1301/1638 branches (+0.1%)
Threadsafe 18056 B (+0.4%) 1448 B (+0.0%) 820 B (+0.0%) Benchmarks
Multiversion 17272 B (+0.4%) 1448 B (+0.0%) 816 B (+0.0%) Readed 29000746676 B (+0.0%)
Migrate 18864 B (+0.4%) 1752 B (+0.0%) 816 B (+0.0%) Proged 1482895246 B (+0.0%)
Error-asserts 18036 B (+0.5%) 1440 B (+0.0%) 812 B (+0.0%) Erased 1568921600 B (+0.0%)
488e84bb Fixed data corruption with multiple write handles
fd5e7f62 Using LFS_ASSERT instead of an runtime check.
2311cd78 Guard null callbacks in lfs_dir_fetchmatch
74f32c83 Fix broken link on README.md for emu device
dbe96d03 Fixes for 'Implicit conversion loses integer precision' warnings.
b062c88a Fix comment typo and -> an

A special thanks to littlefs's sponsors: https://github.com/micropython, @gurgalof

Summary by CodeRabbit

  • Bug Fixes

    • Improved file handle state synchronization and error handling across multiple file operations to enhance system reliability and consistency.
    • Enhanced type safety in critical internal calculations.
  • Tests

    • Added comprehensive test cases for multi-handle file allocation scenarios, including garbage collection and remount verification to ensure correctness.
  • Documentation

    • Updated testing section reference for accuracy.

aryeg and others added 12 commits October 19, 2025 08:35
lfs_dir_fetch relies on an impossible tag match to avoid calling a
NULL callback. Add an explicit cb != NULL check in the match path
so future refactors don’t risk a NULL function-pointer call.
Remove NULL check from condition and assert callback is valid.
Multiple write handles in littlefs has always been a bit confusing
(hopefully improving in littlefs3), but I didn't realize it could lead
to corrupted data.

The problem, as noted by Ictogan1, is that syncs to related file handles
ignores the LFS_F_DIRTY flag. If you open a file twice, and write to one
handle, littlefs doesn't realize the other handle it out-of-date.

This may not seem like a problem, but then littlefs is happy to
reallocate those (still referenced) blocks, leading to data corruption:

  open(a, "quiche.txt")
  write(a)
  sync(a) // syncs a's contents
  open(b, "quiche.txt")
  truncate(b)
  sync(b) // syncs b's contents
  write(b) // may allocate from a
  rewind(a)
  read(a) // potentially corrupted

---

What we want is to set LFS_F_DIRTY in all other file handles during
lfs_file_sync, but doing so would force those file handles to sync
during close. That would be even more confusing (not to mention
backwards incompatible).

In theory setting both LFS_F_DIRTY + LFS_F_ERRED could work, but that
would prevent implicit syncs of writes that haven't actually errored:

  open(a, "quiche.txt")
  write(a)
  open(b, "quiche.txt")
  write(b)
  close(b) // syncs b's contents
  close(a) // should sync a's contents

So, instead, as a somewhat clunky workaround, a new flag: LFS_F_DUSTY,
which indicates a file does not match storage, but should not be synced
during close.

---

It's worth noting this is already fixed in littlefs3, which includes a
more rigorous, and hopefully easier to use sync model. But in the
meantime, this should at least prevent the loss of data.

Added test_alloc_multihandle and test_alloc_multihandle_reuse to prevent
a regression, test_alloc_multihandle_reuse does reproduce the bug.

Found and reproduced by Ictogan1
Fixes for 'Implicit conversion loses integer precision' warnings.
…/fix/emu-block-readme

Fix broken link on README.md for emu device
 lfs: guard null callbacks in lfs_dir_fetchmatch
…lti-whandle-corruption

Fixed data corruption with multiple write handles
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

Walkthrough

Internal file-state synchronization is enhanced by introducing a new LFS_F_DUSTY flag to track desynchronized file handles. Flag definitions are reordered, and file-sync logic now marks sibling handles as dirty after one handle syncs successfully, while removing early-exit behavior for already-errored files. Tests validate multi-handle overwrite scenarios.

Changes

Multi-Handle File Synchronization State

Layer / File(s) Summary
Data Shape
lfs.h
New LFS_F_DUSTY flag (0x00020000) added to enum lfs_open_flags; existing internal flags (LFS_F_DIRTY, LFS_F_WRITING, LFS_F_READING, LFS_F_ERRED, LFS_F_INLINE) reassigned to new bit positions.
Core Implementation
lfs.c
lfs_file_sync_ marks sibling file handles sharing the same metadata as LFS_F_DUSTY after successful sync; removes early-return guard for already-errored files and always attempts flush. Type casts added to strcspn results and CRC perturbation mask. lfs_fs_traverse_ includes both LFS_F_DIRTY and LFS_F_DUSTY flags when deciding whether to traverse non-inline open files. lfs_file_close_ skips sync only when LFS_F_ERRED is set.
Documentation
README.md
"Testing" section updated to reference bd/lfs_emubd.h instead of bd/lfs_testbd.h.
Tests
tests/test_alloc.toml
Two new test cases added: test_alloc_multihandle validates concurrent multi-handle writes and remount consistency; test_alloc_multihandle_reuse extends this across multiple cycles with optional GC and sync operations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • Rbb666
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Merge tag v2.11.3' accurately describes the primary purpose of the pull request—merging a release tag into the repository. It clearly summarizes the main action.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@wdfk-prog
Copy link
Copy Markdown
Collaborator Author

@Rbb666

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lfs.c`:
- Line 3703: The code currently clears both LFS_F_ERRED and LFS_F_DUSTY at the
first write (file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;), which prematurely
removes the "dusty" protection; change it to only clear the error flag here
(leave LFS_F_DUSTY set) and ensure LFS_F_DUSTY is cleared later only after the
tail has been copied and committed (i.e., in lfs_file_flush() / lfs_file_sync_()
completion paths).
- Around line 3485-3495: The code in lfs_file_sync_ currently clears LFS_F_DUSTY
and marks sibling handles dusty unconditionally; change this so the
sibling-marking loop and the clearing of this handle's LFS_F_DUSTY only happen
when the handle actually committed a new version (i.e. when file->flags has
LFS_F_DIRTY). Concretely, in lfs_file_sync_ check if (file->flags & LFS_F_DIRTY)
before running the for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; ...) loop and
before clearing the LFS_F_DUSTY bit from file->flags; leave LFS_F_DUSTY
untouched if the handle was not dirty to avoid dropping its stale-dusty state.
Ensure you still preserve other flag updates (e.g. LFS_F_ERRED) as appropriate.

In `@tests/test_alloc.toml`:
- Around line 346-351: The test case [cases.test_alloc_multihandle_reuse] is
missing the defines.GC entry which other branches (lines referencing GC) depend
on; add a defines.GC define (e.g., defines.GC = [false, true]) to the block so
the generated test includes both GC variations and can exercise the reuse path
referenced by the GC branches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f854eba6-331c-47a1-9b6b-93fbf30d84d9

📥 Commits

Reviewing files that changed from the base of the PR and between 194556b and 30563cc.

📒 Files selected for processing (4)
  • README.md
  • lfs.c
  • lfs.h
  • tests/test_alloc.toml

Comment thread lfs.c
Comment on lines +3485 to +3495
// mark any other file handles as dirty + desync
for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
if (file != f
&& f->type == LFS_TYPE_REG
&& lfs_pair_cmp(f->m.pair, file->m.pair) == 0
&& f->id == file->id) {
f->flags |= LFS_F_DUSTY;
}
}

file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Only transfer LFS_F_DUSTY after this handle actually commits a new version.

A stale handle can reach this block with LFS_F_DUSTY set but LFS_F_DIRTY clear. In that case, lfs_file_sync_() currently clears its dusty bit and marks siblings dusty even though this handle still points at the orphaned CTZ chain. A later alloc scan/GC can then recycle blocks that this still-open handle may read from.

Suggested fix
 static int lfs_file_sync_(lfs_t *lfs, lfs_file_t *file) {
     int err = lfs_file_flush(lfs, file);
+    bool committed = false;
     if (err) {
         file->flags |= LFS_F_ERRED;
         return err;
     }

     if ((file->flags & LFS_F_DIRTY) &&
             !lfs_pair_isnull(file->m.pair)) {
@@
         }

         file->flags &= ~LFS_F_DIRTY;
+        committed = true;
     }

-    // mark any other file handles as dirty + desync
-    for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
-        if (file != f
-                && f->type == LFS_TYPE_REG
-                && lfs_pair_cmp(f->m.pair, file->m.pair) == 0
-                && f->id == file->id) {
-            f->flags |= LFS_F_DUSTY;
+    if (committed) {
+        // mark any other file handles as desynced
+        for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
+            if (file != f
+                    && f->type == LFS_TYPE_REG
+                    && lfs_pair_cmp(f->m.pair, file->m.pair) == 0
+                    && f->id == file->id) {
+                f->flags |= LFS_F_DUSTY;
+            }
         }
+        file->flags &= ~LFS_F_DUSTY;
     }

-    file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;
+    file->flags &= ~LFS_F_ERRED;
     return 0;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lfs.c` around lines 3485 - 3495, The code in lfs_file_sync_ currently clears
LFS_F_DUSTY and marks sibling handles dusty unconditionally; change this so the
sibling-marking loop and the clearing of this handle's LFS_F_DUSTY only happen
when the handle actually committed a new version (i.e. when file->flags has
LFS_F_DIRTY). Concretely, in lfs_file_sync_ check if (file->flags & LFS_F_DIRTY)
before running the for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; ...) loop and
before clearing the LFS_F_DUSTY bit from file->flags; leave LFS_F_DUSTY
untouched if the handle was not dirty to avoid dropping its stale-dusty state.
Ensure you still preserve other flag updates (e.g. LFS_F_ERRED) as appropriate.

Comment thread lfs.c
}

file->flags &= ~LFS_F_ERRED;
file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Do not clear LFS_F_DUSTY on first write.

If a stale handle starts writing, it still depends on the old CTZ chain until lfs_file_flush() has copied the untouched tail and lfs_file_sync_() has committed the replacement. Clearing LFS_F_DUSTY here lets traversal stop protecting the old chain too early, so GC/allocation can reuse blocks before the flush finishes.

Suggested fix
-    file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;
+    file->flags &= ~LFS_F_ERRED;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;
file->flags &= ~LFS_F_ERRED;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lfs.c` at line 3703, The code currently clears both LFS_F_ERRED and
LFS_F_DUSTY at the first write (file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;),
which prematurely removes the "dusty" protection; change it to only clear the
error flag here (leave LFS_F_DUSTY set) and ensure LFS_F_DUSTY is cleared later
only after the tail has been copied and committed (i.e., in lfs_file_flush() /
lfs_file_sync_() completion paths).

Comment thread tests/test_alloc.toml
Comment on lines +346 to +351
[cases.test_alloc_multihandle_reuse]
defines.FILES = 2
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))'
defines.CYCLES = [1, 10]
defines.INFER_BC = [false, true]
defines.SYNC = [false, true]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the missing GC test define.

Lines 371 and 388 branch on GC, but this case never declares defines.GC. That makes the generated test invalid before it can exercise the reuse path.

Suggested fix
 [cases.test_alloc_multihandle_reuse]
 defines.FILES = 2
 defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))'
 defines.CYCLES = [1, 10]
 defines.INFER_BC = [false, true]
+defines.GC = [false, true]
 defines.SYNC = [false, true]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[cases.test_alloc_multihandle_reuse]
defines.FILES = 2
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))'
defines.CYCLES = [1, 10]
defines.INFER_BC = [false, true]
defines.SYNC = [false, true]
[cases.test_alloc_multihandle_reuse]
defines.FILES = 2
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))'
defines.CYCLES = [1, 10]
defines.INFER_BC = [false, true]
defines.GC = [false, true]
defines.SYNC = [false, true]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_alloc.toml` around lines 346 - 351, The test case
[cases.test_alloc_multihandle_reuse] is missing the defines.GC entry which other
branches (lines referencing GC) depend on; add a defines.GC define (e.g.,
defines.GC = [false, true]) to the block so the generated test includes both GC
variations and can exercise the reuse path referenced by the GC branches.

@Rbb666 Rbb666 merged commit 9a3848e into RT-Thread-packages:master May 4, 2026
2 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants