Skip to content

chore: release v0.4.22 - #36

Closed
MagicalTux wants to merge 3 commits into
masterfrom
release-plz-2026-08-17T04-28-37Z
Closed

chore: release v0.4.22#36
MagicalTux wants to merge 3 commits into
masterfrom
release-plz-2026-08-17T04-28-37Z

Conversation

@MagicalTux

@MagicalTux MagicalTux commented Aug 17, 2026

Copy link
Copy Markdown
Member

🤖 New release

  • fstool: 0.4.21 -> 0.4.22
Changelog

0.4.22 - 2026-08-17

Added

  • (littlefs) read, write and in-place edits for lfs2 images
  • (web) create, edit and download images in the browser
  • (memedit) in-memory authoring — blank filesystems, partitioned disks

Fixed

  • (ext) honour journal revoke records across transactions
  • (ext) decode HTree directory roots and honour journal revoke blocks (#32)

Other

  • (littlefs) redraw the tag and skip-list diagrams in our own form

Added

  • (littlefs) new backend for the embedded-flash filesystem (lfs2, disk
    versions 2.0 and 2.1): read, write, and in-place edits. Metadata pairs are
    replayed from their CRC-committed logs (tags, splices, tails, global-state
    deltas, lfs2.1 forward-CRCs) and written back as compactions; files live
    inline in metadata or in CTZ skip-lists, which are rebuilt only from the
    first changed block onwards so a partial write leaves earlier blocks
    untouched. Block allocation reconstructs the in-use map by traversing the
    volume, as littlefs itself does. Wired into create -t littlefs, the TOML
    spec (type = "littlefs", with block_size / block_count / prog_size /
    version / name_max / inline_max options), repack --fs-type littlefs,
    info, add/rm, the in-memory authoring surface, and the browser build.
    littlefs user attributes surface as user.littlefs.<type> extended
    attributes; the format has no symlinks, device nodes or POSIX metadata, so
    those are refused rather than faked. Cross-validated in both directions
    against the reference C implementation via littlefs-python, including
    handing an image back and forth mid-edit.

  • (memedit) new in-memory authoring surface: Workspace formats a blank
    filesystem or lays out a partitioned disk (MBR/GPT), takes files and
    directories, and hands back the image bytes at any point.
    creatable_filesystems() advertises 15 types with their real minimum sizes.

  • (wasm) Workspace and creatable_filesystems() bindings, so the browser
    build can author images as well as read them.

  • (web) "Create a new image" mode: pick a filesystem and size, or build a
    partitioned disk with a filesystem per partition, then add files, delete
    them, browse directories, and download the image — repeatedly, while
    continuing to edit. An uploaded image can also be switched into edit mode.

Fixed

  • (ext) journal recovery now honours revoke records across transactions.
    Replay was single-pass and scoped each revoke to the transaction that
    carried it, so a block revoked in transaction N+1 was still replayed from
    transaction N — it had already been written by the time the revoke record
    was read. That is exactly the case revoke records exist to prevent (a
    metadata block freed and reused as file data), so stale metadata could land
    on live data. Recovery now runs the two passes JBD2 requires: a scan that
    collects committed transactions and builds a revoke table keyed by the
    highest revoking transaction id, then a replay that skips any block whose
    revoke id is at or after the transaction replaying it (the kernel's
    jbd2_journal_test_revoke rule, wrap-safe). An uncommitted tail
    transaction's revoke records are discarded along with its writes.
  • (ext) parse_descriptor_tags walked the tag array in 8-byte steps
    regardless of the journal's real tag size, so a trailing slot too short for
    a 16-byte checksum-v3 tag failed the whole replay instead of ending the
    array; it also read into the 4-byte checksum tail that checksum-v2/v3
    descriptor blocks carry.


This PR was generated with release-plz.

@MagicalTux
MagicalTux force-pushed the release-plz-2026-08-17T04-28-37Z branch 4 times, most recently from adc1e4e to 00ab0c0 Compare August 17, 2026 05:55
littlefs is the fail-safe filesystem embedded flash runs on. This adds a
full backend for on-disk versions 2.0 and 2.1, at the same tier as ext /
FAT / AFFS: probe, read, format, and in-place mutation.

The format has no superblock region, no allocation table and no inode
table, so the backend is built from the two structures it does have:

- Metadata pairs (src/fs/littlefs/mdir.rs): two blocks holding a revision
  count and an append-only log of CRC-terminated commits. `fetch` replays
  a pair's log — tags, create/delete splices, tails, global-state deltas,
  lfs2.1 forward-CRCs — into a flat view; committing writes the whole
  state back as a compaction into the pair's stale block and swaps it,
  splitting across further pairs when the contents outgrow a block. That
  is the operation littlefs itself performs whenever a block fills up, so
  the result is always a volume a stock littlefs can mount and keep
  appending to.
- CTZ skip-lists (src/fs/littlefs/ctz.rs): files too large to inline.
  Because every block points backwards, a write rebuilds the list only
  from the block holding the first changed byte — earlier blocks keep
  their contents and their pointers — which is the copy-on-write shape
  the format is designed around.

Block allocation (src/fs/littlefs/alloc.rs) reconstructs the in-use map
by traversing the threaded pair list and each file's skip-list, as
littlefs does, then keeps it exact as blocks are claimed and released.

Wired into `create -t littlefs` (with block_size / block_count /
prog_size / version / name_max / inline_max knobs), the TOML spec both
bare and per-partition, `repack --fs-type littlefs` on the random-access,
streamed-tar, layered and `--shrink` paths, `info`, `add`/`rm`, the
in-memory authoring surface, and the wasm build.

littlefs stores no POSIX metadata: no modes, owners, timestamps,
symlinks or device nodes. Modes are synthesised on read and symlink /
device creation reports Unsupported so a repack sink skips those entries
instead of mangling them. littlefs user attributes do round-trip, as
`user.littlefs.<type>` extended attributes.

Validation runs against the reference C implementation through
littlefs-python (tests/littlefs_external.rs, skipped when the module
isn't installed): images we write mount and read identically there,
images it writes read identically here, both sides can keep writing to
an image the other produced mid-edit, an in-place patch / append /
truncate of a 120 KB file reads back correctly under its reader, and the
two implementations agree exactly on which blocks are live. The backend
also joins the randomised fs_fuzz harness and the reopen-mutate sweep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MagicalTux
MagicalTux force-pushed the release-plz-2026-08-17T04-28-37Z branch from 00ab0c0 to de4cb1d Compare August 17, 2026 13:46
MagicalTux and others added 2 commits August 17, 2026 22:55
The two ASCII figures in the tag and CTZ module docs were reproduced from
littlefs's SPEC.md, which is BSD-3-Clause. Nothing else in the backend
came from upstream sources — no C was copied and no code was vendored —
so replace them with our own renderings (a bit-range list, and a table of
the pointers each block index stores) and keep the pointer to upstream's
spec as a reference rather than as copied text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant