use littlefs2_core::{path, DirEntry, Metadata, Path};
use trussed_core::{
types::{Bytes, Location},
Error,
};
pub use littlefs2_core::{DynFile, DynFilesystem};
mod certstore;
mod counterstore;
mod filestore;
mod keystore;
pub use certstore::{Certstore, ClientCertstore};
pub use counterstore::{ClientCounterstore, Counter, Counterstore};
pub use filestore::{ClientFilestore, Filestore, ReadDirFilesState, ReadDirState};
pub use keystore::{ClientKeystore, Keystore};
pub trait Store {
fn ifs(&self) -> &dyn DynFilesystem;
fn efs(&self) -> &dyn DynFilesystem;
fn vfs(&self) -> &dyn DynFilesystem;
fn fs(&self, location: Location) -> &dyn DynFilesystem {
match location {
Location::Internal => self.ifs(),
Location::External => self.efs(),
Location::Volatile => self.vfs(),
}
}
}
pub fn create_directories(fs: &dyn DynFilesystem, path: &Path) -> Result<(), Error> {
if let Some(parent) = path.parent() {
fs.create_dir_all(&parent)
.map_err(|_| Error::FilesystemWriteFailure)?;
}
Ok(())
}
#[inline(never)]
pub fn read<const N: usize>(
store: &impl Store,
location: Location,
path: &Path,
) -> Result<Bytes<N>, Error> {
debug_now!("reading {}", &path);
store
.fs(location)
.read(path)
.map_err(|_| Error::FilesystemReadFailure)
}
#[inline(never)]
pub fn write(
store: &impl Store,
location: Location,
path: &Path,
contents: &[u8],
) -> Result<(), Error> {
debug_now!("writing {}", &path);
store
.fs(location)
.write(path, contents)
.map_err(|_| Error::FilesystemWriteFailure)
}
#[inline(never)]
pub fn store(
store: &impl Store,
location: Location,
path: &Path,
contents: &[u8],
) -> Result<(), Error> {
debug_now!("storing {}", &path);
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 fs = store.fs(location);
if fs.remove(path).is_err() {
return false;
}
if location != Location::Volatile {
return true;
}
for parent in path.ancestors().skip(1) {
if &*parent == path!("/") {
break;
}
let Ok(meta) = fs.metadata(&parent) else {
return false;
};
if meta.is_dir() && meta.is_empty() {
if fs.remove_dir(&parent).is_err() {
return false;
}
} else {
break;
}
}
true
}
#[inline(never)]
pub fn exists(store: &impl Store, location: Location, path: &Path) -> bool {
debug_now!("checking existence of {}", &path);
store.fs(location).exists(path)
}
#[inline(never)]
pub fn metadata(
store: &impl Store,
location: Location,
path: &Path,
) -> Result<Option<Metadata>, Error> {
debug_now!("checking existence of {}", &path);
match store.fs(location).metadata(path) {
Ok(metadata) => Ok(Some(metadata)),
Err(littlefs2_core::Error::NO_SUCH_ENTRY) => Ok(None),
Err(_) => Err(Error::FilesystemReadFailure),
}
}
#[inline(never)]
pub fn rename(store: &impl Store, location: Location, from: &Path, to: &Path) -> Result<(), Error> {
debug_now!("renaming {} to {}", &from, &to);
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);
store.fs(location).remove_dir(path).is_ok()
}
#[inline(never)]
pub fn remove_dir_all_where(
store: &impl Store,
location: Location,
path: &Path,
predicate: &dyn Fn(&DirEntry) -> bool,
) -> Result<usize, Error> {
debug_now!("remove_dir'ing {}", &path);
store
.fs(location)
.remove_dir_all_where(path, predicate)
.map_err(|_| Error::FilesystemWriteFailure)
}