From bfde40403cf2c8fbf76fd5e3786a6cbb77289484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 9 Nov 2023 10:25:17 +0100 Subject: [PATCH] Add manage extension --- Cargo.toml | 1 + src/lib.rs | 13 ++- src/manage/mod.rs | 246 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 src/manage/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 463b14d..0375342 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ default = [] wrap-key-to-file = ["chacha20poly1305"] chunked = [] encrypted-chunked = ["chunked", "chacha20poly1305/stream"] +manage = [] virt = ["std", "trussed/virt"] std = [] diff --git a/src/lib.rs b/src/lib.rs index 919dbf4..023ab9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,13 +18,22 @@ pub mod wrap_key_to_file; #[cfg(feature = "chunked")] pub mod streaming; +#[cfg(feature = "manage")] +pub mod manage; + #[derive(Clone, Debug, Default)] #[non_exhaustive] -pub struct StagingBackend {} +pub struct StagingBackend { + #[cfg(feature = "manage")] + pub manage: manage::State, +} impl StagingBackend { pub fn new() -> Self { - Self {} + Self { + #[cfg(feature = "manage")] + manage: manage::State::default(), + } } } diff --git a/src/manage/mod.rs b/src/manage/mod.rs new file mode 100644 index 0000000..5d20d05 --- /dev/null +++ b/src/manage/mod.rs @@ -0,0 +1,246 @@ +use littlefs2::path; +use littlefs2::path::{Path, PathBuf}; +use serde::{Deserialize, Serialize}; +use trussed::{ + serde_extensions::{Extension, ExtensionClient, ExtensionImpl, ExtensionResult}, + store::Store, + Error, +}; + +use crate::StagingBackend; + +pub struct ManageExtension; + +/// Factory reset the entire device +/// +/// This will reset all filesystems +#[derive(Debug, Deserialize, Serialize, Copy, Clone)] +pub struct FactoryResetDeviceRequest; + +/// Factory reset a specific application +/// +/// This will reset all data for a specific client +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct FactoryResetClientRequest { + client: PathBuf, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Deserialize, Serialize, Clone)] +pub enum ManageRequest { + FactoryResetDevice(FactoryResetDeviceRequest), + FactoryResetClient(FactoryResetClientRequest), +} + +impl From for ManageRequest { + fn from(value: FactoryResetClientRequest) -> Self { + Self::FactoryResetClient(value) + } +} + +impl TryFrom for FactoryResetClientRequest { + type Error = Error; + fn try_from(value: ManageRequest) -> Result { + match value { + ManageRequest::FactoryResetClient(v) => Ok(v), + _ => Err(Error::InternalError), + } + } +} + +impl From for ManageRequest { + fn from(value: FactoryResetDeviceRequest) -> Self { + Self::FactoryResetDevice(value) + } +} + +impl TryFrom for FactoryResetDeviceRequest { + type Error = Error; + fn try_from(value: ManageRequest) -> Result { + match value { + ManageRequest::FactoryResetDevice(v) => Ok(v), + _ => Err(Error::InternalError), + } + } +} + +/// Factory reset the entire device +/// +/// This will reset all filesystems +#[derive(Debug, Deserialize, Serialize, Copy, Clone)] +pub struct FactoryResetDeviceReply; + +/// Factory reset a specific application +/// +/// This will reset all data for a specific client +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct FactoryResetClientReply; + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub enum ManageReply { + FactoryResetDevice(FactoryResetDeviceReply), + FactoryResetClient(FactoryResetClientReply), +} + +impl From for ManageReply { + fn from(value: FactoryResetClientReply) -> Self { + Self::FactoryResetClient(value) + } +} + +impl TryFrom for FactoryResetClientReply { + type Error = Error; + fn try_from(value: ManageReply) -> Result { + match value { + ManageReply::FactoryResetClient(v) => Ok(v), + _ => Err(Error::InternalError), + } + } +} + +impl From for ManageReply { + fn from(value: FactoryResetDeviceReply) -> Self { + Self::FactoryResetDevice(value) + } +} + +impl TryFrom for FactoryResetDeviceReply { + type Error = Error; + fn try_from(value: ManageReply) -> Result { + match value { + ManageReply::FactoryResetDevice(v) => Ok(v), + _ => Err(Error::InternalError), + } + } +} + +impl Extension for ManageExtension { + type Request = ManageRequest; + type Reply = ManageReply; +} + +type ManageResult<'a, R, C> = ExtensionResult<'a, ManageExtension, R, C>; + +pub trait ManageClient: ExtensionClient { + /// Factory reset the entire device + /// + /// This will reset all filesystems + fn factory_reset_device(&mut self) -> ManageResult<'_, FactoryResetDeviceReply, Self> { + self.extension(FactoryResetDeviceRequest) + } + + /// Factory reset the entire client + /// + fn factory_reset_client( + &mut self, + client: &Path, + ) -> ManageResult<'_, FactoryResetClientReply, Self> { + self.extension(FactoryResetClientRequest { + client: client.into(), + }) + } +} + +#[derive(Default, Debug, Clone)] +pub struct State { + pub ifs_to_preserve: &'static [&'static Path], + pub efs_to_preserve: &'static [&'static Path], + pub vfs_to_preserve: &'static [&'static Path], +} + +impl> ManageClient for C {} + +impl ExtensionImpl for StagingBackend { + fn extension_request( + &mut self, + _core_ctx: &mut trussed::types::CoreContext, + _backend_ctx: &mut Self::Context, + request: &::Request, + resources: &mut trussed::service::ServiceResources

