Crate littlefs2[][src]

Expand description

littlefs is a filesystem for microcontrollers written in C, that claims to be fail-safe:

  • power-loss resilience, by virtue of copy-on-write guarantees
  • bounded RAM/ROM, with stack-allocated buffers

Since version 2, it has some nifty features such as:

  • dynamic wear-leveling, including detection of bad Flash blocks
  • custom user attributes
  • inline files, avoiding block waste

For more background, see its design notes and the specification of its format.

What is this?

This library, littlefs2, offers an idiomatic Rust API for littlefs.

It follows the design of std::fs as much as reasonable, and builds on the bindings littlefs2-sys.

Some complications arise due to the lack of const generics in Rust, we work around these with the generic-array library, and long for the day when constants associated to traits will be treated as constants by the compiler.

Another complication is the fact that files (and directories) need to be closed before they go out of scope, since the main littlefs state structure contains a linked list which would exhibit UB (undefined behaviour) otherwise, see issue #3 and issue #5. We choose not to call close in drop (as std::fs does), since these operations could panic if for instance littlefs detects Flash corruption (from which the application might otherwise recover).

For this reason, the various File-related open methods are marked as unsafe. Instead, a closure-based API is offered (open_and_then and friends), the same is done for Filesystem::read_dir. Under the hood, this API first calls the unsafe constructor, then calls the user-supplied closure, and finally closes the object.

FOLLOWING SECTION OUT-OF-DATE

The best place to start reading the API docs is here. ⯇

Usage

To use this library, implement littlefs2::driver::Storage. The macro ram_storage! generates examples of this.

Roughly speaking, the Storage trait defines a block device in terms of actual and typenum constants, and an implementation supplies methods to read, erase and write.

The filesystem and each open file need memory for state and caching, this has to be allocated beforehand and passed to constructors.

Design notes

All operations on the filesystem require passing a &mut Storage, which guarantees by Rust’s borrow checker that only one thread can manipulate the filesystem. This design choice (as opposed to consuming the Storage, which would be less verbose) was made to enable use of the underlying flash peripheral outside of the filesystem (the Storage can be dropped and reconstructed). For instance, one could setup an additional filesystem, or handle some flash data manually.

As an experiment, we implemented ReadDirWith. It converts a ReadDir (which needs mutable references, and so is “not quite an iterator” over the files of a directory), into a true iterator, by temporarily binding the mutable references.

Currying with lifetime gymnastics!

In the future, we may extend this approach to other operations, thus adding a secondary API layer.

https://play.rust-lang.org/?edition=2018&gist=c86abf99fc87551cfe3136e398a45d19

Separately, keeping track of the allocations is a chore, we hope that Pin magic will help fix this.

Example

// example storage backend
ram_storage!(tiny);
let mut ram = Ram::default();
let mut storage = RamStorage::new(&mut ram);

// must format before first mount
Filesystem::format(&mut storage).unwrap();
// must allocate state statically before use
let mut alloc = Filesystem::allocate();
let mut fs = Filesystem::mount(&mut alloc, &mut storage).unwrap();

// may use common `OpenOptions`
let mut buf = [0u8; 11];
fs.open_file_with_options_and_then(
    |options| options.read(true).write(true).create(true),
    &PathBuf::from(b"example.txt"),
    |file| {
        file.write(b"Why is black smoke coming out?!")?;
        file.seek(SeekFrom::End(-24)).unwrap();
        assert_eq!(file.read(&mut buf)?, 11);
        Ok(())
    }
).unwrap();
assert_eq!(&buf, b"black smoke");

Re-exports

pub use littlefs2_sys as ll;

Modules

consts
driver

The Storage, Read, Write and Seek driver.

fs

Experimental Filesystem version using closures.

io

Traits and types for core I/O functionality.

macros

cf. Macros documentation

path

Paths

Macros

const_ram_storage
debug

Local version of debug!.

debug_now

Immediate version of debug!.

error

Local version of error!.

error_now

Immediate version of error!.

info

Local version of info!.

info_now

Immediate version of info!.

log

Local version of log!.

log_now

Immediate version of log!.

ram_storage

A configurable implementation of the Storage trait in memory.

trace

Local version of trace!.

trace_now

Immediate version of trace!.

try_debug

Fallible version of debug!.

try_debug_now

Fallible immediate version of debug!.

try_error

Fallible version of error!.

try_error_now

Fallible immediate version of error!.

try_info

Fallible version of info!.

try_info_now

Fallible immediate version of info!.

try_log_now

Fallible immediate version of log!.

try_trace

Fallible version of trace!.

try_trace_now

Fallible immediate version of trace!.

try_warn

Fallible version of warn!.

try_warn_now

Fallible immediate version of warn!.

warn

Local version of warn!.

warn_now

Immediate version of warn!.

Structs

Version

Information about the C backend

Functions

version

get information about the C backend