trussed_staging/
manage.rs1use littlefs2_core::{path, DirEntry, Path};
5use trussed::{serde_extensions::ExtensionImpl, store::Store};
6use trussed_core::{serde_extensions::Extension, types::Location, Error};
7use trussed_manage::{
8 FactoryResetClientReply, FactoryResetClientRequest, FactoryResetDeviceReply,
9 FactoryResetDeviceRequest, ManageExtension, ManageReply, ManageRequest,
10 FACTORY_RESET_MARKER_FILE,
11};
12
13use crate::StagingBackend;
14
15#[derive(Debug, Clone)]
16pub struct State {
17 pub should_preserve_file: fn(&Path, location: Location) -> bool,
30}
31
32impl Default for State {
33 fn default() -> State {
34 State {
35 should_preserve_file: |_, _| false,
36 }
37 }
38}
39
40fn callback(
41 should_preserve_file: fn(&Path, location: Location) -> bool,
42 location: Location,
43) -> impl Fn(&DirEntry) -> bool {
44 move |f| !should_preserve_file(f.path(), location)
45}
46
47impl ExtensionImpl<ManageExtension> for StagingBackend {
48 fn extension_request<P: trussed::Platform>(
49 &mut self,
50 _core_ctx: &mut trussed::types::CoreContext,
51 _backend_ctx: &mut Self::Context,
52 request: &<ManageExtension as Extension>::Request,
53 resources: &mut trussed::service::ServiceResources<P>,
54 ) -> Result<<ManageExtension as Extension>::Reply, Error> {
55 match request {
56 ManageRequest::FactoryResetDevice(FactoryResetDeviceRequest) => {
57 let platform = resources.platform();
58 let store = platform.store();
59
60 for location in [Location::Internal, Location::External, Location::Volatile] {
61 let fs = store.fs(location);
62 fs.remove_dir_all_where(
63 path!("/"),
64 &callback(self.manage.should_preserve_file, location),
65 )
66 .map_err(|_err| {
67 debug!("Failed to delete {location:?} fs: {_err:?}");
68 Error::FunctionFailed
69 })?;
70 if location == Location::External {
71 let is_empty = fs
72 .read_dir_and_then(path!("/"), &mut |dir| match dir.next() {
73 Some(Ok(_)) => Ok(false),
74 Some(Err(err)) => Err(err),
75 None => Ok(true),
76 })
77 .map_err(|_err| {
78 debug!("Failed to check emptyness {location:?} fs: {_err:?}");
79 Error::FunctionFailed
80 })?;
81 if is_empty {
82 fs.write(FACTORY_RESET_MARKER_FILE, &[])
83 .map_err(|_err| {
84 debug!("Failed to write reformat instruction for {location:?} fs: {_err:?}");
85 Error::FunctionFailed
86 })?;
87 }
88 }
89 }
90 Ok(ManageReply::FactoryResetDevice(FactoryResetDeviceReply))
91 }
92 ManageRequest::FactoryResetClient(FactoryResetClientRequest { client }) => {
93 let platform = resources.platform();
94 let store = platform.store();
95
96 if client.parent().is_some() {
97 return Err(Error::InvalidPath);
98 }
99
100 let path = path!("/").join(client);
101
102 for location in [Location::Internal, Location::External, Location::Volatile] {
103 store
104 .fs(location)
105 .remove_dir_all_where(
106 &path,
107 &callback(self.manage.should_preserve_file, location),
108 )
109 .map_err(|_err| {
110 debug!("Failed to delete {location:?} fs: {_err:?}");
111 Error::FunctionFailed
112 })?;
113 }
114 Ok(ManageReply::FactoryResetClient(FactoryResetClientReply))
115 }
116 }
117 }
118}