diff --git a/Cargo.lock b/Cargo.lock index ff53a12..8acf70b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,6 +418,7 @@ dependencies = [ "libc", "serde", "tarpc", + "thiserror", "tokio", "tracing", ] diff --git a/gdriver-client/Cargo.toml b/gdriver-client/Cargo.toml index ac7fbdc..0851930 100644 --- a/gdriver-client/Cargo.toml +++ b/gdriver-client/Cargo.toml @@ -16,6 +16,7 @@ fuser = "0.14.0" bimap = "0.6" libc = "0.2.152" futures = "0.3" +thiserror = "1.0.56" [dependencies.gdriver-common] path = "../gdriver-common" diff --git a/gdriver-client/src/filesystem.rs b/gdriver-client/src/filesystem.rs index f88b2cc..19f7a14 100644 --- a/gdriver-client/src/filesystem.rs +++ b/gdriver-client/src/filesystem.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::os::raw::c_int; use std::time::Duration; @@ -9,28 +9,38 @@ use fuser::{KernelConfig, ReplyEntry, Request}; use tracing::*; use gdriver_common::drive_structure::drive_id::{DriveId, ROOT_ID}; +use gdriver_common::drive_structure::meta::read_metadata_file; use gdriver_common::ipc::gdriver_service::{ errors::GDriverServiceError, GDriverServiceClient, GDriverSettings, }; +use crate::filesystem::attributes::read_inode_attributes_from_meta_file; +use crate::filesystem::errors::FilesystemError; +use crate::prelude::macros::*; use crate::prelude::*; -use crate::{ - reply_error_e, reply_error_o, send_request, send_request_handled, send_request_handled2, - send_request_handled2_consuming, -}; +use tarpc::context::current as current_context; mod macros; //TODO2: Decide if this is a good TTL const TTL: Duration = Duration::from_secs(2); +type Inode = u64; + +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +struct FileIdentifier { + parent: Inode, + name: OsString, +} + pub struct Filesystem { gdriver_client: GDriverServiceClient, - entry_ids: BiMap, - ino_to_file_handles: HashMap>, + entry_ids: BiMap, + ino_to_file_handles: HashMap>, next_ino: u64, gdriver_settings: GDriverSettings, + entry_name_parent_to_ino: BiMap, } impl Filesystem { @@ -41,9 +51,10 @@ impl Filesystem { ino_to_file_handles: HashMap::new(), next_ino: 222, gdriver_settings: GDriverSettings::default(), + entry_name_parent_to_ino: BiMap::new(), } } - fn generate_ino(&mut self) -> u64 { + fn generate_ino(&mut self) -> Inode { let ino = self.next_ino; self.next_ino += 1; ino @@ -52,24 +63,24 @@ impl Filesystem { //region DriveFilesystem ino_to_id impl Filesystem { - fn get_id_from_ino(&self, ino: u64) -> Option<&DriveId> { + fn get_id_from_ino(&self, ino: Inode) -> Option<&DriveId> { self.entry_ids.get_by_left(&ino) } - fn get_ino_from_id(&mut self, id: DriveId) -> u64 { + fn get_ino_from_id(&mut self, id: DriveId) -> Inode { let x = self.entry_ids.get_by_right(&id); if let Some(ino) = x { return *ino; } self.add_id(id) } - fn remove_id(&mut self, id: DriveId) -> Result { + fn remove_id(&mut self, id: DriveId) -> Result { if let Some((ino, _)) = self.entry_ids.remove_by_right(&id) { Ok(ino) } else { Err(Box::from(anyhow!("could not find id {}", id))) } } - fn add_id(&mut self, id: DriveId) -> u64 { + fn add_id(&mut self, id: DriveId) -> Inode { let ino = self.generate_ino(); trace!("adding new ino for drive id: {} => {}", id, ino); self.entry_ids.insert(ino, id); @@ -79,63 +90,131 @@ impl Filesystem { //endregion mod attributes; + impl fuser::Filesystem for Filesystem { //region init fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> StdResult<(), c_int> { self.entry_ids.insert(1, ROOT_ID.clone()); - self.gdriver_settings = - send_request!(self.gdriver_client.get_settings(tarpc::context::current())) - .map_err(|e| { - error!("Got a connection error while fetching settings: {e}"); - libc::ECONNREFUSED - })? - .map_err(|e| { - error!("Got an error while fetching settings: {e}"); - trace!("details: {e:?}"); - libc::EBADMSG - })?; + self.gdriver_settings = send_request!(self.gdriver_client.get_settings(current_context())) + .map_err(|e| { + error!("Got a connection error while fetching settings: {e}"); + libc::ECONNREFUSED + })? + .map_err(|e| { + error!("Got an error while fetching settings: {e}"); + trace!("details: {e:?}"); + libc::EBADMSG + })?; Ok(()) } //endregion //region lookup fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { - let parent_id = self.entry_ids.get_by_left(&parent); - let parent_id = reply_error_o!( - parent_id, - reply, - libc::ENOENT, - "Failed to find drive_id for parent ino: {}", - parent - ); - trace!( - "looking for child of parent:{} with name: {:?}", - parent_id, - name - ); + let metadata = utils::lookup::lookup(self, parent, name.to_os_string()); + match metadata { + Ok(metadata) => { + reply.entry(&TTL, &metadata.into(), 0); + } + Err(e) => { + error!("Got an error during lookup: {e:?}"); + match e { + FilesystemError::Rpc(_) => reply.error(libc::EREMOTEIO), + FilesystemError::IO(_) => reply.error(libc::EIO), + FilesystemError::Service(_) | FilesystemError::NotFound => { + reply.error(libc::ENOENT) + } + FilesystemError::Other(e) => { + dbg!(e); + todo!("Handle other errors and decide what error code should be used here") + } + } + } + } + } + //endregion +} +mod errors { + use gdriver_common::ipc::gdriver_service::errors::GDriverServiceError; + use std::error::Error; + use tarpc::client::RpcError; - let id = reply_error_e!( - send_request_handled2!( - self.gdriver_client.get_file_by_name( - tarpc::context::current(), - name.to_os_string(), - parent_id.clone() - ), - reply - ), - reply, - libc::ENOENT, - "Could not find file by name '{:?}' under parent: {}", - name, - parent_id - ); - send_request_handled2_consuming!( - self.gdriver_client - .get_metadata_for_file(tarpc::context::current(), id), - reply, - parent - ); - - todo!() + #[derive(Debug, thiserror::Error)] + pub enum FilesystemError { + #[error("Error while executing RPC: {0}")] + Rpc(#[from] RpcError), + #[error("Could not find entity specified")] + NotFound, + #[error("IO Error")] + IO(#[source] Box), + #[error("Service returned Error: {0}")] + Service(#[from] GDriverServiceError), + #[error("Some other error occurred: {0}")] + Other(#[source] Box), + } +} +mod utils { + use super::*; + pub mod lookup { + use super::*; + use crate::filesystem::attributes::InodeAttributes; + use crate::filesystem::errors::FilesystemError; + use futures::TryFutureExt; + use gdriver_common::ipc::gdriver_service::errors::GetFileByPathError; + + pub fn lookup( + fs: &mut Filesystem, + parent: Inode, + name: OsString, + ) -> StdResult { + let id: DriveId; + let ino: Inode; + + let name = name.to_os_string(); + let ino_opt = fs.entry_name_parent_to_ino.get_by_left(&FileIdentifier { + parent, + name: name.clone(), + }); + + match ino_opt { + None => { + //we don't know this name with this parent already, so we have to look it up + let parent_id = fs + .entry_ids + .get_by_left(&parent) + .ok_or(FilesystemError::NotFound)?; + trace!( + "looking for child of parent:{} with name: {:?}", + parent_id, + name + ); + id = send_request!(fs.gdriver_client.get_file_by_name( + current_context(), + name.to_os_string(), + parent_id.clone() + ))? + .map_err(GDriverServiceError::from)?; + + ino = fs.add_id(id.clone()); + } + Some(i) => { + ino = *i; + id = fs + .get_id_from_ino(*i) + .ok_or(FilesystemError::NotFound)? + .clone(); + } + } + let open_file_handles = + fs.ino_to_file_handles.get(&ino).map(Vec::len).unwrap_or(0) as u64; + send_request!(fs + .gdriver_client + .get_metadata_for_file(current_context(), id.clone()))? + .map_err(GDriverServiceError::from)?; + let meta_path = fs.gdriver_settings.get_metadata_file_path(&id); + let metadata = read_inode_attributes_from_meta_file(&meta_path, ino, open_file_handles) + .map_err(FilesystemError::IO)?; + Ok(metadata) + } } } diff --git a/gdriver-client/src/filesystem/attributes.rs b/gdriver-client/src/filesystem/attributes.rs index fa4b110..4bc6ae7 100644 --- a/gdriver-client/src/filesystem/attributes.rs +++ b/gdriver-client/src/filesystem/attributes.rs @@ -117,8 +117,8 @@ pub(crate) fn read_inode_attributes_from_metadata( metadata: Metadata, inode: Inode, open_file_handles: u64, -) -> Result { - Ok(InodeAttributes { +) -> InodeAttributes { + InodeAttributes { inode, open_file_handles, size: metadata.size, @@ -131,7 +131,7 @@ pub(crate) fn read_inode_attributes_from_metadata( uid: metadata.uid, gid: metadata.gid, xattrs: metadata.xattrs, - }) + } } pub(crate) fn read_inode_attributes_from_meta_file( meta_path: &Path, @@ -139,7 +139,11 @@ pub(crate) fn read_inode_attributes_from_meta_file( open_file_handles: u64, ) -> Result { let metadata = read_metadata_file(meta_path)?; - read_inode_attributes_from_metadata(metadata, inode, open_file_handles) + Ok(read_inode_attributes_from_metadata( + metadata, + inode, + open_file_handles, + )) } impl From for fuser::FileAttr { diff --git a/gdriver-client/src/filesystem/macros.rs b/gdriver-client/src/filesystem/macros.rs index 54aea3a..b8311f6 100644 --- a/gdriver-client/src/filesystem/macros.rs +++ b/gdriver-client/src/filesystem/macros.rs @@ -43,19 +43,19 @@ mod send_requests { }}; } #[macro_export] - macro_rules! send_request_handled { + macro_rules! send_request_handled_internal { ($func:expr ,$reply:ident, $error_code:expr, $error_msg:expr, $($arg:tt)*) => {{ let x = send_request!($func); reply_error_e!(x, $reply, $error_code, $error_msg, $($arg)*) }}; } #[macro_export] - macro_rules! send_request_handled2 { + macro_rules! send_request_handled { ($func:expr ,$reply:ident) => { - send_request_handled2!($func, $reply, "") + send_request_handled!($func, $reply, "") }; ($func:expr ,$reply:ident, $error_msg:expr) => { - send_request_handled!( + send_request_handled_internal!( $func, $reply, ::libc::EREMOTEIO, @@ -66,12 +66,12 @@ mod send_requests { } #[macro_export] - macro_rules! send_request_handled2_consuming { + macro_rules! send_request_handled_consuming { ($func:expr ,$reply:ident) => { - send_request_handled2_consuming!($func, $reply, ""); + send_request_handled_consuming!($func, $reply, ""); }; ($func:expr ,$reply:ident, $error_msg:expr) => { - let _ = send_request_handled2!($func, $reply, $error_msg); + let _ = send_request_handled!($func, $reply, $error_msg); }; } } diff --git a/gdriver-client/src/prelude.rs b/gdriver-client/src/prelude.rs index fb66c71..9b2586a 100644 --- a/gdriver-client/src/prelude.rs +++ b/gdriver-client/src/prelude.rs @@ -1,2 +1,8 @@ pub(crate) use gdriver_common::prelude::result::*; pub(crate) use gdriver_common::prelude::*; +pub(crate) mod macros { + pub(crate) use crate::{ + reply_error_e, reply_error_o, send_request, send_request_handled_internal, send_request_handled, + send_request_handled_consuming, + }; +} diff --git a/gdriver-common/src/ipc/gdriver_service.rs b/gdriver-common/src/ipc/gdriver_service.rs index 287d97c..6d65e2c 100644 --- a/gdriver-common/src/ipc/gdriver_service.rs +++ b/gdriver-common/src/ipc/gdriver_service.rs @@ -55,6 +55,16 @@ impl GDriverSettings { pub fn downloaded_path(&self) -> &Path { &self.downloaded_path } + + pub fn get_metadata_file_path(&self, id: &DriveId) -> PathBuf { + self.metadata_path.join(id.as_ref()).with_extension("meta") + } + pub fn get_downloaded_file_path(&self, id: &DriveId) -> PathBuf { + self.downloaded_path.join(id.as_ref()) + } + pub fn get_cache_file_path(&self, id: &DriveId) -> PathBuf { + self.cache_path.join(id.as_ref()) + } } impl Default for GDriverSettings {