, + ) -> Result<::Reply, Error> { + match request { + ManageRequest::FactoryResetDevice(FactoryResetDeviceRequest) => { + let platform = resources.platform(); + let store = platform.store(); + let ifs = store.ifs(); + let efs = store.efs(); + let vfs = store.vfs(); + ifs.remove_dir_all_where(path!("/"), &|f| { + let file_name = f.file_name(); + if self.manage.ifs_to_preserve.contains(&file_name) { + return false; + } + true + }) + .map_err(|_err| { + debug!("Failed to delete ifs: {_err:?}"); + Error::FunctionFailed + })?; + efs.remove_dir_all_where(path!("/"), &|f| { + let file_name = f.file_name(); + if self.manage.efs_to_preserve.contains(&file_name) { + return false; + } + true + }) + .map_err(|_err| { + debug!("Failed to delete efs: {_err:?}"); + Error::FunctionFailed + })?; + vfs.remove_dir_all_where(path!("/"), &|f| { + let file_name = f.file_name(); + if self.manage.vfs_to_preserve.contains(&file_name) { + return false; + } + true + }) + .map_err(|_err| { + debug!("Failed to delete vfs: {_err:?}"); + Error::FunctionFailed + })?; + Ok(ManageReply::FactoryResetDevice(FactoryResetDeviceReply)) + } + ManageRequest::FactoryResetClient(FactoryResetClientRequest { client }) => { + let platform = resources.platform(); + let store = platform.store(); + let ifs = store.ifs(); + let efs = store.efs(); + let vfs = store.vfs(); + ifs.remove_dir_all_where(client, &|f| { + let file_name = f.file_name(); + if self.manage.ifs_to_preserve.contains(&file_name) { + return false; + } + true + }) + .map_err(|_err| { + debug!("Failed to delete ifs: {_err:?}"); + Error::FunctionFailed + })?; + efs.remove_dir_all_where(client, &|f| { + let file_name = f.file_name(); + if self.manage.efs_to_preserve.contains(&file_name) { + return false; + } + true + }) + .map_err(|_err| { + debug!("Failed to delete efs: {_err:?}"); + Error::FunctionFailed + })?; + vfs.remove_dir_all_where(client, &|f| { + let file_name = f.file_name(); + if self.manage.vfs_to_preserve.contains(&file_name) { + return false; + } + true + }) + .map_err(|_err| { + debug!("Failed to delete vfs: {_err:?}"); + Error::FunctionFailed + })?; + Ok(ManageReply::FactoryResetClient(FactoryResetClientReply)) + } + } + } +}