From 12b47f245756b9bc0aa86c9c5465263cffb92dc2 Mon Sep 17 00:00:00 2001 From: alogani Date: Sat, 18 Jan 2025 19:34:52 +0100 Subject: [PATCH 1/5] Moving logic to easy_fuser_macro --- Cargo.toml | 7 +- easy_fuser_macro/Cargo.toml | 12 + easy_fuser_macro/src/fuse_driver.rs | 1127 ++++++++++++++++++++++++++ easy_fuser_macro/src/fuse_handler.rs | 559 +++++++++++++ easy_fuser_macro/src/handler_type.rs | 19 + easy_fuser_macro/src/lib.rs | 32 + easy_fuser_macro_test/Cargo.toml | 8 + easy_fuser_macro_test/src/main.rs | 5 + src/fuse_async.rs | 1 + src/fuse_async/fuse_handler.rs | 31 + src/fuse_common.rs | 1 + src/fuse_parallel.rs | 1 + src/fuse_parallel/fuse_handler.rs | 5 + src/fuse_serial.rs | 1 + src/fuse_serial/fuse_handler.rs | 5 + src/lib.rs | 5 + src/types/file_id_type.rs | 6 +- 17 files changed, 1819 insertions(+), 6 deletions(-) create mode 100644 easy_fuser_macro/Cargo.toml create mode 100644 easy_fuser_macro/src/fuse_driver.rs create mode 100644 easy_fuser_macro/src/fuse_handler.rs create mode 100644 easy_fuser_macro/src/handler_type.rs create mode 100644 easy_fuser_macro/src/lib.rs create mode 100644 easy_fuser_macro_test/Cargo.toml create mode 100644 easy_fuser_macro_test/src/main.rs create mode 100644 src/fuse_async.rs create mode 100644 src/fuse_async/fuse_handler.rs create mode 100644 src/fuse_common.rs create mode 100644 src/fuse_parallel.rs create mode 100644 src/fuse_parallel/fuse_handler.rs create mode 100644 src/fuse_serial.rs create mode 100644 src/fuse_serial/fuse_handler.rs diff --git a/Cargo.toml b/Cargo.toml index 22cc500..0e8ca08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,13 @@ repository = "https://github.com/Alogani/easy_fuser" default = [] serial = [] parallel = ["dep:threadpool"] -async = ["dep:async-trait", "dep:tokio"] +async = [ "dep:tokio"] 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.15" @@ -31,11 +32,11 @@ parking_lot = { version = "0.12", features = ["deadlock_detection"], optional = # Async dependencies # easy_fuser_async_macro = { path = "./easy_fuser_async_macro", optional = true } tokio = { version = "1.42.0", features = ["full"], optional = true } -async-trait = { version = "0.1.83", optional = true } +async-trait = { version = "0.1.85" } [dev-dependencies] tempfile = "3.14" env_logger = "0.11" [package.metadata.docs.rs] -features = ["parallel"] \ No newline at end of file +features = ["parallel"] diff --git a/easy_fuser_macro/Cargo.toml b/easy_fuser_macro/Cargo.toml new file mode 100644 index 0000000..5c9763f --- /dev/null +++ b/easy_fuser_macro/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "easy_fuser_macro" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = {version = "2.0", features = [ "full" ] } +quote = "1.0" +proc-macro2 = "1.0" diff --git a/easy_fuser_macro/src/fuse_driver.rs b/easy_fuser_macro/src/fuse_driver.rs new file mode 100644 index 0000000..00b761b --- /dev/null +++ b/easy_fuser_macro/src/fuse_driver.rs @@ -0,0 +1,1127 @@ +use proc_macro2::{Group, TokenStream, TokenTree}; +use quote::{format_ident, quote}; + +use crate::handler_type::HandlerType; + +fn wrap_handler_execution(handler_type: HandlerType, block: TokenStream) -> TokenStream { + match handler_type { + HandlerType::Async => quote! { + self.runtime.spawn(async move { + #block + }); + }, + HandlerType::Serial => block, + HandlerType::Parallel => quote! { + self.threadpool.execute(move || { + #block + }); + }, + } +} + +fn expand_macro_placeholders(handler_type: HandlerType, input: TokenStream) -> TokenStream { + let mut tokens = input.into_iter(); + let mut output = TokenStream::new(); + let mut function_name = String::new(); + let mut arg_names = Vec::new(); + + // Extract function name + while let Some(token) = tokens.next() { + match token { + TokenTree::Ident(ident) if ident == "fn" => { + if let Some(TokenTree::Ident(name)) = tokens.next() { + function_name = name.to_string(); + output.extend(vec![TokenTree::Ident(ident), TokenTree::Ident(name)]); + + // Extract argument names + if let Some(TokenTree::Group(group)) = tokens.next() { + let args = group.stream(); + let mut current_arg = String::new(); + for arg in args.into_iter() { + match arg { + TokenTree::Ident(ident) => { + if ident != "mut" { + current_arg = ident.to_string(); + } + } + TokenTree::Punct(p) if p.as_char() == ':' => { + if !current_arg.is_empty() && current_arg != "self" { + arg_names.push(current_arg.clone()); + } + current_arg.clear(); + } + TokenTree::Punct(p) if p.as_char() == ',' => { + current_arg.clear(); + } + _ => {} + } + } + output.extend(std::iter::once(TokenTree::Group(group))); + } + break; + } + } + _ => output.extend(std::iter::once(token)), + } + } + + if arg_names[0] != "req" { + panic!( + "Invalid function signature: expected 'req', found '{}'", + arg_names[0] + ); + } + arg_names[0] = String::from("&req"); + arg_names.pop(); // remove reply + let req_arg = quote!(&req); + let arg_idents: Vec<_> = arg_names + .iter() + .skip(1) // Skip the first 'req' argument + .map(|arg| format_ident!("{}", arg)) + .collect(); + let all_args = quote! { + #req_arg, #(#arg_idents),* + }; + + // Expand the rest of the tokens + output.extend(expand_macro_tokens( + handler_type, + &function_name, + &all_args, + tokens, + )); + + output +} + +fn expand_macro_tokens( + handler_type: HandlerType, + function_name: &str, + args: &TokenStream, + mut tokens: impl Iterator, +) -> TokenStream { + let mut output = TokenStream::new(); + + while let Some(token) = tokens.next() { + match token { + TokenTree::Punct(punct) if punct.as_char() == '$' => { + if let Some(TokenTree::Ident(ident)) = tokens.next() { + let key = ident.to_string(); + let replacement = match key.as_str() { + "req" => quote!(let request = RequestInfo::from(req);), + "handler" => match handler_type { + HandlerType::Serial => quote!(let handler = &self.handler;), + _ => quote!(let handler = Arc::clone(&self.handler);), + }, + "resolver" => match handler_type { + HandlerType::Serial => quote!(let resolver = &self.resolver;), + _ => quote!(let resolver = Arc::clone(&self.resolver);), + }, + "ino" => quote!(let inode = resolver.resolve_id(ino);), + "parent" => quote!(let parent = resolver.resolve_id(parent);), + "fh" => quote!(let fh = unsafe { BorrowedFileHandle::from_raw(fh) };), + "wrap" => { + if let Some(TokenTree::Group(group)) = tokens.next() { + wrap_handler_execution( + handler_type, + expand_macro_tokens( + handler_type, + function_name, + args, + group.stream().into_iter(), + ), + ) + } else { + panic!("Expected group after $wrap") + } + } + "args" => args.clone(), + "reply_attr" => reply_attr(), + "reply_entry" => reply_entry(), + "warn_error" => { + quote! { + Err(e) => { + warn!(concat!(#function_name, ": ino {:x?}, [{}], {:?}"), ino, e, req); + reply.error(e.raw_error()) + } + } + } + "info_error" => { + quote! { + Err(e) => { + info!(concat!(#function_name, ": ino {:x?}, [{}], {:?}"), ino, e, req); + reply.error(e.raw_error()) + } + } + } + unknown => panic!("Unknown dollar identifier: {}", unknown), + }; + output.extend(replacement); + } else { + panic!("Expected identifier after $"); + } + } + TokenTree::Group(group) => { + let content = expand_macro_tokens( + handler_type, + function_name, + args, + group.stream().into_iter(), + ); + output.extend(std::iter::once(TokenTree::Group(Group::new( + group.delimiter(), + content, + )))); + } + _ => output.extend(std::iter::once(token)), + } + } + + output +} + +fn reply_attr() -> TokenStream { + quote! { + Ok(file_attr) => { + let default_ttl = handler.get_default_ttl(); + let (fuse_attr, ttl, _) = file_attr.to_fuse(ino); + reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr); + }, + } +} + +fn reply_entry() -> TokenStream { + quote! { + Ok(metadata) => { + let default_ttl = handler.get_default_ttl(); + let (id, file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent, name, id, true); + let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); + reply.entry( + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + ); + } + } +} + +fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { + let mut result = Vec::new(); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn access(&mut self, req: &Request, ino: u64, mask: i32, reply: ReplyEmpty) { + $req + $handler + $resolver + $wrap { + $ino + let mask = AccessMask::from_bits_retain(mask); + match handler.access($args) { + Ok(()) => reply.ok(), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn bmap(&mut self, req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { + $req + $handler + $resolver + $wrap { + $ino + match handler.bmap($args) { + Ok(block) => reply.bmap(block), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn copy_file_range( + &mut self, + req: &Request, + ino_in: u64, + fh_in: u64, + offset_in: i64, + ino_out: u64, + fh_out: u64, + offset_out: i64, + len: u64, + flags: u32, + reply: ReplyWrite, + ) { + $req + $handler + $resolver + $wrap { + let ino_in = resolver.resolve_id(ino); + let fh_in = unsafe { BorrowedFileHandle::from_raw(fh_in) }; + let ino_out = resolver.resolve_id(ino); + let fh_out = unsafe { BorrowedFileHandle::from_raw(fh_out) }; + match handler.copy_file_range($args) { + Ok(bytes_written) => reply.written(bytes_written), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn create( + &mut self, + req: &Request, + parent: u64, + name: &OsStr, + mode: u32, + umask: u32, + flags: i32, + reply: ReplyCreate, + ) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + let name = name.as_ref(); + let flags = OpenFlags::from_bits_retain(flags); + match handler.create(&req, $args) { + Ok((file_handle, metadata, response_flags)) => { + let default_ttl = handler.get_default_ttl(); + let (id, file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent, &name, id, true); + let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); + reply.created( + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + file_handle.as_raw(), + response_flags.bits(), + ); + }, + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn fallocate( + &mut self, + req: &Request, + ino: u64, + fh: u64, + offset: i64, + length: i64, + mode: i32, + reply: ReplyEmpty, + ) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let mode = FallocateFlags::from_bits_retain(mode); + match handler.fallocate($args) { + Ok(()) => reply.ok(), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote!{ + fn flush(&mut self, req: &Request, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { + $req + $handler + $resolver + $wrap { + $ino + $fh + match handler.flush($args) { + Ok(()) => reply.ok(), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn forget(&mut self, req: &Request, ino: u64, nlookup: u64) { + $req + let ino = self.resolver.resolve_id(ino); + self.handler.forget(&req, ino, nlookup); + self.resolver.forget(ino, nlookup); + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote!{ + fn fsync(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + $req + $handler + $resolver + $wrap { + $ino + $fh + match handler.fsync($args) { + Ok(()) => reply.ok(), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote!{ + fn fsyncdir(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + $req + $handler + $resolver + $wrap { + $ino + $fh + match handler.fsyncdir($args) { + Ok(()) => reply.ok(), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn getattr(&mut self, req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { + $req + $handler + $resolver + $wrap { + $ino + match handler.getattr($args) { + $reply_attr + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn getlk( + &mut self, + req: &Request<'_>, + ino: u64, + fh: u64, + lock_owner: u64, + start: u64, + end: u64, + typ: i32, + pid: u32, + reply: ReplyLock, + ) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let lock_info = LockInfo { + start, + end, + lock_type: LockType::from_bits_retain(typ), + pid, + }; + match handler.getlk(&req, ino, fh, lock_owner, lock_info) { + Ok(lock) => reply.lock(lock), + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn getxattr(&mut self, req: &Request, ino: u64, name: &OsStr, size: u32, reply: ReplyXattr) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $ino + let name = name.as_ref(); + match handler.getxattr($args) { + Ok(xattr_data) => { + if size == 0 { + reply.size(xattr_data.len() as u32); + } else if size >= xattr_data.len() as u32 { + reply.data(&xattr_data); + } else { + reply.error(ErrorKind::ResultTooLarge.into()); + } + } + $warn_error + }; + }; + } + } + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn ioctl( + &mut self, + req: &Request<'_>, + ino: u64, + fh: u64, + flags: u32, + cmd: u32, + in_data: &[u8], + out_size: u32, + reply: ReplyIoctl, + ) { + $req + $handler + $resolver + let in_data = in_data.to_owned(); + $wrap { + $ino + $fh + let flags = IOCtlFlags::from_bits_retain(flags); + match handler.ioctl($args) { + Ok((result, data)) => reply.ioctl(result, &data), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn link( + &mut self, + req: &Request, + ino: u64, + newparent: u64, + newname: &OsStr, + reply: ReplyEntry, + ) { + $req + $handler + $resolver + let newname = newname.to_owned(); + $wrap { + let newname = newname.as_ref(); + let newparent = resolver.resolve_id(newparent); + match handler.link($args) { + $reply_entry + $warn_error + } + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn listxattr(&mut self, req: &Request, ino: u64, size: u32, reply: ReplyXattr) { + $req + $handler + $resolver + $wrap { + $ino + match handler.listxattr($args) { + Ok(xattr_data) => { + if size == 0 { + reply.size(xattr_data.len() as u32); + } else if size >= xattr_data.len() as u32 { + reply.data(&xattr_data); + } else { + reply.error(ErrorKind::ResultTooLarge.into()); + } + } + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn lookup(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $parent + match handler.lookup($args) { + $reply_entry + // Lookup is preemptivly done in normal situations, we don't need to log an error + // eg: before creating a file + $info_error + } + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn lseek( + &mut self, + req: &Request, + ino: u64, + fh: u64, + offset: i64, + whence: i32, + reply: ReplyLseek, + ) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let seek = seek_from_raw(Some(whence), offset); + match handler.lseek( + &req, + ino, + fh, + seek, + ) { + Ok(new_offset) => reply.offset(new_offset), + $warn_error + }; + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn mkdir( + &mut self, + req: &Request, + parent: u64, + name: &OsStr, + mode: u32, + umask: u32, + reply: ReplyEntry, + ) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $parent + let name = name.as_ref(); + match handler.mkdir($args) { + $reply_entry + $warn_error + } + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn mknod( + &mut self, + req: &Request, + parent: u64, + name: &OsStr, + mode: u32, + umask: u32, + rdev: u32, + reply: ReplyEntry, + ) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $parent + let name = name.as_ref(); + let rdev = DeviceType::from_rdev(rdev.try_into().unwrap()); + match handler.mknod($args) { + $reply_entry + $warn_error + } + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn open(&mut self, req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { + $req + $handler + $resolver + $wrap { + $ino + let flags = OpenFlags::from_bits_retain(flags); + match handler.open($args) { + Ok((file_handle, response_flags)) => { + reply.opened(file_handle.as_raw(), response_flags.bits()) + } + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn opendir(&mut self, req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { + $req + $handler + $resolver + $wrap { + $ino + let flags = OpenFlags::from_bits_retain(flags); + match handler.opendir($args) { + Ok((file_handle, response_flags)) => { + reply.opened(file_handle.as_raw(), response_flags.bits()) + } + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn read( + &mut self, + req: &Request, + ino: u64, + fh: u64, + offset: i64, + size: u32, + flags: i32, + lock_owner: Option, + reply: ReplyData, + ) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let seek = seek_from_raw(Some(offset), 0); + let flags = FUSEOpenFlags::from_bits_retain(flags); + match handler.read($args) { + Ok(data_reply) => reply.data(&data_reply), + $warn_error + }; + }; + } + }, + )); + /*result.push(expand_macro_placeholders( + handler_type, + quote! { + todo!() + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + todo!() + }, + ));*/ + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn readlink(&mut self, req: &Request, ino: u64, reply: ReplyData) { + $req + $handler + $resolver + $wrap { + $ino + match handler.readlink($args) { + Ok(link) => reply.data(&link), + warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn release( + &mut self, + req: &Request, + ino: u64, + fh: u64, + flags: i32, + _lock_owner: Option, + _flush: bool, + reply: ReplyEmpty, + ) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let flags = OpenFlags::from_bits_retain(flags); + match handler.release($args) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders(handler_type, quote! { + fn releasedir(&mut self, req: &Request, ino: u64, fh: u64, flags: i32, reply: ReplyEmpty) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let flags = OpenFlags::from_bits_retain(flags); + match handler.releasedir($args) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + })); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn removexattr(&mut self, req: &Request, ino: u64, name: &OsStr, reply: ReplyEmpty) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $ino + let name = name.as_ref(); + match handler.removexattr($args) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn rename( + &mut self, + req: &Request, + parent: u64, + name: &OsStr, + newparent: u64, + newname: &OsStr, + flags: u32, + reply: ReplyEmpty, + ) { + $req + $handler + $resolver + let name = name.to_owned(); + let newname = newname.to_owned(); + $wrap { + $parent + let name = name.as_ref(); + let newname = newname.as_ref(); + let flags = RenameFlags::from_bits_retain(flags); + match handler.rename($args) { + Ok(()) => { + resolver.rename(parent, &name, newparent, &newname); + reply.ok() + } + $warn_error + } + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn rmdir(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $parent + let name = name.as_ref(); + match handler.rmdir($args) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn setattr( + &mut self, + req: &Request, + ino: u64, + mode: Option, + uid: Option, + gid: Option, + size: Option, + atime: Option, + mtime: Option, + ctime: Option, + fh: Option, + crtime: Option, + chgtime: Option, + bkuptime: Option, + _flags: Option, + reply: ReplyAttr, + ) { + $req + $handler + $resolver + $wrap { + $ino + let attrs = SetAttrRequest { + mode, + uid, + gid, + size, + atime: atime, + mtime: mtime, + ctime: ctime, + crtime: crtime, + chgtime: chgtime, + bkuptime: bkuptime, + flags: None, + file_handle: fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }), + }; + match handler.setattr(&req, ino, attrs) { + $reply_attr + $warn_error + } + } + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn setlk( + &mut self, + req: &Request<'_>, + ino: u64, + fh: u64, + lock_owner: u64, + start: u64, + end: u64, + typ: i32, + pid: u32, + sleep: bool, + reply: ReplyEmpty, + ) { + $req + $handler + $resolver + $wrap { + $ino + $fh + let lock_info = LockInfo { + start, + end, + lock_type: LockType::from_bits_retain(typ), + pid, + }; + match handler.setlk( + &req, + ino, + fh, + lock_owner, + lock_info, + sleep, + ) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn setxattr( + &mut self, + req: &Request, + ino: u64, + name: &OsStr, + value: &[u8], + flags: i32, + position: u32, + reply: ReplyEmpty, + ) { + $req + $handler + $resolver + let name = name.to_owned(); + let value = value.to_owned(); + $wrap { + let name = name.as_ref(); + let flags = FUSESetXAttrFlags::from_bits_retain(flags); + match handler.setxattr($args) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn statfs(&mut self, req: &Request, ino: u64, reply: ReplyStatfs) { + $req + $handler + $resolver + $wrap { + $ino + match handler.statfs($args) { + Ok(statfs) => reply.statfs( + statfs.total_blocks, + statfs.free_blocks, + statfs.available_blocks, + statfs.total_files, + statfs.free_files, + statfs.block_size, + statfs.max_filename_length, + statfs.fragment_size, + ), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn symlink( + &mut self, + req: &Request, + parent: u64, + link_name: &OsStr, + target: &Path, + reply: ReplyEntry, + ) { + $req + $handler + $resolver + let link_name = link_name.to_owned(); + let target = target.to_owned(); + $wrap { + let link_name = link_name.as_ref(); + let target = target.as_ref(); + match handler.symlink($args) { + $reply_entry + $warn_error + } + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn write( + &mut self, + req: &Request, + ino: u64, + fh: u64, + offset: i64, + data: &[u8], + write_flags: u32, + flags: i32, + lock_owner: Option, + reply: ReplyWrite, + ) { + $req + $handler + $resolver + let data = data.to_owned(); + $wrap { + $ino + $fh + let seek = seek_from_raw(Some(offset), 0); + let write_flags = FUSEWriteFlags::from_bits_retain(write_flags); + let flags = OpenFlags::from_bits_retain(flags); + match handler.write($args) { + Ok(bytes_written) => reply.written(bytes_written), + $warn_error + }; + }; + } + }, + )); + result.push(expand_macro_placeholders( + handler_type, + quote! { + fn unlink(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + $req + $handler + $resolver + let name = name.to_owned(); + $wrap { + $parent + let name = name.as_ref(); + match handler.unlink($args) { + Ok(()) => reply.ok(), + $warn_error + }; + }; + } + }, + )); + result +} + +pub(crate) fn generate_fuse_driver_implementation(handler_type: HandlerType) -> TokenStream { + let fn_impls = generate_fuse_operation_handlers(handler_type); + quote! { + #(#fn_impls)* + } +} diff --git a/easy_fuser_macro/src/fuse_handler.rs b/easy_fuser_macro/src/fuse_handler.rs new file mode 100644 index 0000000..51515ca --- /dev/null +++ b/easy_fuser_macro/src/fuse_handler.rs @@ -0,0 +1,559 @@ +use quote::quote; +use syn::{parse_quote, TraitItemFn}; + +use crate::handler_type::HandlerType; + +fn get_function_defs() -> Vec { + let mut result = Vec::new(); + result.push(parse_quote! { + /// Check file access permissions + /// + /// 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 + fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// 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 + fn bmap( + &self, + req: &RequestInfo, + file_id: TId, + blocksize: u32, + idx: u64, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Copy the specified range from the source inode to the destination inode + 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, + flags: u32, // Not implemented yet in standard + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Create and open a file + /// + /// If the file does not exist, first create it with the specified mode, and then + /// open it. Open flags (with the exception of O_NOCTTY) are available in flags. + /// If this method is not implemented or under Linux kernel versions earlier than + /// 2.6.15, the mknod() and open() methods will be called instead. + fn create( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + mode: u32, + umask: u32, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)>; + }); + result.push(parse_quote! { + /// Preallocate or deallocate space to a file + fn fallocate( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + offset: i64, + length: i64, + mode: FallocateFlags, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Flush cached data for an open file + /// + /// Called on each close() of the opened file. Not guaranteed to be called after writes or at all. + /// Used for returning write errors or removing file locks. + fn flush( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Release references to an inode, if the nlookup count reaches zero (to substract from the number of lookups). + fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64); + }); + result.push(parse_quote! { + /// Synchronize file contents + /// + /// If datasync is true, only flush user data, not metadata. + fn fsync( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + datasync: bool, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Synchronize directory contents + /// + /// If the datasync parameter is true, then only the directory contents should + /// be flushed, not the metadata. The file_handle will contain the value set + /// by the opendir method, or will be undefined if the opendir method didn't + /// set any value. + fn fsyncdir( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + datasync: bool, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Modify file attributes + fn getattr( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: Option>, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Test for a POSIX file lock. + fn getlk( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + lock_info: LockInfo, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Get an extended attribute + fn getxattr( + &self, + req: &RequestInfo, + file_id: TId, + name: &OsStr, + size: u32, + ) -> FuseResult>; + }); + result.push(parse_quote! { + /// control device + fn ioctl( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + flags: IOCtlFlags, + cmd: u32, + in_data: Vec, + out_size: u32, + ) -> FuseResult<(i32, Vec)>; + }); + result.push(parse_quote! { + /// Create a hard link. + fn link( + &self, + req: &RequestInfo, + file_id: TId, + newparent: TId, + newname: &OsStr, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// List extended attribute names + fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult>; + }); + result.push(parse_quote! { + /// Retrieve file attributes for a directory entry by name and increment the lookup count associated with the inode. + fn lookup( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Reposition read/write file offset + fn lseek( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Create a new directory + fn mkdir( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + mode: u32, + umask: u32, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Create a new file node (regular file, device, FIFO, socket, etc) + fn mknod( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + mode: u32, + umask: u32, + rdev: DeviceType, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Open a file and return a file handle. + /// + /// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are available in flags. You may store an arbitrary file handle (pointer, index, etc) in file_handle response, and use this in other all other file operations (read, write, flush, release, fsync). Filesystem may also implement stateless file I/O and not store anything in fh. There are also some flags (direct_io, keep_cache) which the filesystem may set, to change the way the file is opened. See fuse_file_info structure in for more details. + fn open( + &self, + req: &RequestInfo, + file_id: TId, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; + }); + result.push(parse_quote! { + /// Open a directory + /// + /// Allows storing a file handle for use in subsequent directory operations. + fn opendir( + &self, + req: &RequestInfo, + file_id: TId, + flags: OpenFlags, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)>; + }); + result.push(parse_quote! { + /// Read data from a file + /// + /// Read should send exactly the number of bytes requested except on EOF or error, otherwise the rest of the data will be substituted with zeroes. An exception to this is when the file has been opened in ‘direct_io’ mode, in which case the return value of the read system call will reflect the return value of this operation. fh will contain the value set by the open method, or will be undefined if the open method didn’t set any value. + /// + /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 lock_owner: only supported with ABI >= 7.9 + fn read( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + size: u32, + flags: FUSEOpenFlags, + lock_owner: Option, + ) -> FuseResult>; + }); + result.push(parse_quote! { + /// Read directory contents + /// + /// Returns a list of directory entries with minimal metadata. + /// + /// Important: The returned file names (OsString) must not contain any slashes ('/'). + /// Including slashes in the file names will result in undefined behavior. + fn readdir( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult>; + }); + result.push(parse_quote! { + + /// Read the target of a symbolic link + fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult>; + }); + result.push(parse_quote! { + /// Release an open file + /// + /// Called when all file descriptors are closed and all memory mappings are unmapped. + /// Guaranteed to be called once for every open() call. + fn release( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: OwnedFileHandle, + flags: OpenFlags, + lock_owner: Option, + flush: bool, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Release an open directory + /// + /// This method is called exactly once for every successful opendir operation. + /// The file_handle parameter will contain the value set by the opendir method, + /// or will be undefined if the opendir method didn't set any value. + fn releasedir( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: OwnedFileHandle, + flags: OpenFlags, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Remove an extended attribute. + fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Rename a file or directory + fn rename( + &self, + req: &RequestInfo, + parent_id: TId, + name: &OsStr, + newparent: TId, + newname: &OsStr, + flags: RenameFlags, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Remove a directory + fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Set file attributes. + fn setattr( + &self, + req: &RequestInfo, + file_id: TId, + attrs: SetAttrRequest<'_>, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Acquire, modify or release a POSIX file lock + /// + /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but + /// otherwise this is not always the case. For checking lock ownership, 'fi->owner' + /// must be used. The l_pid field in 'struct flock' should only be used to fill + /// in this field in getlk(). + fn setlk( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + lock_owner: u64, + lock_info: LockInfo, + sleep: bool, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Set an extended attribute + fn setxattr( + &self, + req: &RequestInfo, + file_id: TId, + name: &OsStr, + value: Vec, + flags: FUSESetXAttrFlags, + position: u32, + ) -> FuseResult<()>; + }); + result.push(parse_quote! { + /// Get file system statistics + fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult; + }); + result.push(parse_quote! { + /// Create a symbolic link. + fn symlink( + &self, + req: &RequestInfo, + parent_id: TId, + link_name: &OsStr, + target: &Path, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Write data to a file + /// + /// Write should return exactly the number of bytes requested except on error. An exception to this is when the file has been opened in ‘direct_io’ mode, in which case the return value of the write system call will reflect the return value of this operation. fh will contain the value set by the open method, or will be undefined if the open method didn’t set any value. + /// + /// write_flags: will contain FUSE_WRITE_CACHE, if this write is from the page cache. If set, the pid, uid, gid, and fh may not match the value that would have been sent if write cachin is disabled flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 lock_owner: only supported with ABI >= 7.9 + fn write( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + seek: SeekFrom, + data: Vec, + write_flags: FUSEWriteFlags, + flags: OpenFlags, + lock_owner: Option, + ) -> FuseResult; + }); + result.push(parse_quote! { + /// Remove a file + fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()>; + }); + result +} + +fn generate_readdir_plus(handler_type: HandlerType) -> proc_macro2::TokenStream { + match handler_type { + HandlerType::Async => quote! { + /// Read directory contents with full file attributes + /// + /// Default implementation combines readdir and lookup operations. + /// + /// Important: The returned file names (OsString) must not contain any slashes ('/'). + /// Including slashes in the file names will result in undefined behavior. + async fn readdirplus( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult> { + let readdir_result = self.readdir(req, file_id.clone(), file_handle).await?; + let mut result = Vec::with_capacity(readdir_result.len()); + for (name, _) in readdir_result.into_iter() { + let metadata = self.lookup(req, file_id.clone(), &name).await?; + result.push((name, metadata)); + } + Ok(result) + } + }, + _ => quote! { + /// Read directory contents with full file attributes + /// + /// Default implementation combines readdir and lookup operations. + /// + /// Important: The returned file names (OsString) must not contain any slashes ('/'). + /// Including slashes in the file names will result in undefined behavior. + fn readdirplus( + &self, + req: &RequestInfo, + file_id: TId, + file_handle: BorrowedFileHandle<'_>, + ) -> FuseResult> { + let readdir_result = self.readdir(req, file_id.clone(), file_handle)?; + let mut result = Vec::with_capacity(readdir_result.len()); + for (name, _) in readdir_result.into_iter() { + let metadata = self.lookup(req, file_id.clone(), &name)?; + result.push((name, metadata)); + } + Ok(result) + } + }, + } +} + +fn generate_function_impls(handler_type: HandlerType) -> Vec { + let function_defs = get_function_defs(); + + let function_impls = function_defs.into_iter().map(|func| { + let func_name = &func.sig.ident; + let args = &func.sig.inputs; + let return_type = &func.sig.output; + let attrs = &func.attrs; + + // Extract argument names without types and self + let arg_names = args.iter().filter_map(|arg| { + if let syn::FnArg::Typed(pat_type) = arg { + if let syn::Pat::Ident(pat_ident) = &*pat_type.pat { + Some(pat_ident.ident.clone()) + } else { + None + } + } else { + None + } + }); + + match handler_type { + HandlerType::Async => quote! { + #(#attrs)* + async fn #func_name(#args) #return_type { + self.get_inner().#func_name(#(#arg_names),*).await + } + }, + _ => quote! { + #(#attrs)* + fn #func_name(#args) #return_type { + self.get_inner().#func_name(#(#arg_names),*) + } + }, + } + }); + + function_impls.collect() +} + +fn get_common_functions() -> proc_macro2::TokenStream { + quote! { + /// 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. + fn get_default_ttl(&self) -> Duration { + Duration::from_secs(1) + } + + /// Initialize the filesystem and configure kernel connection + fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> { + self.get_inner().init(req, config) + } + + /// Perform cleanup operations on filesystem exit + fn destroy(&self) { + self.get_inner().destroy(); + } + } +} + +fn get_dependencies() -> proc_macro2::TokenStream { + quote! { + use std::ffi::{OsStr, OsString}; + use std::path::Path; + use std::time::Duration; + } +} + +pub(crate) fn generate_fuse_handler_trait(handler_type: HandlerType) -> proc_macro2::TokenStream { + let dependencies = get_dependencies(); + let common_functions = get_common_functions(); + let function_impls = generate_function_impls(handler_type); + let readdirplus_fn = generate_readdir_plus(handler_type); + + match handler_type { + HandlerType::Async => quote! { + use async_trait::async_trait; + #dependencies + + #[async_trait] + pub trait FuseHandler: 'static + Send + Sync { + #common_functions + #(#function_impls)* + #readdirplus_fn + } + }, + HandlerType::Parallel => quote! { + #dependencies + + pub trait FuseHandler: 'static + Send + Sync { + #common_functions + #(#function_impls)* + #readdirplus_fn + } + }, + HandlerType::Serial => quote! { + #dependencies + + pub trait FuseHandler: 'static { + #common_functions + #(#function_impls)* + #readdirplus_fn + } + }, + } +} diff --git a/easy_fuser_macro/src/handler_type.rs b/easy_fuser_macro/src/handler_type.rs new file mode 100644 index 0000000..e2881ef --- /dev/null +++ b/easy_fuser_macro/src/handler_type.rs @@ -0,0 +1,19 @@ +extern crate proc_macro; + +use syn::LitStr; + +#[derive(Clone, Copy)] +pub(crate) enum HandlerType { + Async, + Serial, + Parallel, +} + +pub(crate) fn parse_handler_type(input: LitStr) -> HandlerType { + match input.value().as_str() { + "async" => HandlerType::Async, + "serial" => HandlerType::Serial, + "parallel" => HandlerType::Parallel, + _ => panic!("Invalid handler type. Use 'async', 'serial', or 'parallel'"), + } +} diff --git a/easy_fuser_macro/src/lib.rs b/easy_fuser_macro/src/lib.rs new file mode 100644 index 0000000..90eb04f --- /dev/null +++ b/easy_fuser_macro/src/lib.rs @@ -0,0 +1,32 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use syn::{parse_macro_input, LitStr}; + +mod handler_type; +use handler_type::parse_handler_type; + +mod fuse_driver; +use fuse_driver::generate_fuse_driver_implementation; +mod fuse_handler; +use fuse_handler::generate_fuse_handler_trait; + +#[proc_macro] +pub fn implement_fuse_handler(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as LitStr); + let handler_type = parse_handler_type(input); + + let trait_impl = generate_fuse_handler_trait(handler_type); + + trait_impl.into() +} + +#[proc_macro] +pub fn implement_fuse_driver(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as LitStr); + let handler_type = parse_handler_type(input); + + let struct_impl = generate_fuse_driver_implementation(handler_type); + + struct_impl.into() +} diff --git a/easy_fuser_macro_test/Cargo.toml b/easy_fuser_macro_test/Cargo.toml new file mode 100644 index 0000000..66c7a36 --- /dev/null +++ b/easy_fuser_macro_test/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "easy_fuser_macro_test" +version = "0.1.0" +edition = "2021" + +[dependencies] +easy_fuser_macro = { path = "../easy_fuser_macro" } + diff --git a/easy_fuser_macro_test/src/main.rs b/easy_fuser_macro_test/src/main.rs new file mode 100644 index 0000000..a6f1551 --- /dev/null +++ b/easy_fuser_macro_test/src/main.rs @@ -0,0 +1,5 @@ +use easy_fuser_macro::implement_fuse_driver; + +implement_fuse_driver!("serial"); + +fn main() {} diff --git a/src/fuse_async.rs b/src/fuse_async.rs new file mode 100644 index 0000000..f0e1e1c --- /dev/null +++ b/src/fuse_async.rs @@ -0,0 +1 @@ +pub mod fuse_handler; diff --git a/src/fuse_async/fuse_handler.rs b/src/fuse_async/fuse_handler.rs new file mode 100644 index 0000000..78b7ef9 --- /dev/null +++ b/src/fuse_async/fuse_handler.rs @@ -0,0 +1,31 @@ +use crate::types::*; + +use easy_fuser_macro::implement_fuse_handler; + +implement_fuse_handler!("async"); + +/* # Examples +struct MyFuse { + inner: Box>, +} + +#[async_trait] +impl FuseHandler for MyFuse { + fn get_inner(&self) -> &dyn FuseHandler { + self.inner.as_ref() + } + + async fn read( + &self, + _req: &RequestInfo, + _file_id: Inode, + _file_handle: BorrowedFileHandle<'_>, + _seek: SeekFrom, + _size: u32, + _flags: FUSEOpenFlags, + _lock_owner: Option, + ) -> FuseResult> { + return Ok(Vec::new()); + } +} + */ diff --git a/src/fuse_common.rs b/src/fuse_common.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/fuse_common.rs @@ -0,0 +1 @@ + diff --git a/src/fuse_parallel.rs b/src/fuse_parallel.rs new file mode 100644 index 0000000..f0e1e1c --- /dev/null +++ b/src/fuse_parallel.rs @@ -0,0 +1 @@ +pub mod fuse_handler; diff --git a/src/fuse_parallel/fuse_handler.rs b/src/fuse_parallel/fuse_handler.rs new file mode 100644 index 0000000..adb85cc --- /dev/null +++ b/src/fuse_parallel/fuse_handler.rs @@ -0,0 +1,5 @@ +use crate::types::*; + +use easy_fuser_macro::implement_fuse_handler; + +implement_fuse_handler!("parallel"); diff --git a/src/fuse_serial.rs b/src/fuse_serial.rs new file mode 100644 index 0000000..f0e1e1c --- /dev/null +++ b/src/fuse_serial.rs @@ -0,0 +1 @@ +pub mod fuse_handler; diff --git a/src/fuse_serial/fuse_handler.rs b/src/fuse_serial/fuse_handler.rs new file mode 100644 index 0000000..0a8d0b1 --- /dev/null +++ b/src/fuse_serial/fuse_handler.rs @@ -0,0 +1,5 @@ +use crate::types::*; + +use easy_fuser_macro::implement_fuse_handler; + +implement_fuse_handler!("serial"); diff --git a/src/lib.rs b/src/lib.rs index c23ea55..ea193a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,11 @@ 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"); +pub mod fuse_async; +mod fuse_common; +pub mod fuse_parallel; +pub mod fuse_serial; + mod core; mod fuse_handler; diff --git a/src/types/file_id_type.rs b/src/types/file_id_type.rs index 718e50e..985666c 100644 --- a/src/types/file_id_type.rs +++ b/src/types/file_id_type.rs @@ -39,7 +39,7 @@ use super::inode::*; /// - Cons: Path components are stored in reverse order, which may require additional handling. /// - Root: Represented by an empty vector. pub trait FileIdType: - 'static + Debug + Clone + PartialEq + Eq + std::hash::Hash + InodeResolvable + 'static + Debug + Clone + PartialEq + Eq + std::hash::Hash + InodeResolvable + Send { /// Full metadata type for the file system. /// @@ -48,7 +48,7 @@ pub trait FileIdType: /// /// For PathBuf-based: FileAttribute /// - User only needs to provide FileAttribute; Inode is managed internally. - type Metadata; + type Metadata: Send; /// Minimal metadata type for the file system. /// @@ -57,7 +57,7 @@ pub trait FileIdType: /// /// For PathBuf-based: FileKind /// - User only needs to provide FileKind; Inode is managed internally. - type MinimalMetadata; + type MinimalMetadata: Send; #[doc(hidden)] type _Id; From 24157e3c03542206d01df9c511f3dd51115fc76e Mon Sep 17 00:00:00 2001 From: alogani Date: Sun, 19 Jan 2025 09:20:26 +0100 Subject: [PATCH 2/5] Advancement on FuseDriver --- easy_fuser_macro/src/fuse_driver.rs | 371 ++++++++++-- src/core.rs | 2 +- src/fuse_async.rs | 1 + src/fuse_async/fuse_driver.rs | 0 src/fuse_parallel.rs | 1 + src/fuse_parallel/fuse_driver.rs | 0 src/fuse_serial.rs | 1 + src/fuse_serial/fuse_driver.rs | 884 ++++++++++++++++++++++++++++ 8 files changed, 1222 insertions(+), 38 deletions(-) create mode 100644 src/fuse_async/fuse_driver.rs create mode 100644 src/fuse_parallel/fuse_driver.rs create mode 100644 src/fuse_serial/fuse_driver.rs diff --git a/easy_fuser_macro/src/fuse_driver.rs b/easy_fuser_macro/src/fuse_driver.rs index 00b761b..e6c0388 100644 --- a/easy_fuser_macro/src/fuse_driver.rs +++ b/easy_fuser_macro/src/fuse_driver.rs @@ -88,6 +88,7 @@ fn expand_macro_placeholders(handler_type: HandlerType, input: TokenStream) -> T handler_type, &function_name, &all_args, + false, tokens, )); @@ -98,6 +99,7 @@ fn expand_macro_tokens( handler_type: HandlerType, function_name: &str, args: &TokenStream, + mut log_ino: bool, mut tokens: impl Iterator, ) -> TokenStream { let mut output = TokenStream::new(); @@ -108,7 +110,7 @@ fn expand_macro_tokens( if let Some(TokenTree::Ident(ident)) = tokens.next() { let key = ident.to_string(); let replacement = match key.as_str() { - "req" => quote!(let request = RequestInfo::from(req);), + "req" => quote!(let req = RequestInfo::from(req);), "handler" => match handler_type { HandlerType::Serial => quote!(let handler = &self.handler;), _ => quote!(let handler = Arc::clone(&self.handler);), @@ -117,8 +119,20 @@ fn expand_macro_tokens( HandlerType::Serial => quote!(let resolver = &self.resolver;), _ => quote!(let resolver = Arc::clone(&self.resolver);), }, - "ino" => quote!(let inode = resolver.resolve_id(ino);), - "parent" => quote!(let parent = resolver.resolve_id(parent);), + "ino" => { + log_ino = true; + quote!( + let log_ino = ino; + let ino = resolver.resolve_id(ino); + ) + } + "parent" => { + log_ino = true; + quote!( + let log_ino = parent; + let parent = resolver.resolve_id(parent); + ) + } "fh" => quote!(let fh = unsafe { BorrowedFileHandle::from_raw(fh) };), "wrap" => { if let Some(TokenTree::Group(group)) = tokens.next() { @@ -128,6 +142,7 @@ fn expand_macro_tokens( handler_type, function_name, args, + log_ino, group.stream().into_iter(), ), ) @@ -138,22 +153,8 @@ fn expand_macro_tokens( "args" => args.clone(), "reply_attr" => reply_attr(), "reply_entry" => reply_entry(), - "warn_error" => { - quote! { - Err(e) => { - warn!(concat!(#function_name, ": ino {:x?}, [{}], {:?}"), ino, e, req); - reply.error(e.raw_error()) - } - } - } - "info_error" => { - quote! { - Err(e) => { - info!(concat!(#function_name, ": ino {:x?}, [{}], {:?}"), ino, e, req); - reply.error(e.raw_error()) - } - } - } + "warn_error" => error_response(function_name, false, log_ino), + "info_error" => error_response(function_name, true, log_ino), unknown => panic!("Unknown dollar identifier: {}", unknown), }; output.extend(replacement); @@ -166,6 +167,7 @@ fn expand_macro_tokens( handler_type, function_name, args, + log_ino, group.stream().into_iter(), ); output.extend(std::iter::once(TokenTree::Group(Group::new( @@ -180,6 +182,26 @@ fn expand_macro_tokens( output } +fn error_response(function_name: &str, is_info: bool, log_ino: bool) -> TokenStream { + let error_type = if is_info { quote!(info) } else { quote!(warn) }; + match log_ino { + true => quote! { + Err(e) => { + #error_type!(concat!(#function_name, ": ino {:x?}, [{}], {:?}"), log_ino, e, req); + reply.error(e.raw_error()); + return; + } + }, + false => quote! { + Err(e) => { + #error_type!(concat!(#function_name, ": [{}], {:?}"), e, req); + reply.error(e.raw_error()); + return; + } + }, + } +} + fn reply_attr() -> TokenStream { quote! { Ok(file_attr) => { @@ -206,6 +228,152 @@ fn reply_entry() -> TokenStream { } } +fn readdir_impl(handler_type: HandlerType, is_extended_readdir: bool) -> TokenStream { + let fn_name = match is_extended_readdir { + true => quote!(readdirplus), + false => quote!(readdir), + }; + let fn_signature = match is_extended_readdir { + true => quote!(fn #fn_name( + &mut self, + req: &Request, + ino: u64, + fh: u64, + offset: i64, + mut reply: ReplyDirectory, + )), + false => quote!(fn #fn_name( + &mut self, + req: &Request, + ino: u64, + fh: u64, + offset: i64, + mut reply: ReplyDirectoryPlus, + )), + }; + + let extract_metadata = match is_extended_readdir { + true => quote!(TId::extract_metadata(item.1)), + false => quote!(TId::extract_minimal_metadata(item.1)), + }; + + let dirmap_entries_get = match (handler_type, is_extended_readdir) { + (HandlerType::Serial, false) => quote!(&self.dirmap_entries), + (HandlerType::Serial, true) => quote!(&self.dirmapplus_entries), + (_, false) => quote!(Arc::clone(&self.dirmap_entries)), + (_, true) => quote!(Arc::clone(&self.dirmapplus_entries)), + }; + + let dirmap_entries_borrow_mut = match handler_type { + HandlerType::Serial => quote!(dirmap_entries.borrow_mut()), + _ => quote!(dirmap_entries.lock().unwrap()), + }; + + let reply_add = match is_extended_readdir { + true => quote! { + // readdirplus: Add entries with extended attributes + let default_ttl = handler.get_default_ttl(); + while let Some((name, ino, file_attr)) = directory_entries.pop_front() { + let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); + if reply.add( + ino, + new_offset, + name, + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + ) { + #dirmap_entries_borrow_mut + .insert((ino, new_offset), directory_entries); + break; + } + new_offset += 1; + } + reply.ok(); + }, + false => quote! { + // readdir: Add entries until buffer is full + while let Some((name, ino, kind)) = directory_entries.pop_front() { + if reply.add(ino, new_offset, kind, &name) { + #dirmap_entries_borrow_mut + .insert((ino, new_offset), directory_entries); + break; + } + new_offset += 1; + } + reply.ok(); + }, + }; + + expand_macro_placeholders( + handler_type, + quote! { + #fn_signature { + $req + $handler + $resolver + let dirmap_entries = #dirmap_entries_get; + $wrap { + $ino + $fh + + // Validate offset + if offset < 0 { + error!("readdir called with a negative offset"); + reply.error(ErrorKind::InvalidArgument.into()); + return; + } + + // ### Initialize directory deque + let mut directory_entries = match offset { + // First read: fetch children from handler + 0 => match handler.#fn_name($args) { + Ok(children) => { + // Unpack and process children + let (child_list, attr_list): (Vec<_>, Vec<_>) = children + .into_iter() + .map(|item| { + let (child_id, child_attr) = #extract_metadata; + ((item.0, child_id), child_attr) + }) + .unzip(); + + // Add children to resolver and create iterator + resolver + .add_children( + ino, + child_list, + #is_extended_readdir, + ) + .into_iter() + .zip(attr_list.into_iter()) + .map(|((file_name, file_ino), file_attr)| { + (file_name, file_ino, file_attr) + }) + .collect() + } + $warn_error + }, + // Subsequent reads: retrieve saved iterator + _ => match { #dirmap_entries_borrow_mut.remove(&(ino, offset)) } { + Some(directory_entries) => directory_entries, + None => { + // Case when fuse tries to read again after the final item + reply.ok(); + return; + } + }, + }; + + let mut new_offset = offset + 1; + + #reply_add + } + } + }, + ) +} + fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { let mut result = Vec::new(); result.push(expand_macro_placeholders( @@ -263,9 +431,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.written(bytes_written), @@ -293,9 +461,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { let default_ttl = handler.get_default_ttl(); let (id, file_attr) = TId::extract_metadata(metadata); @@ -366,7 +535,6 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec Vec reply.lock(lock), + Ok(lock) => reply.locked(lock), $warn_error } } @@ -533,6 +702,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec Vec TokenStream { + match handler_type { + HandlerType::Serial => quote! { + use std::cell::RefCell; + + pub(crate) struct FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + handler: THandler, + resolver: TId::Resolver, + dirmap_entries: RefCell>, + dirmapplus_entries: RefCell>, + } + + impl FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + /// num_thread is ignored in serial mode, it is kept for consistency with other modes + pub fn new(handler: THandler, _num_threads: usize) -> FuseDriver { + FuseDriver { + handler, + resolver: TId::Resolver::new(), + dirmap_entries: RefCell::new(HashMap::new()), + dirmapplus_entries: RefCell::new(HashMap::new()), + } + } + } + }, + HandlerType::Parallel => quote! { + use std::sync::{Arc, Mutex}; + use threadpool::ThreadPool; + + pub(crate) struct FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + handler: Arc, + resolver: Arc, + dirmap_entries: Arc>>, + dirmapplus_entries: Arc>>, + pub threadpool: ThreadPool, + } + + impl FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + pub fn new(handler: THandler, num_threads: usize) -> FuseDriver { + FuseDriver { + handler: Arc::new(handler), + resolver: Arc::new(TId::create_resolver()), + dirmap_entries: Arc::new(Mutex::new(HashMap::new())), + dirmapplus_entries: Arc::new(Mutex::new(HashMap::new())), + threadpool: ThreadPool::new(num_threads), + } + } + } + }, + HandlerType::Async => quote! { + use std::sync::{Arc, Mutex}; + use tokio::runtime::Runtime; + + pub(crate) struct FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + handler: Arc, + resolver: Arc, + dirmap_entries: Arc>>, + dirmapplus_entries: Arc>>, + pub runtime: Runtime, + } + + impl FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + pub fn new(handler: THandler, _num_threads: usize) -> FuseDriver { + FuseDriver { + handler: Arc::new(handler), + resolver: Arc::new(TId::create_resolver()), + dirmap_entries Arc::new(Mutex::new(HashMap::new())), + dirmapplus_entries: Arc::new(Mutex::new(HashMap::new())), + runtime: Runtime::new().unwrap(), + } + } + } + }, + } +} + +fn get_dependencies() -> proc_macro2::TokenStream { + quote! { + use std::{ + collections::{HashMap, VecDeque}, + ffi::{OsStr, OsString}, + path::Path, + time::{Instant, SystemTime}, + }; + + use libc::c_int; + use log::{error, info, warn}; + + use fuser::{ + self, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, + ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, + ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, + }; + } +} + pub(crate) fn generate_fuse_driver_implementation(handler_type: HandlerType) -> TokenStream { + let dependencies = get_dependencies(); + let fuse_driver_struct = generate_fuse_driver_struct(handler_type); let fn_impls = generate_fuse_operation_handlers(handler_type); quote! { - #(#fn_impls)* + #dependencies + + type DirMapEntries = HashMap<(u64, i64), VecDeque<(OsString, u64, TAttr)>>; + + fn get_random_generation() -> u64 { + Instant::now().elapsed().as_nanos() as u64 + } + + #fuse_driver_struct + + impl FuseDriver + where + TId: FileIdType, + THandler: FuseHandler, + { + #(#fn_impls)* + } } } diff --git a/src/core.rs b/src/core.rs index 52f3f1e..72b1a49 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,6 +1,6 @@ mod fuse_driver; mod fuse_driver_types; -mod inode_mapping; +pub(crate) mod inode_mapping; mod macros; mod thread_mode; diff --git a/src/fuse_async.rs b/src/fuse_async.rs index f0e1e1c..aacc3f2 100644 --- a/src/fuse_async.rs +++ b/src/fuse_async.rs @@ -1 +1,2 @@ +pub(crate) mod fuse_driver; pub mod fuse_handler; diff --git a/src/fuse_async/fuse_driver.rs b/src/fuse_async/fuse_driver.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/fuse_parallel.rs b/src/fuse_parallel.rs index f0e1e1c..aacc3f2 100644 --- a/src/fuse_parallel.rs +++ b/src/fuse_parallel.rs @@ -1 +1,2 @@ +pub(crate) mod fuse_driver; pub mod fuse_handler; diff --git a/src/fuse_parallel/fuse_driver.rs b/src/fuse_parallel/fuse_driver.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/fuse_serial.rs b/src/fuse_serial.rs index f0e1e1c..aacc3f2 100644 --- a/src/fuse_serial.rs +++ b/src/fuse_serial.rs @@ -1 +1,2 @@ +pub(crate) mod fuse_driver; pub mod fuse_handler; diff --git a/src/fuse_serial/fuse_driver.rs b/src/fuse_serial/fuse_driver.rs new file mode 100644 index 0000000..ed5fa8b --- /dev/null +++ b/src/fuse_serial/fuse_driver.rs @@ -0,0 +1,884 @@ +use crate::types::*; + +use super::fuse_handler::FuseHandler; +use crate::core::inode_mapping::*; + +use easy_fuser_macro::implement_fuse_driver; + +//implement_fuse_driver!("serial"); + + +use std::{ + collections::{ + HashMap,VecDeque + },ffi::{ + OsStr,OsString + },path::Path,time::{ + Instant,SystemTime + }, +}; +use libc::c_int; +use log::{ + error,info,warn +}; +use fuser::{ + self,KernelConfig,ReplyAttr,ReplyBmap,ReplyCreate,ReplyData,ReplyDirectory,ReplyDirectoryPlus,ReplyEmpty,ReplyEntry,ReplyIoctl,ReplyLock,ReplyLseek,ReplyOpen,ReplyStatfs,ReplyWrite,ReplyXattr,Request,TimeOrNow, +}; +type DirMapEntries = HashMap<(u64,i64),VecDeque<(OsString,u64,TAttr)>> ; +fn get_random_generation() -> u64 { + Instant::now().elapsed().as_nanos()as u64 +} +use std::cell::RefCell; +pub(crate)struct FuseDriverwhere TId:FileIdType,THandler:FuseHandler ,{ + handler:THandler,resolver:TId::Resolver,dirmap_entries:RefCell> ,dirmapplus_entries:RefCell> , +} +impl FuseDriverwhere TId:FileIdType,THandler:FuseHandler ,{ + #[doc = r" num_thread is ignored in serial mode, it is kept for consistency with other modes"] + pub fn new(handler:THandler,_num_threads:usize) -> FuseDriver{ + FuseDriver { + handler,resolver:TId::Resolver::new(),dirmap_entries:RefCell::new(HashMap::new()),dirmapplus_entries:RefCell::new(HashMap::new()), + } + } + + } +impl FuseDriverwhere TId:FileIdType,THandler:FuseHandler ,{ + fn access(&mut self,req: &Request,ino:u64,mask:i32,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let mask = AccessMask::from_bits_retain(mask); + match handler.access(&req,ino,mask){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("access: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn bmap(&mut self,req: &Request<'_> ,ino:u64,blocksize:u32,idx:u64,reply:ReplyBmap){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + match handler.bmap(&req,ino,blocksize,idx){ + Ok(block) => reply.bmap(block), + Err(e) => { + warn!("bmap: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn copy_file_range(&mut self,req: &Request,ino_in:u64,fh_in:u64,offset_in:i64,ino_out:u64,fh_out:u64,offset_out:i64,len:u64,flags:u32,reply:ReplyWrite,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let ino_in = resolver.resolve_id(ino_in); + let fh_in = unsafe { + BorrowedFileHandle::from_raw(fh_in) + }; + let ino_out = resolver.resolve_id(ino_out); + let fh_out = unsafe { + BorrowedFileHandle::from_raw(fh_out) + }; + match handler.copy_file_range(&req,ino_in,fh_in,offset_in,ino_out,fh_out,offset_out,len,flags){ + Ok(bytes_written) => reply.written(bytes_written), + Err(e) => { + warn!("copy_file_range: [{}], {:?}",e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn create(&mut self,req: &Request,parent:u64,name: &OsStr,mode:u32,umask:u32,flags:i32,reply:ReplyCreate,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let name = name.as_ref(); + let flags = OpenFlags::from_bits_retain(flags); + match handler.create(&req,parent,name,mode,umask,flags){ + Ok((file_handle,metadata,response_flags)) => { + let default_ttl = handler.get_default_ttl(); + let(id,file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent, &name,id,true); + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + reply.created(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),file_handle.as_raw(),response_flags.bits(),); + }, + Err(e) => { + warn!("create: [{}], {:?}",e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn fallocate(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,length:i64,mode:i32,reply:ReplyEmpty,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let mode = FallocateFlags::from_bits_retain(mode); + match handler.fallocate(&req,ino,fh,offset,length,mode){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("fallocate: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn flush(&mut self,req: &Request,ino:u64,fh:u64,lock_owner:u64,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + match handler.flush(&req,ino,fh,lock_owner){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("flush: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn forget(&mut self,req: &Request,ino:u64,nlookup:u64){ + let req = RequestInfo::from(req); + let ino = self.resolver.resolve_id(ino); + self.handler.forget(&req,ino,nlookup); + self.resolver.forget(ino,nlookup); + } + fn fsync(&mut self,req: &Request,ino:u64,fh:u64,datasync:bool,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + match handler.fsync(&req,ino,fh,datasync){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("fsync: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn fsyncdir(&mut self,req: &Request,ino:u64,fh:u64,datasync:bool,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + match handler.fsyncdir(&req,ino,fh,datasync){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("fsyncdir: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn getattr(&mut self,req: &Request,ino:u64,fh:Option ,reply:ReplyAttr){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + match handler.getattr(&req,ino,fh){ + Ok(file_attr) => { + let default_ttl = handler.get_default_ttl(); + let(fuse_attr,ttl,_) = file_attr.to_fuse(ino); + reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr); + }, + Err(e) => { + warn!("getattr: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn getlk(&mut self,req: &Request<'_> ,ino:u64,fh:u64,lock_owner:u64,start:u64,end:u64,typ:i32,pid:u32,reply:ReplyLock,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let lock_info = LockInfo { + start,end,lock_type:LockType::from_bits_retain(typ),pid, + }; + match handler.getlk(&req,ino,fh,lock_owner,lock_info){ + Ok(lock) => reply.lock(lock), + Err(e) => { + warn!("getlk: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn getxattr(&mut self,req: &Request,ino:u64,name: &OsStr,size:u32,reply:ReplyXattr){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let name = name.as_ref(); + match handler.getxattr(&req,ino,name,size){ + Ok(xattr_data) => { + if size==0 { + reply.size(xattr_data.len()as u32); + }else if size>=xattr_data.len()as u32 { + reply.data(&xattr_data); + }else { + reply.error(ErrorKind::ResultTooLarge.into()); + } + } + Err(e) => { + warn!("getxattr: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn ioctl(&mut self,req: &Request<'_> ,ino:u64,fh:u64,flags:u32,cmd:u32,in_data: &[u8],out_size:u32,reply:ReplyIoctl,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let in_data = in_data.to_owned(); + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let flags = IOCtlFlags::from_bits_retain(flags); + match handler.ioctl(&req,ino,fh,flags,cmd,in_data,out_size){ + Ok((result,data)) => reply.ioctl(result, &data), + Err(e) => { + warn!("ioctl: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn link(&mut self,req: &Request,ino:u64,newparent:u64,newname: &OsStr,reply:ReplyEntry,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let newname = newname.to_owned(); + let newname = newname.as_ref(); + let newparent = resolver.resolve_id(newparent); + match handler.link(&req,ino,newparent,newname){ + Ok(metadata) => { + let default_ttl = handler.get_default_ttl(); + let(id,file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent,name,id,true); + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); + } + Err(e) => { + warn!("link: [{}], {:?}",e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn listxattr(&mut self,req: &Request,ino:u64,size:u32,reply:ReplyXattr){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + match handler.listxattr(&req,ino,size){ + Ok(xattr_data) => { + if size==0 { + reply.size(xattr_data.len()as u32); + }else if size>=xattr_data.len()as u32 { + reply.data(&xattr_data); + }else { + reply.error(ErrorKind::ResultTooLarge.into()); + } + } + Err(e) => { + warn!("listxattr: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn lookup(&mut self,req: &Request,parent:u64,name: &OsStr,reply:ReplyEntry){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = parent; + let parent = resolver.resolve_id(parent); + match handler.lookup(&req,parent,name){ + Ok(metadata) => { + let default_ttl = handler.get_default_ttl(); + let(id,file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent,name,id,true); + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); + } + Err(e) => { + info!("lookup: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn lseek(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,whence:i32,reply:ReplyLseek,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let seek = seek_from_raw(Some(whence),offset); + match handler.lseek(&req,ino,fh,seek,){ + Ok(new_offset) => reply.offset(new_offset), + Err(e) => { + warn!("lseek: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn mkdir(&mut self,req: &Request,parent:u64,name: &OsStr,mode:u32,umask:u32,reply:ReplyEntry,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = parent; + let parent = resolver.resolve_id(parent); + let name = name.as_ref(); + match handler.mkdir(&req,parent,name,mode,umask){ + Ok(metadata) => { + let default_ttl = handler.get_default_ttl(); + let(id,file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent,name,id,true); + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); + } + Err(e) => { + warn!("mkdir: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn mknod(&mut self,req: &Request,parent:u64,name: &OsStr,mode:u32,umask:u32,rdev:u32,reply:ReplyEntry,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = parent; + let parent = resolver.resolve_id(parent); + let name = name.as_ref(); + let rdev = DeviceType::from_rdev(rdev.try_into().unwrap()); + match handler.mknod(&req,parent,name,mode,umask,rdev){ + Ok(metadata) => { + let default_ttl = handler.get_default_ttl(); + let(id,file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent,name,id,true); + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); + } + Err(e) => { + warn!("mknod: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn open(&mut self,req: &Request,ino:u64,flags:i32,reply:ReplyOpen){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let flags = OpenFlags::from_bits_retain(flags); + match handler.open(&req,ino,flags){ + Ok((file_handle,response_flags)) => { + reply.opened(file_handle.as_raw(),response_flags.bits()) + } + Err(e) => { + warn!("open: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn opendir(&mut self,req: &Request,ino:u64,flags:i32,reply:ReplyOpen){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let flags = OpenFlags::from_bits_retain(flags); + match handler.opendir(&req,ino,flags){ + Ok((file_handle,response_flags)) => { + reply.opened(file_handle.as_raw(),response_flags.bits()) + } + Err(e) => { + warn!("opendir: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn read(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,size:u32,flags:i32,lock_owner:Option ,reply:ReplyData,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let seek = seek_from_raw(Some(offset),0); + let flags = FUSEOpenFlags::from_bits_retain(flags); + match handler.read(&req,ino,fh,offset,size,flags,lock_owner){ + Ok(data_reply) => reply.data(&data_reply), + Err(e) => { + warn!("read: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn readdir(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,mut reply:ReplyDirectoryPlus,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let dirmap_entries = &self.dirmap_entries; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + if offset<0 { + { + let lvl = (log::Level::Error); + if lvl<=log::STATIC_MAX_LEVEL&&lvl<=log::max_level(){ + log::__private_api::log(log::__private_api::format_args!("readdir called with a negative offset"),lvl, &((log::__private_api::module_path!()),log::__private_api::module_path!(),log::__private_api::loc()),(),); + } + }; + reply.error(ErrorKind::InvalidArgument.into()); + return; + }let mut directory_entries = match offset { + 0 => match handler.readdir(&req,ino,fh,offset){ + Ok(children) => { + let(child_list,attr_list):(Vec<_> ,Vec<_>) = children.into_iter().map(|item|{ + let(child_id,child_attr) = TId::extract_minimal_metadata(item.1); + ((item.0,child_id),child_attr) + }).unzip(); + resolver.add_children(ino,child_list,false,).into_iter().zip(attr_list.into_iter()).map(|((file_name,file_ino),file_attr)|{ + (file_name,file_ino,file_attr) + }).collect() + } + Err(e) => { + warn!("readdir: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }, + _ => match { + dirmap_entries.borrow_mut().remove(&(ino,offset)) + }{ + Some(directory_entries) => directory_entries, + None => { + reply.ok(); + return; + } + + }, + + }; + let mut new_offset = offset+1; + while let Some((name,ino,kind)) = directory_entries.pop_front(){ + if reply.add(ino,new_offset,kind, &name){ + dirmap_entries.borrow_mut().insert((ino,new_offset),directory_entries); + break; + }new_offset+=1; + }reply.ok(); + } + fn readdirplus(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,mut reply:ReplyDirectory,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let dirmap_entries = &self.dirmapplus_entries; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + if offset<0 { + { + let lvl = (log::Level::Error); + if lvl<=log::STATIC_MAX_LEVEL&&lvl<=log::max_level(){ + log::__private_api::log(log::__private_api::format_args!("readdir called with a negative offset"),lvl, &((log::__private_api::module_path!()),log::__private_api::module_path!(),log::__private_api::loc()),(),); + } + }; + reply.error(ErrorKind::InvalidArgument.into()); + return; + }let mut directory_entries = match offset { + 0 => match handler.readdirplus(&req,ino,fh,offset){ + Ok(children) => { + let(child_list,attr_list):(Vec<_> ,Vec<_>) = children.into_iter().map(|item|{ + let(child_id,child_attr) = TId::extract_metadata(item.1); + ((item.0,child_id),child_attr) + }).unzip(); + resolver.add_children(ino,child_list,true,).into_iter().zip(attr_list.into_iter()).map(|((file_name,file_ino),file_attr)|{ + (file_name,file_ino,file_attr) + }).collect() + } + Err(e) => { + warn!("readdirplus: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }, + _ => match { + dirmap_entries.borrow_mut().remove(&(ino,offset)) + }{ + Some(directory_entries) => directory_entries, + None => { + reply.ok(); + return; + } + + }, + + }; + let mut new_offset = offset+1; + let default_ttl = handler.get_default_ttl(); + while let Some((name,ino,file_attr)) = directory_entries.pop_front(){ + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + if reply.add(ino,new_offset,name, &ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),){ + dirmap_entries.borrow_mut().insert((ino,new_offset),directory_entries); + break; + }new_offset+=1; + }reply.ok(); + } + fn readlink(&mut self,req: &Request,ino:u64,reply:ReplyData){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + match handler.readlink(&req,ino){ + Ok(link) => reply.data(&link), + warn_error + + }; + ; + } + fn release(&mut self,req: &Request,ino:u64,fh:u64,flags:i32,_lock_owner:Option ,_flush:bool,reply:ReplyEmpty,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let flags = OpenFlags::from_bits_retain(flags); + match handler.release(&req,ino,fh,flags,_lock_owner,_flush){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("release: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn releasedir(&mut self,req: &Request,ino:u64,fh:u64,flags:i32,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let flags = OpenFlags::from_bits_retain(flags); + match handler.releasedir(&req,ino,fh,flags){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("releasedir: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn removexattr(&mut self,req: &Request,ino:u64,name: &OsStr,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let name = name.as_ref(); + match handler.removexattr(&req,ino,name){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("removexattr: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn rename(&mut self,req: &Request,parent:u64,name: &OsStr,newparent:u64,newname: &OsStr,flags:u32,reply:ReplyEmpty,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let newname = newname.to_owned(); + let log_ino = parent; + let parent = resolver.resolve_id(parent); + let name = name.as_ref(); + let newname = newname.as_ref(); + let flags = RenameFlags::from_bits_retain(flags); + match handler.rename(&req,parent,name,newparent,newname,flags){ + Ok(()) => { + resolver.rename(parent, &name,newparent, &newname); + reply.ok() + } + Err(e) => { + warn!("rename: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn rmdir(&mut self,req: &Request,parent:u64,name: &OsStr,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = parent; + let parent = resolver.resolve_id(parent); + let name = name.as_ref(); + match handler.rmdir(&req,parent,name){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("rmdir: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn setattr(&mut self,req: &Request,ino:u64,mode:Option ,uid:Option ,gid:Option ,size:Option ,atime:Option ,mtime:Option ,ctime:Option ,fh:Option ,crtime:Option ,chgtime:Option ,bkuptime:Option ,_flags:Option ,reply:ReplyAttr,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let attrs = SetAttrRequest { + mode,uid,gid,size,atime:atime,mtime:mtime,ctime:ctime,crtime:crtime,chgtime:chgtime,bkuptime:bkuptime,flags:None,file_handle:fh.map(|fh|unsafe { + BorrowedFileHandle::from_raw(fh) + }), + }; + match handler.setattr(&req,ino,attrs){ + Ok(file_attr) => { + let default_ttl = handler.get_default_ttl(); + let(fuse_attr,ttl,_) = file_attr.to_fuse(ino); + reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr); + }, + Err(e) => { + warn!("setattr: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + } + } + fn setlk(&mut self,req: &Request<'_> ,ino:u64,fh:u64,lock_owner:u64,start:u64,end:u64,typ:i32,pid:u32,sleep:bool,reply:ReplyEmpty,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let lock_info = LockInfo { + start,end,lock_type:LockType::from_bits_retain(typ),pid, + }; + match handler.setlk(&req,ino,fh,lock_owner,lock_info,sleep,){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("setlk: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn setxattr(&mut self,req: &Request,ino:u64,name: &OsStr,value: &[u8],flags:i32,position:u32,reply:ReplyEmpty,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let value = value.to_owned(); + let name = name.as_ref(); + let flags = FUSESetXAttrFlags::from_bits_retain(flags); + match handler.setxattr(&req,ino,name,value,flags,position){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("setxattr: [{}], {:?}",e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn statfs(&mut self,req: &Request,ino:u64,reply:ReplyStatfs){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let log_ino = ino; + let ino = resolver.resolve_id(ino); + match handler.statfs(&req,ino){ + Ok(statfs) => reply.statfs(statfs.total_blocks,statfs.free_blocks,statfs.available_blocks,statfs.total_files,statfs.free_files,statfs.block_size,statfs.max_filename_length,statfs.fragment_size,), + Err(e) => { + warn!("statfs: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn symlink(&mut self,req: &Request,parent:u64,link_name: &OsStr,target: &Path,reply:ReplyEntry,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let link_name = link_name.to_owned(); + let target = target.to_owned(); + let link_name = link_name.as_ref(); + let target = target.as_ref(); + match handler.symlink(&req,parent,link_name,target){ + Ok(metadata) => { + let default_ttl = handler.get_default_ttl(); + let(id,file_attr) = TId::extract_metadata(metadata); + let ino = resolver.lookup(parent,name,id,true); + let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); + reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); + } + Err(e) => { + warn!("symlink: [{}], {:?}",e,req); + reply.error(e.raw_error()); + return; + } + + }; + } + fn write(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,data: &[u8],write_flags:u32,flags:i32,lock_owner:Option ,reply:ReplyWrite,){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let data = data.to_owned(); + let log_ino = ino; + let ino = resolver.resolve_id(ino); + let fh = unsafe { + BorrowedFileHandle::from_raw(fh) + }; + let seek = seek_from_raw(Some(offset),0); + let write_flags = FUSEWriteFlags::from_bits_retain(write_flags); + let flags = OpenFlags::from_bits_retain(flags); + match handler.write(&req,ino,fh,offset,data,write_flags,flags,lock_owner){ + Ok(bytes_written) => reply.written(bytes_written), + Err(e) => { + warn!("write: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + fn unlink(&mut self,req: &Request,parent:u64,name: &OsStr,reply:ReplyEmpty){ + let req = RequestInfo::from(req); + let handler = &self.handler; + let resolver = &self.resolver; + let name = name.to_owned(); + let log_ino = parent; + let parent = resolver.resolve_id(parent); + let name = name.as_ref(); + match handler.unlink(&req,parent,name){ + Ok(()) => reply.ok(), + Err(e) => { + warn!("unlink: ino {:x?}, [{}], {:?}",log_ino,e,req); + reply.error(e.raw_error()); + return; + } + + }; + ; + } + + } \ No newline at end of file From 5404ca5f91593dc7c0af33234cea9502eda5dd6b Mon Sep 17 00:00:00 2001 From: alogani Date: Fri, 7 Feb 2025 04:49:07 +0100 Subject: [PATCH 3/5] Fixing bug PR #52 --- Cargo.toml | 2 +- src/core/macros.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e8ca08..35e08b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easy_fuser" -version = "0.4.0" +version = "0.5.0" edition = "2021" description = "A flexible and idiomatic Fuse implementation for Rust" license = "MIT" diff --git a/src/core/macros.rs b/src/core/macros.rs index a56b993..6f4fd09 100644 --- a/src/core/macros.rs +++ b/src/core/macros.rs @@ -157,7 +157,7 @@ macro_rules! handle_dir_read { }, }; - let mut new_offset = $offset + 1; + let mut new_offset = $offset; // ### Process directory entries if_readdir!( @@ -166,9 +166,10 @@ macro_rules! handle_dir_read { // readdir: Add entries until buffer is full while let Some((name, ino, kind)) = dir_iter.pop_front() { if $reply.add(ino, new_offset, kind, &name) { + dir_iter.push_front((name, ino, kind)); dirmap_iter .safe_borrow_mut() - .insert(($ino, new_offset), dir_iter); + .insert(($ino, new_offset - 1), dir_iter); break; } new_offset += 1; @@ -179,18 +180,19 @@ macro_rules! handle_dir_read { // readdirplus: Add entries with extended attributes let default_ttl = handler.get_default_ttl(); while let Some((name, ino, file_attr)) = dir_iter.pop_front() { - let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); + let (fuse_attr, ttl, generation) = file_attr.clone().to_fuse(ino); if $reply.add( ino, new_offset, - name, + &name, &ttl.unwrap_or(default_ttl), &fuse_attr, generation.unwrap_or(get_random_generation()), ) { + dir_iter.push_front((name, ino, file_attr.clone())); dirmap_iter .safe_borrow_mut() - .insert((ino, new_offset), dir_iter); + .insert((ino, new_offset - 1), dir_iter); break; } new_offset += 1; From 53af9dba4b60f73ff9c4b8a015c416d29466ed9d Mon Sep 17 00:00:00 2001 From: alogani Date: Fri, 7 Feb 2025 07:35:07 +0100 Subject: [PATCH 4/5] Draft completed of FuseDriver. Need to move templates to each mod and update tests --- .vscode/settings.json | 2 +- Cargo.toml | 8 +- easy_fuser_macro/src/fuse_driver.rs | 219 +++-- easy_fuser_macro_test/Cargo.toml | 8 - easy_fuser_macro_test/src/main.rs | 5 - src/core.rs | 8 - src/core/fuse_driver.rs | 1021 -------------------- src/core/fuse_driver_types.rs | 236 ----- src/core/macros.rs | 209 ---- src/core/thread_mode.rs | 69 -- src/fuse_common.rs | 2 + src/{core => fuse_common}/inode_mapping.rs | 0 src/fuse_common/mounting.rs | 0 src/fuse_parallel.rs | 12 +- src/fuse_parallel/fuse_driver.rs | 8 + src/fuse_parallel/mouting.rs | 40 + src/fuse_serial.rs | 12 +- src/fuse_serial/fuse_driver.rs | 880 +---------------- src/fuse_serial/mouting.rs | 34 + src/inode_mapper.rs | 4 +- src/lib.rs | 83 -- src/types/file_id_type.rs | 2 +- src/types/inode.rs | 2 +- src/unix_fs/linux_fs.rs | 2 +- tests/integration_test.rs | 8 +- 25 files changed, 243 insertions(+), 2631 deletions(-) delete mode 100644 easy_fuser_macro_test/Cargo.toml delete mode 100644 easy_fuser_macro_test/src/main.rs delete mode 100644 src/core.rs delete mode 100644 src/core/fuse_driver.rs delete mode 100644 src/core/fuse_driver_types.rs delete mode 100644 src/core/macros.rs delete mode 100644 src/core/thread_mode.rs rename src/{core => fuse_common}/inode_mapping.rs (100%) create mode 100644 src/fuse_common/mounting.rs create mode 100644 src/fuse_parallel/mouting.rs create mode 100644 src/fuse_serial/mouting.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a34a00..79435eb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.cargo.features": ["serial"] + "rust-analyzer.cargo.features": ["serial", "parallel"] } diff --git a/Cargo.toml b/Cargo.toml index 35e08b2..5682b51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,10 @@ homepage = "https://github.com/Alogani/easy_fuser" repository = "https://github.com/Alogani/easy_fuser" [features] -default = [] +default = ["parallel"] serial = [] parallel = ["dep:threadpool"] async = [ "dep:tokio"] -deadlock_detection = ["parallel", "dep:parking_lot"] - [dependencies] # Core dependencies @@ -26,8 +24,6 @@ bitflags = "2.6.0" # Parallel dependencies threadpool = { version = "1.8", optional = true } -# Parking lot is only used for deadlock_detection if feature set -parking_lot = { version = "0.12", features = ["deadlock_detection"], optional = true } # Async dependencies # easy_fuser_async_macro = { path = "./easy_fuser_async_macro", optional = true } @@ -39,4 +35,4 @@ tempfile = "3.14" env_logger = "0.11" [package.metadata.docs.rs] -features = ["parallel"] +features = ["serial", "parallel"] diff --git a/easy_fuser_macro/src/fuse_driver.rs b/easy_fuser_macro/src/fuse_driver.rs index e6c0388..47f5530 100644 --- a/easy_fuser_macro/src/fuse_driver.rs +++ b/easy_fuser_macro/src/fuse_driver.rs @@ -77,9 +77,16 @@ fn expand_macro_placeholders(handler_type: HandlerType, input: TokenStream) -> T let arg_idents: Vec<_> = arg_names .iter() .skip(1) // Skip the first 'req' argument - .map(|arg| format_ident!("{}", arg)) + .map(|arg| { + match arg.as_ref() { + // parent and ino shall be converted from u64 to FileREsolver id + "parent" => format_ident!("parent_id"), + "ino" => format_ident!("ino_id"), + _ => format_ident!("{}", arg), + } + }) .collect(); - let all_args = quote! { + let handler_args = quote! { #req_arg, #(#arg_idents),* }; @@ -87,7 +94,7 @@ fn expand_macro_placeholders(handler_type: HandlerType, input: TokenStream) -> T output.extend(expand_macro_tokens( handler_type, &function_name, - &all_args, + &handler_args, false, tokens, )); @@ -98,7 +105,7 @@ fn expand_macro_placeholders(handler_type: HandlerType, input: TokenStream) -> T fn expand_macro_tokens( handler_type: HandlerType, function_name: &str, - args: &TokenStream, + handler_args: &TokenStream, mut log_ino: bool, mut tokens: impl Iterator, ) -> TokenStream { @@ -119,18 +126,18 @@ fn expand_macro_tokens( HandlerType::Serial => quote!(let resolver = &self.resolver;), _ => quote!(let resolver = Arc::clone(&self.resolver);), }, - "ino" => { + "ino_id" => { log_ino = true; quote!( - let log_ino = ino; - let ino = resolver.resolve_id(ino); + let log_ino: u64 = ino; + let ino_id = resolver.resolve_id(ino); ) } - "parent" => { + "parent_id" => { log_ino = true; quote!( - let log_ino = parent; - let parent = resolver.resolve_id(parent); + let log_ino: u64 = parent; + let parent_id = resolver.resolve_id(parent); ) } "fh" => quote!(let fh = unsafe { BorrowedFileHandle::from_raw(fh) };), @@ -141,7 +148,7 @@ fn expand_macro_tokens( expand_macro_tokens( handler_type, function_name, - args, + handler_args, log_ino, group.stream().into_iter(), ), @@ -150,12 +157,12 @@ fn expand_macro_tokens( panic!("Expected group after $wrap") } } - "args" => args.clone(), + "handler_args" => handler_args.clone(), "reply_attr" => reply_attr(), "reply_entry" => reply_entry(), "warn_error" => error_response(function_name, false, log_ino), "info_error" => error_response(function_name, true, log_ino), - unknown => panic!("Unknown dollar identifier: {}", unknown), + unknown => panic!("Unknown $ identifier: {}", unknown), }; output.extend(replacement); } else { @@ -166,7 +173,7 @@ fn expand_macro_tokens( let content = expand_macro_tokens( handler_type, function_name, - args, + handler_args, log_ino, group.stream().into_iter(), ); @@ -234,7 +241,7 @@ fn readdir_impl(handler_type: HandlerType, is_extended_readdir: bool) -> TokenSt false => quote!(readdir), }; let fn_signature = match is_extended_readdir { - true => quote!(fn #fn_name( + false => quote!(fn #fn_name( &mut self, req: &Request, ino: u64, @@ -242,7 +249,7 @@ fn readdir_impl(handler_type: HandlerType, is_extended_readdir: bool) -> TokenSt offset: i64, mut reply: ReplyDirectory, )), - false => quote!(fn #fn_name( + true => quote!(fn #fn_name( &mut self, req: &Request, ino: u64, @@ -314,7 +321,7 @@ fn readdir_impl(handler_type: HandlerType, is_extended_readdir: bool) -> TokenSt $resolver let dirmap_entries = #dirmap_entries_get; $wrap { - $ino + $ino_id $fh // Validate offset @@ -327,7 +334,7 @@ fn readdir_impl(handler_type: HandlerType, is_extended_readdir: bool) -> TokenSt // ### Initialize directory deque let mut directory_entries = match offset { // First read: fetch children from handler - 0 => match handler.#fn_name($args) { + 0 => match handler.#fn_name(&req, ino_id, fh) { Ok(children) => { // Unpack and process children let (child_list, attr_list): (Vec<_>, Vec<_>) = children @@ -384,9 +391,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -402,8 +409,8 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.bmap(block), $warn_error } @@ -435,7 +442,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.written(bytes_written), $warn_error } @@ -461,10 +468,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { let default_ttl = handler.get_default_ttl(); let (id, file_attr) = TId::extract_metadata(metadata); @@ -501,10 +508,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -520,9 +527,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -535,7 +542,8 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec reply.ok(), $warn_error } @@ -566,9 +574,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -584,9 +592,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec Vec reply.locked(lock), + match handler.getlk(&req, ino_id, fh, lock_owner, lock_info) { + Ok(lock_info) => reply.locked(lock_info.start, + lock_info.end, + lock_info.lock_type.bits(), + lock_info.pid, + ), $warn_error } } @@ -638,9 +650,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { if size == 0 { reply.size(xattr_data.len() as u32); @@ -675,10 +687,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ioctl(result, &data), $warn_error }; @@ -700,12 +712,14 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec { if size == 0 { reply.size(xattr_data.len() as u32); @@ -747,8 +761,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec Vec Vec Vec { reply.opened(file_handle.as_raw(), response_flags.bits()) } @@ -874,9 +889,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { reply.opened(file_handle.as_raw(), response_flags.bits()) } @@ -904,11 +919,17 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec offset, + Err(_) => { + reply.error(ErrorKind::InputOutputError.into()); + return; + } + }), 0); let flags = FUSEOpenFlags::from_bits_retain(flags); - match handler.read($args) { + match handler.read($handler_args) { Ok(data_reply) => reply.data(&data_reply), $warn_error }; @@ -926,10 +947,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.data(&link), - warn_error + $warn_error }; }; } @@ -952,10 +973,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -969,10 +990,10 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -988,9 +1009,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1017,11 +1038,12 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { resolver.rename(parent, &name, newparent, &newname); reply.ok() @@ -1041,9 +1063,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1076,7 +1098,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec Vec Vec Vec reply.ok(), $warn_error }; @@ -1179,8 +1202,8 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.statfs( statfs.total_blocks, statfs.free_blocks, @@ -1214,9 +1237,11 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec offset, + Err(_) => { + reply.error(ErrorKind::InputOutputError.into()); + return; + } + }), 0); let write_flags = FUSEWriteFlags::from_bits_retain(write_flags); let flags = OpenFlags::from_bits_retain(flags); - match handler.write($args) { + match handler.write($handler_args) { Ok(bytes_written) => reply.written(bytes_written), $warn_error }; @@ -1266,9 +1297,9 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1391,7 +1422,7 @@ fn get_dependencies() -> proc_macro2::TokenStream { use log::{error, info, warn}; use fuser::{ - self, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, + self, Filesystem, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, }; @@ -1413,7 +1444,7 @@ pub(crate) fn generate_fuse_driver_implementation(handler_type: HandlerType) -> #fuse_driver_struct - impl FuseDriver + impl fuser::Filesystem for FuseDriver where TId: FileIdType, THandler: FuseHandler, diff --git a/easy_fuser_macro_test/Cargo.toml b/easy_fuser_macro_test/Cargo.toml deleted file mode 100644 index 66c7a36..0000000 --- a/easy_fuser_macro_test/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "easy_fuser_macro_test" -version = "0.1.0" -edition = "2021" - -[dependencies] -easy_fuser_macro = { path = "../easy_fuser_macro" } - diff --git a/easy_fuser_macro_test/src/main.rs b/easy_fuser_macro_test/src/main.rs deleted file mode 100644 index a6f1551..0000000 --- a/easy_fuser_macro_test/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -use easy_fuser_macro::implement_fuse_driver; - -implement_fuse_driver!("serial"); - -fn main() {} diff --git a/src/core.rs b/src/core.rs deleted file mode 100644 index 72b1a49..0000000 --- a/src/core.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod fuse_driver; -mod fuse_driver_types; -pub(crate) mod inode_mapping; -mod macros; -mod thread_mode; - -pub(crate) use fuse_driver_types::FuseDriver; -pub(crate) use inode_mapping::{InodeResolvable, ROOT_INO}; diff --git a/src/core/fuse_driver.rs b/src/core/fuse_driver.rs deleted file mode 100644 index 24a8b18..0000000 --- a/src/core/fuse_driver.rs +++ /dev/null @@ -1,1021 +0,0 @@ -use std::{ - ffi::OsStr, - path::Path, - time::{Instant, SystemTime}, -}; - -use libc::c_int; -use log::{error, info, warn}; - -use fuser::{ - self, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, - ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, - ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, -}; - -use super::{ - fuse_driver_types::{execute_task, FuseDriver}, - inode_mapping::FileIdResolver, - macros::*, - thread_mode::*, -}; -use crate::{fuse_handler::FuseHandler, types::*}; - -fn get_random_generation() -> u64 { - Instant::now().elapsed().as_nanos() as u64 -} - -impl fuser::Filesystem for FuseDriver -where - TId: FileIdType, - THandler: FuseHandler, -{ - fn init(&mut self, req: &Request, config: &mut KernelConfig) -> Result<(), c_int> { - let req = RequestInfo::from(req); - match self.get_handler().init(&req, config) { - Ok(()) => Ok(()), - Err(e) => { - warn!("[{}] init {:?}", e, req); - Err(e.raw_error()) - } - } - } - - fn destroy(&mut self) { - self.get_handler().destroy(); - } - - fn access(&mut self, req: &Request, ino: u64, mask: i32, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.access( - &req, - resolver.resolve_id(ino), - AccessMask::from_bits_retain(mask), - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("access: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn bmap(&mut self, req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.bmap(&req, resolver.resolve_id(ino), blocksize, idx) { - Ok(block) => reply.bmap(block), - Err(e) => { - warn!("bmap: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn copy_file_range( - &mut self, - req: &Request, - ino_in: u64, - fh_in: u64, - offset_in: i64, - ino_out: u64, - fh_out: u64, - offset_out: i64, - len: u64, - flags: u32, - reply: ReplyWrite, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.copy_file_range( - &req, - resolver.resolve_id(ino_in), - unsafe { BorrowedFileHandle::from_raw(fh_in) }, - offset_in, - resolver.resolve_id(ino_out), - unsafe { BorrowedFileHandle::from_raw(fh_out) }, - offset_out, - len, - flags, - ) { - Ok(bytes_written) => reply.written(bytes_written), - Err(e) => { - warn!("copy_file_range: ino {:x?}, [{}], {:?}", ino_in, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn create( - &mut self, - req: &Request, - parent: u64, - name: &OsStr, - mode: u32, - umask: u32, - flags: i32, - reply: ReplyCreate, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - match handler.create( - &req, - resolver.resolve_id(parent), - &name, - mode, - umask, - OpenFlags::from_bits_retain(flags), - ) { - Ok((file_handle, metadata, response_flags)) => { - let default_ttl = handler.get_default_ttl(); - let (id, file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent, &name, id, true); - let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); - reply.created( - &ttl.unwrap_or(default_ttl), - &fuse_attr, - generation.unwrap_or(get_random_generation()), - file_handle.as_raw(), - response_flags.bits(), - ); - } - Err(e) => { - warn!("create: {:?}, parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn fallocate( - &mut self, - req: &Request, - ino: u64, - fh: u64, - offset: i64, - length: i64, - mode: i32, - reply: ReplyEmpty, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.fallocate( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - offset, - length, - FallocateFlags::from_bits_retain(mode), - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("fallocate: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn flush(&mut self, req: &Request, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.flush( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - lock_owner, - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("flush: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn forget(&mut self, req: &Request, ino: u64, nlookup: u64) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - handler.forget(&req, resolver.resolve_id(ino), nlookup); - resolver.forget(ino, nlookup); - } - - fn fsync(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.fsync( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - datasync, - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("fsync: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn fsyncdir(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.fsyncdir( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - datasync, - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("fsyncdir: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn getattr(&mut self, req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - handle_fuse_reply_attr!( - handler, - resolver, - &req, - ino, - reply, - getattr, - ( - &req, - resolver.resolve_id(ino), - fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }) - ) - ); - }); - } - - fn getlk( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, - start: u64, - end: u64, - typ: i32, - pid: u32, - reply: ReplyLock, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - let lock_info = LockInfo { - start, - end, - lock_type: LockType::from_bits_retain(typ), - pid, - }; - match handler.getlk( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - lock_owner, - lock_info, - ) { - Ok(lock_info) => reply.locked( - lock_info.start, - lock_info.end, - lock_info.lock_type.bits(), - lock_info.pid, - ), - Err(e) => { - warn!("getlk: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn getxattr(&mut self, req: &Request, ino: u64, name: &OsStr, size: u32, reply: ReplyXattr) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - match handler.getxattr(&req, resolver.resolve_id(ino), &name, size) { - Ok(xattr_data) => { - if size == 0 { - reply.size(xattr_data.len() as u32); - } else if size >= xattr_data.len() as u32 { - reply.data(&xattr_data); - } else { - reply.error(ErrorKind::ResultTooLarge.into()); - } - } - Err(e) => { - warn!("getxattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn ioctl( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - flags: u32, - cmd: u32, - in_data: &[u8], - out_size: u32, - reply: ReplyIoctl, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let in_data = in_data.to_owned(); - execute_task!(self, { - match handler.ioctl( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - IOCtlFlags::from_bits_retain(flags), - cmd, - in_data, - out_size, - ) { - Ok((result, data)) => reply.ioctl(result, &data), - Err(e) => { - warn!("ioctl: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn link( - &mut self, - req: &Request, - ino: u64, - newparent: u64, - newname: &OsStr, - reply: ReplyEntry, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let newname = newname.to_owned(); - execute_task!(self, { - handle_fuse_reply_entry!( - handler, - resolver, - &req, - newparent, - &newname, - reply, - link, - ( - &req, - resolver.resolve_id(ino), - resolver.resolve_id(newparent), - &newname - ) - ); - }); - } - - fn listxattr(&mut self, req: &Request, ino: u64, size: u32, reply: ReplyXattr) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.listxattr(&req, resolver.resolve_id(ino), size) { - Ok(xattr_data) => { - if size == 0 { - reply.size(xattr_data.len() as u32); - } else if size >= xattr_data.len() as u32 { - reply.data(&xattr_data); - } else { - reply.error(ErrorKind::ResultTooLarge.into()); - } - } - Err(e) => { - warn!("listxattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn lookup(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - handle_fuse_reply_entry!( - handler, - resolver, - &req, - parent, - &name, - reply, - lookup, - (&req, resolver.resolve_id(parent), &name) - ); - }); - } - - fn lseek( - &mut self, - req: &Request, - ino: u64, - fh: u64, - offset: i64, - whence: i32, - reply: ReplyLseek, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.lseek( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - seek_from_raw(Some(whence), offset), - ) { - Ok(new_offset) => reply.offset(new_offset), - Err(e) => { - warn!("lseek: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn mkdir( - &mut self, - req: &Request, - parent: u64, - name: &OsStr, - mode: u32, - umask: u32, - reply: ReplyEntry, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - handle_fuse_reply_entry!( - handler, - resolver, - &req, - parent, - &name, - reply, - mkdir, - (&req, resolver.resolve_id(parent), &name, mode, umask) - ); - }); - } - - fn mknod( - &mut self, - req: &Request, - parent: u64, - name: &OsStr, - mode: u32, - umask: u32, - rdev: u32, - reply: ReplyEntry, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - handle_fuse_reply_entry!( - handler, - resolver, - &req, - parent, - &name, - reply, - mknod, - ( - &req, - resolver.resolve_id(parent), - &name, - mode, - umask, - DeviceType::from_rdev(rdev.try_into().unwrap()) - ) - ); - }); - } - - fn open(&mut self, req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.open( - &req, - resolver.resolve_id(ino), - OpenFlags::from_bits_retain(_flags), - ) { - Ok((file_handle, response_flags)) => { - reply.opened(file_handle.as_raw(), response_flags.bits()) - } - Err(e) => { - warn!("open: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn opendir(&mut self, req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.opendir( - &req, - resolver.resolve_id(ino), - OpenFlags::from_bits_retain(_flags), - ) { - Ok((file_handle, response_flags)) => { - reply.opened(file_handle.as_raw(), response_flags.bits()) - } - Err(e) => { - warn!("opendir: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn read( - &mut self, - req: &Request, - ino: u64, - fh: u64, - offset: i64, - size: u32, - flags: i32, - lock_owner: Option, - reply: ReplyData, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.read( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - seek_from_raw(None, offset), - size, - FUSEOpenFlags::from_bits_retain(flags), - lock_owner, - ) { - Ok(data_reply) => reply.data(&data_reply), - Err(e) => { - warn!("read: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn readdir( - &mut self, - req: &Request, - ino: u64, - fh: u64, - offset: i64, - mut reply: ReplyDirectory, - ) { - handle_dir_read!( - self, - req, - ino, - fh, - offset, - reply, - readdir, - get_dirmap_iter, - ReplyDirectory - ); - } - - fn readdirplus( - &mut self, - req: &Request, - ino: u64, - fh: u64, - offset: i64, - mut reply: ReplyDirectoryPlus, - ) { - handle_dir_read!( - self, - req, - ino, - fh, - offset, - reply, - readdirplus, - get_dirmapplus_iter, - ReplyDirectoryPlus - ); - } - - fn readlink(&mut self, req: &Request, ino: u64, reply: ReplyData) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.readlink(&req, resolver.resolve_id(ino)) { - Ok(link) => reply.data(&link), - Err(e) => { - warn!("[{}] readlink, ino: {:x?}, {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn release( - &mut self, - req: &Request, - ino: u64, - fh: u64, - _flags: i32, - _lock_owner: Option, - _flush: bool, - reply: ReplyEmpty, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.release( - &req, - resolver.resolve_id(ino), - unsafe { OwnedFileHandle::from_raw(fh) }, - OpenFlags::from_bits_retain(_flags), - _lock_owner, - _flush, - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("release: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn releasedir(&mut self, req: &Request, ino: u64, fh: u64, flags: i32, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.releasedir( - &req, - resolver.resolve_id(ino), - unsafe { OwnedFileHandle::from_raw(fh) }, - OpenFlags::from_bits_retain(flags), - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("releasedir: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn removexattr(&mut self, req: &Request, ino: u64, name: &OsStr, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - match handler.removexattr(&req, resolver.resolve_id(ino), &name) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("removexattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn rename( - &mut self, - req: &Request, - parent: u64, - name: &OsStr, - newparent: u64, - newname: &OsStr, - flags: u32, - reply: ReplyEmpty, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - let newname = newname.to_owned(); - execute_task!(self, { - match handler.rename( - &req, - resolver.resolve_id(parent), - &name, - resolver.resolve_id(newparent), - &newname, - RenameFlags::from_bits_retain(flags), - ) { - Ok(()) => { - resolver.rename(parent, &name, newparent, &newname); - reply.ok() - } - Err(e) => { - warn!("[{}] rename: parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) - } - } - }); - } - - fn rmdir(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - match handler.rmdir(&req, resolver.resolve_id(parent), &name) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("[{}] rmdir: parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn setattr( - &mut self, - req: &Request, - ino: u64, - mode: Option, - uid: Option, - gid: Option, - size: Option, - atime: Option, - mtime: Option, - ctime: Option, - fh: Option, - crtime: Option, - chgtime: Option, - bkuptime: Option, - _flags: Option, - reply: ReplyAttr, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let attrs = SetAttrRequest { - mode, - uid, - gid, - size, - atime: atime, - mtime: mtime, - ctime: ctime, - crtime: crtime, - chgtime: chgtime, - bkuptime: bkuptime, - flags: None, - file_handle: fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }), - }; - execute_task!(self, { - handle_fuse_reply_attr!( - handler, - resolver, - &req, - ino, - reply, - setattr, - (&req, resolver.resolve_id(ino), attrs) - ); - }); - } - - fn setlk( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, - start: u64, - end: u64, - typ: i32, - pid: u32, - sleep: bool, - reply: ReplyEmpty, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - let lock_info = LockInfo { - start, - end, - lock_type: LockType::from_bits_retain(typ), - pid, - }; - match handler.setlk( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - lock_owner, - lock_info, - sleep, - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("setlk: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn setxattr( - &mut self, - req: &Request, - ino: u64, - name: &OsStr, - value: &[u8], - flags: i32, - position: u32, - reply: ReplyEmpty, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - let value = value.to_owned(); - execute_task!(self, { - match handler.setxattr( - &req, - resolver.resolve_id(ino), - &name, - value, - FUSESetXAttrFlags::from_bits_retain(flags), - position, - ) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("setxattr: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn statfs(&mut self, req: &Request, ino: u64, reply: ReplyStatfs) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - execute_task!(self, { - match handler.statfs(&req, resolver.resolve_id(ino)) { - Ok(statfs) => reply.statfs( - statfs.total_blocks, - statfs.free_blocks, - statfs.available_blocks, - statfs.total_files, - statfs.free_files, - statfs.block_size, - statfs.max_filename_length, - statfs.fragment_size, - ), - Err(e) => { - warn!("statfs: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn symlink( - &mut self, - req: &Request, - parent: u64, - link_name: &OsStr, - target: &Path, - reply: ReplyEntry, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let link_name = link_name.to_owned(); - let target = target.to_owned(); - execute_task!(self, { - handle_fuse_reply_entry!( - handler, - resolver, - &req, - parent, - &link_name, - reply, - symlink, - (&req, resolver.resolve_id(parent), &link_name, &target) - ); - }); - } - - fn write( - &mut self, - req: &Request, - ino: u64, - fh: u64, - offset: i64, - data: &[u8], - write_flags: u32, - flags: i32, - lock_owner: Option, - reply: ReplyWrite, - ) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let data = data.to_owned(); - execute_task!(self, { - match handler.write( - &req, - resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - seek_from_raw(None, offset), - data, - FUSEWriteFlags::from_bits_retain(write_flags), - OpenFlags::from_bits_retain(flags), - lock_owner, - ) { - Ok(bytes_written) => reply.written(bytes_written), - Err(e) => { - warn!("write: ino {:x?}, [{}], {:?}", ino, e, req); - reply.error(e.raw_error()) - } - }; - }); - } - - fn unlink(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { - let req = RequestInfo::from(req); - let handler = self.get_handler(); - let resolver = self.get_resolver(); - let name = name.to_owned(); - execute_task!(self, { - match handler.unlink(&req, resolver.resolve_id(parent), &name) { - Ok(()) => reply.ok(), - Err(e) => { - warn!("[{}] unlink: parent_ino: {:x?}, {:?}", parent, e, req); - reply.error(e.raw_error()) - } - }; - }); - } -} diff --git a/src/core/fuse_driver_types.rs b/src/core/fuse_driver_types.rs deleted file mode 100644 index d550d7e..0000000 --- a/src/core/fuse_driver_types.rs +++ /dev/null @@ -1,236 +0,0 @@ -#![allow(unused_imports)] - -use std::{ - collections::{HashMap, VecDeque}, - ffi::{OsStr, OsString}, -}; - -use super::inode_mapping::FileIdResolver; -use crate::fuse_handler::FuseHandler; -use crate::types::*; - -type DirIter = HashMap<(u64, i64), VecDeque<(OsString, u64, TAttr)>>; - -#[cfg(feature = "serial")] -mod serial { - use super::*; - - use std::cell::RefCell; - - pub(crate) struct FuseDriver - where - TId: FileIdType, - THandler: FuseHandler, - { - handler: THandler, - resolver: TId::Resolver, - dirmap_iter: RefCell>, - dirmapplus_iter: RefCell>, - } - - impl FuseDriver - where - TId: FileIdType, - THandler: FuseHandler, - { - /// num_thread is ignored in serial mode, it is kept for consistency with other modes - pub fn new(handler: THandler, _num_threads: usize) -> FuseDriver { - FuseDriver { - handler, - resolver: TId::Resolver::new(), - dirmap_iter: RefCell::new(HashMap::new()), - dirmapplus_iter: RefCell::new(HashMap::new()), - } - } - - pub fn get_handler(&self) -> &THandler { - &self.handler - } - - pub fn get_resolver(&self) -> &TId::Resolver { - &self.resolver - } - - pub fn get_dirmap_iter(&self) -> &RefCell> { - &self.dirmap_iter - } - - pub fn get_dirmapplus_iter(&self) -> &RefCell> { - &self.dirmapplus_iter - } - } - - macro_rules! execute_task { - ($self:expr, $block:block) => { - $block - }; - } - - pub(crate) use execute_task; -} - -#[cfg(feature = "parallel")] -mod parallel { - use super::*; - - use std::sync::Arc; - - use threadpool::ThreadPool; - - #[cfg(feature = "deadlock_detection")] - use parking_lot::{Mutex, MutexGuard}; - #[cfg(not(feature = "deadlock_detection"))] - use std::sync::{Mutex, MutexGuard}; - - pub(crate) struct FuseDriver - where - TId: FileIdType, - THandler: FuseHandler, - { - handler: Arc, - resolver: Arc, - dirmap_iter: Arc>>, - dirmapplus_iter: Arc>>, - pub threadpool: ThreadPool, - } - - impl FuseDriver - where - TId: FileIdType, - THandler: FuseHandler, - { - pub fn new(handler: THandler, num_threads: usize) -> FuseDriver { - #[cfg(feature = "deadlock_detection")] - spawn_deadlock_checker(); - FuseDriver { - handler: Arc::new(handler), - resolver: Arc::new(TId::create_resolver()), - dirmap_iter: Arc::new(Mutex::new(HashMap::new())), - dirmapplus_iter: Arc::new(Mutex::new(HashMap::new())), - threadpool: ThreadPool::new(num_threads), - } - } - - pub fn get_handler(&self) -> Arc { - self.handler.clone() - } - - pub fn get_resolver(&self) -> Arc { - self.resolver.clone() - } - - pub fn get_dirmap_iter(&self) -> Arc>> { - self.dirmap_iter.clone() - } - - pub fn get_dirmapplus_iter(&self) -> Arc>> { - self.dirmapplus_iter.clone() - } - } - - macro_rules! execute_task { - ($self:expr, $block:block) => { - $self.threadpool.execute(move || $block); - }; - } - - pub(crate) use execute_task; -} - -#[cfg(feature = "async")] -mod async_task { - use super::*; - - use std::sync::Arc; - use tokio::runtime::Runtime; - use tokio::sync::Mutex; - - pub(crate) struct FuseDriver - where - TId: FileIdType, - THandler: FuseHandler, - { - handler: Arc, - resolver: Arc, - dirmap_iter: Arc>>, - dirmapplus_iter: Arc>>, - pub runtime: Runtime, - } - - impl FuseDriver - where - TId: FileIdType, - THandler: FuseHandler, - { - pub fn new(handler: THandler, _num_threads: usize) -> FuseDriver { - #[cfg(feature = "deadlock_detection")] - spawn_deadlock_checker(); - FuseDriver { - handler: Arc::new(handler), - resolver: Arc::new(TId::create_resolver()), - dirmap_iter: Arc::new(Mutex::new(HashMap::new())), - dirmapplus_iter: Arc::new(Mutex::new(HashMap::new())), - runtime: Runtime::new().unwrap(), - } - } - - pub fn get_handler(&self) -> Arc { - self.handler.clone() - } - - pub fn get_resolver(&self) -> Arc { - self.resolver.clone() - } - - pub fn get_dirmap_iter(&self) -> Arc>> { - self.dirmap_iter.clone() - } - - pub fn get_dirmapplus_iter(&self) -> Arc>> { - self.dirmapplus_iter.clone() - } - } - - macro_rules! execute_task { - ($self:expr, $block:block) => { - $self.runtime.spawn(async move { $block }); - }; - } - - pub(crate) use execute_task; -} - -#[cfg(feature = "deadlock_detection")] -fn spawn_deadlock_checker() { - use log::{error, info}; - use parking_lot::deadlock; - use std::thread; - use std::time::Duration; - - // Create a background thread which checks for deadlocks every 10s - thread::spawn(move || loop { - thread::sleep(Duration::from_secs(10)); - let deadlocks = deadlock::check_deadlock(); - if deadlocks.is_empty() { - info!("# No deadlock"); - continue; - } - - eprintln!("# {} deadlocks detected", deadlocks.len()); - for (i, threads) in deadlocks.iter().enumerate() { - error!("Deadlock #{}", i); - for t in threads { - error!("Thread Id {:#?}\n, {:#?}", t.thread_id(), t.backtrace()); - } - } - }); -} - -#[cfg(feature = "serial")] -pub use serial::*; - -#[cfg(feature = "parallel")] -pub use parallel::*; - -#[cfg(feature = "async")] -pub use async_task::*; diff --git a/src/core/macros.rs b/src/core/macros.rs deleted file mode 100644 index 6f4fd09..0000000 --- a/src/core/macros.rs +++ /dev/null @@ -1,209 +0,0 @@ -macro_rules! handle_fuse_reply_entry { - ($handler:expr, $resolver:expr, $req:expr, $parent:expr, $name:expr, $reply:expr, - $function:ident, ($($args:expr),*)) => { - macro_rules! if_lookup { - (lookup, $choice1:tt, $choice2:tt) => { - $choice1 - }; - ($any:tt, $choice1:tt, $choice2:tt) => { - $choice2 - }; - } - - let handler = $handler; - match handler.$function($($args),*) { - Ok(metadata) => { - let default_ttl = handler.get_default_ttl(); - let (id, file_attr) = TId::extract_metadata(metadata); - let ino = $resolver.lookup($parent, $name, id, true); - let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); - $reply.entry( - &ttl.unwrap_or(default_ttl), - &fuse_attr, - generation.unwrap_or(get_random_generation()), - ); - } - Err(e) => { - if_lookup!($function, { - if e.kind() == ErrorKind::FileNotFound { - // Lookup is preemptivly done in normal situations, we don't need to log an error - // eg: before creating a file - info!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req); - } else { - warn!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req); - }; - }, { - warn!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req); - }); - $reply.error(e.raw_error()) - } - } - }; -} - -macro_rules! handle_fuse_reply_attr { - ($handler:expr, $resolve:expr, $req:expr, $ino:expr, $reply:expr, - $function:ident, ($($args:expr),*)) => { - match $handler.$function($($args),*) { - Ok(file_attr) => { - let default_ttl = $handler.get_default_ttl(); - let (fuse_attr, ttl, _) = file_attr.to_fuse($ino); - $reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr); - } - Err(e) => { - warn!("{}: ino {:x?}, [{}], {:?}", stringify!($function), $ino, e, $req); - $reply.error(e.raw_error()) - } - } - }; -} - -/// Handles directory read operations for FUSE filesystem.//+ -/////+ -/// This macro implements the logic for reading directory contents, supporting both//+ -/// regular directory reads (`readdir`) and extended directory reads (`readdirplus`).//+ -/////+ -/// # Parameters//+ -/////+ -/// * `$self`: The current filesystem instance.//+ -/// * `$req`: The FUSE request object.//+ -/// * `$ino`: The inode number of the directory being read.//+ -/// * `$fh`: The file handle of the open directory.//+ -/// * `$offset`: The offset from which to start reading directory entries.//+ -/// * `$reply`: The FUSE reply object to send the response.//+ -/// * `$handler_method`: The method to call on the handler to retrieve directory entries.//+ -/// * `$unpack_method`: The method to unpack metadata for each directory entry.//+ -/// * `$get_iter_method`: The method to retrieve the directory iterator.//+ -/// * `$reply_type`: The type of reply (readdir or readdirplus).//+ -/////+ -/// # Returns//+ -/////+ -/// This macro doesn't return a value directly, but it populates the `$reply` object//+ -/// with directory entries or an error code.// -macro_rules! handle_dir_read { - ($self:expr, $req:expr, $ino:expr, $fh:expr, $offset:expr, $reply:expr, - $handler_method:ident, $get_iter_method:ident, $reply_type:ty) => {{ - // Inner macro to handle readdir vs readdirplus differences - macro_rules! if_readdir { - (readdir, $choice1:tt, $choice2:tt) => { - $choice1 - }; - (readdirplus, $choice1:tt, $choice2:tt) => { - $choice2 - }; - } - - let req_info = RequestInfo::from($req); - let handler = $self.get_handler(); - let resolver = $self.get_resolver(); - let dirmap_iter = $self.$get_iter_method(); - - execute_task!($self, { - // Validate offset - if $offset < 0 { - error!("readdir called with a negative offset"); - $reply.error(ErrorKind::InvalidArgument.into()); - return; - } - - // ### Initialize directory iterator - let mut dir_iter = match $offset { - // First read: fetch children from handler - 0 => match handler.$handler_method(&req_info, resolver.resolve_id($ino), unsafe { - BorrowedFileHandle::from_raw($fh) - }) { - Ok(children) => { - // Unpack and process children - let (child_list, attr_list): (Vec<_>, Vec<_>) = children - .into_iter() - .map(|item| { - let (child_id, child_attr) = if_readdir!( - $handler_method, - { TId::extract_minimal_metadata(item.1) }, - { TId::extract_metadata(item.1) } - ); - ((item.0, child_id), child_attr) - }) - .unzip(); - - // Add children to resolver and create iterator - resolver - .add_children( - $ino, - child_list, - if_readdir!($handler_method, false, true), - ) - .into_iter() - .zip(attr_list.into_iter()) - .map(|((file_name, file_ino), file_attr)| { - (file_name, file_ino, file_attr) - }) - .collect() - } - Err(e) => { - warn!("readdir {:?}: {:?}", req_info, e); - $reply.error(e.raw_error()); - return; - } - }, - // Subsequent reads: retrieve saved iterator - _ => match { dirmap_iter.safe_borrow_mut().remove(&($ino, $offset)) } { - Some(dirmap_iter) => dirmap_iter, - None => { - // Case when fuse tries to read again after the final item - $reply.ok(); - return; - } - }, - }; - - let mut new_offset = $offset; - - // ### Process directory entries - if_readdir!( - $handler_method, - { - // readdir: Add entries until buffer is full - while let Some((name, ino, kind)) = dir_iter.pop_front() { - if $reply.add(ino, new_offset, kind, &name) { - dir_iter.push_front((name, ino, kind)); - dirmap_iter - .safe_borrow_mut() - .insert(($ino, new_offset - 1), dir_iter); - break; - } - new_offset += 1; - } - $reply.ok(); - }, - { - // readdirplus: Add entries with extended attributes - let default_ttl = handler.get_default_ttl(); - while let Some((name, ino, file_attr)) = dir_iter.pop_front() { - let (fuse_attr, ttl, generation) = file_attr.clone().to_fuse(ino); - if $reply.add( - ino, - new_offset, - &name, - &ttl.unwrap_or(default_ttl), - &fuse_attr, - generation.unwrap_or(get_random_generation()), - ) { - dir_iter.push_front((name, ino, file_attr.clone())); - dirmap_iter - .safe_borrow_mut() - .insert((ino, new_offset - 1), dir_iter); - break; - } - new_offset += 1; - } - $reply.ok(); - } - ); - }); - }}; -} - -pub(super) use handle_dir_read; -pub(super) use handle_fuse_reply_attr; -pub(super) use handle_fuse_reply_entry; diff --git a/src/core/thread_mode.rs b/src/core/thread_mode.rs deleted file mode 100644 index c7ebdc3..0000000 --- a/src/core/thread_mode.rs +++ /dev/null @@ -1,69 +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_common.rs b/src/fuse_common.rs index 8b13789..6e40e14 100644 --- a/src/fuse_common.rs +++ b/src/fuse_common.rs @@ -1 +1,3 @@ +pub(crate) mod inode_mapping; +pub(crate) use inode_mapping::{InodeResolvable, ROOT_INO}; diff --git a/src/core/inode_mapping.rs b/src/fuse_common/inode_mapping.rs similarity index 100% rename from src/core/inode_mapping.rs rename to src/fuse_common/inode_mapping.rs diff --git a/src/fuse_common/mounting.rs b/src/fuse_common/mounting.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/fuse_parallel.rs b/src/fuse_parallel.rs index aacc3f2..c85b935 100644 --- a/src/fuse_parallel.rs +++ b/src/fuse_parallel.rs @@ -1,2 +1,10 @@ -pub(crate) mod fuse_driver; -pub mod fuse_handler; +#![cfg(feature = "parallel")] + +mod fuse_driver; +pub(crate) use fuse_driver::FuseDriver; + +mod fuse_handler; +pub use fuse_handler::FuseHandler; + +mod mouting; +pub use mouting::*; diff --git a/src/fuse_parallel/fuse_driver.rs b/src/fuse_parallel/fuse_driver.rs index e69de29..fa6fa50 100644 --- a/src/fuse_parallel/fuse_driver.rs +++ b/src/fuse_parallel/fuse_driver.rs @@ -0,0 +1,8 @@ +use crate::types::*; + +use super::fuse_handler::FuseHandler; +use crate::fuse_common::inode_mapping::*; + +use easy_fuser_macro::implement_fuse_driver; + +implement_fuse_driver!("parallel"); diff --git a/src/fuse_parallel/mouting.rs b/src/fuse_parallel/mouting.rs new file mode 100644 index 0000000..d32efc7 --- /dev/null +++ b/src/fuse_parallel/mouting.rs @@ -0,0 +1,40 @@ +use std::io; +use std::path::Path; + +use super::{FuseDriver, FuseHandler}; +use crate::types::*; +use fuser::{mount2, spawn_mount2}; +pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; + +#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/mount.md"))] +pub fn mount( + filesystem: FS, + mountpoint: P, + options: &[MountOption], + num_threads: usize, +) -> io::Result<()> +where + T: FileIdType, + FS: FuseHandler, + P: AsRef, +{ + let driver = FuseDriver::new(filesystem, num_threads); + mount2(driver, mountpoint, options) +} + +#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/spawn_mount.md"))] + +pub fn spawn_mount( + filesystem: FS, + mountpoint: P, + options: &[MountOption], + num_threads: usize, +) -> io::Result +where + T: FileIdType, + FS: FuseHandler + Send, + P: AsRef, +{ + let driver = FuseDriver::new(filesystem, num_threads); + spawn_mount2(driver, mountpoint, options) +} diff --git a/src/fuse_serial.rs b/src/fuse_serial.rs index aacc3f2..76b1e98 100644 --- a/src/fuse_serial.rs +++ b/src/fuse_serial.rs @@ -1,2 +1,10 @@ -pub(crate) mod fuse_driver; -pub mod fuse_handler; +#![cfg(feature = "serial")] + +mod fuse_driver; +pub(crate) use fuse_driver::FuseDriver; + +mod fuse_handler; +pub use fuse_handler::FuseHandler; + +mod mouting; +pub use mouting::*; diff --git a/src/fuse_serial/fuse_driver.rs b/src/fuse_serial/fuse_driver.rs index ed5fa8b..420bfbb 100644 --- a/src/fuse_serial/fuse_driver.rs +++ b/src/fuse_serial/fuse_driver.rs @@ -1,884 +1,8 @@ use crate::types::*; use super::fuse_handler::FuseHandler; -use crate::core::inode_mapping::*; +use crate::fuse_common::inode_mapping::*; use easy_fuser_macro::implement_fuse_driver; -//implement_fuse_driver!("serial"); - - -use std::{ - collections::{ - HashMap,VecDeque - },ffi::{ - OsStr,OsString - },path::Path,time::{ - Instant,SystemTime - }, -}; -use libc::c_int; -use log::{ - error,info,warn -}; -use fuser::{ - self,KernelConfig,ReplyAttr,ReplyBmap,ReplyCreate,ReplyData,ReplyDirectory,ReplyDirectoryPlus,ReplyEmpty,ReplyEntry,ReplyIoctl,ReplyLock,ReplyLseek,ReplyOpen,ReplyStatfs,ReplyWrite,ReplyXattr,Request,TimeOrNow, -}; -type DirMapEntries = HashMap<(u64,i64),VecDeque<(OsString,u64,TAttr)>> ; -fn get_random_generation() -> u64 { - Instant::now().elapsed().as_nanos()as u64 -} -use std::cell::RefCell; -pub(crate)struct FuseDriverwhere TId:FileIdType,THandler:FuseHandler ,{ - handler:THandler,resolver:TId::Resolver,dirmap_entries:RefCell> ,dirmapplus_entries:RefCell> , -} -impl FuseDriverwhere TId:FileIdType,THandler:FuseHandler ,{ - #[doc = r" num_thread is ignored in serial mode, it is kept for consistency with other modes"] - pub fn new(handler:THandler,_num_threads:usize) -> FuseDriver{ - FuseDriver { - handler,resolver:TId::Resolver::new(),dirmap_entries:RefCell::new(HashMap::new()),dirmapplus_entries:RefCell::new(HashMap::new()), - } - } - - } -impl FuseDriverwhere TId:FileIdType,THandler:FuseHandler ,{ - fn access(&mut self,req: &Request,ino:u64,mask:i32,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let mask = AccessMask::from_bits_retain(mask); - match handler.access(&req,ino,mask){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("access: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn bmap(&mut self,req: &Request<'_> ,ino:u64,blocksize:u32,idx:u64,reply:ReplyBmap){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - match handler.bmap(&req,ino,blocksize,idx){ - Ok(block) => reply.bmap(block), - Err(e) => { - warn!("bmap: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn copy_file_range(&mut self,req: &Request,ino_in:u64,fh_in:u64,offset_in:i64,ino_out:u64,fh_out:u64,offset_out:i64,len:u64,flags:u32,reply:ReplyWrite,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let ino_in = resolver.resolve_id(ino_in); - let fh_in = unsafe { - BorrowedFileHandle::from_raw(fh_in) - }; - let ino_out = resolver.resolve_id(ino_out); - let fh_out = unsafe { - BorrowedFileHandle::from_raw(fh_out) - }; - match handler.copy_file_range(&req,ino_in,fh_in,offset_in,ino_out,fh_out,offset_out,len,flags){ - Ok(bytes_written) => reply.written(bytes_written), - Err(e) => { - warn!("copy_file_range: [{}], {:?}",e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn create(&mut self,req: &Request,parent:u64,name: &OsStr,mode:u32,umask:u32,flags:i32,reply:ReplyCreate,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let name = name.as_ref(); - let flags = OpenFlags::from_bits_retain(flags); - match handler.create(&req,parent,name,mode,umask,flags){ - Ok((file_handle,metadata,response_flags)) => { - let default_ttl = handler.get_default_ttl(); - let(id,file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent, &name,id,true); - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - reply.created(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),file_handle.as_raw(),response_flags.bits(),); - }, - Err(e) => { - warn!("create: [{}], {:?}",e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn fallocate(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,length:i64,mode:i32,reply:ReplyEmpty,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let mode = FallocateFlags::from_bits_retain(mode); - match handler.fallocate(&req,ino,fh,offset,length,mode){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("fallocate: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn flush(&mut self,req: &Request,ino:u64,fh:u64,lock_owner:u64,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - match handler.flush(&req,ino,fh,lock_owner){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("flush: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn forget(&mut self,req: &Request,ino:u64,nlookup:u64){ - let req = RequestInfo::from(req); - let ino = self.resolver.resolve_id(ino); - self.handler.forget(&req,ino,nlookup); - self.resolver.forget(ino,nlookup); - } - fn fsync(&mut self,req: &Request,ino:u64,fh:u64,datasync:bool,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - match handler.fsync(&req,ino,fh,datasync){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("fsync: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn fsyncdir(&mut self,req: &Request,ino:u64,fh:u64,datasync:bool,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - match handler.fsyncdir(&req,ino,fh,datasync){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("fsyncdir: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn getattr(&mut self,req: &Request,ino:u64,fh:Option ,reply:ReplyAttr){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - match handler.getattr(&req,ino,fh){ - Ok(file_attr) => { - let default_ttl = handler.get_default_ttl(); - let(fuse_attr,ttl,_) = file_attr.to_fuse(ino); - reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr); - }, - Err(e) => { - warn!("getattr: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn getlk(&mut self,req: &Request<'_> ,ino:u64,fh:u64,lock_owner:u64,start:u64,end:u64,typ:i32,pid:u32,reply:ReplyLock,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let lock_info = LockInfo { - start,end,lock_type:LockType::from_bits_retain(typ),pid, - }; - match handler.getlk(&req,ino,fh,lock_owner,lock_info){ - Ok(lock) => reply.lock(lock), - Err(e) => { - warn!("getlk: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn getxattr(&mut self,req: &Request,ino:u64,name: &OsStr,size:u32,reply:ReplyXattr){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let name = name.as_ref(); - match handler.getxattr(&req,ino,name,size){ - Ok(xattr_data) => { - if size==0 { - reply.size(xattr_data.len()as u32); - }else if size>=xattr_data.len()as u32 { - reply.data(&xattr_data); - }else { - reply.error(ErrorKind::ResultTooLarge.into()); - } - } - Err(e) => { - warn!("getxattr: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn ioctl(&mut self,req: &Request<'_> ,ino:u64,fh:u64,flags:u32,cmd:u32,in_data: &[u8],out_size:u32,reply:ReplyIoctl,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let in_data = in_data.to_owned(); - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let flags = IOCtlFlags::from_bits_retain(flags); - match handler.ioctl(&req,ino,fh,flags,cmd,in_data,out_size){ - Ok((result,data)) => reply.ioctl(result, &data), - Err(e) => { - warn!("ioctl: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn link(&mut self,req: &Request,ino:u64,newparent:u64,newname: &OsStr,reply:ReplyEntry,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let newname = newname.to_owned(); - let newname = newname.as_ref(); - let newparent = resolver.resolve_id(newparent); - match handler.link(&req,ino,newparent,newname){ - Ok(metadata) => { - let default_ttl = handler.get_default_ttl(); - let(id,file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent,name,id,true); - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); - } - Err(e) => { - warn!("link: [{}], {:?}",e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn listxattr(&mut self,req: &Request,ino:u64,size:u32,reply:ReplyXattr){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - match handler.listxattr(&req,ino,size){ - Ok(xattr_data) => { - if size==0 { - reply.size(xattr_data.len()as u32); - }else if size>=xattr_data.len()as u32 { - reply.data(&xattr_data); - }else { - reply.error(ErrorKind::ResultTooLarge.into()); - } - } - Err(e) => { - warn!("listxattr: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn lookup(&mut self,req: &Request,parent:u64,name: &OsStr,reply:ReplyEntry){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = parent; - let parent = resolver.resolve_id(parent); - match handler.lookup(&req,parent,name){ - Ok(metadata) => { - let default_ttl = handler.get_default_ttl(); - let(id,file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent,name,id,true); - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); - } - Err(e) => { - info!("lookup: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn lseek(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,whence:i32,reply:ReplyLseek,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let seek = seek_from_raw(Some(whence),offset); - match handler.lseek(&req,ino,fh,seek,){ - Ok(new_offset) => reply.offset(new_offset), - Err(e) => { - warn!("lseek: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn mkdir(&mut self,req: &Request,parent:u64,name: &OsStr,mode:u32,umask:u32,reply:ReplyEntry,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = parent; - let parent = resolver.resolve_id(parent); - let name = name.as_ref(); - match handler.mkdir(&req,parent,name,mode,umask){ - Ok(metadata) => { - let default_ttl = handler.get_default_ttl(); - let(id,file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent,name,id,true); - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); - } - Err(e) => { - warn!("mkdir: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn mknod(&mut self,req: &Request,parent:u64,name: &OsStr,mode:u32,umask:u32,rdev:u32,reply:ReplyEntry,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = parent; - let parent = resolver.resolve_id(parent); - let name = name.as_ref(); - let rdev = DeviceType::from_rdev(rdev.try_into().unwrap()); - match handler.mknod(&req,parent,name,mode,umask,rdev){ - Ok(metadata) => { - let default_ttl = handler.get_default_ttl(); - let(id,file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent,name,id,true); - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); - } - Err(e) => { - warn!("mknod: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn open(&mut self,req: &Request,ino:u64,flags:i32,reply:ReplyOpen){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let flags = OpenFlags::from_bits_retain(flags); - match handler.open(&req,ino,flags){ - Ok((file_handle,response_flags)) => { - reply.opened(file_handle.as_raw(),response_flags.bits()) - } - Err(e) => { - warn!("open: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn opendir(&mut self,req: &Request,ino:u64,flags:i32,reply:ReplyOpen){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let flags = OpenFlags::from_bits_retain(flags); - match handler.opendir(&req,ino,flags){ - Ok((file_handle,response_flags)) => { - reply.opened(file_handle.as_raw(),response_flags.bits()) - } - Err(e) => { - warn!("opendir: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn read(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,size:u32,flags:i32,lock_owner:Option ,reply:ReplyData,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let seek = seek_from_raw(Some(offset),0); - let flags = FUSEOpenFlags::from_bits_retain(flags); - match handler.read(&req,ino,fh,offset,size,flags,lock_owner){ - Ok(data_reply) => reply.data(&data_reply), - Err(e) => { - warn!("read: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn readdir(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,mut reply:ReplyDirectoryPlus,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let dirmap_entries = &self.dirmap_entries; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - if offset<0 { - { - let lvl = (log::Level::Error); - if lvl<=log::STATIC_MAX_LEVEL&&lvl<=log::max_level(){ - log::__private_api::log(log::__private_api::format_args!("readdir called with a negative offset"),lvl, &((log::__private_api::module_path!()),log::__private_api::module_path!(),log::__private_api::loc()),(),); - } - }; - reply.error(ErrorKind::InvalidArgument.into()); - return; - }let mut directory_entries = match offset { - 0 => match handler.readdir(&req,ino,fh,offset){ - Ok(children) => { - let(child_list,attr_list):(Vec<_> ,Vec<_>) = children.into_iter().map(|item|{ - let(child_id,child_attr) = TId::extract_minimal_metadata(item.1); - ((item.0,child_id),child_attr) - }).unzip(); - resolver.add_children(ino,child_list,false,).into_iter().zip(attr_list.into_iter()).map(|((file_name,file_ino),file_attr)|{ - (file_name,file_ino,file_attr) - }).collect() - } - Err(e) => { - warn!("readdir: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }, - _ => match { - dirmap_entries.borrow_mut().remove(&(ino,offset)) - }{ - Some(directory_entries) => directory_entries, - None => { - reply.ok(); - return; - } - - }, - - }; - let mut new_offset = offset+1; - while let Some((name,ino,kind)) = directory_entries.pop_front(){ - if reply.add(ino,new_offset,kind, &name){ - dirmap_entries.borrow_mut().insert((ino,new_offset),directory_entries); - break; - }new_offset+=1; - }reply.ok(); - } - fn readdirplus(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,mut reply:ReplyDirectory,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let dirmap_entries = &self.dirmapplus_entries; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - if offset<0 { - { - let lvl = (log::Level::Error); - if lvl<=log::STATIC_MAX_LEVEL&&lvl<=log::max_level(){ - log::__private_api::log(log::__private_api::format_args!("readdir called with a negative offset"),lvl, &((log::__private_api::module_path!()),log::__private_api::module_path!(),log::__private_api::loc()),(),); - } - }; - reply.error(ErrorKind::InvalidArgument.into()); - return; - }let mut directory_entries = match offset { - 0 => match handler.readdirplus(&req,ino,fh,offset){ - Ok(children) => { - let(child_list,attr_list):(Vec<_> ,Vec<_>) = children.into_iter().map(|item|{ - let(child_id,child_attr) = TId::extract_metadata(item.1); - ((item.0,child_id),child_attr) - }).unzip(); - resolver.add_children(ino,child_list,true,).into_iter().zip(attr_list.into_iter()).map(|((file_name,file_ino),file_attr)|{ - (file_name,file_ino,file_attr) - }).collect() - } - Err(e) => { - warn!("readdirplus: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }, - _ => match { - dirmap_entries.borrow_mut().remove(&(ino,offset)) - }{ - Some(directory_entries) => directory_entries, - None => { - reply.ok(); - return; - } - - }, - - }; - let mut new_offset = offset+1; - let default_ttl = handler.get_default_ttl(); - while let Some((name,ino,file_attr)) = directory_entries.pop_front(){ - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - if reply.add(ino,new_offset,name, &ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),){ - dirmap_entries.borrow_mut().insert((ino,new_offset),directory_entries); - break; - }new_offset+=1; - }reply.ok(); - } - fn readlink(&mut self,req: &Request,ino:u64,reply:ReplyData){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - match handler.readlink(&req,ino){ - Ok(link) => reply.data(&link), - warn_error - - }; - ; - } - fn release(&mut self,req: &Request,ino:u64,fh:u64,flags:i32,_lock_owner:Option ,_flush:bool,reply:ReplyEmpty,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let flags = OpenFlags::from_bits_retain(flags); - match handler.release(&req,ino,fh,flags,_lock_owner,_flush){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("release: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn releasedir(&mut self,req: &Request,ino:u64,fh:u64,flags:i32,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let flags = OpenFlags::from_bits_retain(flags); - match handler.releasedir(&req,ino,fh,flags){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("releasedir: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn removexattr(&mut self,req: &Request,ino:u64,name: &OsStr,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let name = name.as_ref(); - match handler.removexattr(&req,ino,name){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("removexattr: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn rename(&mut self,req: &Request,parent:u64,name: &OsStr,newparent:u64,newname: &OsStr,flags:u32,reply:ReplyEmpty,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let newname = newname.to_owned(); - let log_ino = parent; - let parent = resolver.resolve_id(parent); - let name = name.as_ref(); - let newname = newname.as_ref(); - let flags = RenameFlags::from_bits_retain(flags); - match handler.rename(&req,parent,name,newparent,newname,flags){ - Ok(()) => { - resolver.rename(parent, &name,newparent, &newname); - reply.ok() - } - Err(e) => { - warn!("rename: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn rmdir(&mut self,req: &Request,parent:u64,name: &OsStr,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = parent; - let parent = resolver.resolve_id(parent); - let name = name.as_ref(); - match handler.rmdir(&req,parent,name){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("rmdir: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn setattr(&mut self,req: &Request,ino:u64,mode:Option ,uid:Option ,gid:Option ,size:Option ,atime:Option ,mtime:Option ,ctime:Option ,fh:Option ,crtime:Option ,chgtime:Option ,bkuptime:Option ,_flags:Option ,reply:ReplyAttr,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let attrs = SetAttrRequest { - mode,uid,gid,size,atime:atime,mtime:mtime,ctime:ctime,crtime:crtime,chgtime:chgtime,bkuptime:bkuptime,flags:None,file_handle:fh.map(|fh|unsafe { - BorrowedFileHandle::from_raw(fh) - }), - }; - match handler.setattr(&req,ino,attrs){ - Ok(file_attr) => { - let default_ttl = handler.get_default_ttl(); - let(fuse_attr,ttl,_) = file_attr.to_fuse(ino); - reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr); - }, - Err(e) => { - warn!("setattr: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - } - } - fn setlk(&mut self,req: &Request<'_> ,ino:u64,fh:u64,lock_owner:u64,start:u64,end:u64,typ:i32,pid:u32,sleep:bool,reply:ReplyEmpty,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let lock_info = LockInfo { - start,end,lock_type:LockType::from_bits_retain(typ),pid, - }; - match handler.setlk(&req,ino,fh,lock_owner,lock_info,sleep,){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("setlk: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn setxattr(&mut self,req: &Request,ino:u64,name: &OsStr,value: &[u8],flags:i32,position:u32,reply:ReplyEmpty,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let value = value.to_owned(); - let name = name.as_ref(); - let flags = FUSESetXAttrFlags::from_bits_retain(flags); - match handler.setxattr(&req,ino,name,value,flags,position){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("setxattr: [{}], {:?}",e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn statfs(&mut self,req: &Request,ino:u64,reply:ReplyStatfs){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let log_ino = ino; - let ino = resolver.resolve_id(ino); - match handler.statfs(&req,ino){ - Ok(statfs) => reply.statfs(statfs.total_blocks,statfs.free_blocks,statfs.available_blocks,statfs.total_files,statfs.free_files,statfs.block_size,statfs.max_filename_length,statfs.fragment_size,), - Err(e) => { - warn!("statfs: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn symlink(&mut self,req: &Request,parent:u64,link_name: &OsStr,target: &Path,reply:ReplyEntry,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let link_name = link_name.to_owned(); - let target = target.to_owned(); - let link_name = link_name.as_ref(); - let target = target.as_ref(); - match handler.symlink(&req,parent,link_name,target){ - Ok(metadata) => { - let default_ttl = handler.get_default_ttl(); - let(id,file_attr) = TId::extract_metadata(metadata); - let ino = resolver.lookup(parent,name,id,true); - let(fuse_attr,ttl,generation) = file_attr.to_fuse(ino); - reply.entry(&ttl.unwrap_or(default_ttl), &fuse_attr,generation.unwrap_or(get_random_generation()),); - } - Err(e) => { - warn!("symlink: [{}], {:?}",e,req); - reply.error(e.raw_error()); - return; - } - - }; - } - fn write(&mut self,req: &Request,ino:u64,fh:u64,offset:i64,data: &[u8],write_flags:u32,flags:i32,lock_owner:Option ,reply:ReplyWrite,){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let data = data.to_owned(); - let log_ino = ino; - let ino = resolver.resolve_id(ino); - let fh = unsafe { - BorrowedFileHandle::from_raw(fh) - }; - let seek = seek_from_raw(Some(offset),0); - let write_flags = FUSEWriteFlags::from_bits_retain(write_flags); - let flags = OpenFlags::from_bits_retain(flags); - match handler.write(&req,ino,fh,offset,data,write_flags,flags,lock_owner){ - Ok(bytes_written) => reply.written(bytes_written), - Err(e) => { - warn!("write: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - fn unlink(&mut self,req: &Request,parent:u64,name: &OsStr,reply:ReplyEmpty){ - let req = RequestInfo::from(req); - let handler = &self.handler; - let resolver = &self.resolver; - let name = name.to_owned(); - let log_ino = parent; - let parent = resolver.resolve_id(parent); - let name = name.as_ref(); - match handler.unlink(&req,parent,name){ - Ok(()) => reply.ok(), - Err(e) => { - warn!("unlink: ino {:x?}, [{}], {:?}",log_ino,e,req); - reply.error(e.raw_error()); - return; - } - - }; - ; - } - - } \ No newline at end of file +implement_fuse_driver!("serial"); diff --git a/src/fuse_serial/mouting.rs b/src/fuse_serial/mouting.rs new file mode 100644 index 0000000..373e1d7 --- /dev/null +++ b/src/fuse_serial/mouting.rs @@ -0,0 +1,34 @@ +use std::io; +use std::path::Path; + +use super::{FuseDriver, FuseHandler}; +use crate::types::*; +use fuser::{mount2, spawn_mount2}; +pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; + +#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/mount.md"))] +pub fn mount(filesystem: FS, mountpoint: P, options: &[MountOption]) -> io::Result<()> +where + T: FileIdType, + FS: FuseHandler, + P: AsRef, +{ + let driver = FuseDriver::new(filesystem, 1); + mount2(driver, mountpoint, options) +} + +#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/spawn_mount.md"))] + +pub fn spawn_mount( + filesystem: FS, + mountpoint: P, + options: &[MountOption], +) -> io::Result +where + T: FileIdType, + FS: FuseHandler + Send, + P: AsRef, +{ + let driver = FuseDriver::new(filesystem, 1); + spawn_mount2(driver, mountpoint, options) +} diff --git a/src/inode_mapper.rs b/src/inode_mapper.rs index ad93d37..5dd246a 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::prelude::{Inode, ROOT_INODE}; /// Helper structure for managing inodes and their relationships. /// @@ -538,8 +538,8 @@ mod tests { use std::collections::HashSet; use std::ffi::OsString; + use crate::prelude::ROOT_INODE; use crate::types::Inode; - use crate::ROOT_INODE; #[test] fn test_insert_child_returns_old_inode() { diff --git a/src/lib.rs b/src/lib.rs index ea193a4..0bbfffb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,21 +10,11 @@ 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"); - pub mod fuse_async; mod fuse_common; pub mod fuse_parallel; pub mod fuse_serial; -mod core; mod fuse_handler; pub mod inode_mapper; @@ -33,84 +23,11 @@ pub mod types; pub mod unix_fs; pub use fuse_handler::FuseHandler; -use fuser::{BackgroundSession, MountOption}; pub mod prelude { //! Re-exports the necessary types and functions from the `easy_fuser` crate. pub use super::fuse_handler::FuseHandler; pub use super::types::*; - pub use super::{mount, spawn_mount}; pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; } - -// Implentation of the high-level functions -use std::io; -use std::path::Path; - -use core::FuseDriver; -use fuser::{mount2, spawn_mount2}; -use prelude::*; - -#[doc = include_str!("../docs/mount.md")] -#[cfg(not(feature = "serial"))] -pub fn mount( - filesystem: FS, - mountpoint: P, - options: &[MountOption], - num_threads: usize, -) -> io::Result<()> -where - T: FileIdType, - FS: FuseHandler, - P: AsRef, -{ - let driver = FuseDriver::new(filesystem, num_threads); - mount2(driver, mountpoint, options) -} -#[doc = include_str!("../docs/mount.md")] -#[cfg(feature = "serial")] -pub fn mount(filesystem: FS, mountpoint: P, options: &[MountOption]) -> io::Result<()> -where - T: FileIdType, - FS: FuseHandler, - P: AsRef, -{ - // num_thread argument will not be taken into account in this function due to feature serial - let driver = FuseDriver::new(filesystem, 1); - mount2(driver, mountpoint, options) -} - -#[doc = include_str!("../docs/spawn_mount.md")] -#[cfg(not(feature = "serial"))] -pub fn spawn_mount( - filesystem: FS, - mountpoint: P, - options: &[MountOption], - num_threads: usize, -) -> io::Result -where - T: FileIdType, - FS: FuseHandler + Send, - P: AsRef, -{ - let driver = FuseDriver::new(filesystem, num_threads); - spawn_mount2(driver, mountpoint, options) -} - -#[doc = include_str!("../docs/spawn_mount.md")] -#[cfg(feature = "serial")] -pub fn spawn_mount( - filesystem: FS, - mountpoint: P, - options: &[MountOption], -) -> io::Result -where - T: FileIdType, - FS: FuseHandler + Send, - P: AsRef, -{ - // num_thread argument will not be taken into account in this function due to feature serial - let driver = FuseDriver::new(filesystem, 1); - spawn_mount2(driver, mountpoint, options) -} diff --git a/src/types/file_id_type.rs b/src/types/file_id_type.rs index 985666c..4d799f9 100644 --- a/src/types/file_id_type.rs +++ b/src/types/file_id_type.rs @@ -15,7 +15,7 @@ use std::{ use fuser::FileType as FileKind; -use crate::core::InodeResolvable; +use crate::fuse_common::InodeResolvable; use super::arguments::FileAttribute; use super::inode::*; diff --git a/src/types/inode.rs b/src/types/inode.rs index b0638a1..1f65a25 100644 --- a/src/types/inode.rs +++ b/src/types/inode.rs @@ -1,6 +1,6 @@ //! Inode number in a FUSE (Filesystem in Userspace) filesystem. -use crate::core::ROOT_INO; +use crate::fuse_common::ROOT_INO; /// Represents the mountpoint folder in a FuseFilesystem /// Its value is 1 and should not be modified. diff --git a/src/unix_fs/linux_fs.rs b/src/unix_fs/linux_fs.rs index 1d2b533..ed6b014 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::{cstring_from_path, StatFs}; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index edc98d8..e7a015b 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,4 +1,4 @@ -use easy_fuser::prelude::*; +use easy_fuser::fuse_parallel::*; use easy_fuser::templates::{mirror_fs::*, DefaultFuseHandler}; use std::fs::{self, File}; @@ -21,9 +21,9 @@ fn test_mirror_fs_operations() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] + //#[cfg(feature = "serial")] + //mount(fs, &mntpoint_clone, &[]).unwrap(); + //#[cfg(not(feature = "serial"))] mount(fs, &mntpoint_clone, &[], 4).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish From b0ce80bce4340fb5b3926ce20ee1c719cafb056e Mon Sep 17 00:00:00 2001 From: alogani Date: Fri, 7 Feb 2025 08:10:00 +0100 Subject: [PATCH 5/5] Misc corrections for fuse driver async --- .vscode/settings.json | 2 +- Cargo.toml | 4 +- easy_fuser_macro/src/fuse_driver.rs | 76 +-- src/fuse_async.rs | 12 +- src/fuse_async/fuse_driver.rs | 8 + src/fuse_async/fuse_handler.rs | 26 - src/fuse_async/mouting.rs | 40 ++ src/fuse_common/mounting.rs | 0 src/{ => fuse_common}/templates.rs | 0 .../templates/default_fuse_handler.rs | 0 .../templates/fd_handler_helper.rs | 0 src/{ => fuse_common}/templates/mirror_fs.rs | 0 src/fuse_handler.rs | 594 ------------------ src/lib.rs | 6 - 14 files changed, 101 insertions(+), 667 deletions(-) create mode 100644 src/fuse_async/mouting.rs delete mode 100644 src/fuse_common/mounting.rs rename src/{ => fuse_common}/templates.rs (100%) rename src/{ => fuse_common}/templates/default_fuse_handler.rs (100%) rename src/{ => fuse_common}/templates/fd_handler_helper.rs (100%) rename src/{ => fuse_common}/templates/mirror_fs.rs (100%) delete mode 100644 src/fuse_handler.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 79435eb..d80af9e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.cargo.features": ["serial", "parallel"] + "rust-analyzer.cargo.features": ["serial", "parallel", "async"] } diff --git a/Cargo.toml b/Cargo.toml index 5682b51..64f98dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/Alogani/easy_fuser" default = ["parallel"] serial = [] parallel = ["dep:threadpool"] -async = [ "dep:tokio"] +async = [ "dep:tokio", "dep:async-trait"] [dependencies] # Core dependencies @@ -28,7 +28,7 @@ threadpool = { version = "1.8", optional = true } # Async dependencies # easy_fuser_async_macro = { path = "./easy_fuser_async_macro", optional = true } tokio = { version = "1.42.0", features = ["full"], optional = true } -async-trait = { version = "0.1.85" } +async-trait = { version = "0.1.85", optional = true } [dev-dependencies] tempfile = "3.14" diff --git a/easy_fuser_macro/src/fuse_driver.rs b/easy_fuser_macro/src/fuse_driver.rs index 47f5530..eb77b80 100644 --- a/easy_fuser_macro/src/fuse_driver.rs +++ b/easy_fuser_macro/src/fuse_driver.rs @@ -117,6 +117,10 @@ fn expand_macro_tokens( if let Some(TokenTree::Ident(ident)) = tokens.next() { let key = ident.to_string(); let replacement = match key.as_str() { + "await" => match handler_type { + HandlerType::Async => quote!(.await), + _ => quote!(), + }, "req" => quote!(let req = RequestInfo::from(req);), "handler" => match handler_type { HandlerType::Serial => quote!(let handler = &self.handler;), @@ -334,7 +338,7 @@ fn readdir_impl(handler_type: HandlerType, is_extended_readdir: bool) -> TokenSt // ### Initialize directory deque let mut directory_entries = match offset { // First read: fetch children from handler - 0 => match handler.#fn_name(&req, ino_id, fh) { + 0 => match handler.#fn_name(&req, ino_id, fh)$await { Ok(children) => { // Unpack and process children let (child_list, attr_list): (Vec<_>, Vec<_>) = children @@ -393,7 +397,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -410,7 +414,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.bmap(block), $warn_error } @@ -442,7 +446,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.written(bytes_written), $warn_error } @@ -471,7 +475,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { let default_ttl = handler.get_default_ttl(); let (id, file_attr) = TId::extract_metadata(metadata); @@ -511,7 +515,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -529,7 +533,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -558,7 +562,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -576,7 +580,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error } @@ -594,7 +598,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec reply.locked(lock_info.start, lock_info.end, lock_info.lock_type.bits(), @@ -652,7 +656,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { if size == 0 { reply.size(xattr_data.len() as u32); @@ -690,7 +694,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ioctl(result, &data), $warn_error }; @@ -719,7 +723,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec { if size == 0 { reply.size(xattr_data.len() as u32); @@ -763,7 +767,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec reply.offset(new_offset), $warn_error }; @@ -824,7 +828,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec Vec { reply.opened(file_handle.as_raw(), response_flags.bits()) } @@ -891,7 +895,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { reply.opened(file_handle.as_raw(), response_flags.bits()) } @@ -929,7 +933,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.data(&data_reply), $warn_error }; @@ -948,7 +952,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.data(&link), $warn_error }; @@ -976,7 +980,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -993,7 +997,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1011,7 +1015,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1043,7 +1047,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec { resolver.rename(parent, &name, newparent, &newname); reply.ok() @@ -1065,7 +1069,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1113,7 +1117,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec reply.ok(), $warn_error }; @@ -1186,7 +1190,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1203,7 +1207,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.statfs( statfs.total_blocks, statfs.free_blocks, @@ -1241,7 +1245,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec Vec reply.written(bytes_written), $warn_error }; @@ -1299,7 +1303,7 @@ fn generate_fuse_operation_handlers(handler_type: HandlerType) -> Vec reply.ok(), $warn_error }; @@ -1399,7 +1403,7 @@ fn generate_fuse_driver_struct(handler_type: HandlerType) -> TokenStream { FuseDriver { handler: Arc::new(handler), resolver: Arc::new(TId::create_resolver()), - dirmap_entries Arc::new(Mutex::new(HashMap::new())), + dirmap_entries: Arc::new(Mutex::new(HashMap::new())), dirmapplus_entries: Arc::new(Mutex::new(HashMap::new())), runtime: Runtime::new().unwrap(), } diff --git a/src/fuse_async.rs b/src/fuse_async.rs index aacc3f2..dab7acb 100644 --- a/src/fuse_async.rs +++ b/src/fuse_async.rs @@ -1,2 +1,10 @@ -pub(crate) mod fuse_driver; -pub mod fuse_handler; +#![cfg(feature = "async")] + +mod fuse_driver; +pub(crate) use fuse_driver::FuseDriver; + +mod fuse_handler; +pub use fuse_handler::FuseHandler; + +mod mouting; +pub use mouting::*; diff --git a/src/fuse_async/fuse_driver.rs b/src/fuse_async/fuse_driver.rs index e69de29..06f13d6 100644 --- a/src/fuse_async/fuse_driver.rs +++ b/src/fuse_async/fuse_driver.rs @@ -0,0 +1,8 @@ +use crate::types::*; + +use super::fuse_handler::FuseHandler; +use crate::fuse_common::inode_mapping::*; + +use easy_fuser_macro::implement_fuse_driver; + +implement_fuse_driver!("async"); diff --git a/src/fuse_async/fuse_handler.rs b/src/fuse_async/fuse_handler.rs index 78b7ef9..826f99b 100644 --- a/src/fuse_async/fuse_handler.rs +++ b/src/fuse_async/fuse_handler.rs @@ -3,29 +3,3 @@ use crate::types::*; use easy_fuser_macro::implement_fuse_handler; implement_fuse_handler!("async"); - -/* # Examples -struct MyFuse { - inner: Box>, -} - -#[async_trait] -impl FuseHandler for MyFuse { - fn get_inner(&self) -> &dyn FuseHandler { - self.inner.as_ref() - } - - async fn read( - &self, - _req: &RequestInfo, - _file_id: Inode, - _file_handle: BorrowedFileHandle<'_>, - _seek: SeekFrom, - _size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, - ) -> FuseResult> { - return Ok(Vec::new()); - } -} - */ diff --git a/src/fuse_async/mouting.rs b/src/fuse_async/mouting.rs new file mode 100644 index 0000000..d32efc7 --- /dev/null +++ b/src/fuse_async/mouting.rs @@ -0,0 +1,40 @@ +use std::io; +use std::path::Path; + +use super::{FuseDriver, FuseHandler}; +use crate::types::*; +use fuser::{mount2, spawn_mount2}; +pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; + +#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/mount.md"))] +pub fn mount( + filesystem: FS, + mountpoint: P, + options: &[MountOption], + num_threads: usize, +) -> io::Result<()> +where + T: FileIdType, + FS: FuseHandler, + P: AsRef, +{ + let driver = FuseDriver::new(filesystem, num_threads); + mount2(driver, mountpoint, options) +} + +#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/spawn_mount.md"))] + +pub fn spawn_mount( + filesystem: FS, + mountpoint: P, + options: &[MountOption], + num_threads: usize, +) -> io::Result +where + T: FileIdType, + FS: FuseHandler + Send, + P: AsRef, +{ + let driver = FuseDriver::new(filesystem, num_threads); + spawn_mount2(driver, mountpoint, options) +} diff --git a/src/fuse_common/mounting.rs b/src/fuse_common/mounting.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/templates.rs b/src/fuse_common/templates.rs similarity index 100% rename from src/templates.rs rename to src/fuse_common/templates.rs diff --git a/src/templates/default_fuse_handler.rs b/src/fuse_common/templates/default_fuse_handler.rs similarity index 100% rename from src/templates/default_fuse_handler.rs rename to src/fuse_common/templates/default_fuse_handler.rs diff --git a/src/templates/fd_handler_helper.rs b/src/fuse_common/templates/fd_handler_helper.rs similarity index 100% rename from src/templates/fd_handler_helper.rs rename to src/fuse_common/templates/fd_handler_helper.rs diff --git a/src/templates/mirror_fs.rs b/src/fuse_common/templates/mirror_fs.rs similarity index 100% rename from src/templates/mirror_fs.rs rename to src/fuse_common/templates/mirror_fs.rs diff --git a/src/fuse_handler.rs b/src/fuse_handler.rs deleted file mode 100644 index 5a76a9a..0000000 --- a/src/fuse_handler.rs +++ /dev/null @@ -1,594 +0,0 @@ -/// The `FuseHandler` trait is the core interface for implementing a userspace filesystem via FUSE (Filesystem in Userspace). -/// -/// This trait defines methods that correspond to various filesystem operations. By implementing this trait, -/// you can create custom filesystem behaviors for your FUSE-based filesystem. -/// -/// # Type Parameter -/// -/// - `T`: A type that implements `FileIdType`. This represents the unique identifier for files and directories in your filesystem. -/// -/// # Usage -/// -/// To create a custom filesystem, implement this trait for your struct. You can choose to implement all methods -/// or rely on default implementations provided by one of the provided templates (like `MirrorFs` or `DefaultFuseHandler`). -/// -/// ## Example: Custom Filesystem with MirrorFs as base -/// -/// ```rust, no_run -/// use easy_fuser::templates::{DefaultFuseHandler, mirror_fs::*}; -/// use easy_fuser::prelude::*; -/// use std::path::{Path, PathBuf}; -/// use std::ffi::OsStr; -/// -/// struct MyCustomFs { -/// inner: Box>, -/// // other fields... -/// } -/// -/// impl MyCustomFs { -/// pub fn new(source_path: PathBuf) -> Self { -/// MyCustomFs { inner: Box::new(MirrorFsReadOnly::new(source_path, DefaultFuseHandler::new())) } -/// -/// } -/// } -/// -/// impl FuseHandler for MyCustomFs { -/// fn get_inner(&self) -> &dyn FuseHandler { -/// // Delegate to MirrorFsReadOnly for standard behavior -/// self.inner.as_ref() -/// } -/// -/// fn lookup(&self, req: &RequestInfo, parent_id: PathBuf, name: &OsStr) -> FuseResult { -/// // Custom logic for lookup operation -/// // ... -/// -/// // Delegate to inner handler for standard behavior -/// self.inner.lookup(req, parent_id, name) -/// } -/// -/// // Implement other FuseHandler methods as needed, delegating to self.inner as appropriate -/// // ... -/// } -/// ``` -/// -/// # Important Methods -/// -/// While all methods in this trait are important for a fully functional filesystem, some key methods include: -/// -/// - `lookup`: Look up a directory entry by name and get its attributes. -/// - `getattr`: Get file attributes. -/// - `read`: Read data from a file. -/// - `write`: Write data to a file. -/// - `readdir`: Read directory contents. -/// - `open`: Open a file. -/// - `create`: Create and open a file. -/// -/// # Default Implementations -/// -/// Many methods in this trait have default implementations that delegate to the inner handler returned by `get_inner()`. -/// This allows for easy extension and customization of existing filesystem implementations by chaining/overriding their behaviors. -/// -/// # Multithreading, Safety, Traits & Lifetime -/// -/// ## Using serial feature -/// -/// This trait is bound by the `'static` lifetime. However, it doesn't need `Send` or `Sync`. -/// -/// **Note:** Using `spawn_mount` for your filesystem will require at least the `Send` trait. -/// -/// ## Using parallel or async feature -/// -/// This trait requires `Send + Sync` and `'static` lifetime. -/// -/// **Important:** FUSE will lock some operations to run at the same time with the same inode, such as `readdir`. -/// This behaviour is not well documented and cannot be guaranteed for now. -/// -//// # Additional Resources: -/// For more detailed information, refer to the fuser project documentation, which serves as the foundation for this crate: https://docs.rs/fuser -/// -/// Documentation is inspired by the original fuser documentation -use std::ffi::{OsStr, OsString}; -use std::path::Path; -use std::time::Duration; - -use crate::types::*; - -mod private { - #[cfg(not(feature = "serial"))] - pub trait OptionalSendSync: Sync + Send {} - #[cfg(not(feature = "serial"))] - impl OptionalSendSync for T {} - #[cfg(feature = "serial")] - pub trait OptionalSendSync {} - #[cfg(feature = "serial")] - impl OptionalSendSync for T {} -} -use private::OptionalSendSync; - -pub trait FuseHandler: OptionalSendSync + 'static { - /// 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. - fn get_default_ttl(&self) -> Duration { - Duration::from_secs(1) - } - - /// Initialize the filesystem and configure kernel connection - fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> { - self.get_inner().init(req, config) - } - - /// Perform cleanup operations on filesystem exit - fn destroy(&self) { - self.get_inner().destroy(); - } - - /// Check file access permissions - /// - /// 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 - fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> { - self.get_inner().access(req, file_id, mask) - } - - /// 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 - fn bmap(&self, req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult { - self.get_inner().bmap(req, file_id, blocksize, idx) - } - - /// Copy the specified range from the source inode to the destination inode - 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, - 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, - ) - } - - /// Create and open a file - /// - /// If the file does not exist, first create it with the specified mode, and then - /// open it. Open flags (with the exception of O_NOCTTY) are available in flags. - /// If this method is not implemented or under Linux kernel versions earlier than - /// 2.6.15, the mknod() and open() methods will be called instead. - fn create( - &self, - req: &RequestInfo, - parent_id: 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) - } - - /// Preallocate or deallocate space to a file - fn fallocate( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - offset: i64, - length: i64, - mode: FallocateFlags, - ) -> FuseResult<()> { - self.get_inner() - .fallocate(req, file_id, file_handle, offset, length, mode) - } - - /// Flush cached data for an open file - /// - /// Called on each close() of the opened file. Not guaranteed to be called after writes or at all. - /// Used for returning write errors or removing file locks. - fn flush( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - lock_owner: u64, - ) -> FuseResult<()> { - self.get_inner() - .flush(req, file_id, file_handle, lock_owner) - } - - /// Release references to an inode, if the nlookup count reaches zero (to substract from the number of lookups). - fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) { - self.get_inner().forget(req, file_id, nlookup); - } - - /// Synchronize file contents - /// - /// If datasync is true, only flush user data, not metadata. - fn fsync( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - datasync: bool, - ) -> FuseResult<()> { - self.get_inner().fsync(req, file_id, file_handle, datasync) - } - - /// Synchronize directory contents - /// - /// If the datasync parameter is true, then only the directory contents should - /// be flushed, not the metadata. The file_handle will contain the value set - /// by the opendir method, or will be undefined if the opendir method didn't - /// set any value. - fn fsyncdir( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - datasync: bool, - ) -> FuseResult<()> { - self.get_inner() - .fsyncdir(req, file_id, file_handle, datasync) - } - - /// Modify file attributes - fn getattr( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: Option, - ) -> FuseResult { - self.get_inner().getattr(req, file_id, file_handle) - } - - /// Test for a POSIX file lock. - fn getlk( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - lock_owner: u64, - lock_info: LockInfo, - ) -> FuseResult { - self.get_inner() - .getlk(req, file_id, file_handle, lock_owner, lock_info) - } - - /// Get an extended attribute - fn getxattr( - &self, - req: &RequestInfo, - file_id: TId, - name: &OsStr, - size: u32, - ) -> FuseResult> { - self.get_inner().getxattr(req, file_id, name, size) - } - - /// control device - fn ioctl( - &self, - req: &RequestInfo, - file_id: 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) - } - - /// Create a hard link. - fn link( - &self, - req: &RequestInfo, - file_id: TId, - newparent: TId, - newname: &OsStr, - ) -> FuseResult { - self.get_inner().link(req, file_id, newparent, newname) - } - - /// List extended attribute names - fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult> { - self.get_inner().listxattr(req, file_id, size) - } - - /// Retrieve file attributes for a directory entry by name and increment the lookup count associated with the inode. - fn lookup(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult { - self.get_inner().lookup(req, parent_id, name) - } - - /// Reposition read/write file offset - fn lseek( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - seek: SeekFrom, - ) -> FuseResult { - self.get_inner().lseek(req, file_id, file_handle, seek) - } - - /// Create a new directory - fn mkdir( - &self, - req: &RequestInfo, - parent_id: TId, - name: &OsStr, - mode: u32, - umask: u32, - ) -> FuseResult { - self.get_inner().mkdir(req, parent_id, name, mode, umask) - } - - /// Create a new file node (regular file, device, FIFO, socket, etc) - fn mknod( - &self, - req: &RequestInfo, - parent_id: TId, - name: &OsStr, - mode: u32, - umask: u32, - rdev: DeviceType, - ) -> FuseResult { - self.get_inner() - .mknod(req, parent_id, name, mode, umask, rdev) - } - - /// Open a file and return a file handle. - /// - /// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are available in flags. You may store an arbitrary file handle (pointer, index, etc) in file_handle response, and use this in other all other file operations (read, write, flush, release, fsync). Filesystem may also implement stateless file I/O and not store anything in fh. There are also some flags (direct_io, keep_cache) which the filesystem may set, to change the way the file is opened. See fuse_file_info structure in for more details. - fn open( - &self, - req: &RequestInfo, - file_id: TId, - flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().open(req, file_id, flags) - } - - /// Open a directory - /// - /// Allows storing a file handle for use in subsequent directory operations. - fn opendir( - &self, - req: &RequestInfo, - file_id: TId, - flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().opendir(req, file_id, flags) - } - - /// Read data from a file - /// - /// Read should send exactly the number of bytes requested except on EOF or error, otherwise the rest of the data will be substituted with zeroes. An exception to this is when the file has been opened in ‘direct_io’ mode, in which case the return value of the read system call will reflect the return value of this operation. fh will contain the value set by the open method, or will be undefined if the open method didn’t set any value. - /// - /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 lock_owner: only supported with ABI >= 7.9 - fn read( - &self, - req: &RequestInfo, - file_id: 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) - } - - /// Read directory contents - /// - /// Returns a list of directory entries with minimal metadata. - /// - /// Important: The returned file names (OsString) must not contain any slashes ('/'). - /// Including slashes in the file names will result in undefined behavior. - fn readdir( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - ) -> FuseResult> { - self.get_inner().readdir(req, file_id, file_handle) - } - - /// Read directory contents with full file attributes - /// - /// Default implementation combines readdir and lookup operations. - /// - /// Important: The returned file names (OsString) must not contain any slashes ('/'). - /// Including slashes in the file names will result in undefined behavior. - fn readdirplus( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: BorrowedFileHandle, - ) -> FuseResult> { - let readdir_result = self.readdir(req, file_id.clone(), file_handle)?; - let mut result = Vec::with_capacity(readdir_result.len()); - for (name, _) in readdir_result.into_iter() { - let metadata = self.lookup(req, file_id.clone(), &name)?; - result.push((name, metadata)); - } - Ok(result) - } - - /// Read the target of a symbolic link - fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult> { - self.get_inner().readlink(req, file_id) - } - - /// Release an open file - /// - /// Called when all file descriptors are closed and all memory mappings are unmapped. - /// Guaranteed to be called once for every open() call. - fn release( - &self, - req: &RequestInfo, - file_id: 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) - } - - /// Release an open directory - /// - /// This method is called exactly once for every successful opendir operation. - /// The file_handle parameter will contain the value set by the opendir method, - /// or will be undefined if the opendir method didn't set any value. - fn releasedir( - &self, - req: &RequestInfo, - file_id: TId, - file_handle: OwnedFileHandle, - flags: OpenFlags, - ) -> FuseResult<()> { - self.get_inner() - .releasedir(req, file_id, file_handle, flags) - } - - /// Remove an extended attribute. - fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> { - self.get_inner().removexattr(req, file_id, name) - } - - /// Rename a file or directory - fn rename( - &self, - req: &RequestInfo, - parent_id: TId, - name: &OsStr, - newparent: TId, - newname: &OsStr, - flags: RenameFlags, - ) -> FuseResult<()> { - self.get_inner() - .rename(req, parent_id, name, newparent, newname, flags) - } - - /// Remove a directory - fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { - self.get_inner().rmdir(req, parent_id, name) - } - - /// Set file attributes. - fn setattr( - &self, - req: &RequestInfo, - file_id: TId, - attrs: SetAttrRequest, - ) -> FuseResult { - self.get_inner().setattr(req, file_id, attrs) - } - - /// Acquire, modify or release a POSIX file lock - /// - /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but - /// otherwise this is not always the case. For checking lock ownership, 'fi->owner' - /// must be used. The l_pid field in 'struct flock' should only be used to fill - /// in this field in getlk(). - fn setlk( - &self, - req: &RequestInfo, - file_id: 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) - } - - /// Set an extended attribute - fn setxattr( - &self, - req: &RequestInfo, - file_id: TId, - name: &OsStr, - value: Vec, - flags: FUSESetXAttrFlags, - position: u32, - ) -> FuseResult<()> { - self.get_inner() - .setxattr(req, file_id, name, value, flags, position) - } - - /// Get file system statistics - fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult { - self.get_inner().statfs(req, file_id) - } - - /// Create a symbolic link. - fn symlink( - &self, - req: &RequestInfo, - parent_id: TId, - link_name: &OsStr, - target: &Path, - ) -> FuseResult { - self.get_inner().symlink(req, parent_id, link_name, target) - } - - /// Write data to a file - /// - /// Write should return exactly the number of bytes requested except on error. An exception to this is when the file has been opened in ‘direct_io’ mode, in which case the return value of the write system call will reflect the return value of this operation. fh will contain the value set by the open method, or will be undefined if the open method didn’t set any value. - /// - /// write_flags: will contain FUSE_WRITE_CACHE, if this write is from the page cache. If set, the pid, uid, gid, and fh may not match the value that would have been sent if write cachin is disabled flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 lock_owner: only supported with ABI >= 7.9 - 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.get_inner().write( - req, - file_id, - file_handle, - seek, - data, - write_flags, - flags, - lock_owner, - ) - } - - /// Remove a file - fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> { - self.get_inner().unlink(req, parent_id, name) - } -} diff --git a/src/lib.rs b/src/lib.rs index 0bbfffb..c8078ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,18 +15,12 @@ mod fuse_common; pub mod fuse_parallel; pub mod fuse_serial; -mod fuse_handler; - pub mod inode_mapper; -pub mod templates; pub mod types; pub mod unix_fs; -pub use fuse_handler::FuseHandler; - pub mod prelude { //! Re-exports the necessary types and functions from the `easy_fuser` crate. - pub use super::fuse_handler::FuseHandler; pub use super::types::*; pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter};