Skip to content

Commit

Permalink
Introduce littlefs2-core crate
Browse files Browse the repository at this point in the history
To make it easier to maintain applications using littlefs2, this patch
introduces a littlefs2-core crate with the basic types and traits used
by littlefs2, but without a specific implementation.

Fixes: #55
  • Loading branch information
robin-nitrokey committed Aug 8, 2024
1 parent 63a1265 commit 6178eb0
Show file tree
Hide file tree
Showing 17 changed files with 580 additions and 509 deletions.
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ jobs:

- name: Check
run: |
cargo check --all-targets
cargo check --all-targets --all-features
cargo check --all-targets --no-default-features
cargo check --all-targets --no-default-features --features serde
cargo check --all-targets --no-default-features --features dir-entry-path
cargo check --workspace --all-targets
cargo check --workspace --all-targets --all-features
cargo check --workspace --all-targets --no-default-features
cargo check --workspace --all-targets --no-default-features --features serde
cargo check --workspace --all-targets --no-default-features --features dir-entry-path
- name: Build
run: cargo build --release --verbose
run: cargo build --workspace --release --verbose

- name: Run tests
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: >
cargo test &&
cargo test --release
cargo test --workspace &&
cargo test --workspace --release
- name: Build Documentation
run: cargo doc --no-deps
28 changes: 16 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
[workspace]
members = ["core"]

[workspace.package]
edition = "2021"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/trussed-dev/littlefs2"

[package]
name = "littlefs2"
description = "Idiomatic Rust API for littlefs"
version = "0.4.0"
authors = ["Nicolas Stalder <n@stalder.io>", "Brandon Edens <brandonedens@gmail.com>", "The Trussed developers"]
edition = "2021"
license = "Apache-2.0 OR MIT"
readme = "README.md"
categories = ["embedded", "filesystem", "no-std"]
repository = "https://github.com/trussed-dev/littlefs2"
documentation = "https://docs.rs/littlefs2"

edition.workspace = true
license.workspace = true
repository.workspace = true

[dependencies]
bitflags = "1"
delog = "0.1.0"
generic-array = "0.14"
heapless = "0.7"
littlefs2-core = { version = "0.1", path = "core" }
littlefs2-sys = "0.2"

[dependencies.serde]
version = "1"
default-features = false
features = ["derive"]
optional = true

[dev-dependencies]
ssmarshal = "1"
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde = { version = "1.0", default-features = false }
# trybuild = "1"

[features]
default = ["dir-entry-path", "serde"]
# use experimental closure-based API
dir-entry-path = []
dir-entry-path = ["littlefs2-core/dir-entry-path"]
serde = ["littlefs2-core/serde"]
# enable assertions in backend C code
ll-assertions = ["littlefs2-sys/assertions"]
# enable trace in backend C code
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ We follow [`std::fs`][std-fs] as much as reasonable.

The low-level bindings are provided by the [littlefs2-sys][littlefs2-sys] library.

The core types that are independent of a specific implementation version are provided by the `littlefs2-core` crate, see the [`core`](./core) directory. These types are re-exported from the `littlefs2` crate too.

Upstream release: [v2.2.1][upstream-release]

[geky]: https://github.com/geky
Expand Down
10 changes: 10 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased

Initial release with the core types from `littlefs2`.
19 changes: 19 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "littlefs2-core"
version = "0.1.0"
authors = ["The Trussed developers"]
description = "Core types for the littlefs2 crate"

edition.workspace = true
license.workspace = true
repository.workspace = true

[dependencies]
bitflags = "2.6.0"
generic-array = "0.14"
heapless = "0.7"
serde = { version = "1", default-features = false, features = ["derive"], optional = true }

[features]
dir-entry-path = []
serde = ["dep:serde"]
5 changes: 5 additions & 0 deletions core/src/consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub const PATH_MAX: usize = 255;
pub const PATH_MAX_PLUS_ONE: usize = PATH_MAX + 1;
pub const ATTRBYTES_MAX: u32 = 1_022;
#[allow(non_camel_case_types)]
pub type ATTRBYTES_MAX_TYPE = generic_array::typenum::consts::U1022;
180 changes: 180 additions & 0 deletions core/src/fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
use core::cmp;

use bitflags::bitflags;

use crate::path::{Path, PathBuf};

