littlefs2-rust 0.1.1

Pure Rust littlefs implementation with a mounted block-device API
Documentation
  • Coverage
  • 21.95%
    18 out of 82 items documented1 out of 1 items with examples
  • Size
  • Source code size: 468.88 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.55 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 6s Average build duration of successful builds.
  • all releases: 6s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • duanjr

littlefs-rust

littlefs-rust is a pure Rust implementation of the littlefs v2 on-disk format, published on crates.io as littlefs2-rust because the shorter package name is already occupied. It can format, mount, read, and mutate littlefs images through a synchronous block-device trait, while keeping core library code compatible with no_std + alloc.

Inspired by littlefs. The implementation is independent Rust code, and interoperability with upstream littlefs remains a design constraint.

The project is built around real interoperability instead of format sketches: the default test suite writes real images, remounts them, and compares visible filesystem behavior with the bundled upstream C littlefs implementation in littlefs_c/ wherever a C oracle exists.

Status

The crate currently supports the common mounted filesystem surface needed by embedded-style applications:

Area Supported surface
Device model BlockDevice, MemoryBlockDevice, and default-std FileBlockDevice
Lifecycle format_device, read-only mount_device, owned mutable mount_device_mut
Read APIs stat, read_dir, open_dir, walk, read_file, read_file_into, read_file_at, attrs
Write APIs create, overwrite, append, remove, mkdir, rmdir, rename, user attrs
File handles open flags, read/write/append/truncate, seek, flush, sync, close
Large files CTZ file reads and writes, streaming create and append paths
Directories nested directories, hardtail split chains, split-aware lookup/update/delete/rename/attrs
Durability data-before-metadata write ordering, selected power-loss and fault-injection tests
Resource shape mounted block-device paths avoid resident whole-image ownership

This is not a byte-for-byte rewrite of every internal C state machine. Some low-level policy details, especially allocator checkpointing, complete wear-leveling fidelity, and rare relocation or split-compaction interleavings, remain documented compatibility work. The public filesystem behavior is already covered by broad Rust/C black-box tests.

Installation

[dependencies]
littlefs-rust = { package = "littlefs2-rust", version = "0.1" }

For a no_std build, disable default features. The crate still requires alloc from the target environment.

[dependencies]
littlefs-rust = { package = "littlefs2-rust", version = "0.1", default-features = false }

Quick Start

use littlefs_rust::{Config, FileBlockDevice, FileOptions, Filesystem};

fn main() -> littlefs_rust::Result<()> {
    let cfg = Config {
        block_size: 512,
        block_count: 4096,
    };

    let path = std::env::temp_dir().join("littlefs-rust-example.img");
    let mut device = FileBlockDevice::create_erased(&path, cfg)?;
    Filesystem::format_device(&mut device)?;

    let mut fs = Filesystem::mount_device_mut(device)?;
    fs.create_dir("/logs")?;

    let mut file = fs.open_file(
        "/logs/current.txt",
        FileOptions::new()
            .create(true)
            .write(true)
            .append(true),
    )?;
    file.write_all(b"boot ok\n")?;
    file.sync()?;
    file.close()?;

    fs.append_file("/logs/current.txt", b"network ok\n")?;
    fs.sync()?;

    let device = fs.into_device();
    let ro = Filesystem::mount_device(&device)?;
    assert_eq!(ro.read_file("/logs/current.txt")?, b"boot ok\nnetwork ok\n");

    let _ = std::fs::remove_file(path);
    Ok(())
}

Configuration

Config describes physical geometry:

use littlefs_rust::Config;

let cfg = Config {
    block_size: 512,
    block_count: 4096,
};

FilesystemOptions mirrors the main non-callback fields of C struct lfs_config and is accepted by the *_with_options format and mount APIs. It covers read, program, and cache sizes, lookahead size validation, block_cycles, name/file/attr limits, metadata_max, and inline-file policy.

use littlefs_rust::{FilesystemOptions, InlineMax};

let options = FilesystemOptions {
    cache_size: 128,
    prog_size: 16,
    read_size: 16,
    inline_max: InlineMax::Limit(64),
    ..FilesystemOptions::default()
};

See docs/configuration.md for validation rules, feature flags, and block-device expectations.

Block Device Contract

The block-device trait uses littlefs vocabulary:

  • read(block, off, out) reads bytes inside one logical block.
  • prog(block, off, data) programs bytes inside one block. NOR-style backends should only change bits from 1 to 0 until erase.
  • erase(block) resets a whole block to the erased value.
  • sync() flushes durable backend state when the backend needs it.

The bundled MemoryBlockDevice and FileBlockDevice are real block devices used by integration tests. They are not filesystem behavior mocks.

Documentation

Testing

Run the default compatibility suite:

cargo fmt -- --check
cargo check --no-default-features
cargo test

The suite includes black-box integration tests that exercise real image bytes and compare with upstream C littlefs. Performance and memory evaluation is kept out of default cargo test; see tools/blackbox_eval/README.md.

Known Gaps

The implementation is intended to be useful today, but the remaining gaps are tracked openly:

  • allocator policy still uses an in-memory visible-block bitmap plus a lookahead scan window rather than a fully C-like persistent checkpoint traversal;
  • bad-block and relocation retry behavior covers the important public paths, but not every rare split/relocation/orphan interleaving from C littlefs;
  • split-chain support preserves public lookup and mutation behavior, while not reproducing every adjacent-pair balancing choice in lfs_dir_splittingcompact;
  • read/write handles with arbitrary-position extension may still choose a simpler buffered path to preserve read-your-writes semantics;
  • the resource profile is now in the tens of KiB for common mounted workloads, but the C implementation remains smaller on very tight targets.

Release Notes

See CHANGELOG.md.

License

This crate is distributed under the MIT License. The upstream littlefs C reference is used as a Git submodule for compatibility tests and keeps its own upstream license.