diff --git a/.vscode/settings.json b/.vscode/settings.json index c039ee2..f104b2d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,12 @@ { + "rust-analyzer.cargo.features": [ + "serial", + "passthrough" + ], "files.associations": { "*.rs.j2": "rust", }, - "rust-analyzer.cargo.features": ["serial"], "rust-analyzer.files.exclude": [ "templates" ] -} +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index d32e1d1..1b198d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,13 @@ serial = [] parallel = ["dep:threadpool"] async = ["dep:async-trait", "dep:tokio"] deadlock_detection = ["parallel", "dep:parking_lot"] - +passthrough = [] [dependencies] # Core dependencies log = "0.4" libc = "0.2" -fuser = "0.16" +fuser = { version = "0.16" } bitflags = "2.10" # Parallel dependencies @@ -43,3 +43,7 @@ askama = { version = "0.15.4", features = ["derive"] } [package.metadata.docs.rs] features = ["parallel"] + +[patch.crates-io] +# Use the updated branch +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } \ No newline at end of file diff --git a/README.md b/README.md index b89536e..81d1938 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,13 @@ impl FuseHandler for MyFS { fn main() -> std::io::Result<()> { let fs = MyFS { inner: Box::new(DefaultFuseHandler::new()) }; - #[cfg(feature="serial")] - easy_fuser::mount(fs, Path::new("/mnt/myfs"), &[])?; - #[cfg(not(feature="serial"))] - easy_fuser::mount(fs, Path::new("/mnt/myfs"), &[], 4)?; + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + #[cfg(feature = "parallel")] + num_threads: 4, + }; + easy_fuser::mount(fs, Path::new("/mnt/myfs"), &config)?; Ok(()) } ``` diff --git a/build.rs b/build.rs index 8f7bb56..67350c6 100644 --- a/build.rs +++ b/build.rs @@ -47,8 +47,8 @@ fn main() -> std::io::Result<()> { let content = FuseHandlerTemplate { mode }.render()?; fs::write(mode_dir.join("fuse_handler.rs"), content)?; - let content = MoutingTemplate { mode }.render()?; - fs::write(mode_dir.join("mouting.rs"), content)?; + let content = MountingTemplate { mode }.render()?; + fs::write(mode_dir.join("mounting.rs"), content)?; } Ok(()) diff --git a/examples/ftp_fs/Cargo.toml b/examples/ftp_fs/Cargo.toml index 6364e06..93c5f07 100644 --- a/examples/ftp_fs/Cargo.toml +++ b/examples/ftp_fs/Cargo.toml @@ -21,4 +21,7 @@ tempfile = "3.2" libunftp = "0.20" unftp-sbe-fs = "0.2" async-trait = "0.1.68" -tokio = { version = "1.42", features = ["full"] } \ No newline at end of file +tokio = { version = "1.42", features = ["full"] } + +[patch.crates-io] +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/ftp_fs/src/filesystem.rs b/examples/ftp_fs/src/filesystem.rs index eb357fb..ed95198 100644 --- a/examples/ftp_fs/src/filesystem.rs +++ b/examples/ftp_fs/src/filesystem.rs @@ -83,8 +83,8 @@ impl FuseHandler for FtpFs { _file_handle: BorrowedFileHandle, offset: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { self.with_ftp(|ftp| { let mut cursor = ftp.retr_as_buffer(file_id.to_str().unwrap())?; diff --git a/examples/ftp_fs/src/main.rs b/examples/ftp_fs/src/main.rs index 913c14a..d19ec74 100644 --- a/examples/ftp_fs/src/main.rs +++ b/examples/ftp_fs/src/main.rs @@ -20,8 +20,8 @@ use std::path::PathBuf; use std::process::exit; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use clap::{Parser, ValueEnum}; use ctrlc; @@ -139,7 +139,13 @@ fn main() -> Result<(), Box> { println!("Mount point: {:?}", &mount_point); // Mount the filesystem - easy_fuser::mount(ftp_fs, &mount_point, &[], 4)?; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + num_threads: 4, + }; + + easy_fuser::mount(ftp_fs, &mount_point, &config)?; // If we reach here, the filesystem has been unmounted normally cleanup(&mount_point, &once_flag); diff --git a/examples/hello_fs/Cargo.toml b/examples/hello_fs/Cargo.toml index d05ff5c..fb348e9 100644 --- a/examples/hello_fs/Cargo.toml +++ b/examples/hello_fs/Cargo.toml @@ -9,4 +9,7 @@ logging = ["dep:env_logger", "dep:log"] [dependencies] easy_fuser = { path = "../..", features = ["serial"] } log = { version = "0.4", optional = true } -env_logger = { version = "0.11", optional = true } \ No newline at end of file +env_logger = { version = "0.11", optional = true } + +[patch.crates-io] +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/hello_fs/src/main.rs b/examples/hello_fs/src/main.rs index 2e8d31a..a7449ad 100644 --- a/examples/hello_fs/src/main.rs +++ b/examples/hello_fs/src/main.rs @@ -33,7 +33,7 @@ const HELLO_DIR_ATTR: (Inode, FileAttribute) = ( const HELLO_TXT_CONTENT: &str = "Hello World!\n"; const HELLO_TXT_ATTR: (Inode, FileAttribute) = ( - Inode::from(2), + Inode::new(2), FileAttribute { size: 13, blocks: 1, @@ -110,8 +110,8 @@ impl FuseHandler for HelloFS { _file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { if file_id == HELLO_TXT_ATTR.0 { let offset = match seek { @@ -166,8 +166,12 @@ fn main() { .try_init(); let mountpoint = std::env::args().nth(1).expect("Usage: hello "); - let options = vec![MountOption::RO, MountOption::FSName("hello".to_string())]; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + }; + println!("Mounting FTP filesystem..."); - easy_fuser::mount(HelloFS::new(), Path::new(&mountpoint), &options).unwrap(); + easy_fuser::mount(HelloFS::new(), Path::new(&mountpoint), &config).unwrap(); } diff --git a/examples/in_memory_fs/Cargo.toml b/examples/in_memory_fs/Cargo.toml index 8c23f42..ed6b77f 100644 --- a/examples/in_memory_fs/Cargo.toml +++ b/examples/in_memory_fs/Cargo.toml @@ -11,4 +11,7 @@ logging = ["dep:env_logger", "dep:log"] [dependencies] easy_fuser = { path = "../..", features = ["parallel"] } log = { version = "0.4", optional = true } -env_logger = { version = "0.11", optional = true } \ No newline at end of file +env_logger = { version = "0.11", optional = true } + +[patch.crates-io] +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/in_memory_fs/src/filesystem.rs b/examples/in_memory_fs/src/filesystem.rs index 7ebdb03..5b37f37 100644 --- a/examples/in_memory_fs/src/filesystem.rs +++ b/examples/in_memory_fs/src/filesystem.rs @@ -26,7 +26,7 @@ impl InMemoryFS { pub fn new() -> Self { let mut fs = DataBank { inodes: HashMap::new(), - next_inode: Inode::from(2), // Root is 1 + next_inode: Inode::new(2), // Root is 1 }; // Create root directory @@ -155,11 +155,13 @@ impl FuseHandler for InMemoryFS { mode: u32, _umask: u32, _flags: OpenFlags, + _helper: CreateHelper, ) -> Result< ( OwnedFileHandle, (Inode, FileAttribute), FUSEOpenResponseFlags, + Option, ), PosixError, > { @@ -204,6 +206,7 @@ impl FuseHandler for InMemoryFS { unsafe { OwnedFileHandle::from_raw(0) }, (new_inode.clone(), attr), FUSEOpenResponseFlags::empty(), + None, )) } else { Err(ErrorKind::FileNotFound.to_error("")) @@ -215,8 +218,8 @@ impl FuseHandler for InMemoryFS { req: &RequestInfo, file_id: Inode, _file_handle: BorrowedFileHandle, - offset: i64, - length: i64, + offset: u64, + length: u64, mode: FallocateFlags, ) -> FuseResult<()> { self.access(req, file_id.clone(), AccessMask::CAN_WRITE)?; @@ -260,7 +263,7 @@ impl FuseHandler for InMemoryFS { _req: &RequestInfo, _file_id: Inode, _file_handle: BorrowedFileHandle, - _lock_owner: u64, + _lock_owner: LockOwner, ) -> FuseResult<()> { Ok(()) } @@ -363,8 +366,8 @@ impl FuseHandler for InMemoryFS { _fh: BorrowedFileHandle, offset: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { self.access(req, ino.clone(), AccessMask::CAN_READ)?; let fs = self.fs.lock().unwrap(); @@ -579,7 +582,7 @@ impl FuseHandler for InMemoryFS { data: Vec, _write_flags: FUSEWriteFlags, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult { self.access(req, ino.clone(), AccessMask::CAN_WRITE)?; let mut fs = self.fs.lock().unwrap(); diff --git a/examples/in_memory_fs/src/main.rs b/examples/in_memory_fs/src/main.rs index 0724665..2df012f 100644 --- a/examples/in_memory_fs/src/main.rs +++ b/examples/in_memory_fs/src/main.rs @@ -1,8 +1,8 @@ #![doc = include_str!("../README.md")] use easy_fuser::prelude::*; -use std::ffi::OsStr; use std::path::Path; +use std::fs; const README_CONTENT: &[u8] = include_bytes!("../README.md") as &[u8]; @@ -11,38 +11,8 @@ pub use filesystem::InMemoryFS; fn create_memory_fs() -> InMemoryFS { let memoryfs = InMemoryFS::new(); - #[cfg(feature = "readme")] - { - // An example of interacting directly with the filesystem - let request_info = RequestInfo { - id: 0, - uid: 0, - gid: 0, - pid: 0, - }; // dummy RequestInfo - let (fd, (inode, _), _) = memoryfs - .create( - &request_info, - ROOT_INODE, - OsStr::new("README.md"), - 0o755, - 0, - OpenFlags::empty(), - ) - .unwrap(); - let _ = memoryfs - .write( - &request_info, - inode, - fd.borrow(), - SeekFrom::Start(0), - README_CONTENT.to_vec(), - FUSEWriteFlags::empty(), - OpenFlags::empty(), - None, - ) - .unwrap(); - } + // NOTE: manual call example here is removed because the [`CreateHelper`] + // parameter is not supported memoryfs } @@ -58,13 +28,26 @@ fn main() { let mountpoint = std::env::args() .nth(1) .expect("Usage: in_memory_fs "); - let options = vec![ - MountOption::RW, - MountOption::FSName("in_memory_fs".to_string()), - ]; - + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + num_threads: 4, + }; let memoryfs = create_memory_fs(); println!("Mounting filesystem..."); - easy_fuser::mount(memoryfs, Path::new(&mountpoint), &options, 1).unwrap(); + let session = easy_fuser::spawn_mount(memoryfs, Path::new(&mountpoint), &config).unwrap(); + println!("Filesystem mounted"); + fs::write(Path::new(&mountpoint).join("README.md"), README_CONTENT) + .expect("Failed to write README.md"); + + std::io::stdin().read_line(&mut String::new()).unwrap(); + session + .join(&[]) + .map_err(|(_session, error)| { + println!("Error unmounting filesystem: {:?}", error); + error + }) + .unwrap(); + println!("Filesystem unmounted"); } diff --git a/examples/passthrough_fs/Cargo.toml b/examples/passthrough_fs/Cargo.toml index 7dbd525..d89fbf6 100644 --- a/examples/passthrough_fs/Cargo.toml +++ b/examples/passthrough_fs/Cargo.toml @@ -7,4 +7,7 @@ description = "A FUSE passthrough filesystem example using easy_fuser" [dependencies] easy_fuser = { path = "../..", features = ["parallel"] } clap = { version = "4.5", features = ["derive"] } -ctrlc = "3.4" \ No newline at end of file +ctrlc = "3.4" + +[patch.crates-io] +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/passthrough_fs/src/main.rs b/examples/passthrough_fs/src/main.rs index a373b5e..1a433c3 100644 --- a/examples/passthrough_fs/src/main.rs +++ b/examples/passthrough_fs/src/main.rs @@ -3,14 +3,14 @@ use clap::Parser; use ctrlc; use easy_fuser::prelude::*; -use easy_fuser::templates::mirror_fs::*; use easy_fuser::templates::DefaultFuseHandler; +use easy_fuser::templates::mirror_fs::*; use std::path::PathBuf; -use std::process::exit; use std::process::Command; +use std::process::exit; +use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; -use std::sync::Arc; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -75,7 +75,12 @@ fn main() -> Result<(), Box> { println!("Source directory: {:?}", fs.source_dir()); // Mount the filesystem - mount(fs, &mntpoint, &[], 1)?; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + num_threads: 1, + }; + mount(fs, &mntpoint, &config)?; // If we reach here, the filesystem has been unmounted normally cleanup(&mntpoint, &once_flag); diff --git a/examples/random_fs/Cargo.toml b/examples/random_fs/Cargo.toml index 5b3e8d5..da93b65 100644 --- a/examples/random_fs/Cargo.toml +++ b/examples/random_fs/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" [dependencies] easy_fuser = { path = "../..", features = ["parallel"] } rand = "0.8.5" + +[patch.crates-io] +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/random_fs/src/main.rs b/examples/random_fs/src/main.rs index 5c4c8c3..cb28740 100644 --- a/examples/random_fs/src/main.rs +++ b/examples/random_fs/src/main.rs @@ -77,11 +77,13 @@ impl FuseHandler for RandomFS { _mode: u32, _umask: u32, _flags: OpenFlags, + _helper: CreateHelper, ) -> Result< ( OwnedFileHandle, (Inode, FileAttribute), FUSEOpenResponseFlags, + Option, ), PosixError, > { @@ -93,6 +95,7 @@ impl FuseHandler for RandomFS { unsafe { OwnedFileHandle::from_raw(0) }, (ino, attr), FUSEOpenResponseFlags::empty(), + None, )) } @@ -171,8 +174,8 @@ impl FuseHandler for RandomFS { _fh: BorrowedFileHandle, offset: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { let mut rng = rand::thread_rng(); let lines = rng.gen_range(0..81); @@ -238,7 +241,7 @@ impl FuseHandler for RandomFS { data: Vec, _write_flags: FUSEWriteFlags, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult { Ok(data.len() as u32) } @@ -252,13 +255,14 @@ fn main() { let mountpoint = std::env::args() .nth(1) .expect("Usage: random_fs "); - let options = vec![ - MountOption::RW, - MountOption::FSName("random_fs".to_string()), - ]; + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + num_threads: 1, + }; let fs = RandomFS::new(); println!("Mounting filesystem..."); - easy_fuser::mount(fs, Path::new(&mountpoint), &options, 1).unwrap(); + easy_fuser::mount(fs, Path::new(&mountpoint), &config).unwrap(); } diff --git a/examples/zip_fs/Cargo.toml b/examples/zip_fs/Cargo.toml index dbb265f..c165bb3 100644 --- a/examples/zip_fs/Cargo.toml +++ b/examples/zip_fs/Cargo.toml @@ -12,3 +12,6 @@ ctrlc = "3.4" [dev-dependencies] tempfile = "3.2" + +[patch.crates-io] +fuser = { git = "https://github.com/khanhtranngoccva/fuser", branch = "bleeding-edge" } diff --git a/examples/zip_fs/src/filesystem.rs b/examples/zip_fs/src/filesystem.rs index 6cdde27..c1f1d95 100644 --- a/examples/zip_fs/src/filesystem.rs +++ b/examples/zip_fs/src/filesystem.rs @@ -135,8 +135,8 @@ impl FuseHandler for ZipFs { _file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { let InodeInfo { parent: _, diff --git a/examples/zip_fs/src/main.rs b/examples/zip_fs/src/main.rs index cce2aa3..ab92454 100644 --- a/examples/zip_fs/src/main.rs +++ b/examples/zip_fs/src/main.rs @@ -77,9 +77,13 @@ fn main() -> Result<(), Box> { println!("Mounting ZIP filesystem..."); println!("ZIP file: {:?}", &zip_file); println!("Mount point: {:?}", &mount_point); - + + let config = easy_fuser::prelude::MountConfig { + mount_options: vec![], + acl: easy_fuser::prelude::SessionACL::Owner, + }; // Mount the filesystem - easy_fuser::mount(zip_fs, &mount_point, &[])?; + easy_fuser::mount(zip_fs, &mount_point, &config)?; // If we reach here, the filesystem has been unmounted normally cleanup(&mount_point, &once_flag); diff --git a/src/core/helpers.rs b/src/core/helpers.rs index d1c2a3d..53fe77a 100644 --- a/src/core/helpers.rs +++ b/src/core/helpers.rs @@ -28,6 +28,6 @@ pub(super) fn spawn_deadlock_checker() { }); } -pub(super) fn get_random_generation() -> u64 { - Instant::now().elapsed().as_nanos() as u64 +pub(super) fn get_random_generation() -> fuser::Generation { + fuser::Generation(Instant::now().elapsed().as_nanos() as u64) } diff --git a/src/core/inode_mapping.rs b/src/core/inode_mapping.rs index 35bd3bd..59491b1 100644 --- a/src/core/inode_mapping.rs +++ b/src/core/inode_mapping.rs @@ -9,8 +9,13 @@ use std::{ use std::sync::{RwLock, atomic::AtomicU64}; -use crate::{inode_mapper, types::*}; -use crate::{inode_mapper::InodeMapper, inode_multi_mapper::*}; +use fuser::INodeNo; + +use crate::{ + inode_mapper::{self, InodeMapper}, + inode_multi_mapper::{self, InodeMultiMapper}, + types::*, +}; pub(crate) const ROOT_INO: u64 = 1; @@ -62,23 +67,23 @@ pub trait FileIdResolver: Send + Sync + 'static { type ResolvedType: FileIdType; fn new() -> Self; - fn resolve_id(&self, ino: u64) -> Self::ResolvedType; + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType; fn lookup( &self, - parent: u64, + parent: INodeNo, child: &OsStr, id: ::_Id, increment: bool, - ) -> u64; + ) -> INodeNo; fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ::_Id)>, increment: bool, - ) -> Vec<(OsString, u64)>; - fn forget(&self, ino: u64, nlookup: u64); + ) -> Vec<(OsString, INodeNo)>; + fn forget(&self, ino: INodeNo, nlookup: u64); fn prune(&self, keep: &HashSet); - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr); + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr); } pub struct InodeResolver {} @@ -90,32 +95,32 @@ impl FileIdResolver for InodeResolver { Self {} } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { Inode::from(ino) } - fn lookup(&self, _parent: u64, _child: &OsStr, id: Inode, _increment: bool) -> u64 { + fn lookup(&self, _parent: INodeNo, _child: &OsStr, id: Inode, _increment: bool) -> INodeNo { id.into() } // Do nothing, user should provide its own inode fn add_children( &self, - _parent: u64, + _parent: INodeNo, children: Vec<(OsString, Inode)>, _increment: bool, - ) -> Vec<(OsString, u64)> { + ) -> Vec<(OsString, INodeNo)> { children .into_iter() - .map(|(name, inode)| (name, u64::from(inode))) + .map(|(name, inode)| (name, INodeNo::from(inode))) .collect() } - fn forget(&self, _ino: u64, _nlookup: u64) {} + fn forget(&self, _ino: INodeNo, _nlookup: u64) {} fn prune(&self, _keep: &HashSet) {} - fn rename(&self, _parent: u64, _name: &OsStr, _newparent: u64, _newname: &OsStr) {} + fn rename(&self, _parent: INodeNo, _name: &OsStr, _newparent: INodeNo, _newname: &OsStr) {} } pub struct ComponentsResolver { @@ -131,7 +136,7 @@ impl FileIdResolver for ComponentsResolver { } } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { self.mapper .read() .unwrap() @@ -142,7 +147,7 @@ impl FileIdResolver for ComponentsResolver { .collect() } - fn lookup(&self, parent: u64, child: &OsStr, _id: (), increment: bool) -> u64 { + fn lookup(&self, parent: INodeNo, child: &OsStr, _id: (), increment: bool) -> INodeNo { let parent = Inode::from(parent); { // Optimistically assume the child exists @@ -150,11 +155,11 @@ impl FileIdResolver for ComponentsResolver { if increment { lookup_result.data.fetch_add(1, Ordering::SeqCst); } - return u64::from(lookup_result.inode.clone()); + return INodeNo::from(lookup_result.inode.clone()); } } // This scenario happens if the child node does not exist or the backing ID does not match - u64::from( + INodeNo::from( self.mapper .write() .expect("Failed to acquire write lock") @@ -168,18 +173,20 @@ impl FileIdResolver for ComponentsResolver { fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ())>, increment: bool, - ) -> Vec<(OsString, u64)> { - let value_creator = |value_creator: inode_mapper::ValueCreatorParams| { - if let Some(nlookup) = value_creator.existing_data { - let count = nlookup.load(Ordering::Relaxed); - AtomicU64::new(if increment { count + 1 } else { count }) - } else { - AtomicU64::new(if increment { 1 } else { 0 }) - } - }; + ) -> Vec<(OsString, INodeNo)> { + let value_creator = + |value_creator: inode_mapper::ValueCreatorParams| match value_creator + .existing_data + { + Some(nlookup) => { + let count = nlookup.load(Ordering::Relaxed); + AtomicU64::new(if increment { count + 1 } else { count }) + } + None => AtomicU64::new(if increment { 1 } else { 0 }), + }; let children_with_creator: Vec<_> = children .iter() .map(|(name, _)| (name.clone(), value_creator)) @@ -194,11 +201,11 @@ impl FileIdResolver for ComponentsResolver { inserted_children .into_iter() .zip(children) - .map(|(inode, (name, _))| (name, u64::from(inode))) + .map(|(inode, (name, _))| (name, INodeNo::from(inode))) .collect() } - fn forget(&self, ino: u64, nlookup: u64) { + fn forget(&self, ino: INodeNo, nlookup: u64) { let inode = Inode::from(ino); { // Optimistically assume we don't have to remove yet @@ -212,10 +219,13 @@ impl FileIdResolver for ComponentsResolver { } fn prune(&self, keep: &HashSet) { - self.mapper.write().expect("Failed to acquire write lock").prune(keep); + self.mapper + .write() + .expect("Failed to acquire write lock") + .prune(keep); } - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr) { + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr) { let parent_inode = Inode::from(parent); let newparent_inode = Inode::from(newparent); self.mapper @@ -244,7 +254,7 @@ impl FileIdResolver for PathResolver { } } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { self.resolver .resolve_id(ino) .iter() @@ -254,24 +264,24 @@ impl FileIdResolver for PathResolver { fn lookup( &self, - parent: u64, + parent: INodeNo, child: &OsStr, id: ::_Id, increment: bool, - ) -> u64 { + ) -> INodeNo { self.resolver.lookup(parent, child, id, increment) } fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ::_Id)>, increment: bool, - ) -> Vec<(OsString, u64)> { + ) -> Vec<(OsString, INodeNo)> { self.resolver.add_children(parent, children, increment) } - fn forget(&self, ino: u64, nlookup: u64) { + fn forget(&self, ino: INodeNo, nlookup: u64) { self.resolver.forget(ino, nlookup); } @@ -283,7 +293,7 @@ impl FileIdResolver for PathResolver { self.resolver.prune(&resolver_keep); } - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr) { + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr) { self.resolver.rename(parent, name, newparent, newname); } } @@ -306,17 +316,17 @@ where HybridResolver { mapper: instance } } - fn resolve_id(&self, ino: u64) -> Self::ResolvedType { + fn resolve_id(&self, ino: INodeNo) -> Self::ResolvedType { HybridId::new(Inode::from(ino), self.mapper.clone()) } fn lookup( &self, - parent: u64, + parent: INodeNo, child: &OsStr, id: ::_Id, increment: bool, - ) -> u64 { + ) -> INodeNo { let parent = Inode::from(parent); { // Optimistically assume the child exists @@ -331,12 +341,12 @@ where if increment { lookup_result.data.fetch_add(1, Ordering::SeqCst); } - return u64::from(lookup_result.inode.clone()); + return INodeNo::from(lookup_result.inode.clone()); } } } // This scenario happens if the child node does not exist or the backing ID does not match - u64::from( + INodeNo::from( self.mapper .write() .expect("Failed to acquire write lock") @@ -357,18 +367,20 @@ where fn add_children( &self, - parent: u64, + parent: INodeNo, children: Vec<(OsString, ::_Id)>, increment: bool, - ) -> Vec<(OsString, u64)> { - let value_creator = |value_creator: ValueCreatorParams| { - if let Some(nlookup) = value_creator.existing_data { - let count = nlookup.load(Ordering::Relaxed); - AtomicU64::new(if increment { count + 1 } else { count }) - } else { - AtomicU64::new(if increment { 1 } else { 0 }) - } - }; + ) -> Vec<(OsString, INodeNo)> { + let value_creator = + |value_creator: inode_multi_mapper::ValueCreatorParams| match value_creator + .existing_data + { + Some(nlookup) => { + let count = nlookup.load(Ordering::Relaxed); + AtomicU64::new(if increment { count + 1 } else { count }) + } + None => AtomicU64::new(if increment { 1 } else { 0 }), + }; let children_with_creator: Vec<_> = children .iter() .map(|(name, id)| (name.clone(), id.clone(), value_creator)) @@ -383,11 +395,11 @@ where inserted_children .into_iter() .zip(children) - .map(|(inode, (name, _))| (name, u64::from(inode))) + .map(|(inode, (name, _))| (name, INodeNo::from(inode))) .collect() } - fn forget(&self, ino: u64, nlookup: u64) { + fn forget(&self, ino: INodeNo, nlookup: u64) { let inode = Inode::from(ino); { // Optimistically assume we don't have to remove yet @@ -408,7 +420,7 @@ where // TODO } - fn rename(&self, parent: u64, name: &OsStr, newparent: u64, newname: &OsStr) { + fn rename(&self, parent: INodeNo, name: &OsStr, newparent: INodeNo, newname: &OsStr) { let parent_inode = Inode::from(parent); let newparent_inode = Inode::from(newparent); self.mapper @@ -467,11 +479,11 @@ mod tests { // Test prune let keep = HashSet::new(); resolver.prune(&keep); - + // child_ino should be gone now because refcount was 0 (decremented by earlier forget) and we pruned it. // We can verify it's gone by trying to resolve it and expecting panic (as per other test) or just by knowing prune works. // But calling forget again is definitely wrong if it's gone. - + // If we want to test that prune actually removed it, we should check existence. // But since we can't easily check existence without internal access, we rely on the fact that subsequent operations might fail or the other test. } @@ -480,7 +492,7 @@ mod tests { #[should_panic(expected = "Failed to resolve inode")] fn test_components_resolver_prune_panics_on_resolved_deleted() { let resolver = ComponentsResolver::new(); - let parent_ino = ROOT_INO; + let parent_ino = INodeNo(ROOT_INO); let child_ino = resolver.lookup(parent_ino, OsStr::new("child"), (), true); resolver.forget(child_ino, 1); resolver.prune(&HashSet::new()); @@ -553,7 +565,7 @@ mod tests { // Test lookup for non-existent file let non_existent_ino = resolver.lookup(root_ino, OsStr::new("non_existent"), (), false); - assert_ne!(non_existent_ino, 0); + assert_ne!(non_existent_ino, INodeNo(0)); let non_existent_path = resolver.resolve_id(non_existent_ino); assert_eq!(non_existent_path, PathBuf::from("non_existent")); } @@ -624,7 +636,7 @@ mod tests { // Test lookup for non-existent file let non_existent_ino = resolver.lookup(root_ino, OsStr::new("non_existent"), Some(6), false); - assert_ne!(non_existent_ino, 0); + assert_ne!(non_existent_ino, INodeNo(0)); let non_existent_path = resolver.resolve_id(non_existent_ino); assert_eq!( non_existent_path.first_path(), @@ -701,4 +713,4 @@ mod tests { let renamed_file_path = resolver.resolve_id(file_ino); assert_eq!(renamed_file_path, PathBuf::from("file.txt")); } -} +} \ No newline at end of file diff --git a/src/core/thread_mode.rs b/src/core/thread_mode.rs index c66e5ca..2d3812f 100644 --- a/src/core/thread_mode.rs +++ b/src/core/thread_mode.rs @@ -11,7 +11,6 @@ pub(crate) trait SafeBorrowable { #[cfg(feature = "serial")] mod safe_borrowable_impl { use super::*; - use std::cell::{RefCell, RefMut}; impl SafeBorrowable for RefCell { @@ -24,6 +23,30 @@ mod safe_borrowable_impl { self.borrow_mut() } } + + #[cfg(not(feature = "deadlock_detection"))] + impl SafeBorrowable for std::sync::Mutex { + type Guard<'a> + = std::sync::MutexGuard<'a, T> + where + Self: 'a; + + fn safe_borrow_mut(&self) -> Self::Guard<'_> { + self.lock().unwrap() + } + } + + #[cfg(feature = "deadlock_detection")] + impl SafeBorrowable for parking_lot::Mutex { + type Guard<'a> + = parking_lot::MutexGuard<'a, T> + where + Self: 'a; + + fn safe_borrow_mut(&self) -> Self::Guard<'_> { + self.lock() + } + } } #[cfg(all(feature = "parallel", not(feature = "deadlock_detection")))] diff --git a/src/inode_mapper.rs b/src/inode_mapper.rs index f162fbf..de0c644 100644 --- a/src/inode_mapper.rs +++ b/src/inode_mapper.rs @@ -604,7 +604,7 @@ mod tests { let child_name = OsString::from("child"); // Insert the first child - let first_child_inode = Inode::from(2); + let first_child_inode = Inode::new(2); assert_eq!( mapper.insert_child(&root, child_name.clone(), |value_creator_params| { assert!(value_creator_params.existing_data.is_none()); @@ -668,7 +668,7 @@ mod tests { } path.push(OsString::from(format!("file_{}", i))); entries.push((path, move |_: ValueCreatorParams| i)); - expected_inodes.insert(Inode::from(i + 2)); // Start from 2 to avoid conflict with root_inode + expected_inodes.insert(Inode::new(i + 2)); // Start from 2 to avoid conflict with root_inode } // Perform batch insert @@ -679,7 +679,7 @@ mod tests { // Check if all inserted inodes exist for i in 2..=(FILE_COUNT as u64 + 1) { - let inode = Inode::from(i); + let inode = Inode::new(i); assert!(mapper.get(&inode).is_some(), "{:?} should exist", inode); } @@ -739,13 +739,13 @@ mod tests { assert!(root_path.is_empty()); // Try to resolve a non-existent inode - assert!(mapper.resolve(&Inode::from(999)).is_none()); + assert!(mapper.resolve(&Inode::new(999)).is_none()); } #[test] fn test_resolve_invalid_inode() { let mapper = InodeMapper::new(0); - let invalid_inode = Inode::from(999); + let invalid_inode = Inode::new(999); // Attempt to resolve an invalid inode let result = mapper.resolve(&invalid_inode); @@ -942,11 +942,11 @@ mod tests { #[test] fn test_remove_cascading() { let mut mapper = InodeMapper::new(()); - let child1 = Inode::from(2); - let child2 = Inode::from(3); - let grandchild1 = Inode::from(4); - let grandchild2 = Inode::from(5); - let great_grandchild = Inode::from(6); + let child1 = Inode::new(2); + let child2 = Inode::new(3); + let grandchild1 = Inode::new(4); + let grandchild2 = Inode::new(5); + let great_grandchild = Inode::new(6); // Create a deeper nested structure mapper diff --git a/src/inode_multi_mapper.rs b/src/inode_multi_mapper.rs index ba97340..b96d572 100644 --- a/src/inode_multi_mapper.rs +++ b/src/inode_multi_mapper.rs @@ -972,7 +972,7 @@ mod tests { let child_name = OsString::from("child"); // Insert the first child - let first_child_inode = Inode::from(2); + let first_child_inode = Inode::new(2); assert_eq!( mapper.insert_child(&root, child_name.clone(), None, |value_creator_params| { assert!(value_creator_params.existing_data.is_none()); diff --git a/src/lib.rs b/src/lib.rs index 2d5510b..04cdefb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,8 +23,8 @@ mod core; mod fuse_handler; pub mod inode_mapper; -pub mod session; pub mod inode_multi_mapper; +pub mod session; pub mod templates; pub mod types; pub mod unix_fs; @@ -34,16 +34,15 @@ pub use session::{FusePruner, FuseSession}; pub mod prelude { //! Re-exports the necessary types and functions from the `easy_fuser` crate. + pub use super::MountConfig; pub use super::fuse_handler::FuseHandler; pub use super::session::{FusePruner, FuseSession}; pub use super::types::*; pub use super::{mount, spawn_mount}; - - pub use fuser::{BackgroundSession, MountOption, Session, SessionUnmounter}; + pub use fuser::{MountOption, Session, SessionACL, SessionUnmounter, UnmountOption}; } - #[cfg(feature = "serial")] -include!(concat!(env!("OUT_DIR"), "/serial/mouting.rs")); +include!(concat!(env!("OUT_DIR"), "/serial/mounting.rs")); #[cfg(not(feature = "serial"))] -include!(concat!(env!("OUT_DIR"), "/parallel/mouting.rs")); \ No newline at end of file +include!(concat!(env!("OUT_DIR"), "/parallel/mounting.rs")); \ No newline at end of file diff --git a/src/session.rs b/src/session.rs index a04107e..6f59b94 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,8 +1,9 @@ +use crate::core::FileIdResolver; +use crate::types::FileIdType; +use fuser::{BackgroundSession, UnmountOption}; use std::collections::HashSet; +use std::io; use std::sync::Arc; -use fuser::BackgroundSession; -use crate::types::FileIdType; -use crate::core::FileIdResolver; /// A session for a mounted FUSE filesystem running in the background. /// @@ -37,8 +38,22 @@ impl FuseSession { /// Join the background session, waiting for the filesystem to unmount. /// /// This method blocks until the filesystem is unmounted. - pub fn join(self) { - self.session.join() + pub fn join(self) -> Result<(), io::Error> { + let Self { session, .. } = self; + session.join() + } + + /// Unmount the filesystem and join the background session. + /// + /// This method unmounts the filesystem and joins the background session. + /// + /// # Parameters + /// * `flags`: Flags to pass to the unmount operation. + pub fn umount_and_join(self, flags: &[UnmountOption]) -> Result<(), (Option, io::Error)> { + let Self { session, resolver } = self; + session + .umount_and_join(flags) + .map_err(|(session, error)| (session.map(|session| Self { session, resolver }), error)) } } diff --git a/src/templates/default_fuse_handler.rs b/src/templates/default_fuse_handler.rs index a0fc377..1773bb9 100644 --- a/src/templates/default_fuse_handler.rs +++ b/src/templates/default_fuse_handler.rs @@ -152,14 +152,14 @@ impl FuseHandler for DefaultFuseHandler { file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, - flags: u32, // Not implemented yet in standard + flags: CopyFileRangeFlags, // Not implemented yet in standard ) -> FuseResult { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, if cfg!(debug_assertions) { format!( - "copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {})", + "copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {:?})", file_in.display(), file_handle_in, offset_in, @@ -174,7 +174,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {})", + "[Not Implemented] copy_file_range(file_in: {}, file_handle_in: {:?}, offset_in: {}, file_out: {}, file_handle_out: {:?}, offset_out: {}, len: {}, flags: {:?})", file_in.display(), file_handle_in, offset_in, @@ -195,7 +195,13 @@ impl FuseHandler for DefaultFuseHandler { mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> { + _helper: CreateHelper, + ) -> FuseResult<( + OwnedFileHandle, + TId::Metadata, + FUSEOpenResponseFlags, + Option, + )> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -228,8 +234,8 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - offset: i64, - length: i64, + offset: u64, + length: u64, mode: FallocateFlags, ) -> FuseResult<()> { match self.handling { @@ -264,14 +270,14 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, ) -> FuseResult<()> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, if cfg!(debug_assertions) { - format!( - "flush(file_id: {}, file_handle: {:?}, lock_owner: {})", + format!( + "flush(file_id: {}, file_handle: {:?}, lock_owner: {:?})", file_id.display(), file_handle, lock_owner @@ -281,7 +287,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] flush(file_id: {}, file_handle: {:?}, lock_owner: {})", + "[Not Implemented] flush(file_id: {}, file_handle: {:?}, lock_owner: {:?})", file_id.display(), file_handle, lock_owner @@ -363,7 +369,7 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, ) -> FuseResult { match self.handling { @@ -371,7 +377,7 @@ impl FuseHandler for DefaultFuseHandler { kind, if cfg!(debug_assertions) { format!( - "getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", + "getlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?})", file_id.display(), file_handle, lock_owner, @@ -382,7 +388,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] getlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?})", + "[Not Implemented] getlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?})", file_id.display(), file_handle, lock_owner, @@ -426,7 +432,7 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - flags: IOCtlFlags, + flags: FUSEIoctlFlags, cmd: u32, in_data: Vec, out_size: u32, @@ -635,7 +641,12 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { + _helper: OpenHelper, + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( kind, @@ -658,11 +669,17 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, _file_id: TId, _flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { + _helper: OpenHelper, + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { // Safe because in releasedir we don't use it Ok(( unsafe { OwnedFileHandle::from_raw(0) }, FUSEOpenResponseFlags::empty(), + None, )) } @@ -673,8 +690,8 @@ impl FuseHandler for DefaultFuseHandler { file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - flags: FUSEOpenFlags, - lock_owner: Option, + flags: OpenFlags, + lock_owner: Option, ) -> FuseResult> { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( @@ -781,7 +798,7 @@ impl FuseHandler for DefaultFuseHandler { file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, flush: bool, ) -> FuseResult<()> { match self.handling { @@ -933,7 +950,7 @@ impl FuseHandler for DefaultFuseHandler { _req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, sleep: bool, ) -> FuseResult<()> { @@ -942,7 +959,7 @@ impl FuseHandler for DefaultFuseHandler { kind, if cfg!(debug_assertions) { format!( - "setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", + "setlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?}, sleep: {})", file_id.display(), file_handle, lock_owner, @@ -954,7 +971,7 @@ impl FuseHandler for DefaultFuseHandler { }, )), HandlingMethod::Panic => panic!( - "[Not Implemented] setlk(file_id: {}, file_handle: {:?}, lock_owner: {}, lock_info: {:?}, sleep: {})", + "[Not Implemented] setlk(file_id: {}, file_handle: {:?}, lock_owner: {:?}, lock_info: {:?}, sleep: {})", file_id.display(), file_handle, lock_owner, @@ -1063,7 +1080,7 @@ impl FuseHandler for DefaultFuseHandler { data: Vec, write_flags: FUSEWriteFlags, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, ) -> FuseResult { match self.handling { HandlingMethod::Error(kind) => Err(PosixError::new( diff --git a/src/templates/fd_handler_helper.rs b/src/templates/fd_handler_helper.rs index fd238da..8e9aad7 100644 --- a/src/templates/fd_handler_helper.rs +++ b/src/templates/fd_handler_helper.rs @@ -79,7 +79,7 @@ macro_rules! fd_handler_readonly_methods { _req: &RequestInfo, _file_id: TId, file_handle: BorrowedFileHandle, - _lock_owner: u64, + _lock_owner: LockOwner, ) -> FuseResult<()> { unix_fs::flush(file_handle.as_borrowed_fd()) } @@ -111,8 +111,8 @@ macro_rules! fd_handler_readonly_methods { file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - _flags: FUSEOpenFlags, - _lock_owner: Option, + _flags: OpenFlags, + _lock_owner: Option, ) -> FuseResult> { unix_fs::read(file_handle.as_borrowed_fd(), seek, size as usize) } @@ -123,7 +123,7 @@ macro_rules! fd_handler_readonly_methods { _file_id: TId, file_handle: OwnedFileHandle, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, _flush: bool, ) -> FuseResult<()> { unix_fs::release(file_handle.into_owned_fd()) @@ -143,7 +143,7 @@ macro_rules! fd_handler_readwrite_methods { file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, - _flags: u32, + _flags: CopyFileRangeFlags, ) -> FuseResult { unix_fs::copy_file_range( file_handle_in.as_borrowed_fd(), @@ -159,8 +159,8 @@ macro_rules! fd_handler_readwrite_methods { _req: &RequestInfo, _file_id: TId, file_handle: BorrowedFileHandle, - offset: i64, - length: i64, + offset: u64, + length: u64, mode: FallocateFlags, ) -> FuseResult<()> { unix_fs::fallocate(file_handle.as_borrowed_fd(), offset, length, mode) @@ -175,7 +175,7 @@ macro_rules! fd_handler_readwrite_methods { data: Vec, _write_flags: FUSEWriteFlags, _flags: OpenFlags, - _lock_owner: Option, + _lock_owner: Option, ) -> FuseResult { unix_fs::write(file_handle.as_borrowed_fd(), seek, &data).map(|res| res as u32) } diff --git a/src/templates/mirror_fs.rs b/src/templates/mirror_fs.rs index 74a1c02..9bfbb9c 100644 --- a/src/templates/mirror_fs.rs +++ b/src/templates/mirror_fs.rs @@ -132,12 +132,17 @@ macro_rules! mirror_fs_readonly_methods { _req: &RequestInfo, file_id: PathBuf, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { + _helper: OpenHelper, + ) -> FuseResult<( + OwnedFileHandle, + FUSEOpenResponseFlags, + Option, + )> { let file_path = self.source_path.join(file_id); let fd = unix_fs::open(file_path.as_ref(), flags)?; // Open by definition returns positive Fd or error let file_handle = OwnedFileHandle::from_owned_fd(fd).unwrap(); - Ok((file_handle, FUSEOpenResponseFlags::empty())) + Ok((file_handle, FUSEOpenResponseFlags::empty(), None)) } fn readdir( @@ -179,12 +184,18 @@ macro_rules! mirror_fs_readwrite_methods { mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FileAttribute, FUSEOpenResponseFlags)> { + _helper: CreateHelper, + ) -> FuseResult<( + OwnedFileHandle, + FileAttribute, + FUSEOpenResponseFlags, + Option, + )> { let file_path = self.source_path.join(parent_id).join(name); let (fd, file_attr) = unix_fs::create(&file_path, mode, umask, flags)?; // Open by definition returns positive Fd or error let file_handle = OwnedFileHandle::from_owned_fd(fd).unwrap(); - Ok((file_handle, file_attr, FUSEOpenResponseFlags::empty())) + Ok((file_handle, file_attr, FUSEOpenResponseFlags::empty(), None)) } fn mkdir( diff --git a/src/types.rs b/src/types.rs index fb058a5..632bbdc 100644 --- a/src/types.rs +++ b/src/types.rs @@ -22,8 +22,11 @@ pub mod errors; pub mod file_handle; mod file_id_type; pub mod flags; +pub mod helpers; mod inode; - -pub use self::{arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, inode::*}; - -pub use fuser::{FileType as FileKind, KernelConfig, TimeOrNow}; +pub use self::{ + arguments::*, errors::*, file_handle::*, file_id_type::*, flags::*, helpers::*, inode::*, +}; +pub use fuser::{ + FileType as FileKind, Generation, IoctlFlags, KernelConfig, LockOwner, TimeOrNow, +}; diff --git a/src/types/arguments.rs b/src/types/arguments.rs index 0573c40..49cd5b8 100644 --- a/src/types/arguments.rs +++ b/src/types/arguments.rs @@ -17,12 +17,13 @@ use std::time::{Duration, SystemTime}; -use fuser::FileAttr as FuseFileAttr; +use fuser::{FileAttr as FuseFileAttr, Generation, INodeNo, RequestId}; use fuser::{FileType, Request, TimeOrNow}; use libc::mode_t; +use crate::types::LockType; + use super::BorrowedFileHandle; -use super::LockType; pub use std::io::SeekFrom; @@ -159,13 +160,14 @@ impl StatFs { /// - `pid`: Process ID of the process that initiated the request #[derive(Debug, Clone)] pub struct RequestInfo { - pub id: u64, + pub id: RequestId, pub uid: u32, pub gid: u32, pub pid: u32, } -impl<'a> From<&Request<'a>> for RequestInfo { - fn from(req: &Request<'a>) -> Self { + +impl From<&Request> for RequestInfo { + fn from(req: &Request) -> Self { Self { id: req.unique(), uid: req.uid(), @@ -213,12 +215,15 @@ pub struct FileAttribute { /// - Must be non-zero (FUSE treats zero as an error) /// - Should be unique over the file system's lifetime if exported over NFS /// - Should be a new, previously unused number if an inode is reused after deletion - pub generation: Option, + pub generation: Option, } /// `FuseFileAttr`, `Option`, `Option` impl FileAttribute { - pub(crate) fn to_fuse(self, ino: u64) -> (FuseFileAttr, Option, Option) { + pub(crate) fn to_fuse( + self, + ino: INodeNo, + ) -> (FuseFileAttr, Option, Option) { ( FuseFileAttr { ino, diff --git a/src/types/errors.rs b/src/types/errors.rs index 897a870..5b7e531 100644 --- a/src/types/errors.rs +++ b/src/types/errors.rs @@ -16,6 +16,8 @@ //! - [`ErrorKind::to_error`]: Converts an ErrorKind to a PosixError with a custom message. //! +use fuser::Errno; + use crate::unix_fs::get_errno; use std::any::Any; @@ -90,6 +92,12 @@ where } } +impl From for Errno { + fn from(error: PosixError) -> Self { + Errno::from_i32(error.raw_error()) + } +} + impl Debug for PosixError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PosixError") diff --git a/src/types/file_handle.rs b/src/types/file_handle.rs index f4f7c2c..0a461b0 100644 --- a/src/types/file_handle.rs +++ b/src/types/file_handle.rs @@ -58,6 +58,18 @@ impl OwnedFileHandle { Self(handle) } + /// Creates an OwnedFileHandle from a [`fuser::FileHandle`]. + /// + /// Unsafe because it assumes the provided value is a valid, open file handle. + pub unsafe fn from_fuser_file_handle(handle: fuser::FileHandle) -> Self { + Self(handle.0) + } + + /// Converts the OwnedFileHandle into a [`fuser::FileHandle`]. + pub fn as_fuser_file_handle(&self) -> fuser::FileHandle { + fuser::FileHandle(self.0) + } + /// Borrows the file handle, creating a BorrowedFileHandle with a lifetime tied to self. pub fn borrow(&self) -> BorrowedFileHandle<'_> { BorrowedFileHandle(self.0, PhantomData) @@ -115,6 +127,18 @@ impl<'a> BorrowedFileHandle<'a> { Self(handle, PhantomData) } + /// Creates a BorrowedFileHandle from a [`fuser::FileHandle`]. + /// + /// Unsafe because it assumes the provided value is a valid, open file handle. + pub unsafe fn from_fuser_file_handle(handle: fuser::FileHandle) -> Self { + Self(handle.0, PhantomData) + } + + /// Converts the BorrowedFileHandle into a [`fuser::FileHandle`]. + pub fn as_fuser_file_handle(&self) -> fuser::FileHandle { + fuser::FileHandle(self.0) + } + /// Converts the BorrowedFileHandle into a BorrowedFd. /// /// Note: This method performs an unchecked cast from `u64` to `i32`, which may lead to undefined behavior if the file handle value doesn't fit within an `i32`. diff --git a/src/types/flags.rs b/src/types/flags.rs index 2a2b846..9eddc19 100644 --- a/src/types/flags.rs +++ b/src/types/flags.rs @@ -23,6 +23,37 @@ bitflags! { } } +impl From for AccessMask { + fn from(flags: fuser::AccessFlags) -> Self { + AccessMask::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::AccessFlags { + fn from(flags: AccessMask) -> Self { + fuser::AccessFlags::from_bits_retain(flags.bits()) + } +} + +bitflags! { + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct CopyFileRangeFlags: u64 { + const _ = !0; + } +} + +impl From for CopyFileRangeFlags { + fn from(flags: fuser::CopyFileRangeFlags) -> Self { + CopyFileRangeFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::CopyFileRangeFlags { + fn from(flags: CopyFileRangeFlags) -> Self { + fuser::CopyFileRangeFlags::from_bits_retain(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] /// Flags used in fallocate calls. @@ -98,6 +129,18 @@ bitflags! { } } +impl From for FUSEOpenResponseFlags { + fn from(flags: fuser::FopenFlags) -> Self { + FUSEOpenResponseFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::FopenFlags { + fn from(flags: FUSEOpenResponseFlags) -> Self { + fuser::FopenFlags::from_bits_retain(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FUSEIoctlFlags: u32 { @@ -111,6 +154,18 @@ bitflags! { } } +impl From for FUSEIoctlFlags { + fn from(flags: fuser::IoctlFlags) -> Self { + FUSEIoctlFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::IoctlFlags { + fn from(flags: FUSEIoctlFlags) -> Self { + fuser::IoctlFlags::from_bits_retain(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FUSEReadFlags: i32 { @@ -154,11 +209,15 @@ bitflags! { } } -bitflags! { - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub struct IOCtlFlags: u32 { - // Placeholder for future flags - const _ = !0; +impl From for FUSEWriteFlags { + fn from(flags: fuser::WriteFlags) -> Self { + FUSEWriteFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::WriteFlags { + fn from(flags: FUSEWriteFlags) -> Self { + fuser::WriteFlags::from_bits_retain(flags.bits()) } } @@ -219,6 +278,18 @@ bitflags! { } } +impl From for OpenFlags { + fn from(flags: fuser::OpenFlags) -> Self { + OpenFlags::from_bits_retain(flags.0) + } +} + +impl From for fuser::OpenFlags { + fn from(flags: OpenFlags) -> Self { + fuser::OpenFlags(flags.bits()) + } +} + bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] /// Flags used in rename operations. @@ -232,3 +303,15 @@ bitflags! { const _ = !0; } } + +impl From for RenameFlags { + fn from(flags: fuser::RenameFlags) -> Self { + RenameFlags::from_bits_retain(flags.bits()) + } +} + +impl From for fuser::RenameFlags { + fn from(flags: RenameFlags) -> Self { + fuser::RenameFlags::from_bits_retain(flags.bits()) + } +} \ No newline at end of file diff --git a/src/types/helpers.rs b/src/types/helpers.rs new file mode 100644 index 0000000..42ed997 --- /dev/null +++ b/src/types/helpers.rs @@ -0,0 +1,142 @@ +#[cfg(feature = "passthrough")] +use fuser::BackingId; +use fuser::{ReplyCreate, ReplyOpen}; +#[cfg(feature = "passthrough")] +use std::sync::{Arc, Weak}; + +/// Helper object for the [`open`](crate::FuseHandler::open) method. +pub struct OpenHelper<'a> { + #[allow(dead_code)] + reply_open: &'a ReplyOpen, +} + +/// Helper object for the [`create`](crate::FuseHandler::create) method. +pub struct CreateHelper<'a> { + #[allow(dead_code)] + reply_create: &'a ReplyCreate, +} + +impl<'a> CreateHelper<'a> { + /// Initializes a new [`CreateHelper`] object. + pub(crate) fn new(reply_create: &'a ReplyCreate) -> Self { + Self { reply_create } + } + + /// Registers a backing ID for FUSE passthrough operations and returns + /// a [`PassthroughBackingId`], which can be used as the fourth return value + /// of the [`create`](crate::FuseHandler::create) method. + /// + /// Check [`PassthroughBackingId`] for information on correct usage. + #[cfg(feature = "passthrough")] + pub fn open_backing( + &self, + fd: impl std::os::fd::AsFd, + ) -> Result { + self.reply_create + .open_backing(fd) + .map(|id| PassthroughBackingId { + backing_id: Arc::new(id), + }) + } +} + +impl<'a> OpenHelper<'a> { + /// Initializes a new [`OpenHelper`] object. + pub(crate) fn new(reply_open: &'a ReplyOpen) -> Self { + Self { reply_open } + } + + /// Registers a backing ID for FUSE passthrough operations and returns + /// a [`PassthroughBackingId`], which can be used as the third return value + /// of the [`open`](crate::FuseHandler::open) method. + /// + /// Check [`PassthroughBackingId`] for information on correct usage. + #[cfg(feature = "passthrough")] + pub fn open_backing( + &self, + fd: impl std::os::fd::AsFd, + ) -> Result { + self.reply_open + .open_backing(fd) + .map(|id| PassthroughBackingId { + backing_id: Arc::new(id), + }) + } +} + +/// Passthrough backing ID for FUSE passthrough operations. +/// +/// This structure is created by the [`OpenHelper::open_backing`] +/// and [`CreateHelper::open_backing`] methods, and allows the FUSE +/// kernel module to bypass the FUSE daemon to increase performance. +/// +/// This structure is a no-op if the `passthrough` feature is not +/// enabled. +/// +/// In the scope of a single file ID (e.g. [`Inode`](crate::types::inode::Inode), +/// [`PathBuf`](std::path::PathBuf), [`Vec`](std::ffi::OsString) or +/// [`HybridId`](crate::types::file_id_type::HybridId)), +/// - All active file handle objects ([`OwnedFileHandle`](crate::types::file_handle::OwnedFileHandle)) +/// must be opened in the same mode (passthrough or non-passthrough). +/// - If the file handles are opened in passthrough mode, they must share the same +/// [`PassthroughBackingId`] value. Identical [`PassthroughBackingId`] values can be +/// obtained by cloning an existing value. +/// - If either or both of these rules is not met, the FUSE kernel module will return +/// an [`EIO`](libc::EIO) error code that is very difficult to troubleshoot. It is best +/// to return [`EBUSY`](libc::EBUSY) when you detect this situation, which usually happens +/// when trying to open a file twice may result in two completely different file views. +/// +/// Backing IDs can be downgraded to a [`WeakPassthroughBackingId`] value, similar to +/// the underlying [`Arc`] type. This allows the passthrough ID references to be stored in +/// a file ID to passthrough ID hash table (required to fulfill these above two rules) +/// without interfering with the ability of the backing ID to be released when the last +/// file handle to an inode is closed. +#[derive(Debug, Clone)] +pub struct PassthroughBackingId { + #[cfg(feature = "passthrough")] + pub(crate) backing_id: Arc, +} + +#[cfg(feature = "passthrough")] +impl AsRef for PassthroughBackingId { + fn as_ref(&self) -> &BackingId { + self.backing_id.as_ref() + } +} + +impl PassthroughBackingId { + /// Downgrades a [`PassthroughBackingId`] to a weak reference, which allows it + /// to be stored in a long-lived table without preventing the backing ID from being + /// released when the last file handle to an inode is closed. + #[cfg(feature = "passthrough")] + pub fn downgrade(&self) -> WeakPassthroughBackingId { + WeakPassthroughBackingId { + backing_id: Arc::downgrade(&self.backing_id), + } + } +} + +#[derive(Debug, Clone)] +pub struct WeakPassthroughBackingId { + #[cfg(feature = "passthrough")] + pub(crate) backing_id: Weak, +} + +impl WeakPassthroughBackingId { + #[cfg(feature = "passthrough")] + pub fn new() -> Self { + Self { + backing_id: Weak::new(), + } + } + + /// Upgrades a [`WeakPassthroughBackingId`] to a [`PassthroughBackingId`]. + #[cfg(feature = "passthrough")] + pub fn upgrade(&self) -> Option { + self.backing_id + .upgrade() + .map(|backing_id| PassthroughBackingId { + backing_id: backing_id, + }) + } +} diff --git a/src/types/inode.rs b/src/types/inode.rs index b0638a1..b2bdfcf 100644 --- a/src/types/inode.rs +++ b/src/types/inode.rs @@ -1,10 +1,10 @@ //! Inode number in a FUSE (Filesystem in Userspace) filesystem. - use crate::core::ROOT_INO; +use fuser::INodeNo; /// Represents the mountpoint folder in a FuseFilesystem /// Its value is 1 and should not be modified. -pub const ROOT_INODE: Inode = Inode::from(ROOT_INO); +pub const ROOT_INODE: Inode = Inode::new(ROOT_INO); /// Represents an inode number in a FUSE (Filesystem in Userspace) filesystem. /// @@ -32,22 +32,30 @@ pub const ROOT_INODE: Inode = Inode::from(ROOT_INO); /// using `PathBuf` as `FileIdType`. This alternative approach allows for file /// identification based on paths rather than inode numbers. #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[repr(transparent)] pub struct Inode(u64); impl Inode { /// Allow const creation of Inode. - pub const fn from(value: u64) -> Self { + pub const fn new(value: u64) -> Self { Inode(value) } /// Convenience for creating a new Inode pub fn add_one(&self) -> Self { - Inode::from(u64::from(self.clone()) + 1) + Inode::new(u64::from(self.clone()) + 1) + } +} + +impl From for INodeNo { + /// Converts an Inode into a raw [`fuser::INodeNo`]. + fn from(value: Inode) -> Self { + INodeNo(value.0) } } impl From for u64 { - // Converts a u64 into an Inode. + /// Converts a u64 into an Inode. /// /// This allows for easy creation of Inode instances from raw inode numbers. fn from(value: Inode) -> Self { @@ -55,6 +63,13 @@ impl From for u64 { } } +impl From for Inode { + /// Converts a raw [`fuser::INodeNo`] into an Inode. + fn from(value: INodeNo) -> Self { + Inode(value.0) + } +} + impl From for Inode { /// Converts a number into an Inode. /// diff --git a/src/unix_fs.rs b/src/unix_fs.rs index 2d0b8a8..fa1331f 100644 --- a/src/unix_fs.rs +++ b/src/unix_fs.rs @@ -960,10 +960,22 @@ pub fn create( /// and extending for the given length. pub fn fallocate( fd: BorrowedFd, - offset: i64, - length: i64, + offset: u64, + length: u64, mode: FallocateFlags, ) -> Result<(), PosixError> { + let offset: i64 = offset.try_into().map_err(|_| { + PosixError::new( + ErrorKind::InvalidArgument, + "Offset too large for i64".to_string(), + ) + })?; + let length: i64 = length.try_into().map_err(|_| { + PosixError::new( + ErrorKind::InvalidArgument, + "Length too large for i64".to_string(), + ) + })?; let result = unsafe { unix_impl::fallocate(fd.as_raw_fd(), mode.bits(), offset, length) }; if result == -1 { return Err(PosixError::last_error(format!( diff --git a/templates.rs b/templates.rs index 53d95c5..6fb340a 100644 --- a/templates.rs +++ b/templates.rs @@ -13,7 +13,7 @@ pub struct FuseHandlerTemplate<'a> { } #[derive(Template)] -#[template(path = "mouting.rs.j2")] -pub struct MoutingTemplate<'a> { +#[template(path = "mounting.rs.j2")] +pub struct MountingTemplate<'a> { pub mode: &'a str, } diff --git a/templates/fuse_driver.rs.j2 b/templates/fuse_driver.rs.j2 index bc811bb..b969a04 100644 --- a/templates/fuse_driver.rs.j2 +++ b/templates/fuse_driver.rs.j2 @@ -1,5 +1,3 @@ -{% let send_sync = mode != "serial" %} - use std::{ sync::Arc, collections::{HashMap, VecDeque}, @@ -8,13 +6,12 @@ use std::{ time::SystemTime, }; -use libc::c_int; -use log::{error, info, warn}; - +use log::{info, warn}; use fuser::{ self, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, - ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, + ReplyStatfs, ReplyWrite, ReplyXattr, Request, INodeNo, TimeOrNow, FopenFlags, FileHandle, + WriteFlags, BsdFileFlags }; use crate::fuse_handler::FuseHandler; @@ -23,10 +20,13 @@ use crate::core::helpers::*; use crate::core::thread_mode::*; use crate::core::inode_mapping::FileIdResolver; -type DirIter = HashMap<(u64, i64), VecDeque<(OsString, u64, TAttr)>>; +type DirIter = HashMap<(INodeNo, u64), VecDeque<(OsString, INodeNo, TAttr)>>; {% if mode == "serial" %} - use std::cell::RefCell; + #[cfg(feature = "deadlock_detection")] + use parking_lot::Mutex; + #[cfg(not(feature = "deadlock_detection"))] + use std::sync::Mutex; {% elif mode == "parallel" %} use threadpool::ThreadPool; @@ -46,17 +46,11 @@ where TId: FileIdType, THandler: FuseHandler, { + // Use of `Arc` types is mandatory to satisfy `fuser`'s new Send + Sync requirement. pub resolver: Arc, - - {% if !send_sync %} - handler: THandler, - dirmap_iter: RefCell>, - dirmapplus_iter: RefCell>, - {% else %} - handler: Arc, - dirmap_iter: Arc>>, - dirmapplus_iter: Arc>>, - {% endif %} + handler: Arc, + dirmap_iter: Arc>>, + dirmapplus_iter: Arc>>, {% if mode == "parallel" %} pub threadpool: ThreadPool, @@ -86,10 +80,10 @@ where { pub fn new( handler: THandler, - {% if send_sync %} - num_threads: usize - {% else %} + {% if mode == "serial" %} _num_threads: usize + {% else %} + num_threads: usize {% endif %} ) -> FuseDriver { {% if mode == "parallel" %} @@ -99,15 +93,9 @@ where FuseDriver { resolver: Arc::new(TId::create_resolver()), - {% if mode == "serial" %} - handler, - dirmap_iter: RefCell::new(HashMap::new()), - dirmapplus_iter: RefCell::new(HashMap::new()), - {% else %} - handler: Arc::new(handler), - dirmap_iter: Arc::new(Mutex::new(HashMap::new())), - dirmapplus_iter: Arc::new(Mutex::new(HashMap::new())), - {% endif %} + handler: Arc::new(handler), + dirmap_iter: Arc::new(Mutex::new(HashMap::new())), + dirmapplus_iter: Arc::new(Mutex::new(HashMap::new())), {% if mode == "parallel" %} threadpool: ThreadPool::new(num_threads), @@ -118,31 +106,17 @@ where } } - {% if !send_sync %} - pub fn get_handler(&self) -> &THandler { - &self.handler - } - - pub fn get_dirmap_iter(&self) -> &RefCell> { - &self.dirmap_iter - } - - pub fn get_dirmapplus_iter(&self) -> &RefCell> { - &self.dirmapplus_iter - } - {% else %} - pub fn get_handler(&self) -> Arc { - self.handler.clone() - } + pub fn get_handler(&self) -> Arc { + self.handler.clone() + } - pub fn get_dirmap_iter(&self) -> Arc>> { - self.dirmap_iter.clone() - } + pub fn get_dirmap_iter(&self) -> Arc>> { + self.dirmap_iter.clone() + } - pub fn get_dirmapplus_iter(&self) -> Arc>> { - self.dirmapplus_iter.clone() - } - {% endif %} + pub fn get_dirmapplus_iter(&self) -> Arc>> { + self.dirmapplus_iter.clone() + } } @@ -184,7 +158,7 @@ where {% macro reply_error() -%} {% call spawn_reply() %} - reply.error(e.raw_error()); + reply.error(e.into()); {% endcall %} {%- endmacro %} @@ -215,13 +189,13 @@ where {%- endmacro %} - fn init(&mut self, req: &Request, config: &mut KernelConfig) -> Result<(), c_int> { + fn init(&mut self, req: &Request, config: &mut KernelConfig) -> Result<(), std::io::Error> { let req = RequestInfo::from(req); match self.get_handler().init(&req, config) { Ok(()) => Ok(()), Err(e) => { warn!("[{}] init {:?}", e, req); - Err(e.raw_error()) + Err(std::io::Error::from_raw_os_error(e.raw_error())) } } } @@ -230,7 +204,7 @@ where self.get_handler().destroy(); } - fn access(&mut self, req: &Request, ino: u64, mask: i32, reply: ReplyEmpty) { + fn access(&self, req: &Request, ino: INodeNo, mask: fuser::AccessFlags, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -238,7 +212,7 @@ where match handler.access( &req, resolver.resolve_id(ino), - AccessMask::from_bits_retain(mask), + AccessMask::from(mask), ) { Ok(()) => { {% call spawn_reply() %}reply.ok();{% endcall %} } Err(e) => { @@ -250,7 +224,7 @@ where {% endcall %} } - fn bmap(&mut self, req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { + fn bmap(&self, req: &Request, ino: INodeNo, blocksize: u32, idx: u64, reply: ReplyBmap) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -266,16 +240,16 @@ where } fn copy_file_range( - &mut self, + &self, req: &Request, - ino_in: u64, - fh_in: u64, + ino_in: INodeNo, + fh_in: FileHandle, offset_in: i64, - ino_out: u64, - fh_out: u64, + ino_out: INodeNo, + fh_out: FileHandle, offset_out: i64, len: u64, - flags: u32, + flags: fuser::CopyFileRangeFlags, reply: ReplyWrite, ) { let req = RequestInfo::from(req); @@ -285,13 +259,13 @@ where match handler.copy_file_range( &req, resolver.resolve_id(ino_in), - unsafe { BorrowedFileHandle::from_raw(fh_in) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh_in) }, offset_in, resolver.resolve_id(ino_out), - unsafe { BorrowedFileHandle::from_raw(fh_out) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh_out) }, offset_out, len, - flags, + CopyFileRangeFlags::from(flags), ) { Ok(bytes_written) => { {% call spawn_reply() %} reply.written(bytes_written);{% endcall %} } Err(e) => { @@ -303,9 +277,9 @@ where } fn create( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, mode: u32, umask: u32, @@ -317,6 +291,7 @@ where let resolver = self.resolver.clone(); let name = name.to_owned(); {% call spawn() %} + let helper = CreateHelper::new(&reply); match handler.create( &req, resolver.resolve_id(parent), @@ -324,20 +299,40 @@ where mode, umask, OpenFlags::from_bits_retain(flags), + helper, ) { - Ok((file_handle, metadata, response_flags)) => { + Ok((file_handle, metadata, response_flags, passthrough_backing_id)) => { let default_ttl = handler.get_default_ttl(); let (id, file_attr) = TId::extract_metadata(metadata); let ino = resolver.lookup(parent, &name, id, true); let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino); {% call spawn_reply() %} - reply.created( - &ttl.unwrap_or(default_ttl), - &fuse_attr, - generation.unwrap_or(get_random_generation()), - file_handle.as_raw(), - response_flags.bits(), - ); + match passthrough_backing_id { + #[cfg(feature = "passthrough")] + Some(passthrough_backing_id) => { + let response_flags = + response_flags | FUSEOpenResponseFlags::PASSTHROUGH; + reply.created_passthrough( + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + passthrough_backing_id.as_ref(), + ); + }, + _ => { + let response_flags = + response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; + reply.created( + &ttl.unwrap_or(default_ttl), + &fuse_attr, + generation.unwrap_or(get_random_generation()), + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + ); + } + } {% endcall %} } Err(e) => { @@ -349,12 +344,12 @@ where } fn fallocate( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, - length: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, + length: u64, mode: i32, reply: ReplyEmpty, ) { @@ -365,7 +360,7 @@ where match handler.fallocate( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, offset, length, FallocateFlags::from_bits_retain(mode), @@ -379,7 +374,7 @@ where {% endcall %} } - fn flush(&mut self, req: &Request, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { + fn flush(&self, req: &Request, ino: INodeNo, fh: FileHandle, lock_owner: LockOwner, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -387,7 +382,7 @@ where match handler.flush( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, lock_owner, ) { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } @@ -399,7 +394,7 @@ where {% endcall %} } - fn forget(&mut self, req: &Request, ino: u64, nlookup: u64) { + fn forget(&self, req: &Request, ino: INodeNo, nlookup: u64) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -407,7 +402,7 @@ where resolver.forget(ino, nlookup); } - fn fsync(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + fn fsync(&self, req: &Request, ino: INodeNo, fh: FileHandle, datasync: bool, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -415,7 +410,7 @@ where match handler.fsync( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, datasync, ) { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } @@ -427,7 +422,7 @@ where {% endcall %} } - fn fsyncdir(&mut self, req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + fn fsyncdir(&self, req: &Request, ino: INodeNo, fh: FileHandle, datasync: bool, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -435,7 +430,7 @@ where match handler.fsyncdir( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, datasync, ) { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } @@ -447,7 +442,7 @@ where {% endcall %} } - fn getattr(&mut self, req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { + fn getattr(&self, req: &Request, ino: INodeNo, fh: Option, reply: ReplyAttr) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -455,7 +450,7 @@ where match handler.getattr( &req, resolver.resolve_id(ino), - fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }) + fh.map(|fh| unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }) ) { {{ reply_attr() }} Err(e) => { @@ -467,11 +462,11 @@ where } fn getlk( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + lock_owner: LockOwner, start: u64, end: u64, typ: i32, @@ -491,7 +486,7 @@ where match handler.getlk( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, lock_owner, lock_info, ) { @@ -511,7 +506,7 @@ where {% endcall %} } - fn getxattr(&mut self, req: &Request, ino: u64, name: &OsStr, size: u32, reply: ReplyXattr) { + fn getxattr(&self, req: &Request, ino: INodeNo, name: &OsStr, size: u32, reply: ReplyXattr) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -524,7 +519,7 @@ where } else if size >= xattr_data.len() as u32 { reply.data(&xattr_data); } else { - reply.error(ErrorKind::ResultTooLarge.into()); + reply.error(PosixError::new(ErrorKind::ResultTooLarge, "Result too large for specified buffer size").into()); } {% endcall %} } Err(e) => { @@ -536,11 +531,11 @@ where } fn ioctl( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - flags: u32, + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + flags: fuser::IoctlFlags, cmd: u32, in_data: &[u8], out_size: u32, @@ -554,8 +549,8 @@ where match handler.ioctl( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - IOCtlFlags::from_bits_retain(flags), + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, + FUSEIoctlFlags::from(flags), cmd, in_data, out_size, @@ -570,10 +565,10 @@ where } fn link( - &mut self, + &self, req: &Request, - ino: u64, - newparent: u64, + ino: INodeNo, + newparent: INodeNo, newname: &OsStr, reply: ReplyEntry, ) { @@ -596,7 +591,7 @@ where {% endcall %} } - fn listxattr(&mut self, req: &Request, ino: u64, size: u32, reply: ReplyXattr) { + fn listxattr(&self, req: &Request, ino: INodeNo, size: u32, reply: ReplyXattr) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -608,7 +603,7 @@ where } else if size >= xattr_data.len() as u32 { reply.data(&xattr_data); } else { - reply.error(ErrorKind::ResultTooLarge.into()); + reply.error(PosixError::new(ErrorKind::ResultTooLarge, "Result too large for specified buffer size").into()); } {% endcall %} } Err(e) => { @@ -619,7 +614,7 @@ where {% endcall %} } - fn lookup(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEntry) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -637,10 +632,10 @@ where } fn lseek( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, + ino: INodeNo, + fh: FileHandle, offset: i64, whence: i32, reply: ReplyLseek, @@ -652,7 +647,7 @@ where match handler.lseek( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, seek_from_raw(Some(whence), offset), ) { Ok(new_offset) => { {% call spawn_reply() %} reply.offset(new_offset); {% endcall %} } @@ -665,9 +660,9 @@ where } fn mkdir( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, mode: u32, umask: u32, @@ -694,9 +689,9 @@ where } fn mknod( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, mode: u32, umask: u32, @@ -725,18 +720,37 @@ where {% endcall %} } - fn open(&mut self, req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&self, req: &Request, ino: INodeNo, flags: fuser::OpenFlags, reply: ReplyOpen) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); {% call spawn() %} + let helper = OpenHelper::new(&reply); match handler.open( &req, resolver.resolve_id(ino), - OpenFlags::from_bits_retain(_flags), + OpenFlags::from(flags), + helper, ) { - Ok((file_handle, response_flags)) => { {% call spawn_reply() %} - reply.opened(file_handle.as_raw(), response_flags.bits()) + Ok((file_handle, response_flags, passthrough_backing_id)) => { {% call spawn_reply() %} + match passthrough_backing_id { + #[cfg(feature = "passthrough")] + Some(passthrough_backing_id) => { + let response_flags = response_flags | FUSEOpenResponseFlags::PASSTHROUGH; + reply.opened_passthrough( + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + passthrough_backing_id.as_ref() + ); + } + _ => { + let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; + reply.opened( + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + ) + } + } {% endcall %} } Err(e) => { warn!("open: ino {:x?}, [{}], {:?}", ino, e, req); @@ -746,18 +760,37 @@ where {% endcall %} } - fn opendir(&mut self, req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { + fn opendir(&self, req: &Request, ino: INodeNo, flags: fuser::OpenFlags, reply: ReplyOpen) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); {% call spawn() %} + let helper = OpenHelper::new(&reply); match handler.opendir( &req, resolver.resolve_id(ino), - OpenFlags::from_bits_retain(_flags), + OpenFlags::from(flags), + helper, ) { - Ok((file_handle, response_flags)) => { {% call spawn_reply() %} - reply.opened(file_handle.as_raw(), response_flags.bits()) + Ok((file_handle, response_flags, passthrough_backing_id)) => { {% call spawn_reply() %} + match passthrough_backing_id { + #[cfg(feature = "passthrough")] + Some(passthrough_backing_id) => { + let response_flags = response_flags | FUSEOpenResponseFlags::PASSTHROUGH; + reply.opened_passthrough( + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + passthrough_backing_id.as_ref() + ); + } + _ => { + let response_flags = response_flags & !FUSEOpenResponseFlags::PASSTHROUGH; + reply.opened( + file_handle.as_fuser_file_handle(), + FopenFlags::from(response_flags), + ) + } + } {% endcall %} } Err(e) => { warn!("opendir: ino {:x?}, [{}], {:?}", ino, e, req); @@ -768,14 +801,14 @@ where } fn read( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, size: u32, - flags: i32, - lock_owner: Option, + flags: fuser::OpenFlags, + lock_owner: Option, reply: ReplyData, ) { let req = RequestInfo::from(req); @@ -785,10 +818,12 @@ where match handler.read( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, - seek_from_raw(None, offset), + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, + // The offset is always positive, meaning that whence is + // guaranteed to be SEEK_SET + SeekFrom::Start(offset), size, - FUSEOpenFlags::from_bits_retain(flags), + OpenFlags::from(flags), lock_owner, ) { Ok(data_reply) => { {% call spawn_reply() %} reply.data(&data_reply); {% endcall %} } @@ -809,15 +844,6 @@ where self.get_dirmap_iter(){% endif %}; {% call spawn() %} - // Validate offset - if offset < 0 { - error!("readdir called with a negative offset"); - {% call spawn_reply() %} - reply.error(ErrorKind::InvalidArgument.into()); - {% endcall %} - return; - } - // ### Initialize directory iterator let mut dir_iter = match offset { // First read: fetch children from handler @@ -825,7 +851,7 @@ where readdirplus{% else %} readdir{% endif %} (&req_info, resolver.resolve_id(ino), unsafe { - BorrowedFileHandle::from_raw(fh) + BorrowedFileHandle::from_fuser_file_handle(fh) }) { Ok(children) => { // Unpack and process children @@ -919,28 +945,28 @@ where {%- endmacro %} fn readdir( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, mut reply: ReplyDirectory, ) { {{ readdir_impl(false) }} } fn readdirplus( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - offset: i64, + ino: INodeNo, + fh: FileHandle, + offset: u64, mut reply: ReplyDirectoryPlus, ) { {{ readdir_impl(true) }} } - fn readlink(&mut self, req: &Request, ino: u64, reply: ReplyData) { + fn readlink(&self, req: &Request, ino: INodeNo, reply: ReplyData) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -956,12 +982,12 @@ where } fn release( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, - _flags: i32, - _lock_owner: Option, + ino: INodeNo, + fh: FileHandle, + _flags: fuser::OpenFlags, + _lock_owner: Option, _flush: bool, reply: ReplyEmpty, ) { @@ -972,8 +998,8 @@ where match handler.release( &req, resolver.resolve_id(ino), - unsafe { OwnedFileHandle::from_raw(fh) }, - OpenFlags::from_bits_retain(_flags), + unsafe { OwnedFileHandle::from_fuser_file_handle(fh) }, + OpenFlags::from(_flags), _lock_owner, _flush, ) { @@ -986,7 +1012,7 @@ where {% endcall %} } - fn releasedir(&mut self, req: &Request, ino: u64, fh: u64, flags: i32, reply: ReplyEmpty) { + fn releasedir(&self, req: &Request, ino: INodeNo, fh: FileHandle, flags: fuser::OpenFlags, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -994,8 +1020,8 @@ where match handler.releasedir( &req, resolver.resolve_id(ino), - unsafe { OwnedFileHandle::from_raw(fh) }, - OpenFlags::from_bits_retain(flags), + unsafe { OwnedFileHandle::from_fuser_file_handle(fh) }, + OpenFlags::from(flags), ) { Ok(()) => { {% call spawn_reply() %} reply.ok(); {% endcall %} } Err(e) => { @@ -1006,7 +1032,7 @@ where {% endcall %} } - fn removexattr(&mut self, req: &Request, ino: u64, name: &OsStr, reply: ReplyEmpty) { + fn removexattr(&self, req: &Request, ino: INodeNo, name: &OsStr, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -1023,13 +1049,13 @@ where } fn rename( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, name: &OsStr, - newparent: u64, + newparent: INodeNo, newname: &OsStr, - flags: u32, + flags: fuser::RenameFlags, reply: ReplyEmpty, ) { let req = RequestInfo::from(req); @@ -1044,7 +1070,7 @@ where &name, resolver.resolve_id(newparent), &newname, - RenameFlags::from_bits_retain(flags), + RenameFlags::from(flags), ) { Ok(()) => { resolver.rename(parent, &name, newparent, &newname); @@ -1057,7 +1083,7 @@ where {% endcall %} } - fn rmdir(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn rmdir(&self, req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -1074,9 +1100,9 @@ where } fn setattr( - &mut self, + &self, req: &Request, - ino: u64, + ino: INodeNo, mode: Option, uid: Option, gid: Option, @@ -1084,11 +1110,11 @@ where atime: Option, mtime: Option, ctime: Option, - fh: Option, + fh: Option, crtime: Option, chgtime: Option, bkuptime: Option, - _flags: Option, + _flags: Option, reply: ReplyAttr, ) { let req = RequestInfo::from(req); @@ -1106,7 +1132,7 @@ where chgtime: chgtime, bkuptime: bkuptime, flags: None, - file_handle: fh.map(|fh| unsafe { BorrowedFileHandle::from_raw(fh) }), + file_handle: fh.map(|fh| unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }), }; {% call spawn() %} match handler.setattr( @@ -1124,11 +1150,11 @@ where } fn setlk( - &mut self, - req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, + &self, + req: &Request, + ino: INodeNo, + fh: FileHandle, + lock_owner: LockOwner, start: u64, end: u64, typ: i32, @@ -1149,7 +1175,7 @@ where match handler.setlk( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, lock_owner, lock_info, sleep, @@ -1164,9 +1190,9 @@ where } fn setxattr( - &mut self, + &self, req: &Request, - ino: u64, + ino: INodeNo, name: &OsStr, value: &[u8], flags: i32, @@ -1196,7 +1222,7 @@ where {% endcall %} } - fn statfs(&mut self, req: &Request, ino: u64, reply: ReplyStatfs) { + fn statfs(&self, req: &Request, ino: INodeNo, reply: ReplyStatfs) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); @@ -1223,9 +1249,9 @@ where } fn symlink( - &mut self, + &self, req: &Request, - parent: u64, + parent: INodeNo, link_name: &OsStr, target: &Path, reply: ReplyEntry, @@ -1247,15 +1273,15 @@ where } fn write( - &mut self, + &self, req: &Request, - ino: u64, - fh: u64, + ino: INodeNo, + fh: FileHandle, offset: i64, data: &[u8], - write_flags: u32, - flags: i32, - lock_owner: Option, + write_flags: WriteFlags, + flags: fuser::OpenFlags, + lock_owner: Option, reply: ReplyWrite, ) { let req = RequestInfo::from(req); @@ -1266,11 +1292,11 @@ where match handler.write( &req, resolver.resolve_id(ino), - unsafe { BorrowedFileHandle::from_raw(fh) }, + unsafe { BorrowedFileHandle::from_fuser_file_handle(fh) }, seek_from_raw(None, offset), data, - FUSEWriteFlags::from_bits_retain(write_flags), - OpenFlags::from_bits_retain(flags), + FUSEWriteFlags::from(write_flags), + OpenFlags::from(flags), lock_owner, ) { Ok(bytes_written) => { {% call spawn_reply() %} reply.written(bytes_written); {% endcall %} } @@ -1282,7 +1308,7 @@ where {% endcall %} } - fn unlink(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn unlink(&self, req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) { let req = RequestInfo::from(req); let handler = self.get_handler(); let resolver = self.resolver.clone(); diff --git a/templates/fuse_handler.rs.j2 b/templates/fuse_handler.rs.j2 index 6c5c60b..363de9b 100644 --- a/templates/fuse_handler.rs.j2 +++ b/templates/fuse_handler.rs.j2 @@ -88,9 +88,6 @@ /// /// Documentation is inspired by the original fuser documentation -{% let send_sync = mode != "serial" %} - - use std::ffi::{OsStr, OsString}; use std::path::Path; use std::time::Duration; @@ -102,7 +99,7 @@ use async_trait::async_trait; #[async_trait] {% endif %} -pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send {% endif %} { +pub trait FuseHandler: 'static + Sync + Send { {% macro async_keyword() -%}{% if mode == "async" %}async{% endif %}{%- endmacro %} {% macro await_keyword() -%}{% if mode == "async" %}.await{% endif %}{%- endmacro %} @@ -157,7 +154,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_handle_out: BorrowedFileHandle, offset_out: i64, len: u64, - flags: u32, // Not implemented yet in standard + flags: CopyFileRangeFlags, // Not implemented yet in standard ) -> FuseResult { self.get_inner().copy_file_range( req, @@ -186,9 +183,10 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send mode: u32, umask: u32, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> { + helper: CreateHelper, + ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags, Option)> { self.get_inner() - .create(req, parent_id, name, mode, umask, flags) + .create(req, parent_id, name, mode, umask, flags, helper) {{ await_keyword() }} } @@ -198,8 +196,8 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - offset: i64, - length: i64, + offset: u64, + length: u64, mode: FallocateFlags, ) -> FuseResult<()> { self.get_inner() @@ -216,7 +214,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, ) -> FuseResult<()> { self.get_inner() .flush(req, file_id, file_handle, lock_owner) @@ -277,7 +275,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, ) -> FuseResult { self.get_inner() @@ -303,7 +301,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - flags: IOCtlFlags, + flags: FUSEIoctlFlags, cmd: u32, in_data: Vec, out_size: u32, @@ -385,8 +383,9 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().open(req, file_id, flags) + helper: OpenHelper, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + self.get_inner().open(req, file_id, flags, helper) {{ await_keyword() }} } @@ -398,8 +397,9 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, flags: OpenFlags, - ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> { - self.get_inner().opendir(req, file_id, flags) + helper: OpenHelper, + ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags, Option)> { + self.get_inner().opendir(req, file_id, flags, helper) {{ await_keyword() }} } @@ -415,8 +415,8 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_handle: BorrowedFileHandle, seek: SeekFrom, size: u32, - flags: FUSEOpenFlags, - lock_owner: Option, + flags: OpenFlags, + lock_owner: Option, ) -> FuseResult> { self.get_inner() .read(req, file_id, file_handle, seek, size, flags, lock_owner) @@ -478,7 +478,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send file_id: TId, file_handle: OwnedFileHandle, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, flush: bool, ) -> FuseResult<()> { self.get_inner() @@ -552,7 +552,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send req: &RequestInfo, file_id: TId, file_handle: BorrowedFileHandle, - lock_owner: u64, + lock_owner: LockOwner, lock_info: LockInfo, sleep: bool, ) -> FuseResult<()> { @@ -608,7 +608,7 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send data: Vec, write_flags: FUSEWriteFlags, flags: OpenFlags, - lock_owner: Option, + lock_owner: Option, ) -> FuseResult { self.get_inner().write( req, @@ -627,4 +627,4 @@ pub trait FuseHandler: 'static {% if send_sync %}+ Sync + Send self.get_inner().unlink(req, parent_id, name) {{ await_keyword() }} } -} +} \ No newline at end of file diff --git a/templates/mouting.rs.j2 b/templates/mounting.rs.j2 similarity index 77% rename from templates/mouting.rs.j2 rename to templates/mounting.rs.j2 index 7ae1c82..c81d781 100644 --- a/templates/mouting.rs.j2 +++ b/templates/mounting.rs.j2 @@ -4,7 +4,15 @@ use std::path::Path; use core::FuseDriver; use types::*; use fuser::{mount2, spawn_mount2}; -use fuser::MountOption; +use fuser::{Config, MountOption, SessionACL}; + +pub struct MountConfig { + {% if mode == "parallel" %} + pub num_threads: usize, + {% endif %} + pub mount_options: Vec, + pub acl: SessionACL, +} /// Mounts a FUSE filesystem at the specified mountpoint. @@ -32,25 +40,26 @@ use fuser::MountOption; pub fn mount( filesystem: FS, mountpoint: P, - options: &[MountOption], - {% if mode == "parallel" %} - num_threads: usize, - {% endif %} + config: &MountConfig, ) -> io::Result<()> where T: FileIdType, FS: FuseHandler, P: AsRef, { - let driver = FuseDriver::new( - filesystem, + let mut fuser_config = Config::default(); + let num_threads = { {% if mode == "parallel" %} - num_threads, + config.num_threads {% else %} - 1, + 1 {% endif %} - ); - mount2(driver, mountpoint, options) + }; + fuser_config.n_threads = Some(num_threads); + fuser_config.mount_options = config.mount_options.to_vec(); + fuser_config.acl = config.acl.clone(); + let driver = FuseDriver::new(filesystem, num_threads); + mount2(driver, mountpoint, &fuser_config) } /// Spawns a FUSE filesystem in the background at the specified mountpoint. @@ -87,25 +96,26 @@ where pub fn spawn_mount( filesystem: FS, mountpoint: P, - options: &[MountOption], - {% if mode == "parallel" %} - num_threads: usize, - {% endif %} + config: &MountConfig, ) -> io::Result> where T: FileIdType, FS: FuseHandler + Send, P: AsRef, { - let driver = FuseDriver::new( - filesystem, + let mut fuser_config = Config::default(); + let num_threads = { {% if mode == "parallel" %} - num_threads, + config.num_threads {% else %} - 1, + 1 {% endif %} - ); + }; + fuser_config.n_threads = Some(num_threads); + fuser_config.mount_options = config.mount_options.to_vec(); + fuser_config.acl = config.acl.clone(); + let driver = FuseDriver::new(filesystem, num_threads); let resolver = driver.resolver.clone(); - let session = spawn_mount2(driver, mountpoint, options)?; + let session = spawn_mount2(driver, mountpoint, &fuser_config)?; Ok(FuseSession::new(session, resolver)) } \ No newline at end of file diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 75c2455..457416f 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -21,10 +21,13 @@ fn test_mirror_fs_operations() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] - mount(fs, &mntpoint_clone, &[], 4).unwrap(); + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + #[cfg(feature = "parallel")] + num_threads: 4, + }; + mount(fs, &mntpoint_clone, &config).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish diff --git a/tests/mount_mirror_fs.rs b/tests/mount_mirror_fs.rs index 506ee70..df22c98 100644 --- a/tests/mount_mirror_fs.rs +++ b/tests/mount_mirror_fs.rs @@ -35,11 +35,13 @@ fn mount_fs() { // Mount the filesystem println!("Mounting MirrorFs..."); - #[cfg(feature = "serial")] - let mount_result = mount(fs, &mount_dir, &[]); - #[cfg(not(feature = "serial"))] - let mount_result = mount(fs, &mount_dir, &[], 4); - + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + #[cfg(feature = "parallel")] + num_threads: 4, + }; + let mount_result = mount(fs, &mount_dir, &config); match mount_result { Ok(_) => { println!("MirrorFs mounted successfully. Press Ctrl+C to unmount and exit."); diff --git a/tests/offset_test.rs b/tests/offset_test.rs index 4e17539..3f1934a 100644 --- a/tests/offset_test.rs +++ b/tests/offset_test.rs @@ -20,10 +20,13 @@ fn test_mirror_fs_file_offsets() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] - mount(fs, &mntpoint_clone, &[], 4).unwrap(); + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + #[cfg(feature = "parallel")] + num_threads: 4, + }; + mount(fs, &mntpoint_clone, &config).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish diff --git a/tests/test_recursion.rs b/tests/test_recursion.rs index 7e01db1..4eaf343 100644 --- a/tests/test_recursion.rs +++ b/tests/test_recursion.rs @@ -25,10 +25,13 @@ fn test_mirror_fs_recursion() { let mntpoint_clone = mntpoint.clone(); let handle = std::thread::spawn(move || { let fs = MirrorFs::new(source_path.clone(), DefaultFuseHandler::new()); - #[cfg(feature = "serial")] - mount(fs, &mntpoint_clone, &[]).unwrap(); - #[cfg(not(feature = "serial"))] - mount(fs, &mntpoint_clone, &[], 4).unwrap(); + let config = MountConfig { + mount_options: vec![], + acl: SessionACL::Owner, + #[cfg(feature = "parallel")] + num_threads: 4, + }; + mount(fs, &mntpoint_clone, &config).unwrap(); }); std::thread::sleep(Duration::from_millis(50)); // Wait for the mount to finish