pub type Bytes<SIZE> = generic_array::GenericArray<u8, SIZE>;

bitflags! {
/// Definition of file open flags which can be mixed and matched as appropriate. These definitions
/// are reminiscent of the ones defined by POSIX.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileOpenFlags: i32 {
/// Open file in read only mode.
const READ = 0x1;
/// Open file in write only mode.
const WRITE = 0x2;
/// Open file for reading and writing.
const READWRITE = Self::READ.bits() | Self::WRITE.bits();
/// Create the file if it does not exist.
const CREATE = 0x0100;
/// Fail if creating a file that already exists.
/// TODO: Good name for this
const EXCL = 0x0200;
/// Truncate the file if it already exists.
const TRUNCATE = 0x0400;
/// Open the file in append only mode.
const APPEND = 0x0800;
}
}

/// Regular file vs directory
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FileType {
File,
Dir,
}

impl FileType {
pub fn is_dir(&self) -> bool {
*self == FileType::Dir
}

pub fn is_file(&self) -> bool {
*self == FileType::File
}
}

/// File type (regular vs directory) and size of a file.
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Metadata {
file_type: FileType,
size: usize,
}

impl Metadata {
pub fn new(file_type: FileType, size: usize) -> Self {
Self { file_type, size }
}

pub fn file_type(&self) -> FileType {
self.file_type
}

pub fn is_dir(&self) -> bool {
self.file_type().is_dir()
}

pub fn is_file(&self) -> bool {
self.file_type().is_file()
}

pub fn len(&self) -> usize {
self.size
}

pub fn is_empty(&self) -> bool {
self.size == 0
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
/// Custom user attribute that can be set on files and directories.
///
/// Consists of an numerical identifier between 0 and 255, and arbitrary
/// binary data up to size `ATTRBYTES_MAX`.
///
/// Use [`Filesystem::attribute`](struct.Filesystem.html#method.attribute),
/// [`Filesystem::set_attribute`](struct.Filesystem.html#method.set_attribute), and
/// [`Filesystem::clear_attribute`](struct.Filesystem.html#method.clear_attribute).
pub struct Attribute {
id: u8,
pub data: Bytes<crate::consts::ATTRBYTES_MAX_TYPE>,
pub size: usize,
}

impl Attribute {
pub fn new(id: u8) -> Self {
Attribute {
id,
data: Default::default(),
size: 0,
}
}

pub fn id(&self) -> u8 {
self.id
}

pub fn data(&self) -> &[u8] {
let attr_max = crate::consts::ATTRBYTES_MAX as _;
let len = cmp::min(attr_max, self.size);
&self.data[..len]
}

pub fn set_data(&mut self, data: &[u8]) -> &mut Self {
let attr_max = crate::consts::ATTRBYTES_MAX as _;
let len = cmp::min(attr_max, data.len());
self.data[..len].copy_from_slice(&data[..len]);
self.size = len;
for entry in self.data[len..].iter_mut() {
*entry = 0;
}
self
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DirEntry {
file_name: PathBuf,
metadata: Metadata,
#[cfg(feature = "dir-entry-path")]
path: PathBuf,
}

impl DirEntry {
pub fn new(parent: &Path, file_name: PathBuf, metadata: Metadata) -> Self {
let _ = parent;
Self {
#[cfg(feature = "dir-entry-path")]
path: parent.join(&file_name),
file_name,
metadata,
}
}

// Returns the metadata for the file that this entry points at.
pub fn metadata(&self) -> Metadata {
self.metadata.clone()
}

// Returns the file type for the file that this entry points at.
pub fn file_type(&self) -> FileType {
self.metadata.file_type
}

// Returns the bare file name of this directory entry without any other leading path component.
pub fn file_name(&self) -> &Path {
&self.file_name
}

/// Returns the full path to the file that this entry represents.
///
/// The full path is created by joining the original path to read_dir with the filename of this entry.
#[cfg(feature = "dir-entry-path")]
pub fn path(&self) -> &Path {
&self.path
}

#[cfg(feature = "dir-entry-path")]
#[doc(hidden)]
// This is used in `crypto-service` to "namespace" paths
// by mutating a DirEntry in-place.
pub unsafe fn path_buf_mut(&mut self) -> &mut PathBuf {
&mut self.path
}
}
Loading

0 comments on commit 6178eb0

Please sign in to comment.