From 5df8f738c694f081f1d4cb43e3b75c7bf2999ae3 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 1 Feb 2026 17:19:11 +0100 Subject: [PATCH 01/11] build.rs take into account feature flags --- build.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index e90d1b8..e4a3f77 100644 --- a/build.rs +++ b/build.rs @@ -37,7 +37,18 @@ fn main() -> std::io::Result<()> { let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR not set"); - for mode in [Modes::Serial, Modes::Parallel] { + let mut modes = vec![]; + if env::var("CARGO_FEATURE_SERIAL").is_ok() { + modes.push(Modes::Serial); + } + if env::var("CARGO_FEATURE_PARALLEL").is_ok() { + modes.push(Modes::Parallel); + } + if env::var("CARGO_FEATURE_ASYNC").is_ok() { + modes.push(Modes::Async); + } + + for mode in modes { let mode = mode.as_str(); let mode_dir = Path::new(&out_dir).join(mode); fs::create_dir_all(&mode_dir)?; From 1e2ee9a4dc3377f59777554167a7914c5113ad03 Mon Sep 17 00:00:00 2001 From: "clement@desktop" Date: Sun, 1 Feb 2026 19:41:56 +0000 Subject: [PATCH 02/11] dsl for function signatures --- Cargo.toml | 1 + build.rs | 13 +- easy_fuser_macro/Cargo.toml | 13 + .../src/fuse_handlers_signatures.rs | 326 ++++++++++++++++++ easy_fuser_macro/src/lib.rs | 109 ++++++ src/core/fuse_driver.rs | 2 - src/core/helpers.rs | 4 +- src/lib.rs | 2 +- src/templates/default_fuse_handler.rs | 17 - src/templates/fd_handler_helper.rs | 20 +- src/templates/mirror_fs.rs | 22 +- src/types/file_id_type.rs | 2 +- templates.rs | 2 +- templates/fuse_handler.jinja | 227 +++--------- 14 files changed, 516 insertions(+), 244 deletions(-) create mode 100644 easy_fuser_macro/Cargo.toml create mode 100644 easy_fuser_macro/src/fuse_handlers_signatures.rs create mode 100644 easy_fuser_macro/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2479b7d..b72a459 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ deadlock_detection = ["parallel", "dep:parking_lot"] [dependencies] # Core dependencies +easy_fuser_macro = { version = "0.1", path = "easy_fuser_macro"} log = "0.4" libc = "0.2" fuser = "0.16" diff --git a/build.rs b/build.rs index e4a3f77..846a956 100644 --- a/build.rs +++ b/build.rs @@ -10,7 +10,7 @@ use crate::templates::*; pub enum Modes { Serial, Parallel, - Async + Async, } impl Modes { @@ -23,7 +23,6 @@ impl Modes { } } - fn main() -> std::io::Result<()> { let template_dir = "templates"; println!("cargo:rerun-if-changed={template_dir}"); @@ -54,16 +53,10 @@ fn main() -> std::io::Result<()> { fs::create_dir_all(&mode_dir)?; let content = FuseDriverTemplate { mode }.render()?; - fs::write( - mode_dir.join("fuse_driver.rs"), - content - )?; + fs::write(mode_dir.join("fuse_driver.rs"), content)?; let content = FuseHandlerTemplate { mode }.render()?; - fs::write( - mode_dir.join("fuse_handler.rs"), - content - )?; + fs::write(mode_dir.join("fuse_handler.rs"), content)?; } Ok(()) diff --git a/easy_fuser_macro/Cargo.toml b/easy_fuser_macro/Cargo.toml new file mode 100644 index 0000000..82e8779 --- /dev/null +++ b/easy_fuser_macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "easy_fuser_macro" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +syn = {version = "2.0", features = [ "full" ] } +quote = "1.0" +proc-macro2 = "1.0" +either = "1.15.0" diff --git a/easy_fuser_macro/src/fuse_handlers_signatures.rs b/easy_fuser_macro/src/fuse_handlers_signatures.rs new file mode 100644 index 0000000..ab1b4f9 --- /dev/null +++ b/easy_fuser_macro/src/fuse_handlers_signatures.rs @@ -0,0 +1,326 @@ +use proc_macro2::TokenStream; +use syn::{TraitItemFn, parse_quote}; + + +pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> TraitItemFn { + match func_name { + "get_default_ttl" => parse_quote! { + fn get_default_ttl(&self) -> Duration #tail + }, + "init" => parse_quote! { + fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> #tail + }, + "destroy" => parse_quote! { + fn destroy(&self) #tail + }, + "access" => parse_quote! { + fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> #tail + }, + "bmap" => parse_quote! { + fn bmap( + &self, + req: &RequestInfo, + file_id: TId, + blocksize: u32, + idx: u64, + ) -> FuseResult #tail + }, + "copy_file_range" => parse_quote! { + fn copy_file_range( + &self, + req: &RequestInfo, + file_in: TId, + file_handle_in: BorrowedFileHandle<'_>, + offset_in: i64, + file_out: TId, + file_handle_out: BorrowedFileHandle<'_>, + offset_out: i64, + len: u64, + ) -> FuseResult #tail + }, + "create" => parse_quote! { + fn create( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + mode: u32, + umask: u32, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> #tail + }, + "fallocate" => parse_quote! { + fn fallocate( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + offset: i64, + length: i64, + mode: FallocateFlags, + ) -> FuseResult<()> #tail + }, + "flush" => parse_quote! { + fn flush( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + ) -> FuseResult<()> #tail + }, + "forget" => parse_quote! { + fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) #tail + }, + "fsync" => parse_quote! { + fn fsync( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + datasync: bool, + ) -> FuseResult<()> #tail + }, + "fsyncdir" => parse_quote! { + fn fsyncdir( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + datasync: bool, + ) -> FuseResult<()> #tail + }, + "getattr" => parse_quote! { + fn getattr( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: Option>, + ) -> FuseResult #tail + }, + "getlk" => parse_quote! { + fn getlk( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + lock_info: LockInfo, + ) -> FuseResult #tail + }, + "getxattr" => parse_quote! { + fn getxattr( + &self, + req: &RequestInfo, + file_id: TId, + name: &OsStr, + size: u32, + ) -> FuseResult> #tail + }, + "ioctl" => parse_quote! { + fn ioctl( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + flags: IOCtlFlags, + cmd: u32, + in_data: Vec, + out_size: u32, + ) -> FuseResult<(i32, Vec)> #tail + }, + "link" => parse_quote! { + fn link( + &self, + req: &RequestInfo, + file_id: TId, + newparent: TId, + newname: &OsStr, + ) -> FuseResult #tail + }, + "listxattr" => parse_quote! { + fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> + }, + "lookup" => parse_quote! { + fn lookup( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + ) -> FuseResult #tail + }, + "lseek" => parse_quote! { + fn lseek( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + ) -> FuseResult #tail + }, + "mkdir" => parse_quote! { + fn mkdir( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + mode: u32, + umask: u32, + ) -> FuseResult #tail + }, + "mknod" => parse_quote! { + fn mknod( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + mode: u32, + umask: u32, + rdev: DeviceType, + ) -> FuseResult #tail + }, + "open" => parse_quote! { + fn open( + &self, + req: &RequestInfo, + file_id: TId, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> #tail + }, + "opendir" => parse_quote! { + fn opendir( + &self, + req: &RequestInfo, + file_id: TId, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> #tail + }, + "read" => parse_quote! { + fn read( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + size: u32, + flags: FUSEOpenFlags, + lock_owner: Option, + ) -> FuseResult> #tail + }, + "readdir" => parse_quote! { + fn readdir( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult> #tail + }, + "readdirplus" => parse_quote! { + fn readdirplus( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle, + ) -> FuseResult> #tail + }, + "readlink" => parse_quote! { + fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult> #tail + }, + "release" => parse_quote! { + fn release( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: OwnedFileHandle, + flags: OpenFlags, + lock_owner: Option, + flush: bool, + ) -> FuseResult<()> #tail + }, + "releasedir" => parse_quote! { + fn releasedir( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: OwnedFileHandle, + flags: OpenFlags, + ) -> FuseResult<()> #tail + }, + "removexattr" => parse_quote! { + fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> #tail + }, + "rename" => parse_quote! { + fn rename( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + newparent: TId, + newname: &OsStr, + flags: RenameFlags, + ) -> FuseResult<()> #tail + }, + "rmdir" => parse_quote! { + fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> #tail + }, + "setattr" => parse_quote! { + fn setattr( + &self, + req: &RequestInfo, + file_id: TId, + attrs: SetAttrRequest<'_>, + ) -> FuseResult #tail + }, + "setlk" => parse_quote! { + fn setlk( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + lock_info: LockInfo, + sleep: bool, + ) -> FuseResult<()> #tail + }, + "setxattr" => parse_quote! { + fn setxattr( + &self, + req: &RequestInfo, + file_id: TId, + name: &OsStr, + value: Vec, + flags: FUSESetXAttrFlags, + position: u32, + ) -> FuseResult<()> #tail + }, + "statfs" => parse_quote! { + fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult #tail + }, + "symlink" => parse_quote! { + fn symlink( + &self, + req: &RequestInfo, + parent_id: TId, + link_name: &OsStr, + target: &Path, + ) -> FuseResult #tail + }, + "write" => parse_quote! { + fn write( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + data: Vec, + write_flags: FUSEWriteFlags, + flags: OpenFlags, + lock_owner: Option, + ) -> FuseResult #tail + }, + "unlink" => parse_quote! { + fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> #tail + }, + _ => panic!("unknown function signature"), + } +} diff --git a/easy_fuser_macro/src/lib.rs b/easy_fuser_macro/src/lib.rs new file mode 100644 index 0000000..712f232 --- /dev/null +++ b/easy_fuser_macro/src/lib.rs @@ -0,0 +1,109 @@ +extern crate proc_macro; + +mod fuse_handlers_signatures; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + Attribute, ExprBlock, Ident, LitStr, Result, Token, parse::{Parse, ParseStream}, parse_macro_input, + token::{FatArrow, Group, Semi} +}; +use either::Either; + +struct FnSigInput { + attrs: Vec, + name: Ident, + tail: Either, +} + +impl Parse for FnSigInput { + fn parse(input: ParseStream) -> Result { + let mut attrs = Vec::new(); + while input.peek(Token![#]) { + // Stop if it's an inner attribute #![...] + if input.peek2(Token![!]) { + break; + } + attrs.extend(input.call(Attribute::parse_outer)?); + } + + let name: Ident = input.parse()?; + let _arrow: FatArrow = input.parse()?; + + let tail = if input.peek(Token![;]) { + Either::Left(input.parse::()?) + } else { + Either::Right(input.parse::()?) + }; + + Ok(FnSigInput { + attrs, + name, + tail, + }) + } +} + + +/// Usage: +/// 1. ```fuse_handler_fnsig!{ +/// /// doc... +/// init => { _default_implementation_ } +/// } +/// ``` +/// 1. ```fuse_handler_fnsig!{ +/// /// doc... +/// access => ; +/// } +/// ``` +#[proc_macro] +pub fn fuse_handler_fnsig(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as FnSigInput); + let attrs = input.attrs; + let tail_tokens: proc_macro2::TokenStream = match input.tail { + Either::Left(semi) => quote!( #semi ), + Either::Right(block) => { + quote!( #block ) // reconstruct braced block + } + }; + + let fun_impl = fuse_handlers_signatures::get_fuse_handler_fn_impl( + &input.name.to_string(), + Some(tail_tokens), + ); + quote!( + #(#attrs)* + #fun_impl + ).into() +} + +/* +#[proc_macro] +pub fn delegate(input: TokenStream) -> TokenStream { + let parsed: syn::ExprTuple = parse_macro_input!(input); + let field_expr: Expr = parsed.elems[0].clone(); // self.fuse_handler + let methods_vec: syn::ExprVec = syn::parse2(parsed.elems[1].to_token_stream()).unwrap(); + + let mut delegations = vec![]; + for method in methods_vec.elems { + let method_ident = if let syn::Expr::Path(p) = method { p.path.get_ident().unwrap().clone() } else { panic!("Expected ident") }; + let method_str = method_ident.to_string(); + + // Find matching signature + let sig = METHODS.iter().find(|(name, _)| *name == method_str).map(|(_, sig)| sig).unwrap_or_else(|| panic!("Unknown method: {}", method_str)); + let sig_parsed: syn::Signature = syn::parse_str(sig).unwrap(); + + // Generate delegation + let args: Vec = sig_parsed.inputs.iter().skip(1).map(|arg| if let syn::FnArg::Typed(t) = arg { t.pat.as_ref().clone() } else { panic!() }).collect(); // Skip &self + let arg_idents: Vec<_> = args.iter().map(|pat| if let syn::Pat::Ident(i) = pat { i.ident.clone() } else { panic!() }).collect(); + + delegations.push(quote! { + #sig_parsed { + #field_expr.#method_ident(#(#arg_idents),*) + } + }); + } + + quote!(#(#delegations)*).into() +} + */ diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs index 5fe77bb..8440ed8 100644 --- a/src/core/fuse_driver.rs +++ b/src/core/fuse_driver.rs @@ -6,7 +6,6 @@ mod serial { #[cfg(feature = "parallel")] mod parallel { include!(concat!(env!("OUT_DIR"), "/parallel/fuse_driver.rs")); - } #[cfg(feature = "async")] @@ -22,4 +21,3 @@ pub(crate) use parallel::*; #[cfg(feature = "async")] pub(crate) use async_task::*; - diff --git a/src/core/helpers.rs b/src/core/helpers.rs index f5b2248..d1c2a3d 100644 --- a/src/core/helpers.rs +++ b/src/core/helpers.rs @@ -1,7 +1,7 @@ use std::time::Instant; #[cfg(feature = "deadlock_detection")] -pub (super) fn spawn_deadlock_checker() { +pub(super) fn spawn_deadlock_checker() { use log::{error, info}; use parking_lot::deadlock; use std::thread; @@ -30,4 +30,4 @@ pub (super) fn spawn_deadlock_checker() { pub(super) fn get_random_generation() -> u64 { Instant::now().elapsed().as_nanos() as u64 -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index fd034e2..4906c3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ mod fuse_handler; pub mod inode_mapper; pub mod inode_multi_mapper; -pub mod templates; +//pub mod templates; pub mod types; pub mod unix_fs; diff --git a/src/templates/default_fuse_handler.rs b/src/templates/default_fuse_handler.rs index a0fc377..aaed060 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/templates/default_fuse_handler.rs @@ -1,11 +1,8 @@ use std::{ ffi::{OsStr, OsString}, path::Path, - time::Duration, }; -use fuser::KernelConfig; - use crate::prelude::*; /** @@ -86,20 +83,6 @@ impl DefaultFuseHandler { } impl FuseHandler for DefaultFuseHandler { - fn get_inner(&self) -> &dyn FuseHandler { - panic!("Base Fuse don't have inner type") - } - - fn get_default_ttl(&self) -> Duration { - Duration::from_secs(1) - } - - fn init(&self, _req: &RequestInfo, _config: &mut KernelConfig) -> FuseResult<()> { - Ok(()) - } - - fn destroy(&self) {} - fn access(&self, _req: &RequestInfo, file_id: TId, mask: AccessMask) -> 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..55392ec 100644 --- a/src/templates/fd_handler_helper.rs +++ b/src/templates/fd_handler_helper.rs @@ -69,6 +69,8 @@ If you intend to enforce read-only at the fuse level, prefer the usage of option `MountOption::RO` instead of `FdHandlerHelperReadOnly`. */ +use std::marker::PhantomData; + use crate::prelude::*; use crate::unix_fs; @@ -184,43 +186,35 @@ macro_rules! fd_handler_readwrite_methods { /// Specific documentation is located in parent module documentation. pub struct FdHandlerHelper { - inner: Box>, + phantom: PhantomData, } impl FdHandlerHelper { - pub fn new>(inner: THandler) -> Self { + pub fn new>() -> Self { Self { - inner: Box::new(inner), + phantom: PhantomData, } } } impl FuseHandler for FdHandlerHelper { - fn get_inner(&self) -> &dyn FuseHandler { - self.inner.as_ref() - } - fd_handler_readonly_methods!(); fd_handler_readwrite_methods!(); } /// Specific documentation is located in parent module documentation. pub struct FdHandlerHelperReadOnly { - inner: Box>, + phantom: PhantomData, } impl FdHandlerHelperReadOnly { pub fn new>(inner: THandler) -> Self { Self { - inner: Box::new(inner), + phantom: PhantomData, } } } impl FuseHandler for FdHandlerHelperReadOnly { - fn get_inner(&self) -> &dyn FuseHandler { - self.inner.as_ref() - } - fd_handler_readonly_methods!(); } diff --git a/src/templates/mirror_fs.rs b/src/templates/mirror_fs.rs index 36fc585..81de567 100644 --- a/src/templates/mirror_fs.rs +++ b/src/templates/mirror_fs.rs @@ -285,7 +285,7 @@ macro_rules! mirror_fs_readwrite_methods { } pub trait MirrorFsTrait: FuseHandler { - fn new>(source_path: PathBuf, inner: U) -> Self; + fn new(source_path: PathBuf) -> Self; fn source_dir(&self) -> &Path; } @@ -293,15 +293,11 @@ pub trait MirrorFsTrait: FuseHandler { /// Specific documentation is located in parent module documentation. pub struct MirrorFs { source_path: PathBuf, - inner: Box>, } impl MirrorFsTrait for MirrorFs { - fn new>(source_path: PathBuf, inner: U) -> Self { - Self { - source_path, - inner: Box::new(FdHandlerHelper::new(inner)), - } + fn new(source_path: PathBuf) -> Self { + Self { source_path } } fn source_dir(&self) -> &Path { @@ -310,10 +306,6 @@ impl MirrorFsTrait for MirrorFs { } impl FuseHandler for MirrorFs { - fn get_inner(&self) -> &dyn FuseHandler { - self.inner.as_ref() - } - mirror_fs_readonly_methods!(); mirror_fs_readwrite_methods!(); } @@ -321,15 +313,11 @@ impl FuseHandler for MirrorFs { /// Specific documentation is located in parent module documentation. pub struct MirrorFsReadOnly { source_path: PathBuf, - inner: Box>, } impl MirrorFsTrait for MirrorFsReadOnly { - fn new>(source_path: PathBuf, inner: THandler) -> Self { - Self { - source_path, - inner: Box::new(FdHandlerHelperReadOnly::new(inner)), - } + fn new(source_path: PathBuf) -> Self { + Self { source_path } } fn source_dir(&self) -> &Path { diff --git a/src/types/file_id_type.rs b/src/types/file_id_type.rs index f98a68e..faab8d1 100644 --- a/src/types/file_id_type.rs +++ b/src/types/file_id_type.rs @@ -33,7 +33,7 @@ use fuser::FileType as FileKind; /// - Pros: Slightly lower overhead than PathBuf, allows path to be divided into parts. /// - Cons: Path components are stored in reverse order, which may require additional handling. /// - Root: Represented by an empty vector. -/// +/// /// 3. `Inode`: The user provides their own unique inode numbers. /// - Pros: Direct control over inode assignment. /// - Cons: Requires manual management of inode uniqueness. diff --git a/templates.rs b/templates.rs index c052a0b..4615d25 100644 --- a/templates.rs +++ b/templates.rs @@ -10,4 +10,4 @@ pub struct FuseDriverTemplate<'a> { #[template(path = "fuse_handler.jinja")] pub struct FuseHandlerTemplate<'a> { pub mode: &'a str, -} \ No newline at end of file +} diff --git a/templates/fuse_handler.jinja b/templates/fuse_handler.jinja index 6c5c60b..f97f169 100644 --- a/templates/fuse_handler.jinja +++ b/templates/fuse_handler.jinja @@ -96,6 +96,7 @@ use std::path::Path; use std::time::Duration; use crate::types::*; +use easy_fuser_macro::fuse_handler_fnsig; {% if mode == "async" %} use async_trait::async_trait; @@ -107,9 +108,6 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} {% macro await_keyword() -%}{% if mode == "async" %}.await{% endif %}{%- endmacro %} - /// Delegate unprovided methods to another FuseHandler, enabling composition - fn get_inner(&self) -> &dyn FuseHandler; - /// Provide a default Time-To-Live for file metadata /// /// Can be overriden for each FileAttributes returned. @@ -118,13 +116,19 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send } /// Initialize the filesystem and configure kernel connection + /// + /// A default implementation is provided that does nothing fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> { - self.get_inner().init(req, config) + let _req = req; + let _config = config; + Ok(()) } - /// Perform cleanup operations on filesystem exit - fn destroy(&self) { - self.get_inner().destroy(); + fuse_handler_fnsig!{ + /// Perform cleanup operations on filesystem exit + /// + /// A default implementation is provided that does nothing + destroy => {} } /// Check file access permissions @@ -132,19 +136,13 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send /// This method is called for the access() system call. If the 'default_permissions' /// mount option is given, this method is not called. This method is not called /// under Linux kernel versions 2.4.x - {{ async_keyword() }} fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> { - self.get_inner().access(req, file_id, mask) - {{ await_keyword() }} - } + {{ async_keyword() }} fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()>; /// Map block index within file to block index within device /// /// Note: This makes sense only for block device backed filesystems mounted /// with the 'blkdev' option - {{ async_keyword() }} fn bmap(&self, req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult { - self.get_inner().bmap(req, file_id, blocksize, idx) - {{ await_keyword() }} - } + {{ async_keyword() }} fn bmap(&self, req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult; /// Copy the specified range from the source inode to the destination inode {{ async_keyword() }} fn copy_file_range( @@ -158,19 +156,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send offset_out: i64, len: u64, flags: u32, // Not implemented yet in standard - ) -> FuseResult { - self.get_inner().copy_file_range( - req, - file_in, - file_handle_in, - offset_in, - file_out, - file_handle_out, - offset_out, - len, - flags, - ){{ await_keyword() }} - } + ) -> FuseResult; /// Create and open a file /// @@ -186,11 +172,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> { - self.get_inner() - .create(req, parent_id, name, mode, umask, flags) - {{ await_keyword() }} - } + ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)>; /// Preallocate or deallocate space to a file {{ async_keyword() }} fn fallocate( @@ -201,11 +183,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send offset: i64, length: i64, mode: FallocateFlags, - ) -> FuseResult<()> { - self.get_inner() - .fallocate(req, file_id, file_handle, offset, length, mode) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Flush cached data for an open file /// @@ -217,16 +195,10 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, file_handle: BorrowedFileHandle, lock_owner: u64, - ) -> FuseResult<()> { - self.get_inner() - .flush(req, file_id, file_handle, lock_owner) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Release references to an inode, if the nlookup count reaches zero (to substract from the number of lookups). - {{ async_keyword() }} fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) { - self.get_inner().forget(req, file_id, nlookup){{ await_keyword() }}; - } + {{ async_keyword() }} fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64); /// Synchronize file contents /// @@ -237,10 +209,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, file_handle: BorrowedFileHandle, datasync: bool, - ) -> FuseResult<()> { - self.get_inner().fsync(req, file_id, file_handle, datasync) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Synchronize directory contents /// @@ -254,11 +223,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, file_handle: BorrowedFileHandle, datasync: bool, - ) -> FuseResult<()> { - self.get_inner() - .fsyncdir(req, file_id, file_handle, datasync) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Modify file attributes {{ async_keyword() }} fn getattr( @@ -266,10 +231,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: Option, - ) -> FuseResult { - self.get_inner().getattr(req, file_id, file_handle) - {{ await_keyword() }} - } + ) -> FuseResult; /// Test for a POSIX file lock. {{ async_keyword() }} fn getlk( @@ -279,11 +241,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_handle: BorrowedFileHandle, lock_owner: u64, lock_info: LockInfo, - ) -> FuseResult { - self.get_inner() - .getlk(req, file_id, file_handle, lock_owner, lock_info) - {{ await_keyword() }} - } + ) -> FuseResult; /// Get an extended attribute {{ async_keyword() }} fn getxattr( @@ -292,10 +250,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, name: &OsStr, size: u32, - ) -> FuseResult> { - self.get_inner().getxattr(req, file_id, name, size) - {{ await_keyword() }} - } + ) -> FuseResult>; /// control device {{ async_keyword() }} fn ioctl( @@ -307,11 +262,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send cmd: u32, in_data: Vec, out_size: u32, - ) -> FuseResult<(i32, Vec)> { - self.get_inner() - .ioctl(req, file_id, file_handle, flags, cmd, in_data, out_size) - {{ await_keyword() }} - } + ) -> FuseResult<(i32, Vec)>; /// Create a hard link. {{ async_keyword() }} fn link( @@ -320,22 +271,13 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, newparent: TId, newname: &OsStr, - ) -> FuseResult { - self.get_inner().link(req, file_id, newparent, newname) - {{ await_keyword() }} - } + ) -> FuseResult; /// List extended attribute names - {{ async_keyword() }} fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> { - self.get_inner().listxattr(req, file_id, size) - {{ await_keyword() }} - } + {{ async_keyword() }} fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult>; /// Retrieve file attributes for a directory entry by name and increment the lookup count associated with the inode. - {{ async_keyword() }} fn lookup(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult { - self.get_inner().lookup(req, parent_id, name) - {{ await_keyword() }} - } + {{ async_keyword() }} fn lookup(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult; /// Reposition read/write file offset {{ async_keyword() }} fn lseek( @@ -344,10 +286,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, file_handle: BorrowedFileHandle, seek: SeekFrom, - ) -> FuseResult { - self.get_inner().lseek(req, file_id, file_handle, seek) - {{ await_keyword() }} - } + ) -> FuseResult; /// Create a new directory {{ async_keyword() }} fn mkdir( @@ -357,10 +296,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send name: &OsStr, mode: u32, umask: u32, - ) -> FuseResult { - self.get_inner().mkdir(req, parent_id, name, mode, umask) - {{ await_keyword() }} - } + ) -> FuseResult; /// Create a new file node (regular file, device, FIFO, socket, etc) {{ async_keyword() }} fn mknod( @@ -371,11 +307,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send mode: u32, umask: u32, rdev: DeviceType, - ) -> FuseResult { - self.get_inner() - .mknod(req, parent_id, name, mode, umask, rdev) - {{ await_keyword() }} - } + ) -> FuseResult; /// Open a file and return a file handle. /// @@ -385,10 +317,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().open(req, file_id, flags) - {{ await_keyword() }} - } + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; /// Open a directory /// @@ -398,10 +327,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().opendir(req, file_id, flags) - {{ await_keyword() }} - } + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; /// Read data from a file /// @@ -417,11 +343,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send size: u32, flags: FUSEOpenFlags, lock_owner: Option, - ) -> FuseResult> { - self.get_inner() - .read(req, file_id, file_handle, seek, size, flags, lock_owner) - {{ await_keyword() }} - } + ) -> FuseResult>; /// Read directory contents /// @@ -434,10 +356,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - ) -> FuseResult> { - self.get_inner().readdir(req, file_id, file_handle) - {{ await_keyword() }} - } + ) -> FuseResult>; /// Read directory contents with full file attributes /// @@ -463,10 +382,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send } /// Read the target of a symbolic link - {{ async_keyword() }} fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult> { - self.get_inner().readlink(req, file_id) - {{ await_keyword() }} - } + {{ async_keyword() }} fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult>; /// Release an open file /// @@ -480,11 +396,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send flags: OpenFlags, lock_owner: Option, flush: bool, - ) -> FuseResult<()> { - self.get_inner() - .release(req, file_id, file_handle, flags, lock_owner, flush) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Release an open directory /// @@ -497,17 +409,10 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - ) -> FuseResult<()> { - self.get_inner() - .releasedir(req, file_id, file_handle, flags) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Remove an extended attribute. - {{ async_keyword() }} fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> { - self.get_inner().removexattr(req, file_id, name) - {{ await_keyword() }} - } + {{ async_keyword() }} fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()>; /// Rename a file or directory {{ async_keyword() }} fn rename( @@ -518,17 +423,10 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send newparent: TId, newname: &OsStr, flags: RenameFlags, - ) -> FuseResult<()> { - self.get_inner() - .rename(req, parent_id, name, newparent, newname, flags) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Remove a directory - {{ async_keyword() }} fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { - self.get_inner().rmdir(req, parent_id, name) - {{ await_keyword() }} - } + {{ async_keyword() }} fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; /// Set file attributes. {{ async_keyword() }} fn setattr( @@ -536,10 +434,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, attrs: SetAttrRequest, - ) -> FuseResult { - self.get_inner().setattr(req, file_id, attrs) - {{ await_keyword() }} - } + ) -> FuseResult; /// Acquire, modify or release a POSIX file lock /// @@ -555,11 +450,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send lock_owner: u64, lock_info: LockInfo, sleep: bool, - ) -> FuseResult<()> { - self.get_inner() - .setlk(req, file_id, file_handle, lock_owner, lock_info, sleep) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Set an extended attribute {{ async_keyword() }} fn setxattr( @@ -570,17 +461,10 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send value: Vec, flags: FUSESetXAttrFlags, position: u32, - ) -> FuseResult<()> { - self.get_inner() - .setxattr(req, file_id, name, value, flags, position) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Get file system statistics - {{ async_keyword() }} fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult { - self.get_inner().statfs(req, file_id) - {{ await_keyword() }} - } + {{ async_keyword() }} fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult; /// Create a symbolic link. {{ async_keyword() }} fn symlink( @@ -589,10 +473,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send parent_id: TId, link_name: &OsStr, target: &Path, - ) -> FuseResult { - self.get_inner().symlink(req, parent_id, link_name, target) - {{ await_keyword() }} - } + ) -> FuseResult; /// Write data to a file /// @@ -609,22 +490,8 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option, - ) -> FuseResult { - self.get_inner().write( - req, - file_id, - file_handle, - seek, - data, - write_flags, - flags, - lock_owner, - ){{ await_keyword() }} - } + ) -> FuseResult; /// Remove a file - {{ async_keyword() }} fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { - self.get_inner().unlink(req, parent_id, name) - {{ await_keyword() }} - } + {{ async_keyword() }} fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; } From 04c4f5c9e9c9c383815c32662b5812765ed71757 Mon Sep 17 00:00:00 2001 From: Alogani Date: Mon, 2 Feb 2026 07:26:49 +0100 Subject: [PATCH 03/11] Examples of new delegator API --- src/templates.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/templates.rs b/src/templates.rs index bcc8e00..7c06444 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -36,3 +36,52 @@ pub use default_fuse_handler::DefaultFuseHandler; pub mod fd_handler_helper; pub mod mirror_fs; + +/* +# POSSIBILITY 1: +- Pros: simple to resonnate +- Cons: no delegation (harder to wrap), harder documentation (MirrorFs::write don't exist) +*/ +struct TestFs {} + +pub trait MirrorFs { + fn source() -> PathBuf; +} + +impl MirrorFs for Test { + fn source() -> PathBuf { + todo!() + } +} + +impl FuseHandler for TestFs { + implement_fuse_mirror_fs!(["write"]) + /* Equivalent to + fn write(&self, req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, seek: SeekFrom, data: Vec, + write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option,) -> FuseResult + where Self: MirrorFs { + // implementation_of_mirror_fs.write + } + */ +} + +/* +# POSSIBILITY 2: +- Pros: documentation, easy to wrap +- Cons: lifetime / send+sync ? +*/ + +struct TestFs { + mirror_fs: MirrorFs, // MirrorFs already implements its own functions but doesn't inherit FuseHandler trait +} + +impl FuseHandler for TestFs { + delegate_mirror_fs!(mirror_fs, ["write"]) + /* Equivalent to + fn write(&self, req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, seek: SeekFrom, data: Vec, + write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option,) -> FuseResult + where Self: MirrorFs { + self.#mirror_fs.write(...) + } + */ +} \ No newline at end of file From c17902ed74ab21c87ec4be8a9470da246b800594 Mon Sep 17 00:00:00 2001 From: Alogani Date: Mon, 2 Feb 2026 22:03:06 +0100 Subject: [PATCH 04/11] add delegate method --- .../src/fuse_handlers_signatures.rs | 122 ++++++++++++------ easy_fuser_macro/src/lib.rs | 92 +++++++------ src/templates.rs | 5 +- 3 files changed, 135 insertions(+), 84 deletions(-) diff --git a/easy_fuser_macro/src/fuse_handlers_signatures.rs b/easy_fuser_macro/src/fuse_handlers_signatures.rs index ab1b4f9..adc6191 100644 --- a/easy_fuser_macro/src/fuse_handlers_signatures.rs +++ b/easy_fuser_macro/src/fuse_handlers_signatures.rs @@ -1,20 +1,19 @@ use proc_macro2::TokenStream; -use syn::{TraitItemFn, parse_quote}; +use syn::{Expr, ExprCall, ExprPath, TraitItemFn, parse_quote}; - -pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> TraitItemFn { +pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { match func_name { "get_default_ttl" => parse_quote! { - fn get_default_ttl(&self) -> Duration #tail + fn get_default_ttl(&self) -> Duration }, "init" => parse_quote! { - fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> #tail + fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> }, "destroy" => parse_quote! { - fn destroy(&self) #tail + fn destroy(&self) }, "access" => parse_quote! { - fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> #tail + fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> }, "bmap" => parse_quote! { fn bmap( @@ -23,7 +22,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, blocksize: u32, idx: u64, - ) -> FuseResult #tail + ) -> FuseResult }, "copy_file_range" => parse_quote! { fn copy_file_range( @@ -36,7 +35,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_handle_out: BorrowedFileHandle<'_>, offset_out: i64, len: u64, - ) -> FuseResult #tail + ) -> FuseResult }, "create" => parse_quote! { fn create( @@ -47,7 +46,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> #tail + ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> }, "fallocate" => parse_quote! { fn fallocate( @@ -58,7 +57,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T offset: i64, length: i64, mode: FallocateFlags, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "flush" => parse_quote! { fn flush( @@ -67,10 +66,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "forget" => parse_quote! { - fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) #tail + fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) }, "fsync" => parse_quote! { fn fsync( @@ -79,7 +78,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "fsyncdir" => parse_quote! { fn fsyncdir( @@ -88,7 +87,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "getattr" => parse_quote! { fn getattr( @@ -96,7 +95,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, file_id: TId, file_handle: Option>, - ) -> FuseResult #tail + ) -> FuseResult }, "getlk" => parse_quote! { fn getlk( @@ -106,7 +105,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, - ) -> FuseResult #tail + ) -> FuseResult }, "getxattr" => parse_quote! { fn getxattr( @@ -115,7 +114,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, name: &OsStr, size: u32, - ) -> FuseResult> #tail + ) -> FuseResult> }, "ioctl" => parse_quote! { fn ioctl( @@ -127,7 +126,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T cmd: u32, in_data: Vec, out_size: u32, - ) -> FuseResult<(i32, Vec)> #tail + ) -> FuseResult<(i32, Vec)> }, "link" => parse_quote! { fn link( @@ -136,7 +135,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, newparent: TId, newname: &OsStr, - ) -> FuseResult #tail + ) -> FuseResult }, "listxattr" => parse_quote! { fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> @@ -147,7 +146,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, parent_id: TId, name: &OsStr, - ) -> FuseResult #tail + ) -> FuseResult }, "lseek" => parse_quote! { fn lseek( @@ -156,7 +155,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, - ) -> FuseResult #tail + ) -> FuseResult }, "mkdir" => parse_quote! { fn mkdir( @@ -166,7 +165,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T name: &OsStr, mode: u32, umask: u32, - ) -> FuseResult #tail + ) -> FuseResult }, "mknod" => parse_quote! { fn mknod( @@ -177,7 +176,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T mode: u32, umask: u32, rdev: DeviceType, - ) -> FuseResult #tail + ) -> FuseResult }, "open" => parse_quote! { fn open( @@ -185,7 +184,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> #tail + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> }, "opendir" => parse_quote! { fn opendir( @@ -193,7 +192,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> #tail + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> }, "read" => parse_quote! { fn read( @@ -205,7 +204,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T size: u32, flags: FUSEOpenFlags, lock_owner: Option, - ) -> FuseResult> #tail + ) -> FuseResult> }, "readdir" => parse_quote! { fn readdir( @@ -213,7 +212,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle<'_>, - ) -> FuseResult> #tail + ) -> FuseResult> }, "readdirplus" => parse_quote! { fn readdirplus( @@ -221,10 +220,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - ) -> FuseResult> #tail + ) -> FuseResult> }, "readlink" => parse_quote! { - fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult> #tail + fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult> }, "release" => parse_quote! { fn release( @@ -235,7 +234,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T flags: OpenFlags, lock_owner: Option, flush: bool, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "releasedir" => parse_quote! { fn releasedir( @@ -244,10 +243,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "removexattr" => parse_quote! { - fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> #tail + fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> }, "rename" => parse_quote! { fn rename( @@ -258,10 +257,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T newparent: TId, newname: &OsStr, flags: RenameFlags, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "rmdir" => parse_quote! { - fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> #tail + fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> }, "setattr" => parse_quote! { fn setattr( @@ -269,7 +268,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T req: &RequestInfo, file_id: TId, attrs: SetAttrRequest<'_>, - ) -> FuseResult #tail + ) -> FuseResult }, "setlk" => parse_quote! { fn setlk( @@ -280,7 +279,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T lock_owner: u64, lock_info: LockInfo, sleep: bool, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "setxattr" => parse_quote! { fn setxattr( @@ -291,10 +290,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T value: Vec, flags: FUSESetXAttrFlags, position: u32, - ) -> FuseResult<()> #tail + ) -> FuseResult<()> }, "statfs" => parse_quote! { - fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult #tail + fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult }, "symlink" => parse_quote! { fn symlink( @@ -303,7 +302,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T parent_id: TId, link_name: &OsStr, target: &Path, - ) -> FuseResult #tail + ) -> FuseResult }, "write" => parse_quote! { fn write( @@ -316,11 +315,50 @@ pub fn get_fuse_handler_fn_impl(func_name: &str, tail: Option) -> T write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option, - ) -> FuseResult #tail + ) -> FuseResult }, "unlink" => parse_quote! { - fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> #tail + fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> }, _ => panic!("unknown function signature"), } } + +/// Given a trait method like: +/// fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> +/// Returns an expression: `access(req, file_id, mask)` +pub fn make_method_call_expr(method: &TokenStream) -> Expr { + let method: TraitItemFn = parse_quote! { #method; }; + // Get the method name + let method_name = &method.sig.ident; + + // Collect all parameters except `&self` + let args: Vec = method + .sig + .inputs + .iter() + .skip(1) // skip &self / self + .map(|arg| match arg { + syn::FnArg::Typed(pat_type) => { + let pat = &pat_type.pat; + // We just use the pattern as-is (usually an ident) + parse_quote!(#pat) + } + syn::FnArg::Receiver(_) => unreachable!("&self should have been skipped"), + }) + .collect(); + + // Build the call expression: method_name(arg1, arg2, ...) + let call = Expr::Call(ExprCall { + attrs: vec![], + func: Box::new(Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: syn::Path::from(method_name.clone()), + })), + paren_token: syn::token::Paren::default(), + args: syn::punctuated::Punctuated::from_iter(args), + }); + + call +} \ No newline at end of file diff --git a/easy_fuser_macro/src/lib.rs b/easy_fuser_macro/src/lib.rs index 712f232..c9c88a9 100644 --- a/easy_fuser_macro/src/lib.rs +++ b/easy_fuser_macro/src/lib.rs @@ -2,13 +2,18 @@ extern crate proc_macro; mod fuse_handlers_signatures; +use either::Either; use proc_macro::TokenStream; use quote::quote; use syn::{ - Attribute, ExprBlock, Ident, LitStr, Result, Token, parse::{Parse, ParseStream}, parse_macro_input, - token::{FatArrow, Group, Semi} + Attribute, ExprBlock, Ident, Result, Token, + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + token::{Comma, FatArrow, Semi}, }; -use either::Either; + +use fuse_handlers_signatures::*; struct FnSigInput { attrs: Vec, @@ -36,15 +41,10 @@ impl Parse for FnSigInput { Either::Right(input.parse::()?) }; - Ok(FnSigInput { - attrs, - name, - tail, - }) + Ok(FnSigInput { attrs, name, tail }) } } - /// Usage: /// 1. ```fuse_handler_fnsig!{ /// /// doc... @@ -63,47 +63,61 @@ pub fn fuse_handler_fnsig(input: TokenStream) -> TokenStream { let tail_tokens: proc_macro2::TokenStream = match input.tail { Either::Left(semi) => quote!( #semi ), Either::Right(block) => { - quote!( #block ) // reconstruct braced block + quote!( #block ) // reconstruct braced block } }; - let fun_impl = fuse_handlers_signatures::get_fuse_handler_fn_impl( + let fun_impl = get_fuse_handler_fn_impl( &input.name.to_string(), - Some(tail_tokens), ); quote!( #(#attrs)* - #fun_impl - ).into() + #fun_impl #tail_tokens + ) + .into() +} + +struct DelegateFsInput { + target: Ident, + _comma: Comma, + methods: Punctuated, +} + +impl Parse for DelegateFsInput { + fn parse(input: ParseStream) -> Result { + let target = input.parse::()?; + let _comma = input.parse::()?; + + let content; + syn::braced!(content in input); + + let methods = Punctuated::::parse_terminated(&content)?; + + Ok(Self { + target, + _comma, + methods, + }) + } } -/* #[proc_macro] -pub fn delegate(input: TokenStream) -> TokenStream { - let parsed: syn::ExprTuple = parse_macro_input!(input); - let field_expr: Expr = parsed.elems[0].clone(); // self.fuse_handler - let methods_vec: syn::ExprVec = syn::parse2(parsed.elems[1].to_token_stream()).unwrap(); - - let mut delegations = vec![]; - for method in methods_vec.elems { - let method_ident = if let syn::Expr::Path(p) = method { p.path.get_ident().unwrap().clone() } else { panic!("Expected ident") }; - let method_str = method_ident.to_string(); - - // Find matching signature - let sig = METHODS.iter().find(|(name, _)| *name == method_str).map(|(_, sig)| sig).unwrap_or_else(|| panic!("Unknown method: {}", method_str)); - let sig_parsed: syn::Signature = syn::parse_str(sig).unwrap(); - - // Generate delegation - let args: Vec = sig_parsed.inputs.iter().skip(1).map(|arg| if let syn::FnArg::Typed(t) = arg { t.pat.as_ref().clone() } else { panic!() }).collect(); // Skip &self - let arg_idents: Vec<_> = args.iter().map(|pat| if let syn::Pat::Ident(i) = pat { i.ident.clone() } else { panic!() }).collect(); - - delegations.push(quote! { - #sig_parsed { - #field_expr.#method_ident(#(#arg_idents),*) +pub fn delegate_fs(input: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(input as DelegateFsInput); + + let target = args.target; + + let mut expanded = quote! {}; + + for method in &args.methods { + let fn_impl = get_fuse_handler_fn_impl(&method.to_string()); + let method_expr = make_method_call_expr(&fn_impl); + expanded.extend(quote! { + #fn_impl { + self.#target.#method_expr } }); } - quote!(#(#delegations)*).into() -} - */ + expanded.into() +} \ No newline at end of file diff --git a/src/templates.rs b/src/templates.rs index 7c06444..05df445 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -76,11 +76,10 @@ struct TestFs { } impl FuseHandler for TestFs { - delegate_mirror_fs!(mirror_fs, ["write"]) + delegate_fs!(mirror_fs, ["write"]) /* Equivalent to fn write(&self, req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, seek: SeekFrom, data: Vec, - write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option,) -> FuseResult - where Self: MirrorFs { + write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option,) -> FuseResult { self.#mirror_fs.write(...) } */ From 719e91dcb4059b8c83adf945eb008b6193f65c9a Mon Sep 17 00:00:00 2001 From: Alogani Date: Tue, 3 Feb 2026 21:47:40 +0100 Subject: [PATCH 05/11] finish macro definitions --- Cargo.toml | 2 +- .../src/fuse_handlers_signatures.rs | 89 +++++++++---------- easy_fuser_macro/src/lib.rs | 46 ++++++---- src/core/inode_mapping.rs | 9 +- src/inode_mapper.rs | 11 ++- src/lib.rs | 4 +- src/session.rs | 6 +- 7 files changed, 90 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 209f613..bb30fe0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easy_fuser" -version = "0.4.5" +version = "0.5.0" edition = "2024" description = "A flexible and idiomatic Fuse implementation for Rust" license = "MIT" diff --git a/easy_fuser_macro/src/fuse_handlers_signatures.rs b/easy_fuser_macro/src/fuse_handlers_signatures.rs index adc6191..486f13e 100644 --- a/easy_fuser_macro/src/fuse_handlers_signatures.rs +++ b/easy_fuser_macro/src/fuse_handlers_signatures.rs @@ -1,19 +1,18 @@ -use proc_macro2::TokenStream; use syn::{Expr, ExprCall, ExprPath, TraitItemFn, parse_quote}; -pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { +pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { match func_name { "get_default_ttl" => parse_quote! { - fn get_default_ttl(&self) -> Duration + fn get_default_ttl(&self) -> Duration; }, "init" => parse_quote! { - fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> + fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()>; }, "destroy" => parse_quote! { - fn destroy(&self) + fn destroy(&self); }, "access" => parse_quote! { - fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> + fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()>; }, "bmap" => parse_quote! { fn bmap( @@ -22,7 +21,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, blocksize: u32, idx: u64, - ) -> FuseResult + ) -> FuseResult; }, "copy_file_range" => parse_quote! { fn copy_file_range( @@ -35,7 +34,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_handle_out: BorrowedFileHandle<'_>, offset_out: i64, len: u64, - ) -> FuseResult + ) -> FuseResult; }, "create" => parse_quote! { fn create( @@ -46,7 +45,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> + ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)>; }, "fallocate" => parse_quote! { fn fallocate( @@ -57,7 +56,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { offset: i64, length: i64, mode: FallocateFlags, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "flush" => parse_quote! { fn flush( @@ -66,10 +65,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "forget" => parse_quote! { - fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) + fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64); }, "fsync" => parse_quote! { fn fsync( @@ -78,7 +77,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "fsyncdir" => parse_quote! { fn fsyncdir( @@ -87,7 +86,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "getattr" => parse_quote! { fn getattr( @@ -95,7 +94,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, file_id: TId, file_handle: Option>, - ) -> FuseResult + ) -> FuseResult; }, "getlk" => parse_quote! { fn getlk( @@ -105,7 +104,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, - ) -> FuseResult + ) -> FuseResult; }, "getxattr" => parse_quote! { fn getxattr( @@ -114,7 +113,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, name: &OsStr, size: u32, - ) -> FuseResult> + ) -> FuseResult>; }, "ioctl" => parse_quote! { fn ioctl( @@ -126,7 +125,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { cmd: u32, in_data: Vec, out_size: u32, - ) -> FuseResult<(i32, Vec)> + ) -> FuseResult<(i32, Vec)>; }, "link" => parse_quote! { fn link( @@ -135,10 +134,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, newparent: TId, newname: &OsStr, - ) -> FuseResult + ) -> FuseResult; }, "listxattr" => parse_quote! { - fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> + fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult>; }, "lookup" => parse_quote! { fn lookup( @@ -146,7 +145,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, parent_id: TId, name: &OsStr, - ) -> FuseResult + ) -> FuseResult; }, "lseek" => parse_quote! { fn lseek( @@ -155,7 +154,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, - ) -> FuseResult + ) -> FuseResult; }, "mkdir" => parse_quote! { fn mkdir( @@ -165,7 +164,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { name: &OsStr, mode: u32, umask: u32, - ) -> FuseResult + ) -> FuseResult; }, "mknod" => parse_quote! { fn mknod( @@ -176,7 +175,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { mode: u32, umask: u32, rdev: DeviceType, - ) -> FuseResult + ) -> FuseResult; }, "open" => parse_quote! { fn open( @@ -184,7 +183,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; }, "opendir" => parse_quote! { fn opendir( @@ -192,7 +191,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; }, "read" => parse_quote! { fn read( @@ -204,7 +203,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { size: u32, flags: FUSEOpenFlags, lock_owner: Option, - ) -> FuseResult> + ) -> FuseResult>; }, "readdir" => parse_quote! { fn readdir( @@ -212,7 +211,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle<'_>, - ) -> FuseResult> + ) -> FuseResult>; }, "readdirplus" => parse_quote! { fn readdirplus( @@ -220,10 +219,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - ) -> FuseResult> + ) -> FuseResult>; }, "readlink" => parse_quote! { - fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult> + fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult>; }, "release" => parse_quote! { fn release( @@ -234,7 +233,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { flags: OpenFlags, lock_owner: Option, flush: bool, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "releasedir" => parse_quote! { fn releasedir( @@ -243,10 +242,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "removexattr" => parse_quote! { - fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> + fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()>; }, "rename" => parse_quote! { fn rename( @@ -257,10 +256,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { newparent: TId, newname: &OsStr, flags: RenameFlags, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "rmdir" => parse_quote! { - fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> + fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; }, "setattr" => parse_quote! { fn setattr( @@ -268,7 +267,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { req: &RequestInfo, file_id: TId, attrs: SetAttrRequest<'_>, - ) -> FuseResult + ) -> FuseResult; }, "setlk" => parse_quote! { fn setlk( @@ -279,7 +278,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { lock_owner: u64, lock_info: LockInfo, sleep: bool, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "setxattr" => parse_quote! { fn setxattr( @@ -290,10 +289,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { value: Vec, flags: FUSESetXAttrFlags, position: u32, - ) -> FuseResult<()> + ) -> FuseResult<()>; }, "statfs" => parse_quote! { - fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult + fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult; }, "symlink" => parse_quote! { fn symlink( @@ -302,7 +301,7 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { parent_id: TId, link_name: &OsStr, target: &Path, - ) -> FuseResult + ) -> FuseResult; }, "write" => parse_quote! { fn write( @@ -315,10 +314,10 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option, - ) -> FuseResult + ) -> FuseResult; }, "unlink" => parse_quote! { - fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> + fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; }, _ => panic!("unknown function signature"), } @@ -327,12 +326,9 @@ pub fn get_fuse_handler_fn_impl(func_name: &str) -> TokenStream { /// Given a trait method like: /// fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> /// Returns an expression: `access(req, file_id, mask)` -pub fn make_method_call_expr(method: &TokenStream) -> Expr { - let method: TraitItemFn = parse_quote! { #method; }; - // Get the method name +pub fn make_method_call_expr(method: &TraitItemFn) -> Expr { let method_name = &method.sig.ident; - // Collect all parameters except `&self` let args: Vec = method .sig .inputs @@ -348,7 +344,6 @@ pub fn make_method_call_expr(method: &TokenStream) -> Expr { }) .collect(); - // Build the call expression: method_name(arg1, arg2, ...) let call = Expr::Call(ExprCall { attrs: vec![], func: Box::new(Expr::Path(ExprPath { diff --git a/easy_fuser_macro/src/lib.rs b/easy_fuser_macro/src/lib.rs index c9c88a9..a654051 100644 --- a/easy_fuser_macro/src/lib.rs +++ b/easy_fuser_macro/src/lib.rs @@ -46,15 +46,17 @@ impl Parse for FnSigInput { } /// Usage: -/// 1. ```fuse_handler_fnsig!{ -/// /// doc... -/// init => { _default_implementation_ } -/// } +/// 1. ```rust +/// fuse_handler_fnsig!{ +/// /// doc... +/// init => { _default_implementation_ } +/// } /// ``` -/// 1. ```fuse_handler_fnsig!{ -/// /// doc... -/// access => ; -/// } +/// 2. ```rust +/// fuse_handler_fnsig!{ +/// /// doc... +/// access => ; +/// } /// ``` #[proc_macro] pub fn fuse_handler_fnsig(input: TokenStream) -> TokenStream { @@ -63,16 +65,18 @@ pub fn fuse_handler_fnsig(input: TokenStream) -> TokenStream { let tail_tokens: proc_macro2::TokenStream = match input.tail { Either::Left(semi) => quote!( #semi ), Either::Right(block) => { - quote!( #block ) // reconstruct braced block + quote!( #block ) } }; - let fun_impl = get_fuse_handler_fn_impl( + let trait_fn = get_fuse_handler_trait_fn( &input.name.to_string(), ); + let fn_sig = trait_fn.sig; + quote!( #(#attrs)* - #fun_impl #tail_tokens + #fn_sig #tail_tokens ) .into() } @@ -82,17 +86,13 @@ struct DelegateFsInput { _comma: Comma, methods: Punctuated, } - impl Parse for DelegateFsInput { fn parse(input: ParseStream) -> Result { let target = input.parse::()?; let _comma = input.parse::()?; - let content; - syn::braced!(content in input); - + syn::bracketed!(content in input); let methods = Punctuated::::parse_terminated(&content)?; - Ok(Self { target, _comma, @@ -101,6 +101,15 @@ impl Parse for DelegateFsInput { } } +/// Usage: +/// ```rust +/// struct Test { +/// mirror_fs: MirrorFs +/// } +/// impl MyFuseHandler { +/// delegate_fs! { mirror_fs, [ read, write ] } +/// } +/// ``` #[proc_macro] pub fn delegate_fs(input: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(input as DelegateFsInput); @@ -110,10 +119,11 @@ pub fn delegate_fs(input: TokenStream) -> TokenStream { let mut expanded = quote! {}; for method in &args.methods { - let fn_impl = get_fuse_handler_fn_impl(&method.to_string()); + let fn_impl = get_fuse_handler_trait_fn(&method.to_string()); let method_expr = make_method_call_expr(&fn_impl); + let fn_sig = fn_impl.sig; expanded.extend(quote! { - #fn_impl { + #fn_sig { self.#target.#method_expr } }); diff --git a/src/core/inode_mapping.rs b/src/core/inode_mapping.rs index 35bd3bd..ff7345b 100644 --- a/src/core/inode_mapping.rs +++ b/src/core/inode_mapping.rs @@ -212,7 +212,10 @@ impl FileIdResolver for ComponentsResolver { } fn prune(&self, keep: &HashSet) { - self.mapper.write().expect("Failed to acquire write lock").prune(keep); + self.mapper + .write() + .expect("Failed to acquire write lock") + .prune(keep); } fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr) { @@ -467,11 +470,11 @@ mod tests { // Test prune let keep = HashSet::new(); resolver.prune(&keep); - + // child_ino should be gone now because refcount was 0 (decremented by earlier forget) and we pruned it. // We can verify it's gone by trying to resolve it and expecting panic (as per other test) or just by knowing prune works. // But calling forget again is definitely wrong if it's gone. - + // If we want to test that prune actually removed it, we should check existence. // But since we can't easily check existence without internal access, we rely on the fact that subsequent operations might fail or the other test. } diff --git a/src/inode_mapper.rs b/src/inode_mapper.rs index f162fbf..4df464f 100644 --- a/src/inode_mapper.rs +++ b/src/inode_mapper.rs @@ -549,7 +549,9 @@ impl InodeMapper { } impl InodeMapper -where Data: HasLookupCount + Send + Sync + 'static { +where + Data: HasLookupCount + Send + Sync + 'static, +{ pub fn prune(&mut self, keep: &HashSet>) { let mut to_remove = Vec::new(); @@ -562,8 +564,11 @@ where Data: HasLookupCount + Send + Sync + 'static { if let Some(path_info) = self.resolve(inode) { // path_info is [leaf, parent...] // We need [parent, leaf] - let path_vec: Vec = - path_info.iter().rev().map(|info| (**info.name).clone()).collect(); + let path_vec: Vec = path_info + .iter() + .rev() + .map(|info| (**info.name).clone()) + .collect(); if !keep.contains(&path_vec) { to_remove.push(inode.clone()); } diff --git a/src/lib.rs b/src/lib.rs index b1553bd..4077a39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,15 +23,15 @@ mod core; mod fuse_handler; pub mod inode_mapper; -pub mod session; pub mod inode_multi_mapper; +pub mod session; //pub mod templates; pub mod types; pub mod unix_fs; pub use fuse_handler::FuseHandler; -pub use session::{FusePruner, FuseSession}; use fuser::MountOption; +pub use session::{FusePruner, FuseSession}; pub mod prelude { //! Re-exports the necessary types and functions from the `easy_fuser` crate. diff --git a/src/session.rs b/src/session.rs index a04107e..5221682 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,8 +1,8 @@ +use crate::core::FileIdResolver; +use crate::types::FileIdType; +use fuser::BackgroundSession; use std::collections::HashSet; use std::sync::Arc; -use fuser::BackgroundSession; -use crate::types::FileIdType; -use crate::core::FileIdResolver; /// A session for a mounted FUSE filesystem running in the background. /// From 2e05cda4fc3d289ae83e74f13c4c62769c6ba19a Mon Sep 17 00:00:00 2001 From: Alogani <77880767+Alogani@users.noreply.github.com> Date: Wed, 4 Feb 2026 08:26:39 +0100 Subject: [PATCH 06/11] Update fuse_handler.rs.j2 --- templates/fuse_handler.rs.j2 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index f97f169..a5bab0a 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -103,8 +103,11 @@ use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% endif %} { - +pub trait FuseHandler: 'static + {% if send_sync %} + + Sync + Send {# Never put this Never serial mode #} + {% endif %} +{ {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} {% macro await_keyword() -%}{% if mode == "async" %}.await{% endif %}{%- endmacro %} From c017aea4faeab2756f2a33057a73b7be338fe497 Mon Sep 17 00:00:00 2001 From: Alogani <77880767+Alogani@users.noreply.github.com> Date: Wed, 4 Feb 2026 08:27:54 +0100 Subject: [PATCH 07/11] Update fuse_handler.rs.j2 --- templates/fuse_handler.rs.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index a5bab0a..6699340 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -105,7 +105,7 @@ use async_trait::async_trait; {% endif %} pub trait FuseHandler: 'static {% if send_sync %} - + Sync + Send {# Never put this Never serial mode #} + + Sync + Send {# Never put this on serial mode #} {% endif %} { {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} From 555029ae3d4f6c1714509eb330b0d1d4dde70079 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 11:06:17 +0100 Subject: [PATCH 08/11] add async fuse_driver+fuse_handler --- .vscode/settings.json | 2 +- build.rs | 7 +- src/core.rs | 1 - src/core/thread_mode.rs | 81 ----------- src/fuse_async.rs | 19 +-- src/fuse_parallel.rs | 19 +-- src/fuse_serial.rs | 19 +-- src/lib.rs | 3 - src/types/file_id_type.rs | 3 +- templates.rs | 11 +- templates/fuse_driver.rs.j2 | 146 ++++++++++++++------ templates/fuse_handler.rs.j2 | 38 ++--- templates/fuse_lib.rs.j2 | 24 ++++ templates/{mouting.rs.j2 => mounting.rs.j2} | 6 +- 14 files changed, 170 insertions(+), 209 deletions(-) delete mode 100644 src/core/thread_mode.rs create mode 100644 templates/fuse_lib.rs.j2 rename templates/{mouting.rs.j2 => mounting.rs.j2} (97%) diff --git a/.vscode/settings.json b/.vscode/settings.json index c039ee2..08d0402 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "files.associations": { "*.rs.j2": "rust", }, - "rust-analyzer.cargo.features": ["serial"], + "rust-analyzer.cargo.features": ["serial", "parallel", "async"], "rust-analyzer.files.exclude": [ "templates" ] diff --git a/build.rs b/build.rs index 48e23f4..2cce1e0 100644 --- a/build.rs +++ b/build.rs @@ -58,8 +58,11 @@ fn main() -> std::io::Result<()> { let content = FuseHandlerTemplate { mode }.render()?; fs::write(mode_dir.join("fuse_handler.rs"), content)?; - let content = MoutingTemplate { mode }.render()?; - fs::write(mode_dir.join("mouting.rs"), content)?; + let content = MountingTemplate { mode }.render()?; + fs::write(mode_dir.join("mounting.rs"), content)?; + + let content = FuseLibTemplate { mode }.render()?; + fs::write(mode_dir.join("fuse_lib.rs"), content)?; } Ok(()) diff --git a/src/core.rs b/src/core.rs index b851a0d..7521698 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,6 +1,5 @@ // TODO: move or remove ? pub(crate) mod helpers; -pub(crate) mod thread_mode; mod inode_mapping; diff --git a/src/core/thread_mode.rs b/src/core/thread_mode.rs deleted file mode 100644 index c66e5ca..0000000 --- a/src/core/thread_mode.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -pub(crate) trait SafeBorrowable { - type Guard<'a>: Deref + DerefMut - where - Self: 'a; - - fn safe_borrow_mut(&self) -> Self::Guard<'_>; -} - -#[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; - - fn safe_borrow_mut(&self) -> Self::Guard<'_> { - self.borrow_mut() - } - } -} - -#[cfg(all(feature = "parallel", not(feature = "deadlock_detection")))] -mod safe_borrowable_impl { - use super::*; - - use std::sync::{Mutex, MutexGuard}; - - impl SafeBorrowable for Mutex { - type Guard<'a> - = MutexGuard<'a, T> - where - Self: 'a; - - fn safe_borrow_mut(&self) -> Self::Guard<'_> { - self.lock().unwrap() - } - } -} - -#[cfg(all(feature = "parallel", feature = "deadlock_detection"))] -mod safe_borrowable_impl { - use super::*; - - use parking_lot::{Mutex, MutexGuard}; - - impl SafeBorrowable for Mutex { - type Guard<'a> - = MutexGuard<'a, T> - where - Self: 'a; - - fn safe_borrow_mut(&self) -> Self::Guard<'_> { - self.lock() - } - } -} - -#[cfg(any(feature = "async"))] -mod safe_borrowable_impl { - use super::*; - - use tokio::sync::{Mutex, MutexGuard}; - - impl SafeBorrowable for Mutex { - 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_async.rs b/src/fuse_async.rs index 3a842a9..0532f5d 100644 --- a/src/fuse_async.rs +++ b/src/fuse_async.rs @@ -1,17 +1,2 @@ -#![cfg(feature = "async")] - -pub mod fuse_driver { - include!(concat!(env!("OUT_DIR"), "/async/fuse_driver.rs")); -} -pub mod fuse_handler { - include!(concat!(env!("OUT_DIR"), "/async/fuse_handler.rs")); -} -pub mod mounting { - include!(concat!(env!("OUT_DIR"), "/async/mounting.rs")); -} - -pub use fuse_handler::FuseHandler; -pub use mounting::*; - - -include!(concat!(env!("OUT_DIR"), "/async/preludes.rs")); \ No newline at end of file +#[cfg(feature = "async")] +include!(concat!(env!("OUT_DIR"), "/async/fuse_lib.rs")); \ No newline at end of file diff --git a/src/fuse_parallel.rs b/src/fuse_parallel.rs index c540b4d..492c064 100644 --- a/src/fuse_parallel.rs +++ b/src/fuse_parallel.rs @@ -1,17 +1,2 @@ -#![cfg(feature = "parallel")] - -pub mod fuse_driver { - include!(concat!(env!("OUT_DIR"), "/parallel/fuse_driver.rs")); -} -pub mod fuse_handler { - include!(concat!(env!("OUT_DIR"), "/parallel/fuse_handler.rs")); -} -pub mod mounting { - include!(concat!(env!("OUT_DIR"), "/parallel/mounting.rs")); -} - -pub use fuse_handler::FuseHandler; -pub use mounting::*; - - -include!(concat!(env!("OUT_DIR"), "/parallel/preludes.rs")); \ No newline at end of file +#[cfg(feature = "parallel")] +include!(concat!(env!("OUT_DIR"), "/parallel/fuse_lib.rs")); \ No newline at end of file diff --git a/src/fuse_serial.rs b/src/fuse_serial.rs index c5d2e87..7132721 100644 --- a/src/fuse_serial.rs +++ b/src/fuse_serial.rs @@ -1,17 +1,2 @@ -#![cfg(feature = "serial")] - -pub mod fuse_driver { - include!(concat!(env!("OUT_DIR"), "/serial/fuse_driver.rs")); -} -pub mod fuse_handler { - include!(concat!(env!("OUT_DIR"), "/serial/fuse_handler.rs")); -} -pub mod mounting { - include!(concat!(env!("OUT_DIR"), "/serial/mounting.rs")); -} - -pub use fuse_handler::FuseHandler; -pub use mounting::*; - - -include!(concat!(env!("OUT_DIR"), "/serial/preludes.rs")); \ No newline at end of file +#[cfg(feature = "serial")] +include!(concat!(env!("OUT_DIR"), "/serial/fuse_lib.rs")); \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0e9f89a..15fdb31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,5 @@ #![doc = include_str!("../README.md")] -#[cfg(feature = "async")] -compile_error!("Feature 'async' is not yet implemented."); - #[cfg(all( not(feature = "serial"), not(feature = "parallel"), diff --git a/src/types/file_id_type.rs b/src/types/file_id_type.rs index faab8d1..192f8bf 100644 --- a/src/types/file_id_type.rs +++ b/src/types/file_id_type.rs @@ -60,7 +60,8 @@ use fuser::FileType as FileKind; /// - The user can use the hardlinks of the current filesystem by using `libc::fstat(...).f_fsid` (Persistent) or libc::fstatfs(...).f_dev` (Ephemeral) /// - When a Fuse operation provides an inode, the user can use `BackingId::all_paths()` to retrieve all the paths associated to that inode pub trait FileIdType: - 'static + Debug + Clone + PartialEq + Eq + std::hash::Hash + InodeResolvable + 'static + Debug + Clone + PartialEq + Eq + + Send + std::hash::Hash + InodeResolvable { /// Full metadata type for the file system. /// diff --git a/templates.rs b/templates.rs index 53d95c5..1ec8f2f 100644 --- a/templates.rs +++ b/templates.rs @@ -13,7 +13,14 @@ pub struct FuseHandlerTemplate<'a> { } #[derive(Template)] -#[template(path = "mouting.rs.j2")] -pub struct MoutingTemplate<'a> { +#[template(path = "mounting.rs.j2")] +pub struct MountingTemplate<'a> { pub mode: &'a str, } + +#[derive(Template)] +#[template(path = "fuse_lib.rs.j2")] +pub struct FuseLibTemplate<'a> { + pub mode: &'a str, +} + diff --git a/templates/fuse_driver.rs.j2 b/templates/fuse_driver.rs.j2 index bc811bb..5fd2777 100644 --- a/templates/fuse_driver.rs.j2 +++ b/templates/fuse_driver.rs.j2 @@ -17,13 +17,23 @@ use fuser::{ ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, }; -use crate::fuse_handler::FuseHandler; use crate::types::*; use crate::core::helpers::*; -use crate::core::thread_mode::*; -use crate::core::inode_mapping::FileIdResolver; +use crate::core::FileIdResolver; + +use super::fuse_handler::FuseHandler; type DirIter = HashMap<(u64, i64), VecDeque<(OsString, u64, TAttr)>>; +{% macro borrow_mut() -%} + {% if mode == "serial" %} + borrow_mut() + {% elif mode == "parallel" %} + lock().unwrap() // TODO: parking_lot support + {% elif mode == "async" %} + lock().await + {% endif %} +{%- endmacro %} + {% if mode == "serial" %} use std::cell::RefCell; @@ -158,7 +168,7 @@ where self.threadpool.execute(move || { {% elif mode == "async" %} let runtime = self.runtime.clone(); - runtime.spawn(async move { + self.runtime.spawn(async move { {% endif %} {{ caller() }} @@ -239,7 +249,7 @@ where &req, resolver.resolve_id(ino), AccessMask::from_bits_retain(mask), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %}reply.ok();{% endcall %} } Err(e) => { warn!("access: ino {:x?}, [{}], {:?}", ino, e, req); @@ -255,7 +265,12 @@ where let handler = self.get_handler(); let resolver = self.resolver.clone(); {% call spawn() %} - match handler.bmap(&req, resolver.resolve_id(ino), blocksize, idx) { + match handler.bmap( + &req, + resolver.resolve_id(ino), + blocksize, + idx + ){% if mode == "async" %}.await{% endif %} { Ok(block) => { {% call spawn_reply() %}reply.bmap(block);{% endcall %} } Err(e) => { warn!("bmap: ino {:x?}, [{}], {:?}", ino, e, req); @@ -292,7 +307,7 @@ where offset_out, len, flags, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(bytes_written) => { {% call spawn_reply() %} reply.written(bytes_written);{% endcall %} } Err(e) => { warn!("copy_file_range: ino {:x?}, [{}], {:?}", ino_in, e, req); @@ -324,7 +339,7 @@ where mode, umask, OpenFlags::from_bits_retain(flags), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok((file_handle, metadata, response_flags)) => { let default_ttl = handler.get_default_ttl(); let (id, file_attr) = TId::extract_metadata(metadata); @@ -369,7 +384,7 @@ where offset, length, FallocateFlags::from_bits_retain(mode), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("fallocate: ino {:x?}, [{}], {:?}", ino, e, req); @@ -389,7 +404,7 @@ where resolver.resolve_id(ino), unsafe { BorrowedFileHandle::from_raw(fh) }, lock_owner, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("flush: ino {:x?}, [{}], {:?}", ino, e, req); @@ -417,7 +432,7 @@ where resolver.resolve_id(ino), unsafe { BorrowedFileHandle::from_raw(fh) }, datasync, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("fsync: ino {:x?}, [{}], {:?}", ino, e, req); @@ -437,7 +452,7 @@ where resolver.resolve_id(ino), unsafe { BorrowedFileHandle::from_raw(fh) }, datasync, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("fsyncdir: ino {:x?}, [{}], {:?}", ino, e, req); @@ -456,7 +471,7 @@ where &req, resolver.resolve_id(ino), fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }) - ) { + ){% if mode == "async" %}.await{% endif %} { {{ reply_attr() }} Err(e) => { warn!("getattr: ino {:x?}, [{}], {:?}", ino, e, req); @@ -494,7 +509,7 @@ where unsafe { BorrowedFileHandle::from_raw(fh) }, lock_owner, lock_info, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(lock_info) => { {% call spawn_reply() %} reply.locked( lock_info.start, @@ -517,7 +532,11 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} - match handler.getxattr(&req, resolver.resolve_id(ino), &name, size) { + match handler.getxattr( + &req, + resolver.resolve_id(ino), + &name, size + ){% if mode == "async" %}.await{% endif %} { Ok(xattr_data) => { {% call spawn_reply() %} if size == 0 { reply.size(xattr_data.len() as u32); @@ -559,7 +578,7 @@ where cmd, in_data, out_size, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok((result, data)) => { {% call spawn_reply() %} reply.ioctl(result, &data); {% endcall %} } Err(e) => { warn!("ioctl: ino {:x?}, [{}], {:?}", ino, e, req); @@ -582,11 +601,12 @@ where let resolver = self.resolver.clone(); let newname = newname.to_owned(); {% call spawn() %} - match handler.link(&req, + match handler.link( + &req, resolver.resolve_id(ino), resolver.resolve_id(newparent), &newname - ) { + ){% if mode == "async" %}.await{% endif %} { {% call reply_entry("newparent", "newname") %}{% endcall %} Err(e) => { warn!("link: parent_ino {:x?}, [{}], {:?}", ino, e, req); @@ -601,7 +621,11 @@ where let handler = self.get_handler(); let resolver = self.resolver.clone(); {% call spawn() %} - match handler.listxattr(&req, resolver.resolve_id(ino), size) { + match handler.listxattr( + &req, + resolver.resolve_id(ino), + size + ){% if mode == "async" %}.await{% endif %} { Ok(xattr_data) => { {% call spawn_reply() %} if size == 0 { reply.size(xattr_data.len() as u32); @@ -625,7 +649,11 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} - match handler.lookup(&req, resolver.resolve_id(parent), &name) { + match handler.lookup( + &req, + resolver.resolve_id(parent), + &name + ){% if mode == "async" %}.await{% endif %} { {% call reply_entry("parent", "name") %}{% endcall %} Err(e) => { // Failure is expected if user try to lookup a nonexistent file, it should not emit a warning @@ -654,7 +682,7 @@ where resolver.resolve_id(ino), unsafe { BorrowedFileHandle::from_raw(fh) }, seek_from_raw(Some(whence), offset), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(new_offset) => { {% call spawn_reply() %} reply.offset(new_offset); {% endcall %} } Err(e) => { warn!("lseek: ino {:x?}, [{}], {:?}", ino, e, req); @@ -678,12 +706,13 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} - match handler.mkdir(&req, + match handler.mkdir( + &req, resolver.resolve_id(parent), &name, mode, umask - ) { + ){% if mode == "async" %}.await{% endif %} { {% call reply_entry("parent", "name") %}{% endcall %} Err(e) => { warn!("mkdir: parent_ino {:x?}, [{}], {:?}", parent, e, req); @@ -715,7 +744,7 @@ where mode, umask, DeviceType::from_rdev(rdev.try_into().unwrap()) - ) { + ){% if mode == "async" %}.await{% endif %} { {% call reply_entry("parent", "name") %}{% endcall %} Err(e) => { warn!("mknod: parent_ino {:x?}, [{}], {:?}", parent, e, req); @@ -734,7 +763,7 @@ where &req, resolver.resolve_id(ino), OpenFlags::from_bits_retain(_flags), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok((file_handle, response_flags)) => { {% call spawn_reply() %} reply.opened(file_handle.as_raw(), response_flags.bits()) {% endcall %} } @@ -755,7 +784,7 @@ where &req, resolver.resolve_id(ino), OpenFlags::from_bits_retain(_flags), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok((file_handle, response_flags)) => { {% call spawn_reply() %} reply.opened(file_handle.as_raw(), response_flags.bits()) {% endcall %} } @@ -790,7 +819,7 @@ where size, FUSEOpenFlags::from_bits_retain(flags), lock_owner, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(data_reply) => { {% call spawn_reply() %} reply.data(&data_reply); {% endcall %} } Err(e) => { warn!("read: ino {:x?}, [{}], {:?}", ino, e, req); @@ -824,9 +853,11 @@ where 0 => match handler.{% if is_extended_readdir %} readdirplus{% else %} readdir{% endif %} - (&req_info, resolver.resolve_id(ino), unsafe { - BorrowedFileHandle::from_raw(fh) - }) { + ( + &req_info, + resolver.resolve_id(ino), + unsafe {BorrowedFileHandle::from_raw(fh)} + ){% if mode == "async" %}.await{% endif %} { Ok(children) => { // Unpack and process children let (child_list, attr_list): (Vec<_>, Vec<_>) = children @@ -864,7 +895,7 @@ where } }, // Subsequent reads: retrieve saved iterator - _ => match { dirmap_iter.safe_borrow_mut().remove(&(ino, offset)) } { + _ => match { dirmap_iter.{{ borrow_mut() }}.remove(&(ino, offset)) } { Some(dirmap_iter) => dirmap_iter, None => { // Case when fuse tries to read again after the final item @@ -893,7 +924,7 @@ where ) { dir_iter.push_front((name, ino, file_attr.clone())); dirmap_iter - .safe_borrow_mut() + .{{ borrow_mut() }} .insert((ino, new_offset - 1), dir_iter); break; } @@ -906,7 +937,7 @@ where if reply.add(ino, new_offset, kind, &name) { dir_iter.push_front((name, ino, kind)); dirmap_iter - .safe_borrow_mut() + .{{ borrow_mut() }} .insert((ino, new_offset - 1), dir_iter); break; } @@ -945,7 +976,10 @@ where let handler = self.get_handler(); let resolver = self.resolver.clone(); {% call spawn() %} - match handler.readlink(&req, resolver.resolve_id(ino)) { + match handler.readlink( + &req, + resolver.resolve_id(ino) + ){% if mode == "async" %}.await{% endif %} { Ok(link) => { {% call spawn_reply() %} reply.data(&link); {% endcall %} } Err(e) => { warn!("[{}] readlink, ino: {:x?}, {:?}", ino, e, req); @@ -976,7 +1010,7 @@ where OpenFlags::from_bits_retain(_flags), _lock_owner, _flush, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("release: ino {:x?}, [{}], {:?}", ino, e, req); @@ -996,7 +1030,7 @@ where resolver.resolve_id(ino), unsafe { OwnedFileHandle::from_raw(fh) }, OpenFlags::from_bits_retain(flags), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("releasedir: ino {:x?}, [{}], {:?}", ino, e, req); @@ -1012,7 +1046,11 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} - match handler.removexattr(&req, resolver.resolve_id(ino), &name) { + match handler.removexattr( + &req, + resolver.resolve_id(ino), + &name + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("removexattr: ino {:x?}, [{}], {:?}", ino, e, req); @@ -1045,7 +1083,7 @@ where resolver.resolve_id(newparent), &newname, RenameFlags::from_bits_retain(flags), - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { resolver.rename(parent, &name, newparent, &newname); {% call spawn_reply() %} reply.ok(); {% endcall %} } @@ -1063,7 +1101,11 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} - match handler.rmdir(&req, resolver.resolve_id(parent), &name) { + match handler.rmdir( + &req, + resolver.resolve_id(parent), + &name + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("[{}] rmdir: parent_ino: {:x?}, {:?}", parent, e, req); @@ -1113,7 +1155,7 @@ where &req, resolver.resolve_id(ino), attrs - ) { + ){% if mode == "async" %}.await{% endif %} { {{ reply_attr() }} Err(e) => { warn!("setattr: ino {:x?}, [{}], {:?}", ino, e, req); @@ -1153,7 +1195,7 @@ where lock_owner, lock_info, sleep, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("setlk: ino {:x?}, [{}], {:?}", ino, e, req); @@ -1186,7 +1228,7 @@ where value, FUSESetXAttrFlags::from_bits_retain(flags), position, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("setxattr: ino {:x?}, [{}], {:?}", ino, e, req); @@ -1201,7 +1243,10 @@ where let handler = self.get_handler(); let resolver = self.resolver.clone(); {% call spawn() %} - match handler.statfs(&req, resolver.resolve_id(ino)) { + match handler.statfs( + &req, + resolver.resolve_id(ino) + ){% if mode == "async" %}.await{% endif %} { Ok(statfs) => { {% call spawn_reply() %} reply.statfs( statfs.total_blocks, @@ -1236,7 +1281,12 @@ where let link_name = link_name.to_owned(); let target = target.to_owned(); {% call spawn() %} - match handler.symlink(&req, resolver.resolve_id(parent), &link_name, &target) { + match handler.symlink( + &req, + resolver.resolve_id(parent), + &link_name, + &target + ){% if mode == "async" %}.await{% endif %} { {% call reply_entry("parent", "link_name") %}{% endcall %} Err(e) => { warn!("symlink: parent_ino {:x?}, [{}], {:?}", parent, e, req); @@ -1272,7 +1322,7 @@ where FUSEWriteFlags::from_bits_retain(write_flags), OpenFlags::from_bits_retain(flags), lock_owner, - ) { + ){% if mode == "async" %}.await{% endif %} { Ok(bytes_written) => { {% call spawn_reply() %} reply.written(bytes_written); {% endcall %} } Err(e) => { warn!("write: ino {:x?}, [{}], {:?}", ino, e, req); @@ -1288,7 +1338,11 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} - match handler.unlink(&req, resolver.resolve_id(parent), &name) { + match handler.unlink( + &req, + resolver.resolve_id(parent), + &name + ){% if mode == "async" %}.await{% endif %} { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { warn!("[{}] unlink: parent_ino: {:x?}, {:?}", parent, e, req); diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index 6699340..be814b0 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -103,11 +103,11 @@ use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static - {% if send_sync %} - + Sync + Send {# Never put this on serial mode #} - {% endif %} +pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% endif %} +where + TId: FileIdType {% if mode == "async" %}+ Send{% endif %} { + {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} {% macro await_keyword() -%}{% if mode == "async" %}.await{% endif %}{%- endmacro %} @@ -152,10 +152,10 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_in: TId, - file_handle_in: BorrowedFileHandle, + file_handle_in: BorrowedFileHandle<'_>, offset_in: i64, file_out: TId, - file_handle_out: BorrowedFileHandle, + file_handle_out: BorrowedFileHandle<'_>, offset_out: i64, len: u64, flags: u32, // Not implemented yet in standard @@ -182,7 +182,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, offset: i64, length: i64, mode: FallocateFlags, @@ -196,7 +196,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, lock_owner: u64, ) -> FuseResult<()>; @@ -210,7 +210,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, datasync: bool, ) -> FuseResult<()>; @@ -224,7 +224,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, datasync: bool, ) -> FuseResult<()>; @@ -233,7 +233,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: Option, + file_handle: Option>, ) -> FuseResult; /// Test for a POSIX file lock. @@ -241,7 +241,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, ) -> FuseResult; @@ -260,7 +260,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, flags: IOCtlFlags, cmd: u32, in_data: Vec, @@ -287,7 +287,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, ) -> FuseResult; @@ -341,7 +341,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, size: u32, flags: FUSEOpenFlags, @@ -358,7 +358,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, ) -> FuseResult>; /// Read directory contents with full file attributes @@ -371,7 +371,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, ) -> FuseResult> { let readdir_result = self.readdir(req, file_id.clone(), file_handle) {{ await_keyword() }}?; @@ -449,7 +449,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, sleep: bool, @@ -487,7 +487,7 @@ pub trait FuseHandler: 'static &self, req: &RequestInfo, file_id: TId, - file_handle: BorrowedFileHandle, + file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, data: Vec, write_flags: FUSEWriteFlags, diff --git a/templates/fuse_lib.rs.j2 b/templates/fuse_lib.rs.j2 new file mode 100644 index 0000000..3e9fd36 --- /dev/null +++ b/templates/fuse_lib.rs.j2 @@ -0,0 +1,24 @@ +pub mod fuse_driver { + include!(concat!(env!("OUT_DIR"), "/{{ mode }}/fuse_driver.rs")); +} +pub mod fuse_handler { + include!(concat!(env!("OUT_DIR"), "/{{ mode }}/fuse_handler.rs")); +} +pub mod mounting { + include!(concat!(env!("OUT_DIR"), "/{{ mode }}/mounting.rs")); +} + +pub use fuse_handler::FuseHandler; +pub use mounting::*; + +pub mod prelude { + //! Re-exports the necessary types and functions from the `easy_fuser` crate. + pub use crate::session::{FusePruner, FuseSession}; + pub use crate::types::*; + + pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; + + pub use super::{mount, spawn_mount}; + pub use super::FuseHandler; +} + diff --git a/templates/mouting.rs.j2 b/templates/mounting.rs.j2 similarity index 97% rename from templates/mouting.rs.j2 rename to templates/mounting.rs.j2 index 7ae1c82..07f06db 100644 --- a/templates/mouting.rs.j2 +++ b/templates/mounting.rs.j2 @@ -1,11 +1,13 @@ use std::io; use std::path::Path; -use core::FuseDriver; -use types::*; use fuser::{mount2, spawn_mount2}; use fuser::MountOption; +use crate::types::*; +use crate::FuseSession; +use super::FuseHandler; +use super::fuse_driver::FuseDriver; /// Mounts a FUSE filesystem at the specified mountpoint. /// From 7d5d8f9601a96cbc8b09ffbb63d9b7b5a398decd Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 11:12:36 +0100 Subject: [PATCH 09/11] read comment warning --- templates/fuse_handler.rs.j2 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index be814b0..0b9d663 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -103,7 +103,10 @@ use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% endif %} +pub trait FuseHandler: 'static + {% if send_sync %} + + Sync + Send {# Never put this on serial mode #} + {% endif %} where TId: FileIdType {% if mode == "async" %}+ Send{% endif %} { From 63bb0bfac8342c364821b692535969dea53e26dc Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 14:22:17 +0100 Subject: [PATCH 10/11] update first test --- .../src/fuse_handlers_signatures.rs | 121 ++++++++-------- easy_fuser_macro/src/lib.rs | 48 +++++-- src/{templates.rs => fuse_presets.rs} | 50 +------ .../default_fuse_handler.rs | 89 ++++++------ .../fd_handler_helper.rs | 58 ++++---- src/{templates => fuse_presets}/mirror_fs.rs | 131 +++++++++--------- src/lib.rs | 3 +- templates.rs | 8 ++ templates/fuse_driver.rs.j2 | 18 ++- templates/fuse_handler.rs.j2 | 106 +++++++------- templates/mounting.rs.j2 | 14 +- tests/integration_test.rs | 38 ++++- 12 files changed, 345 insertions(+), 339 deletions(-) rename src/{templates.rs => fuse_presets.rs} (54%) rename src/{templates => fuse_presets}/default_fuse_handler.rs (95%) rename src/{templates => fuse_presets}/fd_handler_helper.rs (86%) rename src/{templates => fuse_presets}/mirror_fs.rs (74%) diff --git a/easy_fuser_macro/src/fuse_handlers_signatures.rs b/easy_fuser_macro/src/fuse_handlers_signatures.rs index 486f13e..320e940 100644 --- a/easy_fuser_macro/src/fuse_handlers_signatures.rs +++ b/easy_fuser_macro/src/fuse_handlers_signatures.rs @@ -12,13 +12,13 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn destroy(&self); }, "access" => parse_quote! { - fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()>; + fn access(&self, req: &RequestInfo, file_id: Self::TId, mask: AccessMask) -> FuseResult<()>; }, "bmap" => parse_quote! { fn bmap( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, blocksize: u32, idx: u64, ) -> FuseResult; @@ -27,31 +27,32 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn copy_file_range( &self, req: &RequestInfo, - file_in: TId, + file_in: Self::TId, file_handle_in: BorrowedFileHandle<'_>, offset_in: i64, - file_out: TId, + file_out: Self::TId, file_handle_out: BorrowedFileHandle<'_>, offset_out: i64, len: u64, + flags: u32, // Not implemented yet in standard ) -> FuseResult; }, "create" => parse_quote! { fn create( &self, req: &RequestInfo, - parent_id: TId, - name: &OsStr, + parent_id: Self::TId, + name: &std::ffi::OsStr, mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)>; + ) -> FuseResult<(OwnedFileHandle, ::Metadata, FUSEOpenResponseFlags)>; }, "fallocate" => parse_quote! { fn fallocate( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, offset: i64, length: i64, @@ -62,19 +63,19 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn flush( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, ) -> FuseResult<()>; }, "forget" => parse_quote! { - fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64); + fn forget(&self, req: &RequestInfo, file_id: Self::TId, nlookup: u64); }, "fsync" => parse_quote! { fn fsync( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, ) -> FuseResult<()>; @@ -83,7 +84,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn fsyncdir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, ) -> FuseResult<()>; @@ -92,7 +93,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn getattr( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: Option>, ) -> FuseResult; }, @@ -100,7 +101,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn getlk( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, @@ -110,8 +111,8 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn getxattr( &self, req: &RequestInfo, - file_id: TId, - name: &OsStr, + file_id: Self::TId, + name: &std::ffi::OsStr, size: u32, ) -> FuseResult>; }, @@ -119,7 +120,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn ioctl( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, flags: IOCtlFlags, cmd: u32, @@ -131,27 +132,27 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn link( &self, req: &RequestInfo, - file_id: TId, - newparent: TId, - newname: &OsStr, - ) -> FuseResult; + file_id: Self::TId, + newparent: Self::TId, + newname: &std::ffi::OsStr, + ) -> FuseResult<::Metadata>; }, "listxattr" => parse_quote! { - fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult>; + fn listxattr(&self, req: &RequestInfo, file_id: Self::TId, size: u32) -> FuseResult>; }, "lookup" => parse_quote! { fn lookup( &self, req: &RequestInfo, - parent_id: TId, - name: &OsStr, - ) -> FuseResult; + parent_id: Self::TId, + name: &std::ffi::OsStr, + ) -> FuseResult<::Metadata>; }, "lseek" => parse_quote! { fn lseek( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, ) -> FuseResult; @@ -160,28 +161,28 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn mkdir( &self, req: &RequestInfo, - parent_id: TId, - name: &OsStr, + parent_id: Self::TId, + name: &std::ffi::OsStr, mode: u32, umask: u32, - ) -> FuseResult; + ) -> FuseResult<::Metadata>; }, "mknod" => parse_quote! { fn mknod( &self, req: &RequestInfo, - parent_id: TId, - name: &OsStr, + parent_id: Self::TId, + name: &std::ffi::OsStr, mode: u32, umask: u32, rdev: DeviceType, - ) -> FuseResult; + ) -> FuseResult<::Metadata>; }, "open" => parse_quote! { fn open( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, flags: OpenFlags, ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; }, @@ -189,7 +190,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn opendir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, flags: OpenFlags, ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; }, @@ -197,7 +198,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn read( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, size: u32, @@ -209,26 +210,26 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn readdir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, - ) -> FuseResult>; + ) -> FuseResult::MinimalMetadata)>>; }, "readdirplus" => parse_quote! { fn readdirplus( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle, - ) -> FuseResult>; + ) -> FuseResult::Metadata)>>; }, "readlink" => parse_quote! { - fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult>; + fn readlink(&self, req: &RequestInfo, file_id: Self::TId) -> FuseResult>; }, "release" => parse_quote! { fn release( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: OwnedFileHandle, flags: OpenFlags, lock_owner: Option, @@ -239,33 +240,33 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn releasedir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: OwnedFileHandle, flags: OpenFlags, ) -> FuseResult<()>; }, "removexattr" => parse_quote! { - fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()>; + fn removexattr(&self, req: &RequestInfo, file_id: Self::TId, name: &std::ffi::OsStr) -> FuseResult<()>; }, "rename" => parse_quote! { fn rename( &self, req: &RequestInfo, - parent_id: TId, - name: &OsStr, - newparent: TId, - newname: &OsStr, + parent_id: Self::TId, + name: &std::ffi::OsStr, + newparent: Self::TId, + newname: &std::ffi::OsStr, flags: RenameFlags, ) -> FuseResult<()>; }, "rmdir" => parse_quote! { - fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; + fn rmdir(&self, req: &RequestInfo, parent_id: Self::TId, name: &std::ffi::OsStr) -> FuseResult<()>; }, "setattr" => parse_quote! { fn setattr( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, attrs: SetAttrRequest<'_>, ) -> FuseResult; }, @@ -273,7 +274,7 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn setlk( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, @@ -284,30 +285,30 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { fn setxattr( &self, req: &RequestInfo, - file_id: TId, - name: &OsStr, + file_id: Self::TId, + name: &std::ffi::OsStr, value: Vec, flags: FUSESetXAttrFlags, position: u32, ) -> FuseResult<()>; }, "statfs" => parse_quote! { - fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult; + fn statfs(&self, req: &RequestInfo, file_id: Self::TId) -> FuseResult; }, "symlink" => parse_quote! { fn symlink( &self, req: &RequestInfo, - parent_id: TId, - link_name: &OsStr, - target: &Path, - ) -> FuseResult; + parent_id: Self::TId, + link_name: &std::ffi::OsStr, + target: &std::path::Path, + ) -> FuseResult<::Metadata>; }, "write" => parse_quote! { fn write( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, data: Vec, @@ -317,14 +318,14 @@ pub fn get_fuse_handler_trait_fn(func_name: &str) -> TraitItemFn { ) -> FuseResult; }, "unlink" => parse_quote! { - fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; + fn unlink(&self, req: &RequestInfo, parent_id: Self::TId, name: &std::ffi::OsStr) -> FuseResult<()>; }, _ => panic!("unknown function signature"), } } /// Given a trait method like: -/// fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> +/// fn access(&self, req: &RequestInfo, file_id: #TId, mask: AccessMask) -> FuseResult<()> /// Returns an expression: `access(req, file_id, mask)` pub fn make_method_call_expr(method: &TraitItemFn) -> Expr { let method_name = &method.sig.ident; diff --git a/easy_fuser_macro/src/lib.rs b/easy_fuser_macro/src/lib.rs index a654051..0b5a4d6 100644 --- a/easy_fuser_macro/src/lib.rs +++ b/easy_fuser_macro/src/lib.rs @@ -6,17 +6,14 @@ use either::Either; use proc_macro::TokenStream; use quote::quote; use syn::{ - Attribute, ExprBlock, Ident, Result, Token, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - token::{Comma, FatArrow, Semi}, + Attribute, ExprBlock, Ident, Result, Token, parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, token::{Async, Comma, FatArrow, Semi} }; use fuse_handlers_signatures::*; struct FnSigInput { attrs: Vec, + asyncness: Option, name: Ident, tail: Either, } @@ -32,6 +29,7 @@ impl Parse for FnSigInput { attrs.extend(input.call(Attribute::parse_outer)?); } + let asyncness: Option = input.parse()?; let name: Ident = input.parse()?; let _arrow: FatArrow = input.parse()?; @@ -41,7 +39,7 @@ impl Parse for FnSigInput { Either::Right(input.parse::()?) }; - Ok(FnSigInput { attrs, name, tail }) + Ok(FnSigInput { attrs, asyncness, name, tail }) } } @@ -62,6 +60,7 @@ impl Parse for FnSigInput { pub fn fuse_handler_fnsig(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as FnSigInput); let attrs = input.attrs; + let asyncness = input.asyncness; let tail_tokens: proc_macro2::TokenStream = match input.tail { Either::Left(semi) => quote!( #semi ), Either::Right(block) => { @@ -70,13 +69,13 @@ pub fn fuse_handler_fnsig(input: TokenStream) -> TokenStream { }; let trait_fn = get_fuse_handler_trait_fn( - &input.name.to_string(), + &input.name.to_string() ); let fn_sig = trait_fn.sig; quote!( #(#attrs)* - #fn_sig #tail_tokens + #asyncness #fn_sig #tail_tokens ) .into() } @@ -112,6 +111,15 @@ impl Parse for DelegateFsInput { /// ``` #[proc_macro] pub fn delegate_fs(input: TokenStream) -> TokenStream { + delegate_fs_impl(false, input) +} + +#[proc_macro] +pub fn delegate_fs_async(input: TokenStream) -> TokenStream { + delegate_fs_impl(true, input) +} + +fn delegate_fs_impl(is_async: bool, input: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(input as DelegateFsInput); let target = args.target; @@ -119,14 +127,26 @@ pub fn delegate_fs(input: TokenStream) -> TokenStream { let mut expanded = quote! {}; for method in &args.methods { - let fn_impl = get_fuse_handler_trait_fn(&method.to_string()); + let fn_impl = get_fuse_handler_trait_fn( + &method.to_string() + ); let method_expr = make_method_call_expr(&fn_impl); let fn_sig = fn_impl.sig; - expanded.extend(quote! { - #fn_sig { - self.#target.#method_expr - } - }); + + if !is_async { + expanded.extend(quote! { + #fn_sig { + self.#target.#method_expr + } + }); + } else { + expanded.extend(quote! { + async #fn_sig { + self.#target.#method_expr.await + } + }); + } + } expanded.into() diff --git a/src/templates.rs b/src/fuse_presets.rs similarity index 54% rename from src/templates.rs rename to src/fuse_presets.rs index 05df445..c7d4d24 100644 --- a/src/templates.rs +++ b/src/fuse_presets.rs @@ -35,52 +35,4 @@ pub use default_fuse_handler::DefaultFuseHandler; pub mod fd_handler_helper; -pub mod mirror_fs; - -/* -# POSSIBILITY 1: -- Pros: simple to resonnate -- Cons: no delegation (harder to wrap), harder documentation (MirrorFs::write don't exist) -*/ -struct TestFs {} - -pub trait MirrorFs { - fn source() -> PathBuf; -} - -impl MirrorFs for Test { - fn source() -> PathBuf { - todo!() - } -} - -impl FuseHandler for TestFs { - implement_fuse_mirror_fs!(["write"]) - /* Equivalent to - fn write(&self, req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, seek: SeekFrom, data: Vec, - write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option,) -> FuseResult - where Self: MirrorFs { - // implementation_of_mirror_fs.write - } - */ -} - -/* -# POSSIBILITY 2: -- Pros: documentation, easy to wrap -- Cons: lifetime / send+sync ? -*/ - -struct TestFs { - mirror_fs: MirrorFs, // MirrorFs already implements its own functions but doesn't inherit FuseHandler trait -} - -impl FuseHandler for TestFs { - delegate_fs!(mirror_fs, ["write"]) - /* Equivalent to - fn write(&self, req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, seek: SeekFrom, data: Vec, - write_flags: FUSEWriteFlags, flags: OpenFlags, lock_owner: Option,) -> FuseResult { - self.#mirror_fs.write(...) - } - */ -} \ No newline at end of file +pub mod mirror_fs; \ No newline at end of file diff --git a/src/templates/default_fuse_handler.rs b/src/fuse_presets/default_fuse_handler.rs similarity index 95% rename from src/templates/default_fuse_handler.rs rename to src/fuse_presets/default_fuse_handler.rs index aaed060..be09a98 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/fuse_presets/default_fuse_handler.rs @@ -1,9 +1,8 @@ use std::{ - ffi::{OsStr, OsString}, - path::Path, + ffi::{OsStr, OsString}, marker::PhantomData, path::Path }; -use crate::prelude::*; +use crate::types::*; /** # DefaultFuseHandler @@ -42,8 +41,9 @@ The `DefaultFuseHandler` can be configured to either return errors or panic when This is a basic skeleton. For more complete implementations, refer to the templates provided in the library. */ -pub struct DefaultFuseHandler { +pub struct DefaultFuseHandler { handling: HandlingMethod, + phantom: PhantomData } enum HandlingMethod { @@ -51,7 +51,7 @@ enum HandlingMethod { Error(ErrorKind), } -impl DefaultFuseHandler { +impl DefaultFuseHandler { /// Creates a new `DefaultFuseHandler` that returns "Not Implemented" errors for each unimplemented FUSE call. /// /// This is useful for gradually implementing FUSE operations, as it allows the filesystem to @@ -59,6 +59,7 @@ impl DefaultFuseHandler { pub fn new() -> Self { DefaultFuseHandler { handling: HandlingMethod::Error(ErrorKind::FunctionNotImplemented), + phantom: PhantomData, } } @@ -69,6 +70,7 @@ impl DefaultFuseHandler { pub fn new_with_panic() -> Self { DefaultFuseHandler { handling: HandlingMethod::Panic, + phantom: PhantomData, } } @@ -78,12 +80,11 @@ impl DefaultFuseHandler { pub fn new_with_custom_error(error_kind: ErrorKind) -> Self { DefaultFuseHandler { handling: HandlingMethod::Error(error_kind), + phantom: PhantomData, } } -} -impl FuseHandler for DefaultFuseHandler { - fn access(&self, _req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> { + pub fn access(&self, _req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -101,7 +102,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn bmap(&self, _req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult { + pub fn bmap(&self, _req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -125,7 +126,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn copy_file_range( + pub fn copy_file_range( &self, _req: &RequestInfo, file_in: TId, @@ -170,7 +171,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn create( + pub fn create( &self, _req: &RequestInfo, parent_id: TId, @@ -206,7 +207,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn fallocate( + pub fn fallocate( &self, _req: &RequestInfo, file_id: TId, @@ -242,7 +243,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn flush( + pub fn flush( &self, _req: &RequestInfo, file_id: TId, @@ -272,9 +273,9 @@ impl FuseHandler for DefaultFuseHandler { } } - fn forget(&self, _req: &RequestInfo, _file_id: TId, _nlookup: u64) {} + pub fn forget(&self, _req: &RequestInfo, _file_id: TId, _nlookup: u64) {} - fn fsync( + pub fn fsync( &self, _req: &RequestInfo, file_id: TId, @@ -304,7 +305,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn fsyncdir( + pub fn fsyncdir( &self, _req: &RequestInfo, _file_id: TId, @@ -314,7 +315,7 @@ impl FuseHandler for DefaultFuseHandler { Ok(()) } - fn getattr( + pub fn getattr( &self, _req: &RequestInfo, file_id: TId, @@ -341,7 +342,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn getlk( + pub fn getlk( &self, _req: &RequestInfo, file_id: TId, @@ -374,7 +375,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn getxattr( + pub fn getxattr( &self, _req: &RequestInfo, file_id: TId, @@ -404,7 +405,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn ioctl( + pub fn ioctl( &self, _req: &RequestInfo, file_id: TId, @@ -443,7 +444,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn link( + pub fn link( &self, _req: &RequestInfo, file_id: TId, @@ -473,7 +474,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn listxattr(&self, _req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> { + pub fn listxattr(&self, _req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -491,7 +492,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn lookup( + pub fn lookup( &self, _req: &RequestInfo, parent_id: TId, @@ -514,7 +515,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn lseek( + pub fn lseek( &self, _req: &RequestInfo, file_id: TId, @@ -544,7 +545,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn mkdir( + pub fn mkdir( &self, _req: &RequestInfo, parent_id: TId, @@ -577,7 +578,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn mknod( + pub fn mknod( &self, _req: &RequestInfo, parent_id: TId, @@ -613,7 +614,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn open( + pub fn open( &self, _req: &RequestInfo, file_id: TId, @@ -636,7 +637,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn opendir( + pub fn opendir( &self, _req: &RequestInfo, _file_id: TId, @@ -649,7 +650,7 @@ impl FuseHandler for DefaultFuseHandler { )) } - fn read( + pub fn read( &self, _req: &RequestInfo, file_id: TId, @@ -688,7 +689,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn readdir( + pub fn readdir( &self, _req: &RequestInfo, file_id: TId, @@ -715,7 +716,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn readdirplus( + pub fn readdirplus( &self, _req: &RequestInfo, file_id: TId, @@ -742,7 +743,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn readlink(&self, _req: &RequestInfo, file_id: TId) -> FuseResult> { + pub fn readlink(&self, _req: &RequestInfo, file_id: TId) -> FuseResult> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -758,7 +759,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn release( + pub fn release( &self, _req: &RequestInfo, file_id: TId, @@ -794,7 +795,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn releasedir( + pub fn releasedir( &self, _req: &RequestInfo, _file_id: TId, @@ -804,7 +805,7 @@ impl FuseHandler for DefaultFuseHandler { Ok(()) } - fn removexattr(&self, _req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> { + pub fn removexattr(&self, _req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -826,7 +827,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn rename( + pub fn rename( &self, _req: &RequestInfo, parent_id: TId, @@ -862,7 +863,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn rmdir(&self, _req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { + pub fn rmdir(&self, _req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -884,7 +885,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn setattr( + pub fn setattr( &self, _req: &RequestInfo, file_id: TId, @@ -911,7 +912,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn setlk( + pub fn setlk( &self, _req: &RequestInfo, file_id: TId, @@ -947,7 +948,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn setxattr( + pub fn setxattr( &self, _req: &RequestInfo, file_id: TId, @@ -981,11 +982,11 @@ impl FuseHandler for DefaultFuseHandler { } } - fn statfs(&self, _req: &RequestInfo, _file_id: TId) -> FuseResult { + pub fn statfs(&self, _req: &RequestInfo, _file_id: TId) -> FuseResult { Ok(StatFs::default()) } - fn symlink( + pub fn symlink( &self, _req: &RequestInfo, parent_id: TId, @@ -1015,7 +1016,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn unlink(&self, _req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { + pub fn unlink(&self, _req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -1037,7 +1038,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn write( + pub fn write( &self, _req: &RequestInfo, file_id: TId, diff --git a/src/templates/fd_handler_helper.rs b/src/fuse_presets/fd_handler_helper.rs similarity index 86% rename from src/templates/fd_handler_helper.rs rename to src/fuse_presets/fd_handler_helper.rs index 55392ec..90ebe2d 100644 --- a/src/templates/fd_handler_helper.rs +++ b/src/fuse_presets/fd_handler_helper.rs @@ -70,46 +70,45 @@ prefer the usage of option `MountOption::RO` instead of `FdHandlerHelperReadOnly */ use std::marker::PhantomData; - -use crate::prelude::*; +use crate::types::*; use crate::unix_fs; macro_rules! fd_handler_readonly_methods { - () => { - fn flush( + ($file_id:path) => { + pub fn flush( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: BorrowedFileHandle, _lock_owner: u64, ) -> FuseResult<()> { unix_fs::flush(file_handle.as_borrowed_fd()) } - fn fsync( + pub fn fsync( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: BorrowedFileHandle, datasync: bool, ) -> FuseResult<()> { unix_fs::fsync(file_handle.as_borrowed_fd(), datasync) } - fn lseek( + pub fn lseek( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: BorrowedFileHandle, seek: SeekFrom, ) -> FuseResult { unix_fs::lseek(file_handle.as_borrowed_fd(), seek) } - fn read( + pub fn read( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, @@ -119,10 +118,10 @@ macro_rules! fd_handler_readonly_methods { unix_fs::read(file_handle.as_borrowed_fd(), seek, size as usize) } - fn release( + pub fn release( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: OwnedFileHandle, _flags: OpenFlags, _lock_owner: Option, @@ -134,14 +133,14 @@ macro_rules! fd_handler_readonly_methods { } macro_rules! fd_handler_readwrite_methods { - () => { - fn copy_file_range( + ($file_id:path) => { + pub fn copy_file_range( &self, _req: &RequestInfo, - _file_in: TId, + _file_in: $file_id, file_handle_in: BorrowedFileHandle, offset_in: i64, - _file_out: TId, + _file_out: $file_id, file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, @@ -156,10 +155,10 @@ macro_rules! fd_handler_readwrite_methods { ) } - fn fallocate( + pub fn fallocate( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: BorrowedFileHandle, offset: i64, length: i64, @@ -168,10 +167,10 @@ macro_rules! fd_handler_readwrite_methods { unix_fs::fallocate(file_handle.as_borrowed_fd(), offset, length, mode) } - fn write( + pub fn write( &self, _req: &RequestInfo, - _file_id: TId, + _file_id: $file_id, file_handle: BorrowedFileHandle, seek: SeekFrom, data: Vec, @@ -190,16 +189,16 @@ pub struct FdHandlerHelper { } impl FdHandlerHelper { - pub fn new>() -> Self { + pub fn new() -> Self { Self { phantom: PhantomData, } } } -impl FuseHandler for FdHandlerHelper { - fd_handler_readonly_methods!(); - fd_handler_readwrite_methods!(); +impl FdHandlerHelper { + fd_handler_readonly_methods!(TId); + fd_handler_readwrite_methods!(TId); } /// Specific documentation is located in parent module documentation. @@ -208,13 +207,16 @@ pub struct FdHandlerHelperReadOnly { } impl FdHandlerHelperReadOnly { - pub fn new>(inner: THandler) -> Self { + pub fn new() -> Self { Self { phantom: PhantomData, } } } -impl FuseHandler for FdHandlerHelperReadOnly { - fd_handler_readonly_methods!(); +impl FdHandlerHelperReadOnly { + fd_handler_readonly_methods!(TId); } + +pub(super) use fd_handler_readonly_methods; +pub(super) use fd_handler_readwrite_methods; \ No newline at end of file diff --git a/src/templates/mirror_fs.rs b/src/fuse_presets/mirror_fs.rs similarity index 74% rename from src/templates/mirror_fs.rs rename to src/fuse_presets/mirror_fs.rs index 9a41690..e0e36f8 100644 --- a/src/templates/mirror_fs.rs +++ b/src/fuse_presets/mirror_fs.rs @@ -12,8 +12,8 @@ The `MirrorFs` struct implements the `FuseHandler` trait, providing a way to cre ## Implementation Details -- Both variants use a `PathBuf` to represent the repository path they're mirroring. -- They wrap another `FuseHandler` implementation, allowing for composition of filesystem behaviors. +- Both variants use a `std::path:: PathBuf` to represent the repository path they're mirroring. +- They wrap another `FuseHandler` implementation, allowing for composition of filesystem behaviors. - Most FUSE operations are implemented by translating paths and delegating to the `unix_fs` module. - The implementation uses macros to define common methods for both read-only and read-write variants. @@ -21,7 +21,7 @@ The `MirrorFs` struct implements the `FuseHandler` trait, providing a way to cre To use these handlers: -1. Create a new `MirrorFsReadOnly` or `MirrorFs` instance by providing a repository path and an inner `FuseHandler` implementation: +1. Create a new `MirrorFsReadOnly` or `MirrorFs` instance by providing a repository path and an inner `FuseHandler` implementation: ```text let read_only_fs = MirrorFsReadOnly::new(repo_path, inner_handler); @@ -70,67 +70,65 @@ If you intend to enforce read-only at the fuse level, prefer the usage of option `MountOption::RO` instead of `MirrorFsReadOnly`. */ -use std::ffi::{OsStr, OsString}; -use std::path::{Path, PathBuf}; -use fd_handler_helper::*; +use std::path::Path; -use crate::prelude::*; -use crate::templates::*; +use super::fd_handler_helper::*; +use crate::types::*; use crate::unix_fs; macro_rules! mirror_fs_readonly_methods { () => { - fn access(&self, _req: &RequestInfo, file_id: PathBuf, mask: AccessMask) -> FuseResult<()> { + pub fn access(&self, _req: &RequestInfo, file_id: std::path:: PathBuf, mask: AccessMask) -> FuseResult<()> { let file_path = self.source_path.join(file_id); unix_fs::access(&file_path, mask) } - fn getattr( + pub fn getattr( &self, _req: &RequestInfo, - file_id: PathBuf, + file_id: std::path:: PathBuf, _file_handle: Option, ) -> FuseResult { let file_path = self.source_path.join(file_id); unix_fs::lookup(&file_path) } - fn getxattr( + pub fn getxattr( &self, _req: &RequestInfo, - file_id: PathBuf, - name: &OsStr, + file_id: std::path:: PathBuf, + name: &std::ffi::OsStr, size: u32, ) -> FuseResult> { let file_path = self.source_path.join(file_id); unix_fs::getxattr(&file_path, name, size) } - fn listxattr( + pub fn listxattr( &self, _req: &RequestInfo, - file_id: PathBuf, + file_id: std::path:: PathBuf, size: u32, ) -> FuseResult> { let file_path = self.source_path.join(file_id); unix_fs::listxattr(&file_path, size) } - fn lookup( + pub fn lookup( &self, _req: &RequestInfo, - parent_id: PathBuf, - name: &OsStr, + parent_id: std::path:: PathBuf, + name: &std::ffi::OsStr, ) -> FuseResult { let file_path = self.source_path.join(parent_id).join(name); unix_fs::lookup(&file_path) } - fn open( + pub fn open( &self, _req: &RequestInfo, - file_id: PathBuf, + file_id: std::path:: PathBuf, flags: OpenFlags, ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { let file_path = self.source_path.join(file_id); @@ -140,29 +138,29 @@ macro_rules! mirror_fs_readonly_methods { Ok((file_handle, FUSEOpenResponseFlags::empty())) } - fn readdir( + pub fn readdir( &self, _req: &RequestInfo, - file_id: PathBuf, + file_id: std::path:: PathBuf, _file_handle: BorrowedFileHandle, - ) -> FuseResult> { + ) -> FuseResult> { let folder_path = self.source_path.join(file_id); let children = unix_fs::readdir(folder_path.as_ref())?; let mut result = Vec::new(); - result.push((OsString::from("."), FileKind::Directory)); - result.push((OsString::from(".."), FileKind::Directory)); + result.push((std::ffi::OsString::from("."), FileKind::Directory)); + result.push((std::ffi::OsString::from(".."), FileKind::Directory)); for (child_name, child_kind) in children { result.push((child_name, child_kind)); } Ok(result) } - fn readlink(&self, _req: &RequestInfo, file_id: PathBuf) -> FuseResult> { + pub fn readlink(&self, _req: &RequestInfo, file_id: std::path:: PathBuf) -> FuseResult> { let file_path = self.source_path.join(file_id); unix_fs::readlink(&file_path) } - fn statfs(&self, _req: &RequestInfo, file_id: PathBuf) -> FuseResult { + pub fn statfs(&self, _req: &RequestInfo, file_id: std::path:: PathBuf) -> FuseResult { let file_path = self.source_path.join(file_id); unix_fs::statfs(&file_path) } @@ -171,11 +169,11 @@ macro_rules! mirror_fs_readonly_methods { macro_rules! mirror_fs_readwrite_methods { () => { - fn create( + pub fn create( &self, _req: &RequestInfo, - parent_id: PathBuf, - name: &OsStr, + parent_id: std::path:: PathBuf, + name: &std::ffi::OsStr, mode: u32, umask: u32, flags: OpenFlags, @@ -187,11 +185,11 @@ macro_rules! mirror_fs_readwrite_methods { Ok((file_handle, file_attr, FUSEOpenResponseFlags::empty())) } - fn mkdir( + pub fn mkdir( &self, _req: &RequestInfo, - parent_id: PathBuf, - name: &OsStr, + parent_id: std::path:: PathBuf, + name: &std::ffi::OsStr, mode: u32, umask: u32, ) -> FuseResult { @@ -199,11 +197,11 @@ macro_rules! mirror_fs_readwrite_methods { unix_fs::mkdir(&file_path, mode, umask) } - fn mknod( + pub fn mknod( &self, _req: &RequestInfo, - parent_id: PathBuf, - name: &OsStr, + parent_id: std::path:: PathBuf, + name: &std::ffi::OsStr, mode: u32, umask: u32, rdev: DeviceType, @@ -212,23 +210,23 @@ macro_rules! mirror_fs_readwrite_methods { unix_fs::mknod(&file_path, mode, umask, rdev) } - fn removexattr( + pub fn removexattr( &self, _req: &RequestInfo, - file_id: PathBuf, - name: &OsStr, + file_id: std::path:: PathBuf, + name: &std::ffi::OsStr, ) -> FuseResult<()> { let file_path = self.source_path.join(file_id); unix_fs::removexattr(&file_path, name) } - fn rename( + pub fn rename( &self, _req: &RequestInfo, - parent_id: PathBuf, - name: &OsStr, - newparent: PathBuf, - newname: &OsStr, + parent_id: std::path:: PathBuf, + name: &std::ffi::OsStr, + newparent: std::path:: PathBuf, + newname: &std::ffi::OsStr, flags: RenameFlags, ) -> FuseResult<()> { let oldpath = self.source_path.join(parent_id).join(name); @@ -236,26 +234,26 @@ macro_rules! mirror_fs_readwrite_methods { unix_fs::rename(&oldpath, &newpath, flags) } - fn rmdir(&self, _req: &RequestInfo, parent_id: PathBuf, name: &OsStr) -> FuseResult<()> { + pub fn rmdir(&self, _req: &RequestInfo, parent_id: std::path:: PathBuf, name: &std::ffi::OsStr) -> FuseResult<()> { let file_path = self.source_path.join(parent_id).join(name); unix_fs::rmdir(&file_path) } - fn setattr( + pub fn setattr( &self, _req: &RequestInfo, - file_id: PathBuf, + file_id: std::path:: PathBuf, attrs: SetAttrRequest, ) -> FuseResult { let file_path = self.source_path.join(file_id); unix_fs::setattr(&file_path, attrs) } - fn setxattr( + pub fn setxattr( &self, _req: &RequestInfo, - file_id: PathBuf, - name: &OsStr, + file_id: std::path:: PathBuf, + name: &std::ffi::OsStr, value: Vec, flags: FUSESetXAttrFlags, position: u32, @@ -264,37 +262,37 @@ macro_rules! mirror_fs_readwrite_methods { unix_fs::setxattr(&file_path, name, &value, flags, position) } - fn symlink( + pub fn symlink( &self, _req: &RequestInfo, - parent_id: PathBuf, - link_name: &OsStr, + parent_id: std::path:: PathBuf, + link_name: &std::ffi::OsStr, target: &std::path::Path, ) -> FuseResult { let file_path = self.source_path.join(parent_id).join(link_name); unix_fs::symlink(&file_path, target) } - fn unlink(&self, _req: &RequestInfo, parent_id: PathBuf, name: &OsStr) -> FuseResult<()> { + pub fn unlink(&self, _req: &RequestInfo, parent_id: std::path:: PathBuf, name: &std::ffi::OsStr) -> FuseResult<()> { let file_path = self.source_path.join(parent_id).join(name); unix_fs::unlink(&file_path) } }; } -pub trait MirrorFsTrait: FuseHandler { - fn new(source_path: PathBuf) -> Self; +pub trait MirrorFsTrait { + fn new(source_path: std::path:: PathBuf) -> Self; fn source_dir(&self) -> &Path; } /// Specific documentation is located in parent module documentation. pub struct MirrorFs { - source_path: PathBuf, + source_path: std::path:: PathBuf, } impl MirrorFsTrait for MirrorFs { - fn new(source_path: PathBuf) -> Self { + fn new(source_path: std::path:: PathBuf) -> Self { Self { source_path } } @@ -303,18 +301,20 @@ impl MirrorFsTrait for MirrorFs { } } -impl FuseHandler for MirrorFs { +impl MirrorFs { mirror_fs_readonly_methods!(); mirror_fs_readwrite_methods!(); + fd_handler_readonly_methods!(std::path::PathBuf); + fd_handler_readwrite_methods!(std::path::PathBuf); } /// Specific documentation is located in parent module documentation. pub struct MirrorFsReadOnly { - source_path: PathBuf, + source_path: std::path:: PathBuf, } impl MirrorFsTrait for MirrorFsReadOnly { - fn new(source_path: PathBuf) -> Self { + fn new(source_path: std::path:: PathBuf) -> Self { Self { source_path } } @@ -323,10 +323,7 @@ impl MirrorFsTrait for MirrorFsReadOnly { } } -impl FuseHandler for MirrorFsReadOnly { - fn get_inner(&self) -> &dyn FuseHandler { - self.inner.as_ref() - } - +impl MirrorFsReadOnly { mirror_fs_readonly_methods!(); + fd_handler_readonly_methods!(std::path::PathBuf); } diff --git a/src/lib.rs b/src/lib.rs index 15fdb31..d81daba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,13 +12,12 @@ mod core; pub mod inode_mapper; pub mod inode_multi_mapper; pub mod session; -// TODO :pub mod templates; pub mod types; pub mod unix_fs; pub mod fuse_async; pub mod fuse_parallel; pub mod fuse_serial; - +pub mod fuse_presets; pub use session::{FusePruner, FuseSession}; diff --git a/templates.rs b/templates.rs index 1ec8f2f..8d23886 100644 --- a/templates.rs +++ b/templates.rs @@ -24,3 +24,11 @@ pub struct FuseLibTemplate<'a> { pub mode: &'a str, } +/* +#[derive(Template)] +#[template(path = "fuse_presets/xxx__handler.rs.j2")] +pub struct PresetXxxHandler { + pub is_async: bool, +} + */ + diff --git a/templates/fuse_driver.rs.j2 b/templates/fuse_driver.rs.j2 index 5fd2777..c7b1fa3 100644 --- a/templates/fuse_driver.rs.j2 +++ b/templates/fuse_driver.rs.j2 @@ -54,7 +54,7 @@ type DirIter = HashMap<(u64, i64), VecDeque<(OsString, u64, TAttr)>>; pub(crate) struct FuseDriver where TId: FileIdType, - THandler: FuseHandler, + THandler: FuseHandler, { pub resolver: Arc, @@ -80,7 +80,7 @@ where impl Drop for FuseDriver where TId: FileIdType, - THandler: FuseHandler, + THandler: FuseHandler, { fn drop(&mut self) { self.threadpool.join(); @@ -92,11 +92,11 @@ where impl FuseDriver where TId: FileIdType, - THandler: FuseHandler, + THandler: FuseHandler, { pub fn new( handler: THandler, - {% if send_sync %} + {% if mode == "parallel" %} num_threads: usize {% else %} _num_threads: usize @@ -160,7 +160,7 @@ where impl fuser::Filesystem for FuseDriver where TId: FileIdType, - THandler: FuseHandler, + THandler: FuseHandler, { {% macro spawn() -%} {% if mode == "parallel" %} @@ -418,8 +418,12 @@ where let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); - handler.forget(&req, resolver.resolve_id(ino), nlookup); - resolver.forget(ino, nlookup); + #[allow(unused_variables)] // No need for replies + {% call spawn() %} + handler.forget(&req, resolver.resolve_id(ino), nlookup) + {% if mode == "async" %}.await{% endif %}; + resolver.forget(ino, nlookup); + {% endcall %} } fn fsync(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index 0b9d663..eb8b7c8 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -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; @@ -103,13 +100,12 @@ use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static - {% if send_sync %} - + Sync + Send {# Never put this on serial mode #} +pub trait FuseHandler: 'static + {% if mode != "serial" %} + + Send + Sync{# Never put this on serial mode #} {% endif %} -where - TId: FileIdType {% if mode == "async" %}+ Send{% endif %} { + type TId: FileIdType {% if mode == "async" %}+ Send{% endif %}; {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} {% macro await_keyword() -%}{% if mode == "async" %}.await{% endif %}{%- endmacro %} @@ -142,22 +138,22 @@ where /// This method is called for the access() system call. If the 'default_permissions' /// mount option is given, this method is not called. This method is not called /// under Linux kernel versions 2.4.x - {{ async_keyword() }} fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()>; + {{ async_keyword() }} fn access(&self, req: &RequestInfo, file_id: Self::TId, mask: AccessMask) -> FuseResult<()>; /// Map block index within file to block index within device /// /// Note: This makes sense only for block device backed filesystems mounted /// with the 'blkdev' option - {{ async_keyword() }} fn bmap(&self, req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult; + {{ async_keyword() }} fn bmap(&self, req: &RequestInfo, file_id: Self::TId, blocksize: u32, idx: u64) -> FuseResult; /// Copy the specified range from the source inode to the destination inode {{ async_keyword() }} fn copy_file_range( &self, req: &RequestInfo, - file_in: TId, + file_in: Self::TId, file_handle_in: BorrowedFileHandle<'_>, offset_in: i64, - file_out: TId, + file_out: Self::TId, file_handle_out: BorrowedFileHandle<'_>, offset_out: i64, len: u64, @@ -173,18 +169,18 @@ where {{ async_keyword() }} fn create( &self, req: &RequestInfo, - parent_id: TId, + parent_id: Self::TId, name: &OsStr, mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)>; + ) -> FuseResult<(OwnedFileHandle, ::Metadata, FUSEOpenResponseFlags)>; /// Preallocate or deallocate space to a file {{ async_keyword() }} fn fallocate( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, offset: i64, length: i64, @@ -198,13 +194,13 @@ where {{ async_keyword() }} fn flush( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, ) -> FuseResult<()>; /// Release references to an inode, if the nlookup count reaches zero (to substract from the number of lookups). - {{ async_keyword() }} fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64); + {{ async_keyword() }} fn forget(&self, req: &RequestInfo, file_id: Self::TId, nlookup: u64); /// Synchronize file contents /// @@ -212,7 +208,7 @@ where {{ async_keyword() }} fn fsync( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, ) -> FuseResult<()>; @@ -226,7 +222,7 @@ where {{ async_keyword() }} fn fsyncdir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, datasync: bool, ) -> FuseResult<()>; @@ -235,7 +231,7 @@ where {{ async_keyword() }} fn getattr( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: Option>, ) -> FuseResult; @@ -243,7 +239,7 @@ where {{ async_keyword() }} fn getlk( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, @@ -253,7 +249,7 @@ where {{ async_keyword() }} fn getxattr( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, name: &OsStr, size: u32, ) -> FuseResult>; @@ -262,7 +258,7 @@ where {{ async_keyword() }} fn ioctl( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, flags: IOCtlFlags, cmd: u32, @@ -274,22 +270,22 @@ where {{ async_keyword() }} fn link( &self, req: &RequestInfo, - file_id: TId, - newparent: TId, + file_id: Self::TId, + newparent: Self::TId, newname: &OsStr, - ) -> FuseResult; + ) -> FuseResult<::Metadata>; /// List extended attribute names - {{ async_keyword() }} fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult>; + {{ async_keyword() }} fn listxattr(&self, req: &RequestInfo, file_id: Self::TId, size: u32) -> FuseResult>; /// Retrieve file attributes for a directory entry by name and increment the lookup count associated with the inode. - {{ async_keyword() }} fn lookup(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult; + {{ async_keyword() }} fn lookup(&self, req: &RequestInfo, parent_id: Self::TId, name: &OsStr) -> FuseResult<::Metadata>; /// Reposition read/write file offset {{ async_keyword() }} fn lseek( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, ) -> FuseResult; @@ -298,22 +294,22 @@ where {{ async_keyword() }} fn mkdir( &self, req: &RequestInfo, - parent_id: TId, + parent_id: Self::TId, name: &OsStr, mode: u32, umask: u32, - ) -> FuseResult; + ) -> FuseResult<::Metadata>; /// Create a new file node (regular file, device, FIFO, socket, etc) {{ async_keyword() }} fn mknod( &self, req: &RequestInfo, - parent_id: TId, + parent_id: Self::TId, name: &OsStr, mode: u32, umask: u32, rdev: DeviceType, - ) -> FuseResult; + ) -> FuseResult<::Metadata>; /// Open a file and return a file handle. /// @@ -321,7 +317,7 @@ where {{ async_keyword() }} fn open( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, flags: OpenFlags, ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; @@ -331,7 +327,7 @@ where {{ async_keyword() }} fn opendir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, flags: OpenFlags, ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; @@ -343,7 +339,7 @@ where {{ async_keyword() }} fn read( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, size: u32, @@ -360,9 +356,9 @@ where {{ async_keyword() }} fn readdir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, - ) -> FuseResult>; + ) -> FuseResult::MinimalMetadata)>>; /// Read directory contents with full file attributes /// @@ -373,9 +369,9 @@ where {{ async_keyword() }} fn readdirplus( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, - ) -> FuseResult> { + ) -> FuseResult::Metadata)>> { let readdir_result = self.readdir(req, file_id.clone(), file_handle) {{ await_keyword() }}?; let mut result = Vec::with_capacity(readdir_result.len()); @@ -388,7 +384,7 @@ where } /// Read the target of a symbolic link - {{ async_keyword() }} fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult>; + {{ async_keyword() }} fn readlink(&self, req: &RequestInfo, file_id: Self::TId) -> FuseResult>; /// Release an open file /// @@ -397,7 +393,7 @@ where {{ async_keyword() }} fn release( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: OwnedFileHandle, flags: OpenFlags, lock_owner: Option, @@ -412,33 +408,33 @@ where {{ async_keyword() }} fn releasedir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: OwnedFileHandle, flags: OpenFlags, ) -> FuseResult<()>; /// Remove an extended attribute. - {{ async_keyword() }} fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()>; + {{ async_keyword() }} fn removexattr(&self, req: &RequestInfo, file_id: Self::TId, name: &OsStr) -> FuseResult<()>; /// Rename a file or directory {{ async_keyword() }} fn rename( &self, req: &RequestInfo, - parent_id: TId, + parent_id: Self::TId, name: &OsStr, - newparent: TId, + newparent: Self::TId, newname: &OsStr, flags: RenameFlags, ) -> FuseResult<()>; /// Remove a directory - {{ async_keyword() }} fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; + {{ async_keyword() }} fn rmdir(&self, req: &RequestInfo, parent_id: Self::TId, name: &OsStr) -> FuseResult<()>; /// Set file attributes. {{ async_keyword() }} fn setattr( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, attrs: SetAttrRequest, ) -> FuseResult; @@ -451,7 +447,7 @@ where {{ async_keyword() }} fn setlk( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, lock_owner: u64, lock_info: LockInfo, @@ -462,7 +458,7 @@ where {{ async_keyword() }} fn setxattr( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, name: &OsStr, value: Vec, flags: FUSESetXAttrFlags, @@ -470,16 +466,16 @@ where ) -> FuseResult<()>; /// Get file system statistics - {{ async_keyword() }} fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult; + {{ async_keyword() }} fn statfs(&self, req: &RequestInfo, file_id: Self::TId) -> FuseResult; /// Create a symbolic link. {{ async_keyword() }} fn symlink( &self, req: &RequestInfo, - parent_id: TId, + parent_id: Self::TId, link_name: &OsStr, target: &Path, - ) -> FuseResult; + ) -> FuseResult<::Metadata>; /// Write data to a file /// @@ -489,7 +485,7 @@ where {{ async_keyword() }} fn write( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, data: Vec, @@ -499,5 +495,5 @@ where ) -> FuseResult; /// Remove a file - {{ async_keyword() }} fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; + {{ async_keyword() }} fn unlink(&self, req: &RequestInfo, parent_id: Self::TId, name: &OsStr) -> FuseResult<()>; } diff --git a/templates/mounting.rs.j2 b/templates/mounting.rs.j2 index 07f06db..04b33f7 100644 --- a/templates/mounting.rs.j2 +++ b/templates/mounting.rs.j2 @@ -31,7 +31,7 @@ use super::fuse_driver::FuseDriver; /// # Returns /// /// `io::Result<()>` indicating success or failure of the mount operation. -pub fn mount( +pub fn mount( filesystem: FS, mountpoint: P, options: &[MountOption], @@ -40,8 +40,8 @@ pub fn mount( {% endif %} ) -> io::Result<()> where - T: FileIdType, - FS: FuseHandler, + TId: FileIdType, + FS: FuseHandler, P: AsRef, { let driver = FuseDriver::new( @@ -86,17 +86,17 @@ where /// Returns `io::Result`, which is: /// * `Ok(BackgroundSession)` on successful mount, providing a handle to manage the mounted filesystem. /// * `Err(io::Error)` if the mount operation fails. -pub fn spawn_mount( +pub fn spawn_mount( filesystem: FS, mountpoint: P, options: &[MountOption], {% if mode == "parallel" %} num_threads: usize, {% endif %} -) -> io::Result> +) -> io::Result> where - T: FileIdType, - FS: FuseHandler + Send, + TId: FileIdType, + FS: FuseHandler + Send, P: AsRef, { let driver = FuseDriver::new( diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 75c2455..85fc954 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,13 +1,39 @@ -use easy_fuser::prelude::*; -use easy_fuser::templates::{DefaultFuseHandler, mirror_fs::*}; +use easy_fuser::fuse_parallel::prelude::*; +use easy_fuser::fuse_presets::mirror_fs::*; +use easy_fuser::fuse_presets::DefaultFuseHandler; + +use easy_fuser_macro::delegate_fs; use std::fs::{self, File}; use std::io::Write; use std::os::unix::fs::MetadataExt; use std::os::unix::fs::PermissionsExt; +use std::path::PathBuf; use std::time::Duration; use tempfile::TempDir; +struct MyFs { + mirror_fs: MirrorFs, + default_fs: DefaultFuseHandler, +} + +impl FuseHandler for MyFs { + type TId = PathBuf; + + delegate_fs!{ mirror_fs, [ // readonly functions + flush, fsync, lseek, read, release, + access, getattr, getxattr, listxattr, lookup, open, readdir, readlink, + ] + } + delegate_fs!{ mirror_fs, [ // readwrite functions + copy_file_range, fallocate, write, + create, mkdir, mknod, removexattr, rename, rmdir, setattr, setxattr, symlink, unlink + ] + } + + delegate_fs! {default_fs, [ bmap, forget, fsyncdir, getlk, ioctl, link, opendir, releasedir, setlk, statfs ]} +} + #[test] fn test_mirror_fs_operations() { // Create temporary directories for mount point and source @@ -20,10 +46,10 @@ fn test_mirror_fs_operations() { // We won't use spawn_mount because it MirrorFs doesn't implement Send in serial mode 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"))] + let fs = MyFs { + mirror_fs: MirrorFs::new(source_path.clone()), + default_fs: DefaultFuseHandler::new() + }; mount(fs, &mntpoint_clone, &[], 4).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish From 3ff7c5429a5f4058c7ae9f2dbfa254e93f590b5a Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 14:56:53 +0100 Subject: [PATCH 11/11] default impl for forget --- templates/fuse_handler.rs.j2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index eb8b7c8..5cbf70c 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -200,7 +200,9 @@ pub trait FuseHandler: 'static ) -> FuseResult<()>; /// Release references to an inode, if the nlookup count reaches zero (to substract from the number of lookups). - {{ async_keyword() }} fn forget(&self, req: &RequestInfo, file_id: Self::TId, nlookup: u64); + /// + /// A default implementation is provided that does nothing. It is advised to implement in case of manual Inode handling to release memory + {{ async_keyword() }} fn forget(&self, req: &RequestInfo, file_id: Self::TId, nlookup: u64) {} /// Synchronize file contents ///