use littlefs2_core::{path, DirEntry, Path};
use trussed::{serde_extensions::ExtensionImpl, store::Store};
use trussed_core::{serde_extensions::Extension, types::Location, Error};
use trussed_manage::{
FactoryResetClientReply, FactoryResetClientRequest, FactoryResetDeviceReply,
FactoryResetDeviceRequest, ManageExtension, ManageReply, ManageRequest,
FACTORY_RESET_MARKER_FILE,
};
use crate::StagingBackend;
#[derive(Debug, Clone)]
pub struct State {
pub should_preserve_file: fn(&Path, location: Location) -> bool,
}
impl Default for State {
fn default() -> State {
State {
should_preserve_file: |_, _| false,
}
}
}
fn callback(
should_preserve_file: fn(&Path, location: Location) -> bool,
location: Location,
) -> impl Fn(&DirEntry) -> bool {
move |f| !should_preserve_file(f.path(), location)
}
impl ExtensionImpl<ManageExtension> for StagingBackend {
fn extension_request<P: trussed::Platform>(
&mut self,
_core_ctx: &mut trussed::types::CoreContext,
_backend_ctx: &mut Self::Context,
request: &<ManageExtension as Extension>::Request,
resources: &mut trussed::service::ServiceResources<P>,
) -> Result<<ManageExtension as Extension>::Reply, Error> {
match request {
ManageRequest::FactoryResetDevice(FactoryResetDeviceRequest) => {
let platform = resources.platform();
let store = platform.store();
for location in [Location::Internal, Location::External, Location::Volatile] {
let fs = store.fs(location);
fs.remove_dir_all_where(
path!("/"),
&callback(self.manage.should_preserve_file, location),
)
.map_err(|_err| {
debug!("Failed to delete {location:?} fs: {_err:?}");
Error::FunctionFailed
})?;
if location == Location::External {
let is_empty = fs
.read_dir_and_then(path!("/"), &mut |dir| match dir.next() {
Some(Ok(_)) => Ok(false),
Some(Err(err)) => Err(err),
None => Ok(true),
})
.map_err(|_err| {
debug!("Failed to check emptyness {location:?} fs: {_err:?}");
Error::FunctionFailed
})?;
if is_empty {
fs.write(FACTORY_RESET_MARKER_FILE, &[])
.map_err(|_err| {
debug!("Failed to write reformat instruction for {location:?} fs: {_err:?}");
Error::FunctionFailed
})?;
}
}
}
Ok(ManageReply::FactoryResetDevice(FactoryResetDeviceReply))
}
ManageRequest::FactoryResetClient(FactoryResetClientRequest { client }) => {
let platform = resources.platform();
let store = platform.store();
if client.parent().is_some() {
return Err(Error::InvalidPath);
}
let path = path!("/").join(client);
for location in [Location::Internal, Location::External, Location::Volatile] {
store
.fs(location)
.remove_dir_all_where(
&path,
&callback(self.manage.should_preserve_file, location),
)
.map_err(|_err| {
debug!("Failed to delete {location:?} fs: {_err:?}");
Error::FunctionFailed
})?;
}
Ok(ManageReply::FactoryResetClient(FactoryResetClientReply))
}
}
}
}