diff --git a/gdriver-client/src/filesystem.rs b/gdriver-client/src/filesystem.rs new file mode 100644 index 0000000..f88b2cc --- /dev/null +++ b/gdriver-client/src/filesystem.rs @@ -0,0 +1,141 @@ +use std::collections::HashMap; +use std::ffi::OsStr; +use std::os::raw::c_int; +use std::time::Duration; + +use anyhow::anyhow; +use bimap::BiMap; +use fuser::{KernelConfig, ReplyEntry, Request}; +use tracing::*; + +use gdriver_common::drive_structure::drive_id::{DriveId, ROOT_ID}; +use gdriver_common::ipc::gdriver_service::{ + errors::GDriverServiceError, GDriverServiceClient, GDriverSettings, +}; + +use crate::prelude::*; +use crate::{ + reply_error_e, reply_error_o, send_request, send_request_handled, send_request_handled2, + send_request_handled2_consuming, +}; + +mod macros; + +//TODO2: Decide if this is a good TTL +const TTL: Duration = Duration::from_secs(2); + +pub struct Filesystem { + gdriver_client: GDriverServiceClient, + + entry_ids: BiMap, + ino_to_file_handles: HashMap>, + next_ino: u64, + gdriver_settings: GDriverSettings, +} + +impl Filesystem { + pub fn new(gdriver_client: GDriverServiceClient) -> Self { + Self { + gdriver_client, + entry_ids: BiMap::new(), + ino_to_file_handles: HashMap::new(), + next_ino: 222, + gdriver_settings: GDriverSettings::default(), + } + } + fn generate_ino(&mut self) -> u64 { + let ino = self.next_ino; + self.next_ino += 1; + ino + } +} + +//region DriveFilesystem ino_to_id +impl Filesystem { + fn get_id_from_ino(&self, ino: u64) -> Option<&DriveId> { + self.entry_ids.get_by_left(&ino) + } + fn get_ino_from_id(&mut self, id: DriveId) -> u64 { + 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 { + 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 { + let ino = self.generate_ino(); + trace!("adding new ino for drive id: {} => {}", id, ino); + self.entry_ids.insert(ino, id); + ino + } +} + +//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 + })?; + + 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 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!() + } +} diff --git a/gdriver-client/src/filesystem/attributes.rs b/gdriver-client/src/filesystem/attributes.rs new file mode 100644 index 0000000..e9495c0 --- /dev/null +++ b/gdriver-client/src/filesystem/attributes.rs @@ -0,0 +1,132 @@ +use std::collections::BTreeMap; +use std::os::raw::c_int; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use tarpc::serde::{Deserialize, Serialize}; + +type Inode = u64; +const BLOCK_SIZE: u64 = 512; + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] +enum FileKind { + File, + Directory, + Symlink, +} + +impl From for fuser::FileType { + fn from(kind: FileKind) -> Self { + match kind { + FileKind::File => fuser::FileType::RegularFile, + FileKind::Directory => fuser::FileType::Directory, + FileKind::Symlink => fuser::FileType::Symlink, + } + } +} + +#[derive(Debug)] +enum XattrNamespace { + Security, + System, + Trusted, + User, +} + +fn parse_xattr_namespace(key: &[u8]) -> Result { + let user = b"user."; + if key.len() < user.len() { + return Err(libc::ENOTSUP); + } + if key[..user.len()].eq(user) { + return Ok(XattrNamespace::User); + } + + let system = b"system."; + if key.len() < system.len() { + return Err(libc::ENOTSUP); + } + if key[..system.len()].eq(system) { + return Ok(XattrNamespace::System); + } + + let trusted = b"trusted."; + if key.len() < trusted.len() { + return Err(libc::ENOTSUP); + } + if key[..trusted.len()].eq(trusted) { + return Ok(XattrNamespace::Trusted); + } + + let security = b"security"; + if key.len() < security.len() { + return Err(libc::ENOTSUP); + } + if key[..security.len()].eq(security) { + return Ok(XattrNamespace::Security); + } + + return Err(libc::ENOTSUP); +} +fn time_now() -> (i64, u32) { + time_from_system_time(&SystemTime::now()) +} + +fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime { + if secs >= 0 { + UNIX_EPOCH + Duration::new(secs as u64, nsecs) + } else { + UNIX_EPOCH - Duration::new((-secs) as u64, nsecs) + } +} + +fn time_from_system_time(system_time: &SystemTime) -> (i64, u32) { + // Convert to signed 64-bit time with epoch at 0 + match system_time.duration_since(UNIX_EPOCH) { + Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()), + Err(before_epoch_error) => ( + -(before_epoch_error.duration().as_secs() as i64), + before_epoch_error.duration().subsec_nanos(), + ), + } +} + +#[derive(Serialize, Deserialize)] +struct InodeAttributes { + pub inode: Inode, + pub open_file_handles: u64, // Ref count of open file handles to this inode + pub size: u64, + pub last_accessed: (i64, u32), + pub last_modified: (i64, u32), + pub last_metadata_changed: (i64, u32), + pub kind: FileKind, + // Permissions and special mode bits + pub mode: u16, + pub hardlinks: u32, + pub uid: u32, + pub gid: u32, + pub xattrs: BTreeMap, Vec>, +} + +impl From for fuser::FileAttr { + fn from(attrs: InodeAttributes) -> Self { + fuser::FileAttr { + ino: attrs.inode, + size: attrs.size, + blocks: (attrs.size + BLOCK_SIZE - 1) / BLOCK_SIZE, + atime: system_time_from_time(attrs.last_accessed.0, attrs.last_accessed.1), + mtime: system_time_from_time(attrs.last_modified.0, attrs.last_modified.1), + ctime: system_time_from_time( + attrs.last_metadata_changed.0, + attrs.last_metadata_changed.1, + ), + crtime: SystemTime::UNIX_EPOCH, + kind: attrs.kind.into(), + perm: attrs.mode, + nlink: attrs.hardlinks, + uid: attrs.uid, + gid: attrs.gid, + rdev: 0, + blksize: BLOCK_SIZE as u32, + flags: 0, + } + } +} diff --git a/gdriver-client/src/filesystem/macros.rs b/gdriver-client/src/filesystem/macros.rs new file mode 100644 index 0000000..54aea3a --- /dev/null +++ b/gdriver-client/src/filesystem/macros.rs @@ -0,0 +1,77 @@ +mod reply { + #[macro_export] + macro_rules! reply_error_o { + ($option_in:expr, $reply:ident, $error_code:expr, $error_msg:expr) => { + reply_error_o!($option_in, $reply, $error_code, $error_msg,) + }; + ($option_in:expr, $reply:ident, $error_code:expr, $error_msg:expr, $($arg:tt)*) => {{ + match $option_in { + None=>{ + ::tracing::error!($error_msg, $($arg)*); + $reply.error($error_code); + return; + }, + Some(x) => x, + } + }}; + } + #[macro_export] + macro_rules! reply_error_e { + ($result_in:expr, $reply:ident, $error_code:expr, $error_msg:expr) => { + reply_error_e!($result_in, $reply, $error_code, $error_msg,) + }; + ($result:expr, $reply:ident, $error_code:expr, $error_msg:expr, $($arg:tt)*) => {{ + match $result { + Ok(x) => x, + Err(e) => { + error!("{}; e:{}",format!($error_msg, $($arg)*), e); + $reply.error($error_code); + return; + } + } + }}; + } +} + +mod send_requests { + #[macro_export] + macro_rules! send_request { + ($func:expr ) => {{ + let handle = ::tokio::runtime::Handle::current(); + let _ = handle.enter(); + futures::executor::block_on($func) + }}; + } + #[macro_export] + macro_rules! send_request_handled { + ($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 { + ($func:expr ,$reply:ident) => { + send_request_handled2!($func, $reply, "") + }; + ($func:expr ,$reply:ident, $error_msg:expr) => { + send_request_handled!( + $func, + $reply, + ::libc::EREMOTEIO, + "Failed send request: {}", + $error_msg + ) + }; + } + + #[macro_export] + macro_rules! send_request_handled2_consuming { + ($func:expr ,$reply:ident) => { + send_request_handled2_consuming!($func, $reply, ""); + }; + ($func:expr ,$reply:ident, $error_msg:expr) => { + let _ = send_request_handled2!($func, $reply, $error_msg); + }; + } +} diff --git a/gdriver-client/src/main.rs b/gdriver-client/src/main.rs index 8b5fc74..d8d9d9f 100644 --- a/gdriver-client/src/main.rs +++ b/gdriver-client/src/main.rs @@ -10,6 +10,8 @@ async fn main() -> Result<()> { service::start().await?; Ok(()) } +pub mod prelude; mod sample; +mod filesystem; mod service;