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/Cargo.toml b/Cargo.toml index a0d16d9..c05d860 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" @@ -18,6 +18,7 @@ libfuse = ["fuser/libfuse"] [dependencies] # Core dependencies +easy_fuser_macro = { version = "0.1", path = "easy_fuser_macro"} fuser = { version = "0.16.0" } log = "0.4" libc = "0.2" diff --git a/build.rs b/build.rs index 8f7bb56..2cce1e0 100644 --- a/build.rs +++ b/build.rs @@ -36,7 +36,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)?; @@ -47,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/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..320e940 --- /dev/null +++ b/easy_fuser_macro/src/fuse_handlers_signatures.rs @@ -0,0 +1,360 @@ +use syn::{Expr, ExprCall, ExprPath, TraitItemFn, parse_quote}; + +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; + }, + "init" => parse_quote! { + fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()>; + }, + "destroy" => parse_quote! { + fn destroy(&self); + }, + "access" => parse_quote! { + fn access(&self, req: &RequestInfo, file_id: Self::TId, mask: AccessMask) -> FuseResult<()>; + }, + "bmap" => parse_quote! { + fn bmap( + &self, + req: &RequestInfo, + file_id: Self::TId, + blocksize: u32, + idx: u64, + ) -> FuseResult; + }, + "copy_file_range" => parse_quote! { + fn copy_file_range( + &self, + req: &RequestInfo, + file_in: Self::TId, + file_handle_in: BorrowedFileHandle<'_>, + offset_in: i64, + 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: Self::TId, + name: &std::ffi::OsStr, + mode: u32, + umask: u32, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, ::Metadata, FUSEOpenResponseFlags)>; + }, + "fallocate" => parse_quote! { + fn fallocate( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + offset: i64, + length: i64, + mode: FallocateFlags, + ) -> FuseResult<()>; + }, + "flush" => parse_quote! { + fn flush( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + ) -> FuseResult<()>; + }, + "forget" => parse_quote! { + fn forget(&self, req: &RequestInfo, file_id: Self::TId, nlookup: u64); + }, + "fsync" => parse_quote! { + fn fsync( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + datasync: bool, + ) -> FuseResult<()>; + }, + "fsyncdir" => parse_quote! { + fn fsyncdir( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + datasync: bool, + ) -> FuseResult<()>; + }, + "getattr" => parse_quote! { + fn getattr( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: Option>, + ) -> FuseResult; + }, + "getlk" => parse_quote! { + fn getlk( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + lock_info: LockInfo, + ) -> FuseResult; + }, + "getxattr" => parse_quote! { + fn getxattr( + &self, + req: &RequestInfo, + file_id: Self::TId, + name: &std::ffi::OsStr, + size: u32, + ) -> FuseResult>; + }, + "ioctl" => parse_quote! { + fn ioctl( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + flags: IOCtlFlags, + cmd: u32, + in_data: Vec, + out_size: u32, + ) -> FuseResult<(i32, Vec)>; + }, + "link" => parse_quote! { + fn link( + &self, + req: &RequestInfo, + file_id: Self::TId, + newparent: Self::TId, + newname: &std::ffi::OsStr, + ) -> FuseResult<::Metadata>; + }, + "listxattr" => parse_quote! { + fn listxattr(&self, req: &RequestInfo, file_id: Self::TId, size: u32) -> FuseResult>; + }, + "lookup" => parse_quote! { + fn lookup( + &self, + req: &RequestInfo, + parent_id: Self::TId, + name: &std::ffi::OsStr, + ) -> FuseResult<::Metadata>; + }, + "lseek" => parse_quote! { + fn lseek( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + ) -> FuseResult; + }, + "mkdir" => parse_quote! { + fn mkdir( + &self, + req: &RequestInfo, + parent_id: Self::TId, + name: &std::ffi::OsStr, + mode: u32, + umask: u32, + ) -> FuseResult<::Metadata>; + }, + "mknod" => parse_quote! { + fn mknod( + &self, + req: &RequestInfo, + parent_id: Self::TId, + name: &std::ffi::OsStr, + mode: u32, + umask: u32, + rdev: DeviceType, + ) -> FuseResult<::Metadata>; + }, + "open" => parse_quote! { + fn open( + &self, + req: &RequestInfo, + file_id: Self::TId, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; + }, + "opendir" => parse_quote! { + fn opendir( + &self, + req: &RequestInfo, + file_id: Self::TId, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; + }, + "read" => parse_quote! { + fn read( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + size: u32, + flags: FUSEOpenFlags, + lock_owner: Option, + ) -> FuseResult>; + }, + "readdir" => parse_quote! { + fn readdir( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult::MinimalMetadata)>>; + }, + "readdirplus" => parse_quote! { + fn readdirplus( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle, + ) -> FuseResult::Metadata)>>; + }, + "readlink" => parse_quote! { + fn readlink(&self, req: &RequestInfo, file_id: Self::TId) -> FuseResult>; + }, + "release" => parse_quote! { + fn release( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: OwnedFileHandle, + flags: OpenFlags, + lock_owner: Option, + flush: bool, + ) -> FuseResult<()>; + }, + "releasedir" => parse_quote! { + fn releasedir( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: OwnedFileHandle, + flags: OpenFlags, + ) -> FuseResult<()>; + }, + "removexattr" => parse_quote! { + fn removexattr(&self, req: &RequestInfo, file_id: Self::TId, name: &std::ffi::OsStr) -> FuseResult<()>; + }, + "rename" => parse_quote! { + fn rename( + &self, + req: &RequestInfo, + 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: Self::TId, name: &std::ffi::OsStr) -> FuseResult<()>; + }, + "setattr" => parse_quote! { + fn setattr( + &self, + req: &RequestInfo, + file_id: Self::TId, + attrs: SetAttrRequest<'_>, + ) -> FuseResult; + }, + "setlk" => parse_quote! { + fn setlk( + &self, + req: &RequestInfo, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + lock_info: LockInfo, + sleep: bool, + ) -> FuseResult<()>; + }, + "setxattr" => parse_quote! { + fn setxattr( + &self, + req: &RequestInfo, + 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: Self::TId) -> FuseResult; + }, + "symlink" => parse_quote! { + fn symlink( + &self, + req: &RequestInfo, + 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: Self::TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + data: Vec, + write_flags: FUSEWriteFlags, + flags: OpenFlags, + lock_owner: Option, + ) -> FuseResult; + }, + "unlink" => parse_quote! { + 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<()> +/// Returns an expression: `access(req, file_id, mask)` +pub fn make_method_call_expr(method: &TraitItemFn) -> Expr { + let method_name = &method.sig.ident; + + 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(); + + 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 new file mode 100644 index 0000000..0b5a4d6 --- /dev/null +++ b/easy_fuser_macro/src/lib.rs @@ -0,0 +1,153 @@ +extern crate proc_macro; + +mod fuse_handlers_signatures; + +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::{Async, Comma, FatArrow, Semi} +}; + +use fuse_handlers_signatures::*; + +struct FnSigInput { + attrs: Vec, + asyncness: Option, + 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 asyncness: Option = input.parse()?; + 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, asyncness, name, tail }) + } +} + +/// Usage: +/// 1. ```rust +/// fuse_handler_fnsig!{ +/// /// doc... +/// init => { _default_implementation_ } +/// } +/// ``` +/// 2. ```rust +/// 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 asyncness = input.asyncness; + let tail_tokens: proc_macro2::TokenStream = match input.tail { + Either::Left(semi) => quote!( #semi ), + Either::Right(block) => { + quote!( #block ) + } + }; + + let trait_fn = get_fuse_handler_trait_fn( + &input.name.to_string() + ); + let fn_sig = trait_fn.sig; + + quote!( + #(#attrs)* + #asyncness #fn_sig #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::bracketed!(content in input); + let methods = Punctuated::::parse_terminated(&content)?; + Ok(Self { + target, + _comma, + methods, + }) + } +} + +/// Usage: +/// ```rust +/// struct Test { +/// mirror_fs: MirrorFs +/// } +/// impl MyFuseHandler { +/// delegate_fs! { mirror_fs, [ read, write ] } +/// } +/// ``` +#[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; + + let mut expanded = quote! {}; + + for method in &args.methods { + 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; + + 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() +} \ No newline at end of file diff --git a/src/core.rs b/src/core.rs index cfe026d..7521698 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,7 +1,6 @@ -mod fuse_driver; -mod helpers; +// TODO: move or remove ? +pub(crate) mod helpers; + mod inode_mapping; -mod thread_mode; -pub(crate) use fuse_driver::FuseDriver; pub(crate) use inode_mapping::{FileIdResolver, InodeResolvable, ROOT_INO}; diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs deleted file mode 100644 index 8440ed8..0000000 --- a/src/core/fuse_driver.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[cfg(feature = "serial")] -mod serial { - include!(concat!(env!("OUT_DIR"), "/serial/fuse_driver.rs")); -} - -#[cfg(feature = "parallel")] -mod parallel { - include!(concat!(env!("OUT_DIR"), "/parallel/fuse_driver.rs")); -} - -#[cfg(feature = "async")] -mod async_task { - include!(concat!(env!("OUT_DIR"), "/async/fuse_driver.rs")); -} - -#[cfg(feature = "serial")] -pub(crate) use serial::*; - -#[cfg(feature = "parallel")] -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 d1c2a3d..6034b9b 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(crate) fn spawn_deadlock_checker() { use log::{error, info}; use parking_lot::deadlock; use std::thread; @@ -28,6 +28,6 @@ pub(super) fn spawn_deadlock_checker() { }); } -pub(super) fn get_random_generation() -> u64 { +pub(crate) fn get_random_generation() -> u64 { Instant::now().elapsed().as_nanos() as u64 } 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/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 new file mode 100644 index 0000000..0532f5d --- /dev/null +++ b/src/fuse_async.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "async")] +include!(concat!(env!("OUT_DIR"), "/async/fuse_lib.rs")); \ No newline at end of file diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs deleted file mode 100644 index cc153cd..0000000 --- a/src/fuse_handler.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(feature = "serial")] -include!(concat!(env!("OUT_DIR"), "/serial/fuse_handler.rs")); - -#[cfg(feature = "parallel")] -include!(concat!(env!("OUT_DIR"), "/parallel/fuse_handler.rs")); - -#[cfg(feature = "async")] -include!(concat!(env!("OUT_DIR"), "/async/fuse_handler.rs")); diff --git a/src/fuse_parallel.rs b/src/fuse_parallel.rs new file mode 100644 index 0000000..492c064 --- /dev/null +++ b/src/fuse_parallel.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "parallel")] +include!(concat!(env!("OUT_DIR"), "/parallel/fuse_lib.rs")); \ No newline at end of file diff --git a/src/templates.rs b/src/fuse_presets.rs similarity index 98% rename from src/templates.rs rename to src/fuse_presets.rs index bcc8e00..c7d4d24 100644 --- a/src/templates.rs +++ b/src/fuse_presets.rs @@ -35,4 +35,4 @@ pub use default_fuse_handler::DefaultFuseHandler; pub mod fd_handler_helper; -pub mod mirror_fs; +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 94% rename from src/templates/default_fuse_handler.rs rename to src/fuse_presets/default_fuse_handler.rs index a0fc377..be09a98 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/fuse_presets/default_fuse_handler.rs @@ -1,12 +1,8 @@ use std::{ - ffi::{OsStr, OsString}, - path::Path, - time::Duration, + ffi::{OsStr, OsString}, marker::PhantomData, path::Path }; -use fuser::KernelConfig; - -use crate::prelude::*; +use crate::types::*; /** # DefaultFuseHandler @@ -45,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 { @@ -54,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 @@ -62,6 +59,7 @@ impl DefaultFuseHandler { pub fn new() -> Self { DefaultFuseHandler { handling: HandlingMethod::Error(ErrorKind::FunctionNotImplemented), + phantom: PhantomData, } } @@ -72,6 +70,7 @@ impl DefaultFuseHandler { pub fn new_with_panic() -> Self { DefaultFuseHandler { handling: HandlingMethod::Panic, + phantom: PhantomData, } } @@ -81,26 +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 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<()> { + pub fn access(&self, _req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -118,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, @@ -142,7 +126,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn copy_file_range( + pub fn copy_file_range( &self, _req: &RequestInfo, file_in: TId, @@ -187,7 +171,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn create( + pub fn create( &self, _req: &RequestInfo, parent_id: TId, @@ -223,7 +207,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn fallocate( + pub fn fallocate( &self, _req: &RequestInfo, file_id: TId, @@ -259,7 +243,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn flush( + pub fn flush( &self, _req: &RequestInfo, file_id: TId, @@ -289,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, @@ -321,7 +305,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn fsyncdir( + pub fn fsyncdir( &self, _req: &RequestInfo, _file_id: TId, @@ -331,7 +315,7 @@ impl FuseHandler for DefaultFuseHandler { Ok(()) } - fn getattr( + pub fn getattr( &self, _req: &RequestInfo, file_id: TId, @@ -358,7 +342,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn getlk( + pub fn getlk( &self, _req: &RequestInfo, file_id: TId, @@ -391,7 +375,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn getxattr( + pub fn getxattr( &self, _req: &RequestInfo, file_id: TId, @@ -421,7 +405,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn ioctl( + pub fn ioctl( &self, _req: &RequestInfo, file_id: TId, @@ -460,7 +444,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn link( + pub fn link( &self, _req: &RequestInfo, file_id: TId, @@ -490,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, @@ -508,7 +492,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn lookup( + pub fn lookup( &self, _req: &RequestInfo, parent_id: TId, @@ -531,7 +515,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn lseek( + pub fn lseek( &self, _req: &RequestInfo, file_id: TId, @@ -561,7 +545,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn mkdir( + pub fn mkdir( &self, _req: &RequestInfo, parent_id: TId, @@ -594,7 +578,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn mknod( + pub fn mknod( &self, _req: &RequestInfo, parent_id: TId, @@ -630,7 +614,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn open( + pub fn open( &self, _req: &RequestInfo, file_id: TId, @@ -653,7 +637,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn opendir( + pub fn opendir( &self, _req: &RequestInfo, _file_id: TId, @@ -666,7 +650,7 @@ impl FuseHandler for DefaultFuseHandler { )) } - fn read( + pub fn read( &self, _req: &RequestInfo, file_id: TId, @@ -705,7 +689,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn readdir( + pub fn readdir( &self, _req: &RequestInfo, file_id: TId, @@ -732,7 +716,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn readdirplus( + pub fn readdirplus( &self, _req: &RequestInfo, file_id: TId, @@ -759,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, @@ -775,7 +759,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn release( + pub fn release( &self, _req: &RequestInfo, file_id: TId, @@ -811,7 +795,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn releasedir( + pub fn releasedir( &self, _req: &RequestInfo, _file_id: TId, @@ -821,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, @@ -843,7 +827,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn rename( + pub fn rename( &self, _req: &RequestInfo, parent_id: TId, @@ -879,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, @@ -901,7 +885,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn setattr( + pub fn setattr( &self, _req: &RequestInfo, file_id: TId, @@ -928,7 +912,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn setlk( + pub fn setlk( &self, _req: &RequestInfo, file_id: TId, @@ -964,7 +948,7 @@ impl FuseHandler for DefaultFuseHandler { } } - fn setxattr( + pub fn setxattr( &self, _req: &RequestInfo, file_id: TId, @@ -998,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, @@ -1032,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, @@ -1054,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 83% rename from src/templates/fd_handler_helper.rs rename to src/fuse_presets/fd_handler_helper.rs index fd238da..90ebe2d 100644 --- a/src/templates/fd_handler_helper.rs +++ b/src/fuse_presets/fd_handler_helper.rs @@ -69,45 +69,46 @@ If you intend to enforce read-only at the fuse level, prefer the usage of option `MountOption::RO` instead of `FdHandlerHelperReadOnly`. */ -use crate::prelude::*; +use std::marker::PhantomData; +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, @@ -117,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, @@ -132,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, @@ -154,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, @@ -166,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, @@ -184,43 +185,38 @@ 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!(); +impl FdHandlerHelper { + fd_handler_readonly_methods!(TId); + fd_handler_readwrite_methods!(TId); } /// Specific documentation is located in parent module documentation. pub struct FdHandlerHelperReadOnly { - inner: Box>, + phantom: PhantomData, } impl FdHandlerHelperReadOnly { - pub fn new>(inner: THandler) -> Self { + pub fn new() -> 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!(); +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 73% rename from src/templates/mirror_fs.rs rename to src/fuse_presets/mirror_fs.rs index 74a1c02..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,42 +262,38 @@ 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, inner: U) -> 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, - inner: Box>, + source_path: std::path:: PathBuf, } 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: std::path:: PathBuf) -> Self { + Self { source_path } } fn source_dir(&self) -> &Path { @@ -307,27 +301,21 @@ impl MirrorFsTrait for MirrorFs { } } -impl FuseHandler for MirrorFs { - fn get_inner(&self) -> &dyn FuseHandler { - self.inner.as_ref() - } - +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, - inner: Box>, + source_path: std::path:: PathBuf, } 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: std::path:: PathBuf) -> Self { + Self { source_path } } fn source_dir(&self) -> &Path { @@ -335,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/fuse_serial.rs b/src/fuse_serial.rs new file mode 100644 index 0000000..7132721 --- /dev/null +++ b/src/fuse_serial.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "serial")] +include!(concat!(env!("OUT_DIR"), "/serial/fuse_lib.rs")); \ No newline at end of file diff --git a/src/inode_mapper.rs b/src/inode_mapper.rs index f162fbf..bc202c4 100644 --- a/src/inode_mapper.rs +++ b/src/inode_mapper.rs @@ -4,7 +4,7 @@ use std::ffi::{OsStr, OsString}; use std::hash::Hash; use std::sync::Arc; -use super::{Inode, ROOT_INODE}; +use crate::types::{Inode, ROOT_INODE}; use std::sync::atomic::{AtomicU64, Ordering}; /// Helper structure for managing inodes and their relationships. @@ -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()); } @@ -594,8 +599,7 @@ mod tests { use std::collections::HashSet; use std::ffi::OsString; - use crate::ROOT_INODE; - use crate::types::Inode; + use crate::types::{Inode, ROOT_INODE}; #[test] fn test_insert_child_returns_old_inode() { diff --git a/src/inode_multi_mapper.rs b/src/inode_multi_mapper.rs index ba97340..8a256a6 100644 --- a/src/inode_multi_mapper.rs +++ b/src/inode_multi_mapper.rs @@ -962,8 +962,7 @@ mod tests { use std::collections::HashSet; use std::ffi::OsString; - use crate::ROOT_INODE; - use crate::types::Inode; + use crate::types::{Inode, ROOT_INODE}; #[test] fn test_insert_child_returns_old_inode() { diff --git a/src/lib.rs b/src/lib.rs index 2d5510b..d81daba 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"), @@ -10,40 +7,17 @@ compile_error!("Feature 'async' is not yet implemented."); ))] compile_error!("At least one of the features 'serial', 'parallel', or 'async' must be enabled"); -#[cfg(all(feature = "serial", any(feature = "parallel", feature = "async")))] -compile_error!("Feature 'serial' cannot be used with feature parallel or async"); - -#[cfg(all(feature = "parallel", any(feature = "serial", feature = "async")))] -compile_error!("Feature 'parallel' cannot be used with feature serial or async"); - -#[cfg(all(feature = "async", any(feature = "serial", feature = "parallel")))] -compile_error!("Feature 'async' cannot be used with feature serial or parallel"); - mod core; -mod fuse_handler; pub mod inode_mapper; -pub mod session; pub mod inode_multi_mapper; -pub mod templates; +pub mod session; pub mod types; pub mod unix_fs; -pub use fuse_handler::FuseHandler; -pub use session::{FusePruner, FuseSession}; - -pub mod prelude { - //! Re-exports the necessary types and functions from the `easy_fuser` crate. - pub use super::fuse_handler::FuseHandler; - pub use super::session::{FusePruner, FuseSession}; - pub use super::types::*; - pub use super::{mount, spawn_mount}; - - pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; -} +pub mod fuse_async; +pub mod fuse_parallel; +pub mod fuse_serial; +pub mod fuse_presets; - -#[cfg(feature = "serial")] -include!(concat!(env!("OUT_DIR"), "/serial/mouting.rs")); -#[cfg(not(feature = "serial"))] -include!(concat!(env!("OUT_DIR"), "/parallel/mouting.rs")); \ No newline at end of file +pub use session::{FusePruner, FuseSession}; 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. /// diff --git a/src/types/file_id_type.rs b/src/types/file_id_type.rs index fe4d2f7..4406820 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/src/unix_fs/linux_fs.rs b/src/unix_fs/linux_fs.rs index b57b8cc..eef74d7 100644 --- a/src/unix_fs/linux_fs.rs +++ b/src/unix_fs/linux_fs.rs @@ -4,7 +4,7 @@ use std::{ path::Path, }; -use crate::PosixError; +use crate::types::PosixError; use libc::{self, c_char, c_int, c_uint, off_t, size_t, ssize_t}; use super::{StatFs, cstring_from_path}; diff --git a/templates.rs b/templates.rs index 53d95c5..8d23886 100644 --- a/templates.rs +++ b/templates.rs @@ -13,7 +13,22 @@ 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, +} + +/* +#[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 bc811bb..c7b1fa3 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; @@ -44,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, @@ -70,7 +80,7 @@ where impl Drop for FuseDriver where TId: FileIdType, - THandler: FuseHandler, + THandler: FuseHandler, { fn drop(&mut self) { self.threadpool.join(); @@ -82,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 @@ -150,7 +160,7 @@ where impl fuser::Filesystem for FuseDriver where TId: FileIdType, - THandler: FuseHandler, + THandler: FuseHandler, { {% macro spawn() -%} {% if mode == "parallel" %} @@ -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); @@ -403,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) { @@ -417,7 +436,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 +456,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 +475,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 +513,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 +536,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 +582,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 +605,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 +625,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 +653,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 +686,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 +710,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 +748,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 +767,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 +788,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 +823,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 +857,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 +899,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 +928,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 +941,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 +980,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 +1014,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 +1034,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 +1050,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 +1087,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 +1105,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 +1159,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 +1199,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 +1232,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 +1247,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 +1285,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 +1326,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 +1342,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 6c5c60b..5cbf70c 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -88,28 +88,28 @@ /// /// 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; use crate::types::*; +use easy_fuser_macro::fuse_handler_fnsig; {% if mode == "async" %} use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% endif %} { +pub trait FuseHandler: 'static + {% if mode != "serial" %} + + Send + Sync{# Never put this on serial mode #} + {% 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 %} - /// 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 +118,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,45 +138,27 @@ 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: 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 { - self.get_inner().bmap(req, file_id, blocksize, idx) - {{ await_keyword() }} - } + {{ 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_handle_in: BorrowedFileHandle, + file_in: Self::TId, + file_handle_in: BorrowedFileHandle<'_>, offset_in: i64, - file_out: TId, - file_handle_out: BorrowedFileHandle, + file_out: Self::TId, + file_handle_out: BorrowedFileHandle<'_>, 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 /// @@ -181,31 +169,23 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ 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)> { - self.get_inner() - .create(req, parent_id, name, mode, umask, flags) - {{ await_keyword() }} - } + ) -> FuseResult<(OwnedFileHandle, ::Metadata, FUSEOpenResponseFlags)>; /// Preallocate or deallocate space to a file {{ async_keyword() }} fn fallocate( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, 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 /// @@ -214,19 +194,15 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn flush( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::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() }}; - } + /// + /// 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 /// @@ -234,13 +210,10 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn fsync( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, datasync: bool, - ) -> FuseResult<()> { - self.get_inner().fsync(req, file_id, file_handle, datasync) - {{ await_keyword() }} - } + ) -> FuseResult<()>; /// Synchronize directory contents /// @@ -251,131 +224,94 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn fsyncdir( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::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( &self, req: &RequestInfo, - file_id: TId, - file_handle: Option, - ) -> FuseResult { - self.get_inner().getattr(req, file_id, file_handle) - {{ await_keyword() }} - } + file_id: Self::TId, + file_handle: Option>, + ) -> FuseResult; /// Test for a POSIX file lock. {{ async_keyword() }} fn getlk( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + 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( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, name: &OsStr, size: u32, - ) -> FuseResult> { - self.get_inner().getxattr(req, file_id, name, size) - {{ await_keyword() }} - } + ) -> FuseResult>; /// control device {{ async_keyword() }} fn ioctl( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, flags: IOCtlFlags, 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( &self, req: &RequestInfo, - file_id: TId, - newparent: TId, + file_id: Self::TId, + newparent: Self::TId, newname: &OsStr, - ) -> FuseResult { - self.get_inner().link(req, file_id, newparent, newname) - {{ await_keyword() }} - } + ) -> FuseResult<::Metadata>; /// 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: 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 { - self.get_inner().lookup(req, parent_id, name) - {{ await_keyword() }} - } + {{ 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_handle: BorrowedFileHandle, + file_id: Self::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( &self, req: &RequestInfo, - parent_id: TId, + parent_id: Self::TId, name: &OsStr, mode: u32, umask: u32, - ) -> FuseResult { - self.get_inner().mkdir(req, parent_id, name, mode, umask) - {{ await_keyword() }} - } + ) -> 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 { - self.get_inner() - .mknod(req, parent_id, name, mode, umask, rdev) - {{ await_keyword() }} - } + ) -> FuseResult<::Metadata>; /// Open a file and return a file handle. /// @@ -383,12 +319,9 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn open( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().open(req, file_id, flags) - {{ await_keyword() }} - } + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; /// Open a directory /// @@ -396,12 +329,9 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn opendir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().opendir(req, file_id, flags) - {{ await_keyword() }} - } + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; /// Read data from a file /// @@ -411,17 +341,13 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn read( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, 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 /// @@ -432,12 +358,9 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn readdir( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - ) -> FuseResult> { - self.get_inner().readdir(req, file_id, file_handle) - {{ await_keyword() }} - } + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult::MinimalMetadata)>>; /// Read directory contents with full file attributes /// @@ -448,9 +371,9 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn readdirplus( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - ) -> FuseResult> { + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult::Metadata)>> { let readdir_result = self.readdir(req, file_id.clone(), file_handle) {{ await_keyword() }}?; let mut result = Vec::with_capacity(readdir_result.len()); @@ -463,10 +386,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: Self::TId) -> FuseResult>; /// Release an open file /// @@ -475,16 +395,12 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn release( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, file_handle: OwnedFileHandle, 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 /// @@ -494,52 +410,35 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn releasedir( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::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: 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<()> { - 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: 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 { - self.get_inner().setattr(req, file_id, attrs) - {{ await_keyword() }} - } + ) -> FuseResult; /// Acquire, modify or release a POSIX file lock /// @@ -550,49 +449,35 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn setlk( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, 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( &self, req: &RequestInfo, - file_id: TId, + file_id: Self::TId, name: &OsStr, 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: 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 { - self.get_inner().symlink(req, parent_id, link_name, target) - {{ await_keyword() }} - } + ) -> FuseResult<::Metadata>; /// Write data to a file /// @@ -602,29 +487,15 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {{ async_keyword() }} fn write( &self, req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, + file_id: Self::TId, + file_handle: BorrowedFileHandle<'_>, seek: SeekFrom, data: Vec, 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: Self::TId, name: &OsStr) -> FuseResult<()>; } 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 92% rename from templates/mouting.rs.j2 rename to templates/mounting.rs.j2 index 7ae1c82..04b33f7 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. /// @@ -29,7 +31,7 @@ use fuser::MountOption; /// # Returns /// /// `io::Result<()>` indicating success or failure of the mount operation. -pub fn mount( +pub fn mount( filesystem: FS, mountpoint: P, options: &[MountOption], @@ -38,8 +40,8 @@ pub fn mount( {% endif %} ) -> io::Result<()> where - T: FileIdType, - FS: FuseHandler, + TId: FileIdType, + FS: FuseHandler, P: AsRef, { let driver = FuseDriver::new( @@ -84,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 66b4d38..908a009 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