From 66493c4a2465487765fb65632b20df94c3697da9 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 16 Jan 2026 17:02:12 +0700 Subject: [PATCH 01/20] breaking-feat: passthrough API --- .vscode/settings.json | 2 +- Cargo.toml | 4 +-- src/core/fuse_driver.rs | 40 ++++++++++++++++++++++----- src/fuse_handler.rs | 11 +++++--- src/templates/default_fuse_handler.rs | 7 +++-- src/templates/mirror_fs.rs | 9 ++++-- src/types.rs | 3 +- src/types/opens.rs | 39 ++++++++++++++++++++++++++ 8 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 src/types/opens.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a34a00..51eb545 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.cargo.features": ["serial"] + "rust-analyzer.cargo.features": ["serial", "passthrough"] } diff --git a/Cargo.toml b/Cargo.toml index 158cb99..630c9db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,13 @@ serial = [] parallel = ["dep:threadpool"] async = ["dep:async-trait", "dep:tokio"] deadlock_detection = ["parallel", "dep:parking_lot"] - +passthrough = ["fuser/abi-7-40"] [dependencies] # Core dependencies log = "0.4" libc = "0.2" -fuser = "0.16" +fuser = { version = "0.16", features = ["abi-7-40"] } bitflags = "2.10" # Parallel dependencies diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs index 24a8b18..bf3115a 100644 --- a/src/core/fuse_driver.rs +++ b/src/core/fuse_driver.rs @@ -14,7 +14,7 @@ use fuser::{ }; use super::{ - fuse_driver_types::{execute_task, FuseDriver}, + fuse_driver_types::{FuseDriver, execute_task}, inode_mapping::FileIdResolver, macros::*, thread_mode::*, @@ -549,14 +549,27 @@ where let handler = self.get_handler(); let resolver = self.get_resolver(); execute_task!(self, { + let open_helper = OpenHelper::new(&reply); match handler.open( &req, resolver.resolve_id(ino), OpenFlags::from_bits_retain(_flags), + open_helper, ) { - Ok((file_handle, response_flags)) => { - reply.opened(file_handle.as_raw(), response_flags.bits()) - } + Ok((file_handle, response_flags, backing_id)) => match backing_id { + #[cfg(feature = "passthrough")] + Some(backing_id) => { + reply.opened_passthrough( + file_handle.as_raw(), + response_flags.bits(), + backing_id.as_ref(), + ); + } + _ => { + let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; + reply.opened(file_handle.as_raw(), response_flags.bits()) + } + }, Err(e) => { warn!("open: ino {:x?}, [{}], {:?}", ino, e, req); reply.error(e.raw_error()) @@ -570,14 +583,27 @@ where let handler = self.get_handler(); let resolver = self.get_resolver(); execute_task!(self, { + let open_helper = OpenHelper::new(&reply); match handler.opendir( &req, resolver.resolve_id(ino), OpenFlags::from_bits_retain(_flags), + open_helper, ) { - Ok((file_handle, response_flags)) => { - reply.opened(file_handle.as_raw(), response_flags.bits()) - } + Ok((file_handle, response_flags, backing_id)) => match backing_id { + #[cfg(feature = "passthrough")] + Some(backing_id) => { + reply.opened_passthrough( + file_handle.as_raw(), + response_flags.bits(), + backing_id.as_ref(), + ); + } + _ => { + let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; + reply.opened(file_handle.as_raw(), response_flags.bits()) + } + }, Err(e) => { warn!("opendir: ino {:x?}, [{}], {:?}", ino, e, req); reply.error(e.raw_error()) diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 5a76a9a..39db937 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -103,6 +103,7 @@ mod private { #[cfg(feature = "serial")] impl OptionalSendSync for T {} } +use fuser::BackingId; use private::OptionalSendSync; pub trait FuseHandler: OptionalSendSync + 'static { @@ -367,8 +368,9 @@ pub trait FuseHandler: OptionalSendSync + 'static { req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().open(req, file_id, flags) + helper: OpenHelper, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + self.get_inner().open(req, file_id, flags, helper) } /// Open a directory @@ -379,8 +381,9 @@ pub trait FuseHandler: OptionalSendSync + 'static { req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().opendir(req, file_id, flags) + helper: OpenHelper, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + self.get_inner().opendir(req, file_id, flags, helper) } /// Read data from a file diff --git a/src/templates/default_fuse_handler.rs b/src/templates/default_fuse_handler.rs index 164f2fb..df7b758 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/templates/default_fuse_handler.rs @@ -631,7 +631,8 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { + _helper: OpenHelper, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -654,11 +655,13 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, _file_id: TId, _flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { + _helper: OpenHelper, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { // Safe because in releasedir we don't use it Ok(( unsafe { OwnedFileHandle::from_raw(0) }, FUSEOpenResponseFlags::empty(), + None, )) } diff --git a/src/templates/mirror_fs.rs b/src/templates/mirror_fs.rs index 36fc585..d37ca74 100644 --- a/src/templates/mirror_fs.rs +++ b/src/templates/mirror_fs.rs @@ -134,12 +134,17 @@ macro_rules! mirror_fs_readonly_methods { _req: &RequestInfo, file_id: PathBuf, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { + _helper: OpenHelper, + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { let file_path = self.source_path.join(file_id); let fd = unix_fs::open(file_path.as_ref(), flags)?; // Open by definition returns positive Fd or error let file_handle = OwnedFileHandle::from_owned_fd(fd).unwrap(); - Ok((file_handle, FUSEOpenResponseFlags::empty())) + Ok((file_handle, FUSEOpenResponseFlags::empty(), None)) } fn readdir( diff --git a/src/types.rs b/src/types.rs index fb058a5..d058fcd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -23,7 +23,8 @@ pub mod file_handle; mod file_id_type; pub mod flags; mod inode; +pub mod opens; -pub use self::{arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, inode::*}; +pub use self::{arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, inode::*, opens::*}; pub use fuser::{FileType as FileKind, KernelConfig, TimeOrNow}; diff --git a/src/types/opens.rs b/src/types/opens.rs new file mode 100644 index 0000000..275fd00 --- /dev/null +++ b/src/types/opens.rs @@ -0,0 +1,39 @@ +#[cfg(feature = "passthrough")] +use fuser::BackingId; +use fuser::ReplyOpen; +#[cfg(feature = "passthrough")] +use std::sync::Arc; + +pub struct OpenHelper<'a> { + reply_open: &'a ReplyOpen, +} + +impl<'a> OpenHelper<'a> { + pub(crate) fn new(reply_open: &'a ReplyOpen) -> Self { + Self { reply_open } + } + + #[cfg(feature = "passthrough")] + pub fn open_backing( + self, + fd: impl std::os::fd::AsFd, + ) -> Result { + self.reply_open + .open_backing(fd) + .map(|id| PassthroughBackingId { + backing_id: Arc::new(id), + }) + } +} + +#[derive(Debug, Clone)] +pub struct PassthroughBackingId { + #[cfg(feature = "passthrough")] + pub(crate) backing_id: Arc, +} + +impl AsRef for PassthroughBackingId { + fn as_ref(&self) -> &BackingId { + self.backing_id.as_ref() + } +} From c1589d00b67bab769f6b7d8a02b7623649986be2 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 16 Jan 2026 17:04:44 +0700 Subject: [PATCH 02/20] fix: remove extra incompatible backing ID import --- src/fuse_handler.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 39db937..8a5392e 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -103,7 +103,6 @@ mod private { #[cfg(feature = "serial")] impl OptionalSendSync for T {} } -use fuser::BackingId; use private::OptionalSendSync; pub trait FuseHandler: OptionalSendSync + 'static { From 258611ff3597813cd1281111a5bf6a22c2a347ea Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 16 Jan 2026 17:10:17 +0700 Subject: [PATCH 03/20] fix: repair gated features --- src/types/opens.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/opens.rs b/src/types/opens.rs index 275fd00..9b09a83 100644 --- a/src/types/opens.rs +++ b/src/types/opens.rs @@ -32,6 +32,7 @@ pub struct PassthroughBackingId { pub(crate) backing_id: Arc, } +#[cfg(feature = "passthrough")] impl AsRef for PassthroughBackingId { fn as_ref(&self) -> &BackingId { self.backing_id.as_ref() From dcc4823864e09fe234a9ec9f6e6f834bc6b197fd Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 16 Jan 2026 17:18:42 +0700 Subject: [PATCH 04/20] fix: minor dead code suppressions --- Cargo.toml | 2 +- src/types/opens.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 630c9db..7b187de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ passthrough = ["fuser/abi-7-40"] # Core dependencies log = "0.4" libc = "0.2" -fuser = { version = "0.16", features = ["abi-7-40"] } +fuser = { version = "0.16" } bitflags = "2.10" # Parallel dependencies diff --git a/src/types/opens.rs b/src/types/opens.rs index 9b09a83..e893cae 100644 --- a/src/types/opens.rs +++ b/src/types/opens.rs @@ -5,6 +5,7 @@ use fuser::ReplyOpen; use std::sync::Arc; pub struct OpenHelper<'a> { + #[allow(dead_code)] reply_open: &'a ReplyOpen, } From 3ae9a8eb825f5ad2960b8cd009114801e04b54f3 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 16 Jan 2026 17:02:12 +0700 Subject: [PATCH 05/20] breaking-feat: passthrough API --- src/fuse_handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 8a5392e..39db937 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -103,6 +103,7 @@ mod private { #[cfg(feature = "serial")] impl OptionalSendSync for T {} } +use fuser::BackingId; use private::OptionalSendSync; pub trait FuseHandler: OptionalSendSync + 'static { From f52ee3733ecf9b52a645cd1c739bed962435d788 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 16 Jan 2026 17:04:44 +0700 Subject: [PATCH 06/20] fix: remove extra incompatible backing ID import --- src/fuse_handler.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 39db937..8a5392e 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -103,7 +103,6 @@ mod private { #[cfg(feature = "serial")] impl OptionalSendSync for T {} } -use fuser::BackingId; use private::OptionalSendSync; pub trait FuseHandler: OptionalSendSync + 'static { From e64706c35b7ae346432bcc436f31ab4c9b00d2fb Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Tue, 20 Jan 2026 10:21:25 +0700 Subject: [PATCH 07/20] feat: enable passthrough for file creates --- Cargo.toml | 4 + src/core/fuse_driver.rs | 37 +++- src/fuse_handler.rs | 22 ++- src/templates/default_fuse_handler.rs | 245 ++++++++++++++------------ src/templates/mirror_fs.rs | 10 +- src/types.rs | 4 +- src/types/{opens.rs => helpers.rs} | 25 ++- 7 files changed, 219 insertions(+), 128 deletions(-) rename src/types/{opens.rs => helpers.rs} (61%) diff --git a/Cargo.toml b/Cargo.toml index 7b187de..befa77f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,3 +39,7 @@ env_logger = "0.11" [package.metadata.docs.rs] features = ["parallel"] + +[patch.crates-io] +# Bleeding edge patch for FUSE create() passthrough, will be removed when patch is merged +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "passthrough" } \ No newline at end of file diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs index bf3115a..209b68c 100644 --- a/src/core/fuse_driver.rs +++ b/src/core/fuse_driver.rs @@ -131,6 +131,7 @@ where let resolver = self.get_resolver(); let name = name.to_owned(); execute_task!(self, { + let helper = CreateHelper::new(&reply); match handler.create( &req, resolver.resolve_id(parent), @@ -138,19 +139,39 @@ where mode, umask, OpenFlags::from_bits_retain(flags), + helper, ) { - Ok((file_handle, metadata, response_flags)) => { + Ok((file_handle, metadata, response_flags, passthrough_backing_id)) => { let default_ttl = handler.get_default_ttl(); let (id, file_attr) = TId::extract_metadata(metadata); let ino = resolver.lookup(parent, &name, id, true); let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); - reply.created( - &ttl.unwrap_or(default_ttl), - &fuse_attr, - generation.unwrap_or(get_random_generation()), - file_handle.as_raw(), - response_flags.bits(), - ); + match passthrough_backing_id { + #[cfg(feature = "passthrough")] + Some(passthrough_backing_id) => { + let response_flags = + response_flags | FUSEOpenResponseFlags::PASSTHROUGH; + reply.created_passthrough( + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + file_handle.as_raw(), + response_flags.bits(), + passthrough_backing_id.as_ref(), + ); + } + _ => { + let response_flags = + response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; + reply.created( + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + file_handle.as_raw(), + response_flags.bits(), + ); + } + } } Err(e) => { warn!("create: {:?}, parent_ino: {:x?}, {:?}", parent, e, req); diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 8a5392e..8331ee5 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -183,9 +183,15 @@ pub trait FuseHandler: OptionalSendSync + 'static { mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> { + helper: CreateHelper, + ) -> FuseResult<( + OwnedFileHandle, + TId::Metadata, + FUSEOpenResponseFlags, + Option, + )> { self.get_inner() - .create(req, parent_id, name, mode, umask, flags) + .create(req, parent_id, name, mode, umask, flags, helper) } /// Preallocate or deallocate space to a file @@ -368,7 +374,11 @@ pub trait FuseHandler: OptionalSendSync + 'static { file_id: TId, flags: OpenFlags, helper: OpenHelper, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { self.get_inner().open(req, file_id, flags, helper) } @@ -381,7 +391,11 @@ pub trait FuseHandler: OptionalSendSync + 'static { file_id: TId, flags: OpenFlags, helper: OpenHelper, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { self.get_inner().opendir(req, file_id, flags, helper) } diff --git a/src/templates/default_fuse_handler.rs b/src/templates/default_fuse_handler.rs index df7b758..25ba51e 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/templates/default_fuse_handler.rs @@ -155,8 +155,9 @@ impl FuseHandler for DefaultFuseHandler { flags: u32, // Not implemented yet in standard ) -> FuseResult { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( "copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {})", file_in.display(), @@ -170,8 +171,8 @@ impl FuseHandler for DefaultFuseHandler { ) } else { String::new() - }) - ), + }, + )), HandlingMethod::Panic => panic!( "[Not Implemented] copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {})", file_in.display(), @@ -194,7 +195,13 @@ impl FuseHandler for DefaultFuseHandler { mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> { + _helper: CreateHelper, + ) -> FuseResult<( + OwnedFileHandle, + TId::Metadata, + FUSEOpenResponseFlags, + Option, + )> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -232,8 +239,9 @@ impl FuseHandler for DefaultFuseHandler { mode: FallocateFlags, ) -> FuseResult<()> { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( "fallocate(file_id: {}, file_handle: {:?}, offset: {}, length: {}, mode: {:?})", file_id.display(), @@ -244,8 +252,8 @@ impl FuseHandler for DefaultFuseHandler { ) } else { String::new() - }) - ), + }, + )), HandlingMethod::Panic => panic!( "[Not Implemented] fallocate(file_id: {}, file_handle: {:?}, offset: {}, length: {}, mode: {:?})", file_id.display(), @@ -365,20 +373,21 @@ impl FuseHandler for DefaultFuseHandler { lock_info: LockInfo, ) -> FuseResult { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", - file_id.display(), - file_handle, - lock_owner, - lock_info - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", + file_id.display(), + file_handle, + lock_owner, + lock_info + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", file_id.display(), file_handle, @@ -601,21 +610,22 @@ impl FuseHandler for DefaultFuseHandler { rdev: DeviceType, ) -> FuseResult { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "mknod(parent_id: {}, name: {:?}, mode: {}, umask: {}, rdev: {:?})", - parent_id.display(), - Path::new(name), - mode, - umask, - rdev - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "mknod(parent_id: {}, name: {:?}, mode: {}, umask: {}, rdev: {:?})", + parent_id.display(), + Path::new(name), + mode, + umask, + rdev + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] mknod(parent_id: {}, name: {:?}, mode: {}, umask: {}, rdev: {:?})", parent_id.display(), Path::new(name), @@ -632,7 +642,11 @@ impl FuseHandler for DefaultFuseHandler { file_id: TId, flags: OpenFlags, _helper: OpenHelper, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -656,7 +670,11 @@ impl FuseHandler for DefaultFuseHandler { _file_id: TId, _flags: OpenFlags, _helper: OpenHelper, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { // Safe because in releasedir we don't use it Ok(( unsafe { OwnedFileHandle::from_raw(0) }, @@ -676,22 +694,23 @@ impl FuseHandler for DefaultFuseHandler { lock_owner: Option, ) -> FuseResult> { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, flags: {:?}, lock_owner: {:?})", - file_id.display(), - file_handle, - seek, - size, - flags, - lock_owner - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, flags: {:?}, lock_owner: {:?})", + file_id.display(), + file_handle, + seek, + size, + flags, + lock_owner + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, flags: {:?}, lock_owner: {:?})", file_id.display(), file_handle, @@ -783,21 +802,22 @@ impl FuseHandler for DefaultFuseHandler { flush: bool, ) -> FuseResult<()> { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "release(file_id: {}, file_handle: {:?}, flags: {:?}, lock_owner: {:?}, flush: {})", - file_id.display(), - file_handle, - flags, - lock_owner, - flush - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "release(file_id: {}, file_handle: {:?}, flags: {:?}, lock_owner: {:?}, flush: {})", + file_id.display(), + file_handle, + flags, + lock_owner, + flush + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] release(file_id: {}, file_handle: {:?}, flags: {:?}, lock_owner: {:?}, flush: {})", file_id.display(), file_handle, @@ -850,21 +870,22 @@ impl FuseHandler for DefaultFuseHandler { flags: RenameFlags, ) -> FuseResult<()> { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "rename(parent_id: {}, name: {:?}, newparent: {}, newname: {:?}, flags: {:?})", - parent_id.display(), - Path::new(name), - newparent.display(), - Path::new(newname), - flags - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "rename(parent_id: {}, name: {:?}, newparent: {}, newname: {:?}, flags: {:?})", + parent_id.display(), + Path::new(name), + newparent.display(), + Path::new(newname), + flags + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] rename(parent_id: {}, name: {:?}, newparent: {}, newname: {:?}, flags: {:?})", parent_id.display(), Path::new(name), @@ -934,21 +955,22 @@ impl FuseHandler for DefaultFuseHandler { sleep: bool, ) -> FuseResult<()> { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", - file_id.display(), - file_handle, - lock_owner, - lock_info, - sleep - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", + file_id.display(), + file_handle, + lock_owner, + lock_info, + sleep + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", file_id.display(), file_handle, @@ -1061,23 +1083,24 @@ impl FuseHandler for DefaultFuseHandler { lock_owner: Option, ) -> FuseResult { match self.handling { - HandlingMethod::Error(kind) => Err( - PosixError::new(kind, if cfg!(debug_assertions) { + HandlingMethod::Error(kind) => Err(PosixError::new( + kind, + if cfg!(debug_assertions) { format!( - "write(file_id: {}, file_handle: {:?}, seek: {:?}, data_len: {}, write_flags: {:?}, flags: {:?}, lock_owner: {:?})", - file_id.display(), - file_handle, - seek, - data.len(), - write_flags, - flags, - lock_owner - ) - } else { - String::new() - }) - ), - HandlingMethod::Panic => panic!( + "write(file_id: {}, file_handle: {:?}, seek: {:?}, data_len: {}, write_flags: {:?}, flags: {:?}, lock_owner: {:?})", + file_id.display(), + file_handle, + seek, + data.len(), + write_flags, + flags, + lock_owner + ) + } else { + String::new() + }, + )), + HandlingMethod::Panic => panic!( "[Not Implemented] write(file_id: {}, file_handle: {:?}, seek: {:?}, data_len: {}, write_flags: {:?}, flags: {:?}, lock_owner: {:?})", file_id.display(), file_handle, diff --git a/src/templates/mirror_fs.rs b/src/templates/mirror_fs.rs index d37ca74..d392628 100644 --- a/src/templates/mirror_fs.rs +++ b/src/templates/mirror_fs.rs @@ -186,12 +186,18 @@ macro_rules! mirror_fs_readwrite_methods { mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FileAttribute, FUSEOpenResponseFlags)> { + _helper: CreateHelper, + ) -> FuseResult<( + OwnedFileHandle, + FileAttribute, + FUSEOpenResponseFlags, + Option, + )> { let file_path = self.source_path.join(parent_id).join(name); let (fd, file_attr) = unix_fs::create(&file_path, mode, umask, flags)?; // Open by definition returns positive Fd or error let file_handle = OwnedFileHandle::from_owned_fd(fd).unwrap(); - Ok((file_handle, file_attr, FUSEOpenResponseFlags::empty())) + Ok((file_handle, file_attr, FUSEOpenResponseFlags::empty(), None)) } fn mkdir( diff --git a/src/types.rs b/src/types.rs index d058fcd..e1502b1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -23,8 +23,8 @@ pub mod file_handle; mod file_id_type; pub mod flags; mod inode; -pub mod opens; +pub mod helpers; -pub use self::{arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, inode::*, opens::*}; +pub use self::{arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, inode::*, helpers::*}; pub use fuser::{FileType as FileKind, KernelConfig, TimeOrNow}; diff --git a/src/types/opens.rs b/src/types/helpers.rs similarity index 61% rename from src/types/opens.rs rename to src/types/helpers.rs index e893cae..e16cdd3 100644 --- a/src/types/opens.rs +++ b/src/types/helpers.rs @@ -1,6 +1,6 @@ #[cfg(feature = "passthrough")] use fuser::BackingId; -use fuser::ReplyOpen; +use fuser::{ReplyCreate, ReplyOpen}; #[cfg(feature = "passthrough")] use std::sync::Arc; @@ -9,6 +9,29 @@ pub struct OpenHelper<'a> { reply_open: &'a ReplyOpen, } +pub struct CreateHelper<'a> { + #[allow(dead_code)] + reply_create: &'a ReplyCreate, +} + +impl<'a> CreateHelper<'a> { + pub(crate) fn new(reply_create: &'a ReplyCreate) -> Self { + Self { reply_create } + } + + #[cfg(feature = "passthrough")] + pub fn open_backing( + self, + fd: impl std::os::fd::AsFd, + ) -> Result { + self.reply_create + .open_backing(fd) + .map(|id| PassthroughBackingId { + backing_id: Arc::new(id), + }) + } +} + impl<'a> OpenHelper<'a> { pub(crate) fn new(reply_open: &'a ReplyOpen) -> Self { Self { reply_open } From 2702e87becc128b4fc2a9ce078f3380a0d30474f Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Tue, 20 Jan 2026 14:53:46 +0700 Subject: [PATCH 08/20] feat: add stub weak pointer for parity with weak api --- src/types/helpers.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/types/helpers.rs b/src/types/helpers.rs index e16cdd3..72d3774 100644 --- a/src/types/helpers.rs +++ b/src/types/helpers.rs @@ -62,3 +62,36 @@ impl AsRef for PassthroughBackingId { self.backing_id.as_ref() } } + +impl PassthroughBackingId { + #[cfg(feature = "passthrough")] + pub fn downgrade(&self) -> WeakPassthroughBackingId { + WeakPassthroughBackingId { + backing_id: Arc::downgrade(&self.backing_id), + } + } +} + +#[derive(Debug, Clone)] +pub struct WeakPassthroughBackingId { + #[cfg(feature = "passthrough")] + pub(crate) backing_id: Weak, +} + +impl WeakPassthroughBackingId { + #[cfg(feature = "passthrough")] + pub fn new() -> Self { + Self { + backing_id: Weak::new(), + } + } + + #[cfg(feature = "passthrough")] + pub fn upgrade(&self) -> Option { + self.backing_id + .upgrade() + .map(|backing_id| PassthroughBackingId { + backing_id: backing_id, + }) + } +} From 980ab40a06e41e03f6cf9b81c96b2df315dec09f Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Wed, 21 Jan 2026 10:10:53 +0700 Subject: [PATCH 09/20] fix: add API parity --- Cargo.toml | 2 +- src/core/fuse_driver.rs | 382 ++++++++++++++------------ src/core/fuse_driver_types.rs | 3 +- src/core/inode_mapping.rs | 94 ++++--- src/core/macros.rs | 17 +- src/fuse_handler.rs | 32 ++- src/inode_mapper.rs | 20 +- src/templates/default_fuse_handler.rs | 41 +-- src/templates/fd_handler_helper.rs | 13 +- src/types.rs | 10 +- src/types/arguments.rs | 19 +- src/types/errors.rs | 8 + src/types/file_handle.rs | 24 ++ src/types/flags.rs | 105 ++++++- src/types/helpers.rs | 2 +- src/types/inode.rs | 25 +- 16 files changed, 496 insertions(+), 301 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index befa77f..dbeddc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ serial = [] parallel = ["dep:threadpool"] async = ["dep:async-trait", "dep:tokio"] deadlock_detection = ["parallel", "dep:parking_lot"] -passthrough = ["fuser/abi-7-40"] +passthrough = [] [dependencies] # Core dependencies diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs index 209b68c..daffa0d 100644 --- a/src/core/fuse_driver.rs +++ b/src/core/fuse_driver.rs @@ -1,16 +1,15 @@ +use log::{info, warn}; use std::{ ffi::OsStr, path::Path, time::{Instant, SystemTime}, }; -use libc::c_int; -use log::{error, info, warn}; - use fuser::{ - self, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, - ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, - ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, + self, AccessFlags, Errno, FileHandle, FopenFlags, Generation, INodeNo, IoctlFlags, + KernelConfig, LockOwner, ReadFlags, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, + ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, + ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, WriteFlags, }; use super::{ @@ -21,8 +20,8 @@ use super::{ }; use crate::{fuse_handler::FuseHandler, types::*}; -fn get_random_generation() -> u64 { - Instant::now().elapsed().as_nanos() as u64 +fn get_random_generation() -> Generation { + Generation(Instant::now().elapsed().as_nanos() as u64) } impl fuser::Filesystem for FuseDriver @@ -30,13 +29,13 @@ where TId: FileIdType, THandler: FuseHandler, { - fn init(&mut self, req: &Request, config: &mut KernelConfig) -> Result<(), c_int> { + fn init(&mut self, req: &Request, config: &mut KernelConfig) -> Result<(), Errno> { let req = RequestInfo::from(req); match self.get_handler().init(&req, config) { Ok(()) => Ok(()), Err(e) => { warn!("[{}] init {:?}", e, req); - Err(e.raw_error()) + Err(e.into()) } } } @@ -45,26 +44,24 @@ where self.get_handler().destroy(); } - fn access(&mut self, req: &Request, ino: u64, mask: i32, reply: ReplyEmpty) { + fn access(&self, req: &Request, ino: INodeNo, mask: AccessFlags, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); execute_task!(self, { - match handler.access( - &req, - resolver.resolve_id(ino), - AccessMask::from_bits_retain(mask), - ) { - Ok(()) => reply.ok(), + match handler.access(&req, resolver.resolve_id(ino), AccessMask::from(mask)) { + Ok(()) => { + reply.ok(); + } Err(e) => { warn!("access: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()); } }; }); } - fn bmap(&mut self, req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { + fn bmap(&self, req: &Request, ino: INodeNo, blocksize: u32, idx: u64, reply: ReplyBmap) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -73,23 +70,23 @@ where Ok(block) => reply.bmap(block), Err(e) => { warn!("bmap: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn copy_file_range( - &mut self, + &self, req: &Request, - ino_in: u64, - fh_in: u64, + ino_in: INodeNo, + fh_in: FileHandle, offset_in: i64, - ino_out: u64, - fh_out: u64, + ino_out: INodeNo, + fh_out: FileHandle, offset_out: i64, len: u64, - flags: u32, + flags: fuser::CopyFileRangeFlags, reply: ReplyWrite, ) { let req = RequestInfo::from(req); @@ -99,27 +96,27 @@ where match handler.copy_file_range( &req, resolver.resolve_id(ino_in), - unsafe { BorrowedFileHandle::from_raw(fh_in) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh_in) }, offset_in, resolver.resolve_id(ino_out), - unsafe { BorrowedFileHandle::from_raw(fh_out) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh_out) }, offset_out, len, - flags, + CopyFileRangeFlags::from(flags), ) { Ok(bytes_written) => reply.written(bytes_written), Err(e) => { warn!("copy_file_range: ino {:x?}, [{}], {:?}", ino_in, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn create( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, mode: u32, umask: u32, @@ -155,8 +152,8 @@ where &ttl.unwrap_or(default_ttl), &fuse_attr, generation.unwrap_or(get_random_generation()), - file_handle.as_raw(), - response_flags.bits(), + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), passthrough_backing_id.as_ref(), ); } @@ -167,25 +164,25 @@ where &ttl.unwrap_or(default_ttl), &fuse_attr, generation.unwrap_or(get_random_generation()), - file_handle.as_raw(), - response_flags.bits(), + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), ); } } } Err(e) => { warn!("create: {:?}, parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn fallocate( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, + ino: INodeNo, + fh: FileHandle, offset: i64, length: i64, mode: i32, @@ -198,7 +195,7 @@ where match handler.fallocate( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, offset, length, FallocateFlags::from_bits_retain(mode), @@ -206,13 +203,20 @@ where Ok(()) => reply.ok(), Err(e) => { warn!("fallocate: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn flush(&mut self, req: &Request, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { + fn flush( + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + lock_owner: LockOwner, + reply: ReplyEmpty, + ) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -220,19 +224,19 @@ where match handler.flush( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, lock_owner, ) { Ok(()) => reply.ok(), Err(e) => { warn!("flush: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn forget(&mut self, req: &Request, ino: u64, nlookup: u64) { + fn forget(&self, req: &Request, ino: INodeNo, nlookup: u64) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -240,7 +244,14 @@ where resolver.forget(ino, nlookup); } - fn fsync(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + fn fsync( + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + datasync: bool, + reply: ReplyEmpty, + ) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -248,19 +259,26 @@ where match handler.fsync( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, datasync, ) { Ok(()) => reply.ok(), Err(e) => { warn!("fsync: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn fsyncdir(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + fn fsyncdir( + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + datasync: bool, + reply: ReplyEmpty, + ) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -268,19 +286,19 @@ where match handler.fsyncdir( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, datasync, ) { Ok(()) => reply.ok(), Err(e) => { warn!("fsyncdir: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn getattr(&mut self, req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { + fn getattr(&self, req: &Request, ino: INodeNo, fh: Option, reply: ReplyAttr) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -295,18 +313,18 @@ where ( &req, resolver.resolve_id(ino), - fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }) + fh.map(|fh| unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }) ) ); }); } fn getlk( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + lock_owner: LockOwner, start: u64, end: u64, typ: i32, @@ -326,7 +344,7 @@ where match handler.getlk( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, lock_owner, lock_info, ) { @@ -338,13 +356,13 @@ where ), Err(e) => { warn!("getlk: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn getxattr(&mut self, req: &Request, ino: u64, name: &OsStr, size: u32, reply: ReplyXattr) { + fn getxattr(&self, req: &Request, ino: INodeNo, name: &OsStr, size: u32, reply: ReplyXattr) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -357,23 +375,29 @@ where } else if size >= xattr_data.len() as u32 { reply.data(&xattr_data); } else { - reply.error(ErrorKind::ResultTooLarge.into()); + reply.error( + PosixError::new( + ErrorKind::ResultTooLarge, + "returned result is too large", + ) + .into(), + ); } } Err(e) => { warn!("getxattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn ioctl( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - flags: u32, + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + flags: IoctlFlags, cmd: u32, in_data: &[u8], out_size: u32, @@ -387,8 +411,8 @@ where match handler.ioctl( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - IOCtlFlags::from_bits_retain(flags), + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, + FUSEIoctlFlags::from(flags), cmd, in_data, out_size, @@ -396,17 +420,17 @@ where Ok((result, data)) => reply.ioctl(result, &data), Err(e) => { warn!("ioctl: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn link( - &mut self, + &self, req: &Request, - ino: u64, - newparent: u64, + ino: INodeNo, + newparent: INodeNo, newname: &OsStr, reply: ReplyEntry, ) { @@ -433,7 +457,7 @@ where }); } - fn listxattr(&mut self, req: &Request, ino: u64, size: u32, reply: ReplyXattr) { + fn listxattr(&self, req: &Request, ino: INodeNo, size: u32, reply: ReplyXattr) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -445,18 +469,24 @@ where } else if size >= xattr_data.len() as u32 { reply.data(&xattr_data); } else { - reply.error(ErrorKind::ResultTooLarge.into()); + reply.error( + PosixError::new( + ErrorKind::ResultTooLarge, + "returned result is too large than allowed size", + ) + .into(), + ); } } Err(e) => { warn!("listxattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn lookup(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEntry) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -476,10 +506,10 @@ where } fn lseek( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, + ino: INodeNo, + fh: FileHandle, offset: i64, whence: i32, reply: ReplyLseek, @@ -491,22 +521,22 @@ where match handler.lseek( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, seek_from_raw(Some(whence), offset), ) { Ok(new_offset) => reply.offset(new_offset), Err(e) => { warn!("lseek: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn mkdir( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, mode: u32, umask: u32, @@ -531,9 +561,9 @@ where } fn mknod( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, mode: u32, umask: u32, @@ -565,7 +595,7 @@ where }); } - fn open(&mut self, req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&self, req: &Request, ino: INodeNo, flags: fuser::OpenFlags, reply: ReplyOpen) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -574,32 +604,32 @@ where match handler.open( &req, resolver.resolve_id(ino), - OpenFlags::from_bits_retain(_flags), + OpenFlags::from(flags), open_helper, ) { Ok((file_handle, response_flags, backing_id)) => match backing_id { #[cfg(feature = "passthrough")] Some(backing_id) => { reply.opened_passthrough( - file_handle.as_raw(), - response_flags.bits(), + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), backing_id.as_ref(), ); } _ => { let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; - reply.opened(file_handle.as_raw(), response_flags.bits()) + reply.opened(file_handle.as_fuser_file_handle(), FopenFlags::from(response_flags)) } }, Err(e) => { warn!("open: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn opendir(&mut self, req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { + fn opendir(&self, req: &Request, ino: INodeNo, flags: fuser::OpenFlags, reply: ReplyOpen) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -608,40 +638,44 @@ where match handler.opendir( &req, resolver.resolve_id(ino), - OpenFlags::from_bits_retain(_flags), + OpenFlags::from(flags), open_helper, ) { Ok((file_handle, response_flags, backing_id)) => match backing_id { #[cfg(feature = "passthrough")] Some(backing_id) => { reply.opened_passthrough( - file_handle.as_raw(), - response_flags.bits(), + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), backing_id.as_ref(), ); } _ => { let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; - reply.opened(file_handle.as_raw(), response_flags.bits()) + reply.opened( + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + ) } }, Err(e) => { warn!("opendir: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn read( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, size: u32, - flags: i32, - lock_owner: Option, + read_flags: ReadFlags, + flags: u32, + lock_owner: Option, reply: ReplyData, ) { let req = RequestInfo::from(req); @@ -651,27 +685,28 @@ where match handler.read( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - seek_from_raw(None, offset), + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, + SeekFrom::Start(offset), size, - FUSEOpenFlags::from_bits_retain(flags), + FUSEReadFlags::from(read_flags), + OpenFlags::from_bits_retain(flags as i32), lock_owner, ) { Ok(data_reply) => reply.data(&data_reply), Err(e) => { warn!("read: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn readdir( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, mut reply: ReplyDirectory, ) { handle_dir_read!( @@ -688,11 +723,11 @@ where } fn readdirplus( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, mut reply: ReplyDirectoryPlus, ) { handle_dir_read!( @@ -708,7 +743,7 @@ where ); } - fn readlink(&mut self, req: &Request, ino: u64, reply: ReplyData) { + fn readlink(&self, req: &Request, ino: INodeNo, reply: ReplyData) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -717,19 +752,19 @@ where Ok(link) => reply.data(&link), Err(e) => { warn!("[{}] readlink, ino: {:x?}, {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn release( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - _flags: i32, - _lock_owner: Option, + ino: INodeNo, + fh: FileHandle, + flags: i32, + lock_owner: Option, _flush: bool, reply: ReplyEmpty, ) { @@ -740,21 +775,28 @@ where match handler.release( &req, resolver.resolve_id(ino), - unsafe { OwnedFileHandle::from_raw(fh) }, - OpenFlags::from_bits_retain(_flags), - _lock_owner, + unsafe { OwnedFileHandle::from_fuser_file_handle(fh) }, + OpenFlags::from_bits_retain(flags), + lock_owner, _flush, ) { Ok(()) => reply.ok(), Err(e) => { warn!("release: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn releasedir(&mut self, req: &Request, ino: u64, fh: u64, flags: i32, reply: ReplyEmpty) { + fn releasedir( + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + flags: i32, + reply: ReplyEmpty, + ) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -762,19 +804,19 @@ where match handler.releasedir( &req, resolver.resolve_id(ino), - unsafe { OwnedFileHandle::from_raw(fh) }, + unsafe { OwnedFileHandle::from_fuser_file_handle(fh) }, OpenFlags::from_bits_retain(flags), ) { Ok(()) => reply.ok(), Err(e) => { warn!("releasedir: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn removexattr(&mut self, req: &Request, ino: u64, name: &OsStr, reply: ReplyEmpty) { + fn removexattr(&self, req: &Request, ino: INodeNo, name: &OsStr, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -784,20 +826,20 @@ where Ok(()) => reply.ok(), Err(e) => { warn!("removexattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn rename( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, - newparent: u64, + newparent: INodeNo, newname: &OsStr, - flags: u32, + flags: fuser::RenameFlags, reply: ReplyEmpty, ) { let req = RequestInfo::from(req); @@ -812,7 +854,7 @@ where &name, resolver.resolve_id(newparent), &newname, - RenameFlags::from_bits_retain(flags), + RenameFlags::from(flags), ) { Ok(()) => { resolver.rename(parent, &name, newparent, &newname); @@ -820,13 +862,13 @@ where } Err(e) => { warn!("[{}] rename: parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } } }); } - fn rmdir(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn rmdir(&self, req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -836,16 +878,16 @@ where Ok(()) => reply.ok(), Err(e) => { warn!("[{}] rmdir: parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn setattr( - &mut self, + &self, req: &Request, - ino: u64, + ino: INodeNo, mode: Option, uid: Option, gid: Option, @@ -853,7 +895,7 @@ where atime: Option, mtime: Option, ctime: Option, - fh: Option, + fh: Option, crtime: Option, chgtime: Option, bkuptime: Option, @@ -875,7 +917,7 @@ where chgtime: chgtime, bkuptime: bkuptime, flags: None, - file_handle: fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }), + file_handle: fh.map(|fh| unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }), }; execute_task!(self, { handle_fuse_reply_attr!( @@ -891,11 +933,11 @@ where } fn setlk( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + lock_owner: LockOwner, start: u64, end: u64, typ: i32, @@ -916,7 +958,7 @@ where match handler.setlk( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, lock_owner, lock_info, sleep, @@ -924,16 +966,16 @@ where Ok(()) => reply.ok(), Err(e) => { warn!("setlk: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } fn setxattr( - &mut self, + &self, req: &Request, - ino: u64, + ino: INodeNo, name: &OsStr, value: &[u8], flags: i32, @@ -957,13 +999,13 @@ where Ok(()) => reply.ok(), Err(e) => { warn!("setxattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn statfs(&mut self, req: &Request, ino: u64, reply: ReplyStatfs) { + fn statfs(&self, req: &Request, ino: INodeNo, reply: ReplyStatfs) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -981,16 +1023,16 @@ where ), Err(e) => { warn!("statfs: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()); } }; }); } fn symlink( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, link_name: &OsStr, target: &Path, reply: ReplyEntry, @@ -1015,15 +1057,15 @@ where } fn write( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, + ino: INodeNo, + fh: FileHandle, offset: i64, data: &[u8], - write_flags: u32, + write_flags: WriteFlags, flags: i32, - lock_owner: Option, + lock_owner: Option, reply: ReplyWrite, ) { let req = RequestInfo::from(req); @@ -1034,23 +1076,23 @@ where match handler.write( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, seek_from_raw(None, offset), data, - FUSEWriteFlags::from_bits_retain(write_flags), + FUSEWriteFlags::from(write_flags), OpenFlags::from_bits_retain(flags), lock_owner, ) { Ok(bytes_written) => reply.written(bytes_written), Err(e) => { warn!("write: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); } - fn unlink(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn unlink(&self, req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.get_resolver(); @@ -1060,7 +1102,7 @@ where Ok(()) => reply.ok(), Err(e) => { warn!("[{}] unlink: parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) + reply.error(e.into()) } }; }); diff --git a/src/core/fuse_driver_types.rs b/src/core/fuse_driver_types.rs index d550d7e..f0640f0 100644 --- a/src/core/fuse_driver_types.rs +++ b/src/core/fuse_driver_types.rs @@ -9,7 +9,7 @@ use super::inode_mapping::FileIdResolver; use crate::fuse_handler::FuseHandler; use crate::types::*; -type DirIter = HashMap<(u64, i64), VecDeque<(OsString, u64, TAttr)>>; +type DirIter = HashMap<(INodeNo, u64), VecDeque<(OsString, INodeNo, TAttr)>>; #[cfg(feature = "serial")] mod serial { @@ -226,6 +226,7 @@ fn spawn_deadlock_checker() { }); } +use fuser::INodeNo; #[cfg(feature = "serial")] pub use serial::*; diff --git a/src/core/inode_mapping.rs b/src/core/inode_mapping.rs index b7c697e..8ec7187 100644 --- a/src/core/inode_mapping.rs +++ b/src/core/inode_mapping.rs @@ -4,10 +4,11 @@ use std::{ sync::atomic::Ordering, }; -use std::sync::{atomic::AtomicU64, RwLock}; +use std::sync::{RwLock, atomic::AtomicU64}; -use crate::inode_mapper::*; -use crate::types::*; +use crate::inode_mapper::InodeMapper; +use crate::{inode_mapper, types::*}; +use fuser::INodeNo; pub(crate) const ROOT_INO: u64 = 1; @@ -48,22 +49,22 @@ pub trait FileIdResolver: Send + Sync + 'static { type ResolvedType: FileIdType; fn new() -> Self; - fn resolve_id(&self, ino: u64) -> Self::ResolvedType; + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType; fn lookup( &self, - parent: u64, + parent: INodeNo, child: &OsStr, id: ::_Id, increment: bool, - ) -> u64; + ) -> INodeNo; fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ::_Id)>, increment: bool, - ) -> Vec<(OsString, u64)>; - fn forget(&self, ino: u64, nlookup: u64); - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr); + ) -> Vec<(OsString, INodeNo)>; + fn forget(&self, ino: INodeNo, nlookup: u64); + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr); } pub struct InodeResolver {} @@ -75,30 +76,30 @@ impl FileIdResolver for InodeResolver { Self {} } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { Inode::from(ino) } - fn lookup(&self, _parent: u64, _child: &OsStr, id: Inode, _increment: bool) -> u64 { + fn lookup(&self, _parent: INodeNo, _child: &OsStr, id: Inode, _increment: bool) -> INodeNo { id.into() } // Do nothing, user should provide its own inode fn add_children( &self, - _parent: u64, + _parent: INodeNo, children: Vec<(OsString, Inode)>, _increment: bool, - ) -> Vec<(OsString, u64)> { + ) -> Vec<(OsString, INodeNo)> { children .into_iter() - .map(|(name, inode)| (name, u64::from(inode))) + .map(|(name, inode)| (name, INodeNo::from(inode))) .collect() } - fn forget(&self, _ino: u64, _nlookup: u64) {} + fn forget(&self, _ino: INodeNo, _nlookup: u64) {} - fn rename(&self, _parent: u64, _name: &OsStr, _newparent: u64, _newname: &OsStr) {} + fn rename(&self, _parent: INodeNo, _name: &OsStr, _newparent: INodeNo, _newname: &OsStr) {} } pub struct ComponentsResolver { @@ -114,7 +115,7 @@ impl FileIdResolver for ComponentsResolver { } } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { self.mapper .read() .unwrap() @@ -125,7 +126,7 @@ impl FileIdResolver for ComponentsResolver { .collect() } - fn lookup(&self, parent: u64, child: &OsStr, _id: (), increment: bool) -> u64 { + fn lookup(&self, parent: INodeNo, child: &OsStr, _id: (), increment: bool) -> INodeNo { let parent = Inode::from(parent); { // Optimistically assume the child exists @@ -133,10 +134,10 @@ impl FileIdResolver for ComponentsResolver { if increment { lookup_result.data.fetch_add(1, Ordering::SeqCst); } - return u64::from(lookup_result.inode.clone()); + return INodeNo::from(lookup_result.inode.clone()); } } - u64::from( + INodeNo::from( self.mapper .write() .expect("Failed to acquire write lock") @@ -149,27 +150,24 @@ impl FileIdResolver for ComponentsResolver { fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ())>, increment: bool, - ) -> Vec<(OsString, u64)> { + ) -> Vec<(OsString, INodeNo)> { + let value_creator = + |value_creator: inode_mapper::ValueCreatorParams| match value_creator + .existing_data + { + Some(nlookup) => { + let count = nlookup.load(Ordering::Relaxed); + AtomicU64::new(if increment { count + 1 } else { count }) + } + None => AtomicU64::new(if increment { 1 } else { 0 }), + }; let children_with_creator: Vec<_> = children .iter() - .map(|(name, _)| { - ( - name.clone(), - |value_creator: ValueCreatorParams| match value_creator.existing_data - { - Some(nlookup) => { - let count = nlookup.load(Ordering::Relaxed); - AtomicU64::new(if increment { count + 1 } else { count }) - } - None => AtomicU64::new(if increment { 1 } else { 0 }), - }, - ) - }) + .map(|(name, _)| (name.clone(), value_creator)) .collect(); - let parent_inode = Inode::from(parent); let inserted_children = self .mapper @@ -181,11 +179,11 @@ impl FileIdResolver for ComponentsResolver { inserted_children .into_iter() .zip(children) - .map(|(inode, (name, _))| (name, u64::from(inode))) + .map(|(inode, (name, _))| (name, INodeNo::from(inode))) .collect() } - fn forget(&self, ino: u64, nlookup: u64) { + fn forget(&self, ino: INodeNo, nlookup: u64) { let inode = Inode::from(ino); { // Optimistically assume we don't have to remove yet @@ -198,7 +196,7 @@ impl FileIdResolver for ComponentsResolver { self.mapper.write().unwrap().remove(&inode).unwrap(); } - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr) { + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr) { let parent_inode = Inode::from(parent); let newparent_inode = Inode::from(newparent); self.mapper @@ -227,7 +225,7 @@ impl FileIdResolver for PathResolver { } } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { self.resolver .resolve_id(ino) .iter() @@ -237,28 +235,28 @@ impl FileIdResolver for PathResolver { fn lookup( &self, - parent: u64, + parent: INodeNo, child: &OsStr, id: ::_Id, increment: bool, - ) -> u64 { + ) -> INodeNo { self.resolver.lookup(parent, child, id, increment) } fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ::_Id)>, increment: bool, - ) -> Vec<(OsString, u64)> { + ) -> Vec<(OsString, INodeNo)> { self.resolver.add_children(parent, children, increment) } - fn forget(&self, ino: u64, nlookup: u64) { + fn forget(&self, ino: INodeNo, nlookup: u64) { self.resolver.forget(ino, nlookup); } - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr) { + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr) { self.resolver.rename(parent, name, newparent, newname); } } @@ -370,7 +368,7 @@ mod tests { // Test lookup for non-existent file let non_existent_ino = resolver.lookup(root_ino, OsStr::new("non_existent"), (), false); - assert_ne!(non_existent_ino, 0); + assert_ne!(non_existent_ino, INodeNo(0)); let non_existent_path = resolver.resolve_id(non_existent_ino); assert_eq!(non_existent_path, PathBuf::from("non_existent")); } diff --git a/src/core/macros.rs b/src/core/macros.rs index 6f4fd09..7911d72 100644 --- a/src/core/macros.rs +++ b/src/core/macros.rs @@ -35,7 +35,7 @@ macro_rules! handle_fuse_reply_entry { }, { warn!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req); }); - $reply.error(e.raw_error()) + $reply.error(e.into()) } } }; @@ -52,7 +52,7 @@ macro_rules! handle_fuse_reply_attr { } Err(e) => { warn!("{}: ino {:x?}, [{}], {:?}", stringify!($function), $ino, e, $req); - $reply.error(e.raw_error()) + $reply.error(e.into()); } } }; @@ -99,18 +99,11 @@ macro_rules! handle_dir_read { let dirmap_iter = $self.$get_iter_method(); execute_task!($self, { - // Validate offset - if $offset < 0 { - error!("readdir called with a negative offset"); - $reply.error(ErrorKind::InvalidArgument.into()); - return; - } - // ### Initialize directory iterator let mut dir_iter = match $offset { // First read: fetch children from handler 0 => match handler.$handler_method(&req_info, resolver.resolve_id($ino), unsafe { - BorrowedFileHandle::from_raw($fh) + BorrowedFileHandle::from_fuser_file_handle($fh) }) { Ok(children) => { // Unpack and process children @@ -142,7 +135,7 @@ macro_rules! handle_dir_read { } Err(e) => { warn!("readdir {:?}: {:?}", req_info, e); - $reply.error(e.raw_error()); + $reply.error(e.into()); return; } }, @@ -192,7 +185,7 @@ macro_rules! handle_dir_read { dir_iter.push_front((name, ino, file_attr.clone())); dirmap_iter .safe_borrow_mut() - .insert((ino, new_offset - 1), dir_iter); + .insert(($ino, new_offset - 1), dir_iter); break; } new_offset += 1; diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 8331ee5..8673e51 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -103,6 +103,7 @@ mod private { #[cfg(feature = "serial")] impl OptionalSendSync for T {} } +use fuser::LockOwner; use private::OptionalSendSync; pub trait FuseHandler: OptionalSendSync + 'static { @@ -154,7 +155,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, - flags: u32, // Not implemented yet in standard + flags: CopyFileRangeFlags, // Not implemented yet in standard ) -> FuseResult { self.get_inner().copy_file_range( req, @@ -217,7 +218,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, ) -> FuseResult<()> { self.get_inner() .flush(req, file_id, file_handle, lock_owner) @@ -274,7 +275,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, ) -> FuseResult { self.get_inner() @@ -298,7 +299,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - flags: IOCtlFlags, + flags: FUSEIoctlFlags, cmd: u32, in_data: Vec, out_size: u32, @@ -411,11 +412,20 @@ pub trait FuseHandler: OptionalSendSync + 'static { file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - flags: FUSEOpenFlags, - lock_owner: Option, + read_flags: FUSEReadFlags, + flags: OpenFlags, + lock_owner: Option, ) -> FuseResult> { - self.get_inner() - .read(req, file_id, file_handle, seek, size, flags, lock_owner) + self.get_inner().read( + req, + file_id, + file_handle, + seek, + size, + read_flags, + flags, + lock_owner, + ) } /// Read directory contents @@ -469,7 +479,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, flush: bool, ) -> FuseResult<()> { self.get_inner() @@ -537,7 +547,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, sleep: bool, ) -> FuseResult<()> { @@ -589,7 +599,7 @@ pub trait FuseHandler: OptionalSendSync + 'static { data: Vec, write_flags: FUSEWriteFlags, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, ) -> FuseResult { self.get_inner().write( req, diff --git a/src/inode_mapper.rs b/src/inode_mapper.rs index 3f0fdd7..8df1e57 100644 --- a/src/inode_mapper.rs +++ b/src/inode_mapper.rs @@ -555,7 +555,7 @@ mod tests { let child_name = OsString::from("child"); // Insert the first child - let first_child_inode = Inode::from(2); + let first_child_inode = Inode::new(2); assert_eq!( mapper.insert_child(&root, child_name.clone(), |value_creator_params| { assert!(value_creator_params.existing_data.is_none()); @@ -619,7 +619,7 @@ mod tests { } path.push(OsString::from(format!("file_{}", i))); entries.push((path, move |_: ValueCreatorParams| i)); - expected_inodes.insert(Inode::from(i + 2)); // Start from 2 to avoid conflict with root_inode + expected_inodes.insert(Inode::new(i + 2)); // Start from 2 to avoid conflict with root_inode } // Perform batch insert @@ -630,7 +630,7 @@ mod tests { // Check if all inserted inodes exist for i in 2..=(FILE_COUNT as u64 + 1) { - let inode = Inode::from(i); + let inode = Inode::new(i); assert!(mapper.get(&inode).is_some(), "{:?} should exist", inode); } @@ -690,13 +690,13 @@ mod tests { assert!(root_path.is_empty()); // Try to resolve a non-existent inode - assert!(mapper.resolve(&Inode::from(999)).is_none()); + assert!(mapper.resolve(&Inode::new(999)).is_none()); } #[test] fn test_resolve_invalid_inode() { let mapper = InodeMapper::new(0); - let invalid_inode = Inode::from(999); + let invalid_inode = Inode::new(999); // Attempt to resolve an invalid inode let result = mapper.resolve(&invalid_inode); @@ -893,11 +893,11 @@ mod tests { #[test] fn test_remove_cascading() { let mut mapper = InodeMapper::new(()); - let child1 = Inode::from(2); - let child2 = Inode::from(3); - let grandchild1 = Inode::from(4); - let grandchild2 = Inode::from(5); - let great_grandchild = Inode::from(6); + let child1 = Inode::new(2); + let child2 = Inode::new(3); + let grandchild1 = Inode::new(4); + let grandchild2 = Inode::new(5); + let great_grandchild = Inode::new(6); // Create a deeper nested structure mapper diff --git a/src/templates/default_fuse_handler.rs b/src/templates/default_fuse_handler.rs index 25ba51e..ff86e02 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/templates/default_fuse_handler.rs @@ -152,14 +152,14 @@ impl FuseHandler for DefaultFuseHandler { file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, - flags: u32, // Not implemented yet in standard + flags: CopyFileRangeFlags, // Not implemented yet in standard ) -> FuseResult { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, if cfg!(debug_assertions) { format!( - "copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {})", + "copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {:?})", file_in.display(), file_handle_in, offset_in, @@ -174,7 +174,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {})", + "[Not Implemented] copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {:?})", file_in.display(), file_handle_in, offset_in, @@ -270,14 +270,14 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, ) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, if cfg!(debug_assertions) { format!( - "flush(file_id: {}, file_handle: {:?}, lock_owner: {})", + "flush(file_id: {}, file_handle: {:?}, lock_owner: {:?})", file_id.display(), file_handle, lock_owner @@ -287,7 +287,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] flush(file_id: {}, file_handle: {:?}, lock_owner: {})", + "[Not Implemented] flush(file_id: {}, file_handle: {:?}, lock_owner: {:?})", file_id.display(), file_handle, lock_owner @@ -369,7 +369,7 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, ) -> FuseResult { match self.handling { @@ -377,7 +377,7 @@ impl FuseHandler for DefaultFuseHandler { kind, if cfg!(debug_assertions) { format!( - "getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", + "getlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?})", file_id.display(), file_handle, lock_owner, @@ -388,7 +388,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", + "[Not Implemented] getlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?})", file_id.display(), file_handle, lock_owner, @@ -432,7 +432,7 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - flags: IOCtlFlags, + flags: FUSEIoctlFlags, cmd: u32, in_data: Vec, out_size: u32, @@ -690,19 +690,21 @@ impl FuseHandler for DefaultFuseHandler { file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - flags: FUSEOpenFlags, - lock_owner: Option, + read_flags: FUSEReadFlags, + flags: OpenFlags, + lock_owner: Option, ) -> FuseResult> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, if cfg!(debug_assertions) { format!( - "read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, flags: {:?}, lock_owner: {:?})", + "read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, read_flags: {:?}, flags: {:?}, lock_owner: {:?})", file_id.display(), file_handle, seek, size, + read_flags, flags, lock_owner ) @@ -711,11 +713,12 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, flags: {:?}, lock_owner: {:?})", + "[Not Implemented] read(file_id: {}, file_handle: {:?}, seek: {:?}, size: {}, read_flags: {:?}, flags: {:?}, lock_owner: {:?})", file_id.display(), file_handle, seek, size, + read_flags, flags, lock_owner ), @@ -798,7 +801,7 @@ impl FuseHandler for DefaultFuseHandler { file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, flush: bool, ) -> FuseResult<()> { match self.handling { @@ -950,7 +953,7 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, sleep: bool, ) -> FuseResult<()> { @@ -959,7 +962,7 @@ impl FuseHandler for DefaultFuseHandler { kind, if cfg!(debug_assertions) { format!( - "setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", + "setlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?}, sleep: {})", file_id.display(), file_handle, lock_owner, @@ -971,7 +974,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", + "[Not Implemented] setlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?}, sleep: {})", file_id.display(), file_handle, lock_owner, @@ -1080,7 +1083,7 @@ impl FuseHandler for DefaultFuseHandler { data: Vec, write_flags: FUSEWriteFlags, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, ) -> FuseResult { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( diff --git a/src/templates/fd_handler_helper.rs b/src/templates/fd_handler_helper.rs index fd238da..05a8696 100644 --- a/src/templates/fd_handler_helper.rs +++ b/src/templates/fd_handler_helper.rs @@ -79,7 +79,7 @@ macro_rules! fd_handler_readonly_methods { _req: &RequestInfo, _file_id: TId, file_handle: BorrowedFileHandle, - _lock_owner: u64, + _lock_owner: LockOwner, ) -> FuseResult<()> { unix_fs::flush(file_handle.as_borrowed_fd()) } @@ -111,8 +111,9 @@ macro_rules! fd_handler_readonly_methods { file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _read_flags: FUSEReadFlags, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { unix_fs::read(file_handle.as_borrowed_fd(), seek, size as usize) } @@ -123,7 +124,7 @@ macro_rules! fd_handler_readonly_methods { _file_id: TId, file_handle: OwnedFileHandle, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, _flush: bool, ) -> FuseResult<()> { unix_fs::release(file_handle.into_owned_fd()) @@ -143,7 +144,7 @@ macro_rules! fd_handler_readwrite_methods { file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, - _flags: u32, + _flags: CopyFileRangeFlags, ) -> FuseResult { unix_fs::copy_file_range( file_handle_in.as_borrowed_fd(), @@ -175,7 +176,7 @@ macro_rules! fd_handler_readwrite_methods { data: Vec, _write_flags: FUSEWriteFlags, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult { unix_fs::write(file_handle.as_borrowed_fd(), seek, &data).map(|res| res as u32) } diff --git a/src/types.rs b/src/types.rs index e1502b1..6bc1d76 100644 --- a/src/types.rs +++ b/src/types.rs @@ -22,9 +22,9 @@ pub mod errors; pub mod file_handle; mod file_id_type; pub mod flags; -mod inode; pub mod helpers; - -pub use self::{arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, inode::*, helpers::*}; - -pub use fuser::{FileType as FileKind, KernelConfig, TimeOrNow}; +mod inode; +pub use self::{ + arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, helpers::*, inode::*, +}; +pub use fuser::{FileType as FileKind, Generation, KernelConfig, LockOwner, TimeOrNow}; diff --git a/src/types/arguments.rs b/src/types/arguments.rs index 8357c03..3a864fb 100644 --- a/src/types/arguments.rs +++ b/src/types/arguments.rs @@ -17,12 +17,13 @@ use std::time::{Duration, SystemTime}; -use fuser::FileAttr as FuseFileAttr; +use fuser::{FileAttr as FuseFileAttr, Generation, INodeNo, RequestId}; use fuser::{FileType, Request, TimeOrNow}; use libc::mode_t; +use crate::types::LockType; + use super::BorrowedFileHandle; -use super::LockType; pub use std::io::SeekFrom; @@ -161,13 +162,14 @@ impl StatFs { /// - `pid`: Process ID of the process that initiated the request #[derive(Debug, Clone)] pub struct RequestInfo { - pub id: u64, + pub id: RequestId, pub uid: u32, pub gid: u32, pub pid: u32, } -impl<'a> From<&Request<'a>> for RequestInfo { - fn from(req: &Request<'a>) -> Self { + +impl From<&Request> for RequestInfo { + fn from(req: &Request) -> Self { Self { id: req.unique(), uid: req.uid(), @@ -215,12 +217,15 @@ pub struct FileAttribute { /// - Must be non-zero (FUSE treats zero as an error) /// - Should be unique over the file system's lifetime if exported over NFS /// - Should be a new, previously unused number if an inode is reused after deletion - pub generation: Option, + pub generation: Option, } /// `FuseFileAttr`, `Option`, `Option` impl FileAttribute { - pub(crate) fn to_fuse(self, ino: u64) -> (FuseFileAttr, Option, Option) { + pub(crate) fn to_fuse( + self, + ino: INodeNo, + ) -> (FuseFileAttr, Option, Option) { ( FuseFileAttr { ino, diff --git a/src/types/errors.rs b/src/types/errors.rs index 897a870..5b7e531 100644 --- a/src/types/errors.rs +++ b/src/types/errors.rs @@ -16,6 +16,8 @@ //! - [`ErrorKind::to_error`]: Converts an ErrorKind to a PosixError with a custom message. //! +use fuser::Errno; + use crate::unix_fs::get_errno; use std::any::Any; @@ -90,6 +92,12 @@ where } } +impl From for Errno { + fn from(error: PosixError) -> Self { + Errno::from_i32(error.raw_error()) + } +} + impl Debug for PosixError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PosixError") diff --git a/src/types/file_handle.rs b/src/types/file_handle.rs index f4f7c2c..0a461b0 100644 --- a/src/types/file_handle.rs +++ b/src/types/file_handle.rs @@ -58,6 +58,18 @@ impl OwnedFileHandle { Self(handle) } + /// Creates an OwnedFileHandle from a [`fuser::FileHandle`]. + /// + /// Unsafe because it assumes the provided value is a valid, open file handle. + pub unsafe fn from_fuser_file_handle(handle: fuser::FileHandle) -> Self { + Self(handle.0) + } + + /// Converts the OwnedFileHandle into a [`fuser::FileHandle`]. + pub fn as_fuser_file_handle(&self) -> fuser::FileHandle { + fuser::FileHandle(self.0) + } + /// Borrows the file handle, creating a BorrowedFileHandle with a lifetime tied to self. pub fn borrow(&self) -> BorrowedFileHandle<'_> { BorrowedFileHandle(self.0, PhantomData) @@ -115,6 +127,18 @@ impl<'a> BorrowedFileHandle<'a> { Self(handle, PhantomData) } + /// Creates a BorrowedFileHandle from a [`fuser::FileHandle`]. + /// + /// Unsafe because it assumes the provided value is a valid, open file handle. + pub unsafe fn from_fuser_file_handle(handle: fuser::FileHandle) -> Self { + Self(handle.0, PhantomData) + } + + /// Converts the BorrowedFileHandle into a [`fuser::FileHandle`]. + pub fn as_fuser_file_handle(&self) -> fuser::FileHandle { + fuser::FileHandle(self.0) + } + /// Converts the BorrowedFileHandle into a BorrowedFd. /// /// Note: This method performs an unchecked cast from `u64` to `i32`, which may lead to undefined behavior if the file handle value doesn't fit within an `i32`. diff --git a/src/types/flags.rs b/src/types/flags.rs index 2a2b846..bc31985 100644 --- a/src/types/flags.rs +++ b/src/types/flags.rs @@ -23,6 +23,37 @@ bitflags! { } } +impl From for AccessMask { + fn from(flags: fuser::AccessFlags) -> Self { + AccessMask::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::AccessFlags { + fn from(flags: AccessMask) -> Self { + fuser::AccessFlags::from_bits_retain(flags.bits()) + } +} + +bitflags! { + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct CopyFileRangeFlags: u64 { + const _ = !0; + } +} + +impl From for CopyFileRangeFlags { + fn from(flags: fuser::CopyFileRangeFlags) -> Self { + CopyFileRangeFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::CopyFileRangeFlags { + fn from(flags: CopyFileRangeFlags) -> Self { + fuser::CopyFileRangeFlags::from_bits_retain(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] /// Flags used in fallocate calls. @@ -98,6 +129,18 @@ bitflags! { } } +impl From for FUSEOpenResponseFlags { + fn from(flags: fuser::FopenFlags) -> Self { + FUSEOpenResponseFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::FopenFlags { + fn from(flags: FUSEOpenResponseFlags) -> Self { + fuser::FopenFlags::from_bits_retain(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FUSEIoctlFlags: u32 { @@ -111,6 +154,18 @@ bitflags! { } } +impl From for FUSEIoctlFlags { + fn from(flags: fuser::IoctlFlags) -> Self { + FUSEIoctlFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::IoctlFlags { + fn from(flags: FUSEIoctlFlags) -> Self { + fuser::IoctlFlags::from_bits_retain(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FUSEReadFlags: i32 { @@ -119,6 +174,18 @@ bitflags! { } } +impl From for FUSEReadFlags { + fn from(flags: fuser::ReadFlags) -> Self { + FUSEReadFlags::from_bits_retain(flags.bits() as i32) + } +} + +impl From for fuser::ReadFlags { + fn from(flags: FUSEReadFlags) -> Self { + fuser::ReadFlags::from_bits_retain(flags.bits() as u32) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FUSEReleaseFlags: i32 { @@ -154,11 +221,15 @@ bitflags! { } } -bitflags! { - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub struct IOCtlFlags: u32 { - // Placeholder for future flags - const _ = !0; +impl From for FUSEWriteFlags { + fn from(flags: fuser::WriteFlags) -> Self { + FUSEWriteFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::WriteFlags { + fn from(flags: FUSEWriteFlags) -> Self { + fuser::WriteFlags::from_bits_retain(flags.bits()) } } @@ -219,6 +290,18 @@ bitflags! { } } +impl From for OpenFlags { + fn from(flags: fuser::OpenFlags) -> Self { + OpenFlags::from_bits_retain(flags.0) + } +} + +impl From for fuser::OpenFlags { + fn from(flags: OpenFlags) -> Self { + fuser::OpenFlags(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] /// Flags used in rename operations. @@ -232,3 +315,15 @@ bitflags! { const _ = !0; } } + +impl From for RenameFlags { + fn from(flags: fuser::RenameFlags) -> Self { + RenameFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::RenameFlags { + fn from(flags: RenameFlags) -> Self { + fuser::RenameFlags::from_bits_retain(flags.bits()) + } +} \ No newline at end of file diff --git a/src/types/helpers.rs b/src/types/helpers.rs index 72d3774..4b2db7a 100644 --- a/src/types/helpers.rs +++ b/src/types/helpers.rs @@ -2,7 +2,7 @@ use fuser::BackingId; use fuser::{ReplyCreate, ReplyOpen}; #[cfg(feature = "passthrough")] -use std::sync::Arc; +use std::sync::{Arc, Weak}; pub struct OpenHelper<'a> { #[allow(dead_code)] diff --git a/src/types/inode.rs b/src/types/inode.rs index b0638a1..b2bdfcf 100644 --- a/src/types/inode.rs +++ b/src/types/inode.rs @@ -1,10 +1,10 @@ //! Inode number in a FUSE (Filesystem in Userspace) filesystem. - use crate::core::ROOT_INO; +use fuser::INodeNo; /// Represents the mountpoint folder in a FuseFilesystem /// Its value is 1 and should not be modified. -pub const ROOT_INODE: Inode = Inode::from(ROOT_INO); +pub const ROOT_INODE: Inode = Inode::new(ROOT_INO); /// Represents an inode number in a FUSE (Filesystem in Userspace) filesystem. /// @@ -32,22 +32,30 @@ pub const ROOT_INODE: Inode = Inode::from(ROOT_INO); /// using `PathBuf` as `FileIdType`. This alternative approach allows for file /// identification based on paths rather than inode numbers. #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[repr(transparent)] pub struct Inode(u64); impl Inode { /// Allow const creation of Inode. - pub const fn from(value: u64) -> Self { + pub const fn new(value: u64) -> Self { Inode(value) } /// Convenience for creating a new Inode pub fn add_one(&self) -> Self { - Inode::from(u64::from(self.clone()) + 1) + Inode::new(u64::from(self.clone()) + 1) + } +} + +impl From for INodeNo { + /// Converts an Inode into a raw [`fuser::INodeNo`]. + fn from(value: Inode) -> Self { + INodeNo(value.0) } } impl From for u64 { - // Converts a u64 into an Inode. + /// Converts a u64 into an Inode. /// /// This allows for easy creation of Inode instances from raw inode numbers. fn from(value: Inode) -> Self { @@ -55,6 +63,13 @@ impl From for u64 { } } +impl From for Inode { + /// Converts a raw [`fuser::INodeNo`] into an Inode. + fn from(value: INodeNo) -> Self { + Inode(value.0) + } +} + impl From for Inode { /// Converts a number into an Inode. /// From 3d619e20b2011007daf7270cd3c1ae077bb9cfd0 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Mon, 26 Jan 2026 16:30:48 +0700 Subject: [PATCH 10/20] fix: open_backing should not require object ownership --- src/types/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/helpers.rs b/src/types/helpers.rs index 4b2db7a..efb680d 100644 --- a/src/types/helpers.rs +++ b/src/types/helpers.rs @@ -21,7 +21,7 @@ impl<'a> CreateHelper<'a> { #[cfg(feature = "passthrough")] pub fn open_backing( - self, + &self, fd: impl std::os::fd::AsFd, ) -> Result { self.reply_create @@ -39,7 +39,7 @@ impl<'a> OpenHelper<'a> { #[cfg(feature = "passthrough")] pub fn open_backing( - self, + &self, fd: impl std::os::fd::AsFd, ) -> Result { self.reply_open From 4b02585134ca3e71b31db72d0c1623ca885c98b6 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Mon, 26 Jan 2026 12:50:43 +0700 Subject: [PATCH 11/20] fix: minor doc and ide flag fixes --- src/types/helpers.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/types/helpers.rs b/src/types/helpers.rs index efb680d..9afecb1 100644 --- a/src/types/helpers.rs +++ b/src/types/helpers.rs @@ -50,6 +50,33 @@ impl<'a> OpenHelper<'a> { } } +/// Passthrough backing ID for FUSE passthrough operations. +/// +/// This structure is created by the [`OpenHelper::open_backing`] +/// and [`CreateHelper::open_backing`] methods, and allows the FUSE +/// kernel module to bypass the FUSE daemon to increase performance. +/// +/// This structure is a no-op if the `passthrough` feature is not +/// enabled. +/// +/// In the scope of a single file ID (e.g. [`Inode`](crate::types::inode::Inode), +/// [`PathBuf`](std::path::PathBuf), [`Vec`](std::ffi::OsString) or +/// [`HybridId`](crate::types::file_id_type::HybridId)), +/// - All active file handle objects ([`OwnedFileHandle`](crate::types::file_handle::OwnedFileHandle)) +/// must be opened in the same mode (passthrough or non-passthrough). +/// - If the file handles are opened in passthrough mode, they must share the same +/// [`PassthroughBackingId`] value. Identical [`PassthroughBackingId`] values can be +/// obtained by cloning an existing value. +/// - If either or both of these rules is not met, the FUSE kernel module will return +/// an [`EIO`](libc::EIO) error code that is very difficult to troubleshoot. It is best +/// to return [`EBUSY`](libc::EBUSY) when you detect this situation, which usually happens +/// when trying to open a file twice may result in two completely different file views. +/// +/// Backing IDs can be downgraded to a [`WeakPassthroughBackingId`] value, similar to +/// the underlying [`Arc`] type. This allows the passthrough ID references to be stored in +/// a file ID to passthrough ID hash table (required to fulfill these above two rules) +/// without interfering with the ability of the backing ID to be released when the last +/// file handle to an inode is closed. #[derive(Debug, Clone)] pub struct PassthroughBackingId { #[cfg(feature = "passthrough")] From 230f7925213e018113d0e2f1032a22b2e59013c0 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Mon, 26 Jan 2026 12:48:03 +0700 Subject: [PATCH 12/20] doc: add kernel caveat to passthrough ID --- .vscode/settings.json | 2 +- src/types/helpers.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 51eb545..1765111 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.cargo.features": ["serial", "passthrough"] + "rust-analyzer.cargo.features": ["parallel", "passthrough"] } diff --git a/src/types/helpers.rs b/src/types/helpers.rs index 9afecb1..42ed997 100644 --- a/src/types/helpers.rs +++ b/src/types/helpers.rs @@ -4,21 +4,29 @@ use fuser::{ReplyCreate, ReplyOpen}; #[cfg(feature = "passthrough")] use std::sync::{Arc, Weak}; +/// Helper object for the [`open`](crate::FuseHandler::open) method. pub struct OpenHelper<'a> { #[allow(dead_code)] reply_open: &'a ReplyOpen, } +/// Helper object for the [`create`](crate::FuseHandler::create) method. pub struct CreateHelper<'a> { #[allow(dead_code)] reply_create: &'a ReplyCreate, } impl<'a> CreateHelper<'a> { + /// Initializes a new [`CreateHelper`] object. pub(crate) fn new(reply_create: &'a ReplyCreate) -> Self { Self { reply_create } } + /// Registers a backing ID for FUSE passthrough operations and returns + /// a [`PassthroughBackingId`], which can be used as the fourth return value + /// of the [`create`](crate::FuseHandler::create) method. + /// + /// Check [`PassthroughBackingId`] for information on correct usage. #[cfg(feature = "passthrough")] pub fn open_backing( &self, @@ -33,10 +41,16 @@ impl<'a> CreateHelper<'a> { } impl<'a> OpenHelper<'a> { + /// Initializes a new [`OpenHelper`] object. pub(crate) fn new(reply_open: &'a ReplyOpen) -> Self { Self { reply_open } } + /// Registers a backing ID for FUSE passthrough operations and returns + /// a [`PassthroughBackingId`], which can be used as the third return value + /// of the [`open`](crate::FuseHandler::open) method. + /// + /// Check [`PassthroughBackingId`] for information on correct usage. #[cfg(feature = "passthrough")] pub fn open_backing( &self, @@ -91,6 +105,9 @@ impl AsRef for PassthroughBackingId { } impl PassthroughBackingId { + /// Downgrades a [`PassthroughBackingId`] to a weak reference, which allows it + /// to be stored in a long-lived table without preventing the backing ID from being + /// released when the last file handle to an inode is closed. #[cfg(feature = "passthrough")] pub fn downgrade(&self) -> WeakPassthroughBackingId { WeakPassthroughBackingId { @@ -113,6 +130,7 @@ impl WeakPassthroughBackingId { } } + /// Upgrades a [`WeakPassthroughBackingId`] to a [`PassthroughBackingId`]. #[cfg(feature = "passthrough")] pub fn upgrade(&self) -> Option { self.backing_id From 91708e06f4b827f4586e48c3bd29f2dbdc0f4bda Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Wed, 28 Jan 2026 17:06:36 +0700 Subject: [PATCH 13/20] fix: change PoC fuser impl to bleeding-edge branch --- src/core/fuse_driver.rs | 26 ++++++++++-------- src/core/fuse_driver_types.rs | 50 ++++++++++++++++++++--------------- src/core/thread_mode.rs | 45 +++++++++++++++++++++++++++---- src/fuse_handler.rs | 4 +-- 4 files changed, 86 insertions(+), 39 deletions(-) diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs index daffa0d..3fb475a 100644 --- a/src/core/fuse_driver.rs +++ b/src/core/fuse_driver.rs @@ -1,12 +1,13 @@ use log::{info, warn}; use std::{ ffi::OsStr, + io, path::Path, time::{Instant, SystemTime}, }; use fuser::{ - self, AccessFlags, Errno, FileHandle, FopenFlags, Generation, INodeNo, IoctlFlags, + self, AccessFlags, BsdFileFlags, FileHandle, FopenFlags, Generation, INodeNo, IoctlFlags, KernelConfig, LockOwner, ReadFlags, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, WriteFlags, @@ -29,13 +30,13 @@ where TId: FileIdType, THandler: FuseHandler, { - fn init(&mut self, req: &Request, config: &mut KernelConfig) -> Result<(), Errno> { + fn init(&mut self, req: &Request, config: &mut KernelConfig) -> io::Result<()> { let req = RequestInfo::from(req); match self.get_handler().init(&req, config) { Ok(()) => Ok(()), Err(e) => { warn!("[{}] init {:?}", e, req); - Err(e.into()) + Err(io::Error::from_raw_os_error(e.raw_error())) } } } @@ -618,7 +619,10 @@ where } _ => { let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; - reply.opened(file_handle.as_fuser_file_handle(), FopenFlags::from(response_flags)) + reply.opened( + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + ) } }, Err(e) => { @@ -763,7 +767,7 @@ where req: &Request, ino: INodeNo, fh: FileHandle, - flags: i32, + flags: fuser::OpenFlags, lock_owner: Option, _flush: bool, reply: ReplyEmpty, @@ -776,7 +780,7 @@ where &req, resolver.resolve_id(ino), unsafe { OwnedFileHandle::from_fuser_file_handle(fh) }, - OpenFlags::from_bits_retain(flags), + OpenFlags::from(flags), lock_owner, _flush, ) { @@ -794,7 +798,7 @@ where req: &Request, ino: INodeNo, fh: FileHandle, - flags: i32, + flags: fuser::OpenFlags, reply: ReplyEmpty, ) { let req = RequestInfo::from(req); @@ -805,7 +809,7 @@ where &req, resolver.resolve_id(ino), unsafe { OwnedFileHandle::from_fuser_file_handle(fh) }, - OpenFlags::from_bits_retain(flags), + OpenFlags::from(flags), ) { Ok(()) => reply.ok(), Err(e) => { @@ -899,7 +903,7 @@ where crtime: Option, chgtime: Option, bkuptime: Option, - _flags: Option, + _flags: Option, reply: ReplyAttr, ) { let req = RequestInfo::from(req); @@ -1064,7 +1068,7 @@ where offset: i64, data: &[u8], write_flags: WriteFlags, - flags: i32, + flags: fuser::OpenFlags, lock_owner: Option, reply: ReplyWrite, ) { @@ -1080,7 +1084,7 @@ where seek_from_raw(None, offset), data, FUSEWriteFlags::from(write_flags), - OpenFlags::from_bits_retain(flags), + OpenFlags::from(flags), lock_owner, ) { Ok(bytes_written) => reply.written(bytes_written), diff --git a/src/core/fuse_driver_types.rs b/src/core/fuse_driver_types.rs index f0640f0..4d07846 100644 --- a/src/core/fuse_driver_types.rs +++ b/src/core/fuse_driver_types.rs @@ -16,6 +16,12 @@ mod serial { use super::*; use std::cell::RefCell; + use std::sync::Arc; + + #[cfg(feature = "deadlock_detection")] + use parking_lot::{Mutex, MutexGuard}; + #[cfg(not(feature = "deadlock_detection"))] + use std::sync::{Mutex, MutexGuard}; pub(crate) struct FuseDriver where @@ -24,8 +30,8 @@ mod serial { { handler: THandler, resolver: TId::Resolver, - dirmap_iter: RefCell>, - dirmapplus_iter: RefCell>, + dirmap_iter: Arc>>, + dirmapplus_iter: Arc>>, } impl FuseDriver @@ -38,8 +44,8 @@ mod serial { FuseDriver { handler, resolver: TId::Resolver::new(), - dirmap_iter: RefCell::new(HashMap::new()), - dirmapplus_iter: RefCell::new(HashMap::new()), + dirmap_iter: Arc::new(Mutex::new(HashMap::new())), + dirmapplus_iter: Arc::new(Mutex::new(HashMap::new())), } } @@ -51,12 +57,12 @@ mod serial { &self.resolver } - pub fn get_dirmap_iter(&self) -> &RefCell> { - &self.dirmap_iter + pub fn get_dirmap_iter(&self) -> Arc>> { + self.dirmap_iter.clone() } - pub fn get_dirmapplus_iter(&self) -> &RefCell> { - &self.dirmapplus_iter + pub fn get_dirmapplus_iter(&self) -> Arc>> { + self.dirmapplus_iter.clone() } } @@ -208,19 +214,21 @@ fn spawn_deadlock_checker() { use std::time::Duration; // Create a background thread which checks for deadlocks every 10s - thread::spawn(move || loop { - thread::sleep(Duration::from_secs(10)); - let deadlocks = deadlock::check_deadlock(); - if deadlocks.is_empty() { - info!("# No deadlock"); - continue; - } - - eprintln!("# {} deadlocks detected", deadlocks.len()); - for (i, threads) in deadlocks.iter().enumerate() { - error!("Deadlock #{}", i); - for t in threads { - error!("Thread Id {:#?}\n, {:#?}", t.thread_id(), t.backtrace()); + thread::spawn(move || { + loop { + thread::sleep(Duration::from_secs(10)); + let deadlocks = deadlock::check_deadlock(); + if deadlocks.is_empty() { + info!("# No deadlock"); + continue; + } + + eprintln!("# {} deadlocks detected", deadlocks.len()); + for (i, threads) in deadlocks.iter().enumerate() { + error!("Deadlock #{}", i); + for t in threads { + error!("Thread Id {:#?}\n, {:#?}", t.thread_id(), t.backtrace()); + } } } }); diff --git a/src/core/thread_mode.rs b/src/core/thread_mode.rs index c7ebdc3..2d3812f 100644 --- a/src/core/thread_mode.rs +++ b/src/core/thread_mode.rs @@ -11,16 +11,42 @@ pub(crate) trait SafeBorrowable { #[cfg(feature = "serial")] mod safe_borrowable_impl { use super::*; - use std::cell::{RefCell, RefMut}; impl SafeBorrowable for RefCell { - type Guard<'a> = RefMut<'a, T> where Self: 'a; + type Guard<'a> + = RefMut<'a, T> + where + Self: 'a; fn safe_borrow_mut(&self) -> Self::Guard<'_> { self.borrow_mut() } } + + #[cfg(not(feature = "deadlock_detection"))] + impl SafeBorrowable for std::sync::Mutex { + type Guard<'a> + = std::sync::MutexGuard<'a, T> + where + Self: 'a; + + fn safe_borrow_mut(&self) -> Self::Guard<'_> { + self.lock().unwrap() + } + } + + #[cfg(feature = "deadlock_detection")] + impl SafeBorrowable for parking_lot::Mutex { + type Guard<'a> + = parking_lot::MutexGuard<'a, T> + where + Self: 'a; + + fn safe_borrow_mut(&self) -> Self::Guard<'_> { + self.lock() + } + } } #[cfg(all(feature = "parallel", not(feature = "deadlock_detection")))] @@ -30,7 +56,10 @@ mod safe_borrowable_impl { use std::sync::{Mutex, MutexGuard}; impl SafeBorrowable for Mutex { - type Guard<'a> = MutexGuard<'a, T> where Self: 'a; + type Guard<'a> + = MutexGuard<'a, T> + where + Self: 'a; fn safe_borrow_mut(&self) -> Self::Guard<'_> { self.lock().unwrap() @@ -45,7 +74,10 @@ mod safe_borrowable_impl { use parking_lot::{Mutex, MutexGuard}; impl SafeBorrowable for Mutex { - type Guard<'a> = MutexGuard<'a, T> where Self: 'a; + type Guard<'a> + = MutexGuard<'a, T> + where + Self: 'a; fn safe_borrow_mut(&self) -> Self::Guard<'_> { self.lock() @@ -60,7 +92,10 @@ mod safe_borrowable_impl { use tokio::sync::{Mutex, MutexGuard}; impl SafeBorrowable for Mutex { - type Guard<'a> = MutexGuard<'a, T> where Self: 'a; + type Guard<'a> + = MutexGuard<'a, T> + where + Self: 'a; async fn safe_borrow_mut(&self) -> Self::Guard<'_> { self.lock().await diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs index 8673e51..95df752 100644 --- a/src/fuse_handler.rs +++ b/src/fuse_handler.rs @@ -99,9 +99,9 @@ mod private { #[cfg(not(feature = "serial"))] impl OptionalSendSync for T {} #[cfg(feature = "serial")] - pub trait OptionalSendSync {} + pub trait OptionalSendSync: Sync + Send {} #[cfg(feature = "serial")] - impl OptionalSendSync for T {} + impl OptionalSendSync for T {} } use fuser::LockOwner; use private::OptionalSendSync; From a2fa39c36372e69807c7a7560acb392bb8f409df Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Wed, 28 Jan 2026 17:36:32 +0700 Subject: [PATCH 14/20] misc: use bleeding edge fuser library --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dbeddc8..2a9c22c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,4 +42,4 @@ features = ["parallel"] [patch.crates-io] # Bleeding edge patch for FUSE create() passthrough, will be removed when patch is merged -fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "passthrough" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } \ No newline at end of file From 2b3665bd89ea7c40514877877c992a4bcc168f29 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Wed, 28 Jan 2026 17:50:18 +0700 Subject: [PATCH 15/20] fix: should use fuser::OpenFlags --- src/core/fuse_driver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs index 3fb475a..46c4e05 100644 --- a/src/core/fuse_driver.rs +++ b/src/core/fuse_driver.rs @@ -678,7 +678,7 @@ where offset: u64, size: u32, read_flags: ReadFlags, - flags: u32, + flags: fuser::OpenFlags, lock_owner: Option, reply: ReplyData, ) { @@ -693,7 +693,7 @@ where SeekFrom::Start(offset), size, FUSEReadFlags::from(read_flags), - OpenFlags::from_bits_retain(flags as i32), + OpenFlags::from(flags), lock_owner, ) { Ok(data_reply) => reply.data(&data_reply), From a941d822d832960c3a8d06a8934bda12d4f9b1db Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Tue, 3 Feb 2026 17:59:14 +0700 Subject: [PATCH 16/20] fix: test fixes --- README.md | 12 ++++++++++-- templates/fuse_driver.jinja | 6 ++++-- tests/integration_test.rs | 16 ++++++++++++---- tests/mount_mirror_fs.rs | 16 ++++++++++++---- tests/offset_test.rs | 16 ++++++++++++---- tests/test_recursion.rs | 16 ++++++++++++---- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b89536e..89fb125 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,18 @@ impl FuseHandler for MyFS { fn main() -> std::io::Result<()> { let fs = MyFS { inner: Box::new(DefaultFuseHandler::new()) }; + let mut config = Config::default(); + config.acl = fuser::SessionACL::Owner; #[cfg(feature="serial")] - easy_fuser::mount(fs, Path::new("/mnt/myfs"), &[])?; + { + config.n_threads = None; + } #[cfg(not(feature="serial"))] - easy_fuser::mount(fs, Path::new("/mnt/myfs"), &[], 4)?; + { + config.n_threads = Some(4); + } + config.mount_options = vec![]; + easy_fuser::mount(fs, Path::new("/mnt/myfs"), &config)?; Ok(()) } ``` diff --git a/templates/fuse_driver.jinja b/templates/fuse_driver.jinja index e93c00e..485277b 100644 --- a/templates/fuse_driver.jinja +++ b/templates/fuse_driver.jinja @@ -771,9 +771,10 @@ where match passthrough_backing_id { #[cfg(feature = "passthrough")] Some(passthrough_backing_id) => { + let response_flags = response_flags | FUSEOpenResponseFlags::PASSTHROUGH; reply.opened_passthrough( file_handle.as_fuser_file_handle(), - FopenFlags::from(response_flags) | FopenFlags::PASSTHROUGH, + FopenFlags::from(response_flags), passthrough_backing_id.as_ref() ); } @@ -810,9 +811,10 @@ where match passthrough_backing_id { #[cfg(feature = "passthrough")] Some(passthrough_backing_id) => { + let response_flags = response_flags | FUSEOpenResponseFlags::PASSTHROUGH; reply.opened_passthrough( file_handle.as_fuser_file_handle(), - FopenFlags::from(response_flags) | FopenFlags::PASSTHROUGH, + FopenFlags::from(response_flags), passthrough_backing_id.as_ref() ); } diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 75c2455..e0c78e2 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -21,10 +21,18 @@ fn test_mirror_fs_operations() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] - mount(fs, &mntpoint_clone, &[], 4).unwrap(); + let config = { + let mut config = Config::default(); + config.acl = fuser::SessionACL::Owner; + if cfg!(feature = "serial") { + config.n_threads = None; + } else { + config.n_threads = Some(4); + } + config.mount_options = vec![]; + config + }; + mount(fs, &mntpoint_clone, &config).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish diff --git a/tests/mount_mirror_fs.rs b/tests/mount_mirror_fs.rs index 506ee70..89d21ac 100644 --- a/tests/mount_mirror_fs.rs +++ b/tests/mount_mirror_fs.rs @@ -35,11 +35,19 @@ fn mount_fs() { // Mount the filesystem println!("Mounting MirrorFs..."); - #[cfg(feature = "serial")] - let mount_result = mount(fs, &mount_dir, &[]); - #[cfg(not(feature = "serial"))] - let mount_result = mount(fs, &mount_dir, &[], 4); + let config = { + let mut config = Config::default(); + config.acl = fuser::SessionACL::Owner; + if cfg!(feature = "serial") { + config.n_threads = None; + } else { + config.n_threads = Some(4); + } + config.mount_options = vec![]; + config + }; + let mount_result = mount(fs, &mount_dir, &config); match mount_result { Ok(_) => { println!("MirrorFs mounted successfully. Press Ctrl+C to unmount and exit."); diff --git a/tests/offset_test.rs b/tests/offset_test.rs index 4e17539..54429d8 100644 --- a/tests/offset_test.rs +++ b/tests/offset_test.rs @@ -20,10 +20,18 @@ fn test_mirror_fs_file_offsets() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] - mount(fs, &mntpoint_clone, &[], 4).unwrap(); + let config = { + let mut config = Config::default(); + config.acl = fuser::SessionACL::Owner; + if cfg!(feature = "serial") { + config.n_threads = None; + } else { + config.n_threads = Some(4); + } + config.mount_options = vec![]; + config + }; + mount(fs, &mntpoint_clone, &config).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish diff --git a/tests/test_recursion.rs b/tests/test_recursion.rs index 7e01db1..00f7582 100644 --- a/tests/test_recursion.rs +++ b/tests/test_recursion.rs @@ -25,10 +25,18 @@ fn test_mirror_fs_recursion() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] - mount(fs, &mntpoint_clone, &[], 4).unwrap(); + let config = { + let mut config = Config::default(); + config.acl = fuser::SessionACL::Owner; + if cfg!(feature = "serial") { + config.n_threads = None; + } else { + config.n_threads = Some(4); + } + config.mount_options = vec![]; + config + }; + mount(fs, &mntpoint_clone, &config).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish From 3f77b1d2a096e0f9b53d98c053e6ebfa07535b06 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Tue, 3 Feb 2026 18:45:47 +0700 Subject: [PATCH 17/20] fix: example fixes --- examples/ftp_fs/Cargo.toml | 6 ++- examples/ftp_fs/src/main.rs | 10 ++++- examples/hello_fs/Cargo.toml | 6 ++- examples/hello_fs/src/main.rs | 12 ++++-- examples/in_memory_fs/Cargo.toml | 6 ++- examples/in_memory_fs/src/filesystem.rs | 15 +++++--- examples/in_memory_fs/src/main.rs | 51 ++++++++----------------- examples/passthrough_fs/Cargo.toml | 6 ++- examples/passthrough_fs/src/main.rs | 12 ++++-- examples/random_fs/Cargo.toml | 4 ++ examples/random_fs/src/main.rs | 17 +++++---- examples/zip_fs/Cargo.toml | 4 ++ examples/zip_fs/src/filesystem.rs | 2 +- examples/zip_fs/src/main.rs | 7 +++- src/inode_multi_mapper.rs | 2 +- src/lib.rs | 4 +- src/types.rs | 2 +- templates/fuse_driver.jinja | 17 +++++---- templates/fuse_handler.jinja | 5 +-- 19 files changed, 109 insertions(+), 79 deletions(-) diff --git a/examples/ftp_fs/Cargo.toml b/examples/ftp_fs/Cargo.toml index 6364e06..7a67c66 100644 --- a/examples/ftp_fs/Cargo.toml +++ b/examples/ftp_fs/Cargo.toml @@ -21,4 +21,8 @@ tempfile = "3.2" libunftp = "0.20" unftp-sbe-fs = "0.2" async-trait = "0.1.68" -tokio = { version = "1.42", features = ["full"] } \ No newline at end of file +tokio = { version = "1.42", features = ["full"] } + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file diff --git a/examples/ftp_fs/src/main.rs b/examples/ftp_fs/src/main.rs index 913c14a..945f929 100644 --- a/examples/ftp_fs/src/main.rs +++ b/examples/ftp_fs/src/main.rs @@ -139,7 +139,15 @@ fn main() -> Result<(), Box> { println!("Mount point: {:?}", &mount_point); // Mount the filesystem - easy_fuser::mount(ftp_fs, &mount_point, &[], 4)?; + let config = { + let mut config = easy_fuser::prelude::Config::default(); + config.acl = easy_fuser::prelude::SessionACL::Owner; + config.n_threads = Some(4); + config.mount_options = vec![]; + config + }; + + easy_fuser::mount(ftp_fs, &mount_point, &config)?; // If we reach here, the filesystem has been unmounted normally cleanup(&mount_point, &once_flag); diff --git a/examples/hello_fs/Cargo.toml b/examples/hello_fs/Cargo.toml index d05ff5c..2453a50 100644 --- a/examples/hello_fs/Cargo.toml +++ b/examples/hello_fs/Cargo.toml @@ -9,4 +9,8 @@ logging = ["dep:env_logger", "dep:log"] [dependencies] easy_fuser = { path = "../..", features = ["serial"] } log = { version = "0.4", optional = true } -env_logger = { version = "0.11", optional = true } \ No newline at end of file +env_logger = { version = "0.11", optional = true } + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file diff --git a/examples/hello_fs/src/main.rs b/examples/hello_fs/src/main.rs index 61198a6..c7ab89e 100644 --- a/examples/hello_fs/src/main.rs +++ b/examples/hello_fs/src/main.rs @@ -33,7 +33,7 @@ const HELLO_DIR_ATTR: (Inode, FileAttribute) = ( const HELLO_TXT_CONTENT: &str = "Hello World!\n"; const HELLO_TXT_ATTR: (Inode, FileAttribute) = ( - Inode::from(2), + Inode::new(2), FileAttribute { size: 13, blocks: 1, @@ -111,7 +111,7 @@ impl FuseHandler for HelloFS { seek: SeekFrom, size: u32, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult> { if file_id == HELLO_TXT_ATTR.0 { let offset = match seek { @@ -166,8 +166,12 @@ fn main() { .try_init(); let mountpoint = std::env::args().nth(1).expect("Usage: hello "); - let options = vec![MountOption::RO, MountOption::FSName("hello".to_string())]; + let mut config = easy_fuser::prelude::Config::default(); + config.acl = easy_fuser::prelude::SessionACL::Owner; + config.n_threads = Some(1); + config.mount_options = vec![MountOption::RO, MountOption::FSName("hello".to_string())]; + println!("Mounting FTP filesystem..."); - easy_fuser::mount(HelloFS::new(), Path::new(&mountpoint), &options).unwrap(); + easy_fuser::mount(HelloFS::new(), Path::new(&mountpoint), &config).unwrap(); } diff --git a/examples/in_memory_fs/Cargo.toml b/examples/in_memory_fs/Cargo.toml index 8c23f42..acdf4f1 100644 --- a/examples/in_memory_fs/Cargo.toml +++ b/examples/in_memory_fs/Cargo.toml @@ -11,4 +11,8 @@ logging = ["dep:env_logger", "dep:log"] [dependencies] easy_fuser = { path = "../..", features = ["parallel"] } log = { version = "0.4", optional = true } -env_logger = { version = "0.11", optional = true } \ No newline at end of file +env_logger = { version = "0.11", optional = true } + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file diff --git a/examples/in_memory_fs/src/filesystem.rs b/examples/in_memory_fs/src/filesystem.rs index a4c22a2..5b37f37 100644 --- a/examples/in_memory_fs/src/filesystem.rs +++ b/examples/in_memory_fs/src/filesystem.rs @@ -26,7 +26,7 @@ impl InMemoryFS { pub fn new() -> Self { let mut fs = DataBank { inodes: HashMap::new(), - next_inode: Inode::from(2), // Root is 1 + next_inode: Inode::new(2), // Root is 1 }; // Create root directory @@ -155,11 +155,13 @@ impl FuseHandler for InMemoryFS { mode: u32, _umask: u32, _flags: OpenFlags, + _helper: CreateHelper, ) -> Result< ( OwnedFileHandle, (Inode, FileAttribute), FUSEOpenResponseFlags, + Option, ), PosixError, > { @@ -204,6 +206,7 @@ impl FuseHandler for InMemoryFS { unsafe { OwnedFileHandle::from_raw(0) }, (new_inode.clone(), attr), FUSEOpenResponseFlags::empty(), + None, )) } else { Err(ErrorKind::FileNotFound.to_error("")) @@ -215,8 +218,8 @@ impl FuseHandler for InMemoryFS { req: &RequestInfo, file_id: Inode, _file_handle: BorrowedFileHandle, - offset: i64, - length: i64, + offset: u64, + length: u64, mode: FallocateFlags, ) -> FuseResult<()> { self.access(req, file_id.clone(), AccessMask::CAN_WRITE)?; @@ -260,7 +263,7 @@ impl FuseHandler for InMemoryFS { _req: &RequestInfo, _file_id: Inode, _file_handle: BorrowedFileHandle, - _lock_owner: u64, + _lock_owner: LockOwner, ) -> FuseResult<()> { Ok(()) } @@ -364,7 +367,7 @@ impl FuseHandler for InMemoryFS { offset: SeekFrom, size: u32, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult> { self.access(req, ino.clone(), AccessMask::CAN_READ)?; let fs = self.fs.lock().unwrap(); @@ -579,7 +582,7 @@ impl FuseHandler for InMemoryFS { data: Vec, _write_flags: FUSEWriteFlags, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult { self.access(req, ino.clone(), AccessMask::CAN_WRITE)?; let mut fs = self.fs.lock().unwrap(); diff --git a/examples/in_memory_fs/src/main.rs b/examples/in_memory_fs/src/main.rs index 0724665..d40e1ce 100644 --- a/examples/in_memory_fs/src/main.rs +++ b/examples/in_memory_fs/src/main.rs @@ -1,8 +1,8 @@ #![doc = include_str!("../README.md")] use easy_fuser::prelude::*; -use std::ffi::OsStr; use std::path::Path; +use std::fs; const README_CONTENT: &[u8] = include_bytes!("../README.md") as &[u8]; @@ -11,38 +11,6 @@ pub use filesystem::InMemoryFS; fn create_memory_fs() -> InMemoryFS { let memoryfs = InMemoryFS::new(); - #[cfg(feature = "readme")] - { - // An example of interacting directly with the filesystem - let request_info = RequestInfo { - id: 0, - uid: 0, - gid: 0, - pid: 0, - }; // dummy RequestInfo - let (fd, (inode, _), _) = memoryfs - .create( - &request_info, - ROOT_INODE, - OsStr::new("README.md"), - 0o755, - 0, - OpenFlags::empty(), - ) - .unwrap(); - let _ = memoryfs - .write( - &request_info, - inode, - fd.borrow(), - SeekFrom::Start(0), - README_CONTENT.to_vec(), - FUSEWriteFlags::empty(), - OpenFlags::empty(), - None, - ) - .unwrap(); - } memoryfs } @@ -58,7 +26,10 @@ fn main() { let mountpoint = std::env::args() .nth(1) .expect("Usage: in_memory_fs "); - let options = vec![ + let mut config = easy_fuser::prelude::Config::default(); + config.acl = easy_fuser::prelude::SessionACL::Owner; + config.n_threads = Some(1); + config.mount_options = vec![ MountOption::RW, MountOption::FSName("in_memory_fs".to_string()), ]; @@ -66,5 +37,15 @@ fn main() { let memoryfs = create_memory_fs(); println!("Mounting filesystem..."); - easy_fuser::mount(memoryfs, Path::new(&mountpoint), &options, 1).unwrap(); + let session = easy_fuser::spawn_mount(memoryfs, Path::new(&mountpoint), &config).unwrap(); + println!("Filesystem mounted"); + fs::write( + Path::new(&mountpoint).join("README.md"), + README_CONTENT, + ) + .expect("Failed to write README.md"); + + std::io::stdin().read_line(&mut String::new()).unwrap(); + session.umount_and_join().unwrap(); + println!("Filesystem unmounted"); } diff --git a/examples/passthrough_fs/Cargo.toml b/examples/passthrough_fs/Cargo.toml index 7dbd525..6e5825d 100644 --- a/examples/passthrough_fs/Cargo.toml +++ b/examples/passthrough_fs/Cargo.toml @@ -7,4 +7,8 @@ description = "A FUSE passthrough filesystem example using easy_fuser" [dependencies] easy_fuser = { path = "../..", features = ["parallel"] } clap = { version = "4.5", features = ["derive"] } -ctrlc = "3.4" \ No newline at end of file +ctrlc = "3.4" + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file diff --git a/examples/passthrough_fs/src/main.rs b/examples/passthrough_fs/src/main.rs index a373b5e..43ad3f0 100644 --- a/examples/passthrough_fs/src/main.rs +++ b/examples/passthrough_fs/src/main.rs @@ -3,14 +3,14 @@ use clap::Parser; use ctrlc; use easy_fuser::prelude::*; -use easy_fuser::templates::mirror_fs::*; use easy_fuser::templates::DefaultFuseHandler; +use easy_fuser::templates::mirror_fs::*; use std::path::PathBuf; -use std::process::exit; use std::process::Command; +use std::process::exit; +use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; -use std::sync::Arc; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -75,7 +75,11 @@ fn main() -> Result<(), Box> { println!("Source directory: {:?}", fs.source_dir()); // Mount the filesystem - mount(fs, &mntpoint, &[], 1)?; + let mut config = easy_fuser::prelude::Config::default(); + config.acl = easy_fuser::prelude::SessionACL::Owner; + config.n_threads = Some(1); + config.mount_options = vec![]; + mount(fs, &mntpoint, &config)?; // If we reach here, the filesystem has been unmounted normally cleanup(&mntpoint, &once_flag); diff --git a/examples/random_fs/Cargo.toml b/examples/random_fs/Cargo.toml index 5b3e8d5..bdc5d6e 100644 --- a/examples/random_fs/Cargo.toml +++ b/examples/random_fs/Cargo.toml @@ -6,3 +6,7 @@ edition = "2021" [dependencies] easy_fuser = { path = "../..", features = ["parallel"] } rand = "0.8.5" + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file diff --git a/examples/random_fs/src/main.rs b/examples/random_fs/src/main.rs index 0118a71..049eb3b 100644 --- a/examples/random_fs/src/main.rs +++ b/examples/random_fs/src/main.rs @@ -77,11 +77,13 @@ impl FuseHandler for RandomFS { _mode: u32, _umask: u32, _flags: OpenFlags, + _helper: CreateHelper, ) -> Result< ( OwnedFileHandle, (Inode, FileAttribute), FUSEOpenResponseFlags, + Option, ), PosixError, > { @@ -93,6 +95,7 @@ impl FuseHandler for RandomFS { unsafe { OwnedFileHandle::from_raw(0) }, (ino, attr), FUSEOpenResponseFlags::empty(), + None, )) } @@ -172,7 +175,7 @@ impl FuseHandler for RandomFS { offset: SeekFrom, size: u32, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult> { let mut rng = rand::thread_rng(); let lines = rng.gen_range(0..81); @@ -238,7 +241,7 @@ impl FuseHandler for RandomFS { data: Vec, _write_flags: FUSEWriteFlags, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult { Ok(data.len() as u32) } @@ -252,13 +255,13 @@ fn main() { let mountpoint = std::env::args() .nth(1) .expect("Usage: random_fs "); - let options = vec![ - MountOption::RW, - MountOption::FSName("random_fs".to_string()), - ]; + let mut config = easy_fuser::prelude::Config::default(); + config.acl = easy_fuser::prelude::SessionACL::Owner; + config.n_threads = Some(1); + config.mount_options = vec![MountOption::RW, MountOption::FSName("random_fs".to_string())]; let fs = RandomFS::new(); println!("Mounting filesystem..."); - easy_fuser::mount(fs, Path::new(&mountpoint), &options, 1).unwrap(); + easy_fuser::mount(fs, Path::new(&mountpoint), &config).unwrap(); } diff --git a/examples/zip_fs/Cargo.toml b/examples/zip_fs/Cargo.toml index dbb265f..8dea325 100644 --- a/examples/zip_fs/Cargo.toml +++ b/examples/zip_fs/Cargo.toml @@ -12,3 +12,7 @@ ctrlc = "3.4" [dev-dependencies] tempfile = "3.2" + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file diff --git a/examples/zip_fs/src/filesystem.rs b/examples/zip_fs/src/filesystem.rs index dfc534e..c1f1d95 100644 --- a/examples/zip_fs/src/filesystem.rs +++ b/examples/zip_fs/src/filesystem.rs @@ -136,7 +136,7 @@ impl FuseHandler for ZipFs { seek: SeekFrom, size: u32, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult> { let InodeInfo { parent: _, diff --git a/examples/zip_fs/src/main.rs b/examples/zip_fs/src/main.rs index cce2aa3..5b08500 100644 --- a/examples/zip_fs/src/main.rs +++ b/examples/zip_fs/src/main.rs @@ -77,9 +77,14 @@ fn main() -> Result<(), Box> { println!("Mounting ZIP filesystem..."); println!("ZIP file: {:?}", &zip_file); println!("Mount point: {:?}", &mount_point); + + let mut config = easy_fuser::prelude::Config::default(); + config.acl = easy_fuser::prelude::SessionACL::Owner; + config.n_threads = Some(1); + config.mount_options = vec![]; // Mount the filesystem - easy_fuser::mount(zip_fs, &mount_point, &[])?; + easy_fuser::mount(zip_fs, &mount_point, &config)?; // If we reach here, the filesystem has been unmounted normally cleanup(&mount_point, &once_flag); diff --git a/src/inode_multi_mapper.rs b/src/inode_multi_mapper.rs index 94628e6..ff9dfa5 100644 --- a/src/inode_multi_mapper.rs +++ b/src/inode_multi_mapper.rs @@ -979,7 +979,7 @@ mod tests { let child_name = OsString::from("child"); // Insert the first child - let first_child_inode = Inode::from(2); + let first_child_inode = Inode::new(2); assert_eq!( mapper.insert_child(&root, child_name.clone(), None, |value_creator_params| { assert!(value_creator_params.existing_data.is_none()); diff --git a/src/lib.rs b/src/lib.rs index c22d831..9d748db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,15 +30,13 @@ pub mod unix_fs; pub use fuse_handler::FuseHandler; use fuser::BackgroundSession; -use fuser::Config; pub mod prelude { //! Re-exports the necessary types and functions from the `easy_fuser` crate. pub use super::fuse_handler::FuseHandler; pub use super::types::*; pub use super::{mount, spawn_mount}; - - pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; + pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter, Config, SessionACL}; } // Implentation of the high-level functions diff --git a/src/types.rs b/src/types.rs index 66f0a3d..632bbdc 100644 --- a/src/types.rs +++ b/src/types.rs @@ -28,5 +28,5 @@ pub use self::{ arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, helpers::*, inode::*, }; pub use fuser::{ - Config, FileType as FileKind, Generation, IoctlFlags, KernelConfig, LockOwner, TimeOrNow, + FileType as FileKind, Generation, IoctlFlags, KernelConfig, LockOwner, TimeOrNow, }; diff --git a/templates/fuse_driver.jinja b/templates/fuse_driver.jinja index 485277b..cf072ee 100644 --- a/templates/fuse_driver.jinja +++ b/templates/fuse_driver.jinja @@ -24,7 +24,10 @@ use crate::core::inode_mapping::FileIdResolver; type DirIter = HashMap<(INodeNo, u64), VecDeque<(OsString, INodeNo, TAttr)>>; {% if mode == "serial" %} - use std::cell::RefCell; + #[cfg(feature = "deadlock_detection")] + use parking_lot::Mutex; + #[cfg(not(feature = "deadlock_detection"))] + use std::sync::Mutex; {% elif mode == "parallel" %} use std::sync::Arc; @@ -49,8 +52,8 @@ where {% if !send_sync %} handler: THandler, resolver: TId::Resolver, - dirmap_iter: RefCell>, - dirmapplus_iter: RefCell>, + dirmap_iter: Mutex>, + dirmapplus_iter: Mutex>, {% else %} handler: Arc, resolver: Arc, @@ -101,8 +104,8 @@ where {% if mode == "serial" %} handler, resolver: TId::Resolver::new(), - dirmap_iter: RefCell::new(HashMap::new()), - dirmapplus_iter: RefCell::new(HashMap::new()), + dirmap_iter: Mutex::new(HashMap::new()), + dirmapplus_iter: Mutex::new(HashMap::new()), {% else %} handler: Arc::new(handler), resolver: Arc::new(TId::create_resolver()), @@ -128,11 +131,11 @@ where &self.resolver } - pub fn get_dirmap_iter(&self) -> &RefCell> { + pub fn get_dirmap_iter(&self) -> &Mutex> { &self.dirmap_iter } - pub fn get_dirmapplus_iter(&self) -> &RefCell> { + pub fn get_dirmapplus_iter(&self) -> &Mutex> { &self.dirmapplus_iter } {% else %} diff --git a/templates/fuse_handler.jinja b/templates/fuse_handler.jinja index db91e25..ff462be 100644 --- a/templates/fuse_handler.jinja +++ b/templates/fuse_handler.jinja @@ -88,9 +88,6 @@ /// /// Documentation is inspired by the original fuser documentation -{% let send_sync = mode != "serial" %} - - use std::ffi::{OsStr, OsString}; use std::path::Path; use std::time::Duration; @@ -102,7 +99,7 @@ use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% endif %} { +pub trait FuseHandler: 'static + Sync + Send { {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} {% macro await_keyword() -%}{% if mode == "async" %}.await{% endif %}{%- endmacro %} From c2549a433775f99ec72d802794d953611d2b7890 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 6 Feb 2026 12:18:27 +0700 Subject: [PATCH 18/20] fix: example crate fixes --- examples/ftp_fs/Cargo.toml | 3 +-- examples/ftp_fs/src/main.rs | 14 ++++++-------- examples/hello_fs/Cargo.toml | 3 +-- examples/hello_fs/src/main.rs | 8 ++++---- examples/in_memory_fs/Cargo.toml | 3 +-- examples/in_memory_fs/src/main.rs | 30 +++++++++++++++-------------- examples/passthrough_fs/Cargo.toml | 3 +-- examples/passthrough_fs/src/main.rs | 9 +++++---- examples/random_fs/Cargo.toml | 3 +-- examples/random_fs/src/main.rs | 9 +++++---- examples/zip_fs/Cargo.toml | 3 +-- examples/zip_fs/src/main.rs | 9 ++++----- 12 files changed, 46 insertions(+), 51 deletions(-) diff --git a/examples/ftp_fs/Cargo.toml b/examples/ftp_fs/Cargo.toml index 7a67c66..93c5f07 100644 --- a/examples/ftp_fs/Cargo.toml +++ b/examples/ftp_fs/Cargo.toml @@ -24,5 +24,4 @@ async-trait = "0.1.68" tokio = { version = "1.42", features = ["full"] } [patch.crates-io] -# Use the updated branch -fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/ftp_fs/src/main.rs b/examples/ftp_fs/src/main.rs index 945f929..d19ec74 100644 --- a/examples/ftp_fs/src/main.rs +++ b/examples/ftp_fs/src/main.rs @@ -20,8 +20,8 @@ use std::path::PathBuf; use std::process::exit; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use clap::{Parser, ValueEnum}; use ctrlc; @@ -139,14 +139,12 @@ fn main() -> Result<(), Box> { println!("Mount point: {:?}", &mount_point); // Mount the filesystem - let config = { - let mut config = easy_fuser::prelude::Config::default(); - config.acl = easy_fuser::prelude::SessionACL::Owner; - config.n_threads = Some(4); - config.mount_options = vec![]; - config + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + num_threads: 4, }; - + easy_fuser::mount(ftp_fs, &mount_point, &config)?; // If we reach here, the filesystem has been unmounted normally diff --git a/examples/hello_fs/Cargo.toml b/examples/hello_fs/Cargo.toml index 2453a50..fb348e9 100644 --- a/examples/hello_fs/Cargo.toml +++ b/examples/hello_fs/Cargo.toml @@ -12,5 +12,4 @@ log = { version = "0.4", optional = true } env_logger = { version = "0.11", optional = true } [patch.crates-io] -# Use the updated branch -fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/hello_fs/src/main.rs b/examples/hello_fs/src/main.rs index c7ab89e..a7449ad 100644 --- a/examples/hello_fs/src/main.rs +++ b/examples/hello_fs/src/main.rs @@ -167,10 +167,10 @@ fn main() { let mountpoint = std::env::args().nth(1).expect("Usage: hello "); - let mut config = easy_fuser::prelude::Config::default(); - config.acl = easy_fuser::prelude::SessionACL::Owner; - config.n_threads = Some(1); - config.mount_options = vec![MountOption::RO, MountOption::FSName("hello".to_string())]; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + }; println!("Mounting FTP filesystem..."); easy_fuser::mount(HelloFS::new(), Path::new(&mountpoint), &config).unwrap(); diff --git a/examples/in_memory_fs/Cargo.toml b/examples/in_memory_fs/Cargo.toml index acdf4f1..ed6b77f 100644 --- a/examples/in_memory_fs/Cargo.toml +++ b/examples/in_memory_fs/Cargo.toml @@ -14,5 +14,4 @@ log = { version = "0.4", optional = true } env_logger = { version = "0.11", optional = true } [patch.crates-io] -# Use the updated branch -fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/in_memory_fs/src/main.rs b/examples/in_memory_fs/src/main.rs index d40e1ce..2df012f 100644 --- a/examples/in_memory_fs/src/main.rs +++ b/examples/in_memory_fs/src/main.rs @@ -11,6 +11,8 @@ pub use filesystem::InMemoryFS; fn create_memory_fs() -> InMemoryFS { let memoryfs = InMemoryFS::new(); + // NOTE: manual call example here is removed because the [`CreateHelper`] + // parameter is not supported memoryfs } @@ -26,26 +28,26 @@ fn main() { let mountpoint = std::env::args() .nth(1) .expect("Usage: in_memory_fs "); - let mut config = easy_fuser::prelude::Config::default(); - config.acl = easy_fuser::prelude::SessionACL::Owner; - config.n_threads = Some(1); - config.mount_options = vec![ - MountOption::RW, - MountOption::FSName("in_memory_fs".to_string()), - ]; - + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + num_threads: 4, + }; let memoryfs = create_memory_fs(); println!("Mounting filesystem..."); let session = easy_fuser::spawn_mount(memoryfs, Path::new(&mountpoint), &config).unwrap(); println!("Filesystem mounted"); - fs::write( - Path::new(&mountpoint).join("README.md"), - README_CONTENT, - ) - .expect("Failed to write README.md"); + fs::write(Path::new(&mountpoint).join("README.md"), README_CONTENT) + .expect("Failed to write README.md"); std::io::stdin().read_line(&mut String::new()).unwrap(); - session.umount_and_join().unwrap(); + session + .join(&[]) + .map_err(|(_session, error)| { + println!("Error unmounting filesystem: {:?}", error); + error + }) + .unwrap(); println!("Filesystem unmounted"); } diff --git a/examples/passthrough_fs/Cargo.toml b/examples/passthrough_fs/Cargo.toml index 6e5825d..d89fbf6 100644 --- a/examples/passthrough_fs/Cargo.toml +++ b/examples/passthrough_fs/Cargo.toml @@ -10,5 +10,4 @@ clap = { version = "4.5", features = ["derive"] } ctrlc = "3.4" [patch.crates-io] -# Use the updated branch -fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/passthrough_fs/src/main.rs b/examples/passthrough_fs/src/main.rs index 43ad3f0..1a433c3 100644 --- a/examples/passthrough_fs/src/main.rs +++ b/examples/passthrough_fs/src/main.rs @@ -75,10 +75,11 @@ fn main() -> Result<(), Box> { println!("Source directory: {:?}", fs.source_dir()); // Mount the filesystem - let mut config = easy_fuser::prelude::Config::default(); - config.acl = easy_fuser::prelude::SessionACL::Owner; - config.n_threads = Some(1); - config.mount_options = vec![]; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + num_threads: 1, + }; mount(fs, &mntpoint, &config)?; // If we reach here, the filesystem has been unmounted normally diff --git a/examples/random_fs/Cargo.toml b/examples/random_fs/Cargo.toml index bdc5d6e..da93b65 100644 --- a/examples/random_fs/Cargo.toml +++ b/examples/random_fs/Cargo.toml @@ -8,5 +8,4 @@ easy_fuser = { path = "../..", features = ["parallel"] } rand = "0.8.5" [patch.crates-io] -# Use the updated branch -fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/random_fs/src/main.rs b/examples/random_fs/src/main.rs index 049eb3b..cb28740 100644 --- a/examples/random_fs/src/main.rs +++ b/examples/random_fs/src/main.rs @@ -255,10 +255,11 @@ fn main() { let mountpoint = std::env::args() .nth(1) .expect("Usage: random_fs "); - let mut config = easy_fuser::prelude::Config::default(); - config.acl = easy_fuser::prelude::SessionACL::Owner; - config.n_threads = Some(1); - config.mount_options = vec![MountOption::RW, MountOption::FSName("random_fs".to_string())]; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + num_threads: 1, + }; let fs = RandomFS::new(); diff --git a/examples/zip_fs/Cargo.toml b/examples/zip_fs/Cargo.toml index 8dea325..c165bb3 100644 --- a/examples/zip_fs/Cargo.toml +++ b/examples/zip_fs/Cargo.toml @@ -14,5 +14,4 @@ ctrlc = "3.4" tempfile = "3.2" [patch.crates-io] -# Use the updated branch -fuser = { git = "https://github.com/cberner/fuser", branch = "master" } \ No newline at end of file +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/zip_fs/src/main.rs b/examples/zip_fs/src/main.rs index 5b08500..ab92454 100644 --- a/examples/zip_fs/src/main.rs +++ b/examples/zip_fs/src/main.rs @@ -78,11 +78,10 @@ fn main() -> Result<(), Box> { println!("ZIP file: {:?}", &zip_file); println!("Mount point: {:?}", &mount_point); - let mut config = easy_fuser::prelude::Config::default(); - config.acl = easy_fuser::prelude::SessionACL::Owner; - config.n_threads = Some(1); - config.mount_options = vec![]; - + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + }; // Mount the filesystem easy_fuser::mount(zip_fs, &mount_point, &config)?; From 77ae37f2f03b0ee81d27f340208d6a1dba50f93a Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 6 Mar 2026 11:31:43 +0700 Subject: [PATCH 19/20] fix: separate join with umount_and_join() --- src/session.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/session.rs b/src/session.rs index f7fad8d..f98adda 100644 --- a/src/session.rs +++ b/src/session.rs @@ -38,8 +38,19 @@ impl FuseSession { /// Join the background session, waiting for the filesystem to unmount. /// /// This method blocks until the filesystem is unmounted. - pub fn join(self, flags: &[UnmountOption]) -> Result<(), (Option, io::Error)> { - let Self { session, resolver } = self; + pub fn join(self) -> Result<(), io::Error> { + let Self { session, .. } = self; + session.join() + } + + /// Unmount the filesystem and join the background session. + /// + /// This method unmounts the filesystem and joins the background session. + /// + /// # Parameters + /// * `flags`: Flags to pass to the unmount operation. + pub fn umount_and_join(self, flags: &[UnmountOption]) -> Result<(), (Option, io::Error)> { + let Self { session, .. } = self; session .umount_and_join(flags) .map_err(|(session, error)| (session.map(|session| Self { session, resolver }), error)) From 5807427745b4f734dbb533c532893f493530ce41 Mon Sep 17 00:00:00 2001 From: Khanh Tran Date: Fri, 6 Mar 2026 11:35:49 +0700 Subject: [PATCH 20/20] fix: minor compiler errors --- src/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/session.rs b/src/session.rs index f98adda..6f59b94 100644 --- a/src/session.rs +++ b/src/session.rs @@ -50,7 +50,7 @@ impl FuseSession { /// # Parameters /// * `flags`: Flags to pass to the unmount operation. pub fn umount_and_join(self, flags: &[UnmountOption]) -> Result<(), (Option, io::Error)> { - let Self { session, .. } = self; + let Self { session, resolver } = self; session .umount_and_join(flags) .map_err(|(session, error)| (session.map(|session| Self { session, resolver }), error))