littlefs2-rust 0.1.1

Pure Rust littlefs implementation with a mounted block-device API
Documentation
# File Sync Semantics

This document records the current mounted-file sync contract. It is intentionally
written as an implementation guide for future convergence with littlefs C rather
than as a marketing-level API summary.

## Current Contract

- `FileHandle::flush` writes dirty handle contents into a remountable filesystem
  state. After a successful `flush`, closing the handle is a no-op for that
  version of the file unless later writes make the handle dirty again.
- `FileHandle::sync` first performs `flush`, then enters the mounted
  block-device `sync` boundary once more. This gives callers an explicit fsync
  style operation even when the underlying metadata commit path has already
  flushed the internal program cache.
- For write-only partial overwrites of existing CTZ files, `flush` streams a
  replacement CTZ chain by reading old data in block-sized chunks and overlaying
  pending patches before the metadata commit exposes the new head.
- `FilesystemMut::sync` flushes the mounted block cache and calls
  `BlockDevice::sync`. It does not have access to dirty file handles because
  Rust's mutable borrow rules keep an open `FileHandle` as the exclusive owner
  of its mounted filesystem borrow. Dirty file contents therefore need
  `FileHandle::flush`, `FileHandle::sync`, or `FileHandle::close`.
- `FileBlockDevice::sync` forwards to host `File::sync_data`. The in-memory
  block device treats sync as a successful boundary with no extra work.
- Successful mounted metadata operations currently return after their program
  cache has crossed the block-device sync boundary. This is stricter than a
  pure writeback cache and keeps the contract easy to reason about until the cache
  grows a more C-like lazy policy.

## Failure Boundary

The important retry cases are covered by black-box tests, not mocks:

- If the sync inside a file flush fails after programming reached the backend,
  the error is returned and the handle remains retryable. A second flush must
  converge to the intended C-readable file contents. This is checked for both
  newly-written buffered content and existing CTZ write-only partial overwrite.
- Existing CTZ streaming append must also keep its stream state after a metadata
  sync failure. The black-box test retries the handle sync, then performs a
  rename, an empty-directory removal, and a filesystem sync before asking C to
  read the final file.
- If a file's contents have already been flushed and the explicit sync boundary
  fails, retrying `FileHandle::sync` must not rewrite the metadata commit. The
  retry should only re-enter the block-device sync boundary.
- `FilesystemMut::sync` is deliberately not a hidden dirty-handle flush. A
  dropped unclosed file handle is not committed by a later filesystem sync; the
  filesystem remains usable, and C sees only operations that were explicitly
  closed/flushed/synced.
- When a sync failure leaves programmed bytes in the backend, the image still
  has to be mountable. Tests verify the resulting image through both Rust and
  upstream C littlefs.

## Open Convergence Work

- Expand mixed sync schedules inside split, relocation, pending move, and
  orphan repair transactions. The public file-handle contract is now covered
  for append, write-only partial overwrite, explicit retry, and dropped dirty
  handles, but the lower-level metadata state machines still need broader
  combinations.
- Decide whether future metadata operations should expose a lazier cache policy
  that more closely matches littlefs' internal program cache. If that changes,
  the public contract above must be updated before code changes land.
- Keep expanding block-device fault injection around sync failures that occur
  during split, relocation, pending move, and orphan repair transactions. The
  seeded mixed matrix now covers one combined path, but more seeds and
  operation shrinking would make failures easier to diagnose.