diff --git a/Cargo.toml b/Cargo.toml index 6084de22824..132ca38cf2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,3 +127,6 @@ test-attestation-cert-ids = [] [package.metadata.docs.rs] features = ["serde-extensions", "virt"] rustdoc-args = ["--cfg", "docsrs"] + +[patch.crates-io] +littlefs2 = { git = "https://github.com/trussed-dev/littlefs2.git", rev = "ebd27e49ca321089d01d8c9b169c4aeb58ceeeca" } diff --git a/src/service.rs b/src/service.rs index 03b88ca97c8..02027624b31 100644 --- a/src/service.rs +++ b/src/service.rs @@ -15,10 +15,12 @@ pub use crate::pipe::ServiceEndpoint; use crate::pipe::TrussedResponder; use crate::platform::*; pub use crate::store::{ + self, certstore::{Certstore as _, ClientCertstore}, counterstore::{ClientCounterstore, Counterstore as _}, filestore::{ClientFilestore, Filestore, ReadDirFilesState, ReadDirState}, keystore::{ClientKeystore, Keystore}, + DynFilesystem, }; use crate::types::ui::Status; use crate::types::*; @@ -324,14 +326,14 @@ impl ServiceResources

{ Request::DebugDumpStore(_request) => { info_now!(":: PERSISTENT"); - recursively_list(self.platform.store().ifs(), path!("/")); + recursively_list(self.platform.store().fs(Location::Internal), path!("/")); info_now!(":: VOLATILE"); - recursively_list(self.platform.store().vfs(), path!("/")); + recursively_list(self.platform.store().fs(Location::Volatile), path!("/")); - fn recursively_list(fs: &'static crate::store::Fs, path: &Path) { + fn recursively_list(fs: &dyn DynFilesystem, path: &Path) { // let fs = store.vfs(); - fs.read_dir_and_then(path, |dir| { + fs.read_dir_and_then(path, &mut |dir| { for (i, entry) in dir.enumerate() { let entry = entry.unwrap(); if i < 2 { @@ -343,7 +345,7 @@ impl ServiceResources

{ recursively_list(fs, entry.path()); } if entry.file_type().is_file() { - let _contents: Vec = fs.read(entry.path()).unwrap(); + let _contents = fs.read::<256>(entry.path()).unwrap(); // info_now!("{} ?= {}", entry.metadata().len(), contents.len()).ok(); // info_now!("{:?}", &contents).ok(); } @@ -869,19 +871,19 @@ impl Service { self.resources .platform .store() - .ifs() + .fs(Location::Internal) .available_blocks() .unwrap(), self.resources .platform .store() - .efs() + .fs(Location::External) .available_blocks() .unwrap(), self.resources .platform .store() - .vfs() + .fs(Location::Volatile) .available_blocks() .unwrap(), ); diff --git a/src/store.rs b/src/store.rs index 8e91fa8287a..9145d95b5d6 100644 --- a/src/store.rs +++ b/src/store.rs @@ -76,7 +76,12 @@ use crate::types::*; #[allow(unused_imports)] #[cfg(feature = "semihosting")] use cortex_m_semihosting::hprintln; -use littlefs2::path::Path; +use littlefs2::{ + fs::{DirEntry, Metadata}, + path::Path, +}; + +pub use littlefs2::object_safe::{DynFile, DynFilesystem, DynStorage}; pub mod certstore; pub mod counterstore; @@ -128,6 +133,13 @@ pub unsafe trait Store: Copy { fn ifs(self) -> &'static Fs; fn efs(self) -> &'static Fs; fn vfs(self) -> &'static Fs; + fn fs(&self, location: Location) -> &dyn DynFilesystem { + match location { + Location::Internal => self.ifs().fs, + Location::External => self.efs().fs, + Location::Volatile => self.vfs().fs, + } + } } pub struct Fs { @@ -480,7 +492,7 @@ macro_rules! store { } // TODO: replace this with "fs.create_dir_all(path.parent())" -pub fn create_directories(fs: &Filesystem, path: &Path) -> Result<(), Error> { +pub fn create_directories(fs: &dyn DynFilesystem, path: &Path) -> Result<(), Error> { // hprintln!("preparing {:?}", core::str::from_utf8(path).unwrap()).ok(); let path_bytes = path.as_ref().as_bytes(); @@ -511,13 +523,11 @@ pub fn read( path: &Path, ) -> Result, Error> { debug_now!("reading {}", &path); - match location { - Location::Internal => store.ifs().read(path), - Location::External => store.efs().read(path), - Location::Volatile => store.vfs().read(path), - } - .map(Bytes::from) - .map_err(|_| Error::FilesystemReadFailure) + store + .fs(location) + .read(path) + .map(From::from) + .map_err(|_| Error::FilesystemReadFailure) } /// Writes contents to path in location of store. @@ -529,12 +539,10 @@ pub fn write( contents: &[u8], ) -> Result<(), Error> { debug_now!("writing {}", &path); - match location { - Location::Internal => store.ifs().write(path, contents), - Location::External => store.efs().write(path, contents), - Location::Volatile => store.vfs().write(path, contents), - } - .map_err(|_| Error::FilesystemWriteFailure) + store + .fs(location) + .write(path, contents) + .map_err(|_| Error::FilesystemWriteFailure) } /// Creates parent directory if necessary, then writes. @@ -546,33 +554,23 @@ pub fn store( contents: &[u8], ) -> Result<(), Error> { debug_now!("storing {}", &path); - match location { - Location::Internal => create_directories(store.ifs(), path)?, - Location::External => create_directories(store.efs(), path)?, - Location::Volatile => create_directories(store.vfs(), path)?, - } - write(store, location, path, contents) + create_directories(store.fs(location), path)?; + store + .fs(location) + .write(path, contents) + .map_err(|_| Error::FilesystemWriteFailure) } #[inline(never)] pub fn delete(store: impl Store, location: Location, path: &Path) -> bool { debug_now!("deleting {}", &path); - let outcome = match location { - Location::Internal => store.ifs().remove(path), - Location::External => store.efs().remove(path), - Location::Volatile => store.vfs().remove(path), - }; - outcome.is_ok() + store.fs(location).remove(path).is_ok() } #[inline(never)] pub fn exists(store: impl Store, location: Location, path: &Path) -> bool { debug_now!("checking existence of {}", &path); - match location { - Location::Internal => path.exists(store.ifs()), - Location::External => path.exists(store.efs()), - Location::Volatile => path.exists(store.vfs()), - } + store.fs(location).exists(path) } #[inline(never)] @@ -582,12 +580,7 @@ pub fn metadata( path: &Path, ) -> Result, Error> { debug_now!("checking existence of {}", &path); - let result = match location { - Location::Internal => store.ifs().metadata(path), - Location::External => store.efs().metadata(path), - Location::Volatile => store.vfs().metadata(path), - }; - match result { + match store.fs(location).metadata(path) { Ok(metadata) => Ok(Some(metadata)), Err(littlefs2::io::Error::NoSuchEntry) => Ok(None), Err(_) => Err(Error::FilesystemReadFailure), @@ -597,42 +590,30 @@ pub fn metadata( #[inline(never)] pub fn rename(store: impl Store, location: Location, from: &Path, to: &Path) -> Result<(), Error> { debug_now!("renaming {} to {}", &from, &to); - match location { - Location::Internal => store.ifs().rename(from, to), - Location::External => store.efs().rename(from, to), - Location::Volatile => store.vfs().rename(from, to), - } - .map_err(|_| Error::FilesystemWriteFailure) + store + .fs(location) + .rename(from, to) + .map_err(|_| Error::FilesystemWriteFailure) } #[inline(never)] pub fn remove_dir(store: impl Store, location: Location, path: &Path) -> bool { debug_now!("remove_dir'ing {}", &path); - let outcome = match location { - Location::Internal => store.ifs().remove_dir(path), - Location::External => store.efs().remove_dir(path), - Location::Volatile => store.vfs().remove_dir(path), - }; - outcome.is_ok() + store.fs(location).remove_dir(path).is_ok() } #[inline(never)] -pub fn remove_dir_all_where

( +pub fn remove_dir_all_where( store: impl Store, location: Location, path: &Path, - predicate: P, -) -> Result -where - P: Fn(&DirEntry) -> bool, -{ + predicate: &dyn Fn(&DirEntry) -> bool, +) -> Result { debug_now!("remove_dir'ing {}", &path); - let outcome = match location { - Location::Internal => store.ifs().remove_dir_all_where(path, &predicate), - Location::External => store.efs().remove_dir_all_where(path, &predicate), - Location::Volatile => store.vfs().remove_dir_all_where(path, &predicate), - }; - outcome.map_err(|_| Error::FilesystemWriteFailure) + store + .fs(location) + .remove_dir_all_where(path, predicate) + .map_err(|_| Error::FilesystemWriteFailure) } // pub fn delete_volatile(store: impl Store, handle: &ObjectHandle) -> bool { diff --git a/src/store/certstore.rs b/src/store/certstore.rs index 63b0ba187ac..54352b68d0f 100644 --- a/src/store/certstore.rs +++ b/src/store/certstore.rs @@ -1,7 +1,4 @@ -use littlefs2::{ - path, - path::{Path, PathBuf}, -}; +use littlefs2::{path, path::PathBuf}; use rand_chacha::ChaCha8Rng; use crate::{ diff --git a/src/store/counterstore.rs b/src/store/counterstore.rs index da3d5bdc783..1b14c82d35f 100644 --- a/src/store/counterstore.rs +++ b/src/store/counterstore.rs @@ -1,7 +1,4 @@ -use littlefs2::{ - path, - path::{Path, PathBuf}, -}; +use littlefs2::{path, path::PathBuf}; use rand_chacha::ChaCha8Rng; use crate::{ diff --git a/src/store/filestore.rs b/src/store/filestore.rs index 6f6cc17ac17..13ead46c576 100644 --- a/src/store/filestore.rs +++ b/src/store/filestore.rs @@ -1,8 +1,8 @@ use crate::{ error::{Error, Result}, // service::ReadDirState, - store::{self, Store}, - types::{LfsStorage, Location, Message, UserAttribute}, + store::{self, DynFilesystem, Store}, + types::{Location, Message, UserAttribute}, Bytes, }; use littlefs2::path; @@ -27,7 +27,6 @@ use littlefs2::{ path::{Path, PathBuf}, }; -use super::Fs; pub type ClientId = PathBuf; pub struct ClientFilestore @@ -142,17 +141,17 @@ pub trait Filestore { /// Generic implementation allowing the use of any filesystem. impl ClientFilestore { - fn read_dir_first_impl( + fn read_dir_first_impl( &mut self, clients_dir: &Path, location: Location, not_before: Option<&Path>, - fs: &'static Fs, ) -> Result> { + let fs = self.store.fs(location); let dir = self.actual_path(clients_dir)?; Ok(fs - .read_dir_and_then(&dir, |it| { + .read_dir_and_then(&dir, &mut |it| { // this is an iterator with Item = (usize, Result) it.enumerate() // skip over `.` and `..` @@ -197,21 +196,22 @@ impl ClientFilestore { }) .ok()) } - fn read_dir_next_impl( + fn read_dir_next_impl( &mut self, state: ReadDirState, - fs: &'static Fs, ) -> Result> { + // TODO: check what happens (or used to happen) for FS != IFS let ReadDirState { real_dir, last, location, } = state; + let fs = self.store.fs(location); // all we want to do here is skip just past the previously found entry // in the directory iterator, then return it (plus state to continue on next call) Ok(fs - .read_dir_and_then(&real_dir, |it| { + .read_dir_and_then(&real_dir, &mut |it| { // skip over previous it.enumerate() .nth(last + 1) @@ -234,17 +234,17 @@ impl ClientFilestore { }) .ok()) } - fn read_dir_files_first_impl( + fn read_dir_files_first_impl( &mut self, clients_dir: &Path, location: Location, user_attribute: Option, - fs: &'static Fs, ) -> Result, ReadDirFilesState)>> { + let fs = self.store.fs(location); let dir = self.actual_path(clients_dir)?; Ok(fs - .read_dir_and_then(&dir, |it| { + .read_dir_and_then(&dir, &mut |it| { // this is an iterator with Item = (usize, Result) it.enumerate() // todo: try ?-ing out of this (the API matches std::fs, where read/write errors @@ -280,7 +280,8 @@ impl ClientFilestore { real_dir: dir.clone(), last: i, location, - user_attribute, + // TODO: check if we can avoid that clone + user_attribute: user_attribute.clone(), }; // The semantics is that for a non-existent file, we return None (not an error) let data = store::read(self.store, location, entry.path()).ok(); @@ -295,22 +296,23 @@ impl ClientFilestore { .ok()) } - fn read_dir_files_next_impl( + fn read_dir_files_next_impl( &mut self, state: ReadDirFilesState, - fs: &'static Fs, ) -> Result, ReadDirFilesState)>> { + // TODO: check what happens (or used to happen) for FS != IFS let ReadDirFilesState { real_dir, last, location, user_attribute, } = state; + let fs = self.store.fs(location); // all we want to do here is skip just past the previously found entry // in the directory iterator, then return it (plus state to continue on next call) Ok(fs - .read_dir_and_then(&real_dir, |it| { + .read_dir_and_then(&real_dir, &mut |it| { // skip over previous it.enumerate() .skip(last + 1) @@ -340,7 +342,8 @@ impl ClientFilestore { real_dir: real_dir.clone(), last: i, location, - user_attribute, + // TODO: check if we can avoid that clone + user_attribute: user_attribute.clone(), }; // The semantics is that for a non-existent file, we return None (not an error) let data = store::read(self.store, location, entry.path()).ok(); @@ -404,7 +407,7 @@ impl Filestore for ClientFilestore { fn remove_dir_all(&mut self, path: &Path, location: Location) -> Result { let path = self.actual_path(path)?; - store::remove_dir_all_where(self.store, location, &path, |_| true) + store::remove_dir_all_where(self.store, location, &path, &|_| true) .map_err(|_| Error::InternalError) } fn remove_dir_all_where( @@ -415,7 +418,7 @@ impl Filestore for ClientFilestore { ) -> Result { let path = self.actual_path(path)?; - store::remove_dir_all_where(self.store, location, &path, predicate) + store::remove_dir_all_where(self.store, location, &path, &predicate) .map_err(|_| Error::InternalError) } @@ -425,25 +428,11 @@ impl Filestore for ClientFilestore { location: Location, not_before: Option<&Path>, ) -> Result> { - match location { - Location::Internal => { - self.read_dir_first_impl(clients_dir, location, not_before, self.store.ifs()) - } - Location::External => { - self.read_dir_first_impl(clients_dir, location, not_before, self.store.efs()) - } - Location::Volatile => { - self.read_dir_first_impl(clients_dir, location, not_before, self.store.vfs()) - } - } + self.read_dir_first_impl(clients_dir, location, not_before) } fn read_dir_next(&mut self, state: ReadDirState) -> Result> { - match state.location { - Location::Internal => self.read_dir_next_impl(state, self.store.ifs()), - Location::External => self.read_dir_next_impl(state, self.store.efs()), - Location::Volatile => self.read_dir_next_impl(state, self.store.vfs()), - } + self.read_dir_next_impl(state) } fn read_dir_files_first( @@ -452,37 +441,14 @@ impl Filestore for ClientFilestore { location: Location, user_attribute: Option, ) -> Result, ReadDirFilesState)>> { - match location { - Location::Internal => self.read_dir_files_first_impl( - clients_dir, - location, - user_attribute, - self.store.ifs(), - ), - Location::External => self.read_dir_files_first_impl( - clients_dir, - location, - user_attribute, - self.store.efs(), - ), - Location::Volatile => self.read_dir_files_first_impl( - clients_dir, - location, - user_attribute, - self.store.vfs(), - ), - } + self.read_dir_files_first_impl(clients_dir, location, user_attribute) } fn read_dir_files_next( &mut self, state: ReadDirFilesState, ) -> Result, ReadDirFilesState)>> { - match state.location { - Location::Internal => self.read_dir_files_next_impl(state, self.store.ifs()), - Location::External => self.read_dir_files_next_impl(state, self.store.efs()), - Location::Volatile => self.read_dir_files_next_impl(state, self.store.vfs()), - } + self.read_dir_files_next_impl(state) } fn locate_file( @@ -497,16 +463,16 @@ impl Filestore for ClientFilestore { let clients_dir = underneath.unwrap_or_else(|| path!("/")); let dir = self.actual_path(clients_dir)?; - let fs = self.store.ifs(); + let fs = self.store.fs(Location::Internal); info_now!("base dir {:?}", &dir); - fn recursively_locate( - fs: &'static crate::store::Fs, + fn recursively_locate( + fs: &dyn DynFilesystem, dir: &Path, filename: &Path, ) -> Option { - fs.read_dir_and_then(dir, |it| { + fs.read_dir_and_then(dir, &mut |it| { it.map(|entry| entry.unwrap()) .skip(2) .filter_map(|entry| { diff --git a/src/store/keystore.rs b/src/store/keystore.rs index 235825475e6..01f12e22686 100644 --- a/src/store/keystore.rs +++ b/src/store/keystore.rs @@ -1,7 +1,4 @@ -use littlefs2::{ - path, - path::{Path, PathBuf}, -}; +use littlefs2::{path, path::PathBuf}; use rand_chacha::ChaCha8Rng; use crate::{ @@ -162,12 +159,12 @@ impl Keystore for ClientKeystore { fn delete_all(&self, location: Location) -> Result { let secret_path = self.key_directory(key::Secrecy::Secret); let secret_deleted = - store::remove_dir_all_where(self.store, location, &secret_path, |dir_entry| { + store::remove_dir_all_where(self.store, location, &secret_path, &|dir_entry| { dir_entry.file_name().as_ref().len() >= 4 })?; let public_path = self.key_directory(key::Secrecy::Public); let public_deleted = - store::remove_dir_all_where(self.store, location, &public_path, |dir_entry| { + store::remove_dir_all_where(self.store, location, &public_path, &|dir_entry| { dir_entry.file_name().as_ref().len() >= 4 })?; Ok(secret_deleted + public_deleted) @@ -223,18 +220,8 @@ impl Keystore for ClientKeystore { fn location(&self, secrecy: key::Secrecy, id: &KeyId) -> Option { let path = self.key_path(secrecy, id); - if path.exists(self.store.vfs()) { - return Some(Location::Volatile); - } - - if path.exists(self.store.ifs()) { - return Some(Location::Internal); - } - - if path.exists(self.store.efs()) { - return Some(Location::External); - } - - None + [Location::Volatile, Location::Internal, Location::External] + .into_iter() + .find(|&location| self.store.fs(location).exists(&path)) } }