diff --git a/README.md b/README.md index 6b4919bcc..f7b2b3b52 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +This is a fork of the excellent [`fuser`](https://github.com/cberner/fuser) Rust crate for FUSE bindings, with some Mountpoint-specific changes to improve performance of concurrent operations. We'll be working to upstream these changes soon. + +--- + # FUSE (Filesystem in Userspace) for Rust ![CI](https://github.com/cberner/fuser/actions/workflows/ci.yml/badge.svg) diff --git a/build.rs b/build.rs index 328ce5ad4..92ffe7fbf 100644 --- a/build.rs +++ b/build.rs @@ -1,18 +1,25 @@ fn main() { + // Register rustc cfg for switching between mount implementations. + // When fuser MSRV is updated to v1.77 or above, we should switch from 'cargo:' to 'cargo::' syntax. + println!("cargo:rustc-check-cfg=cfg(fuser_mount_impl, values(\"pure-rust\", \"libfuse2\", \"libfuse3\"))"); + #[cfg(all(not(feature = "libfuse"), not(target_os = "linux")))] unimplemented!("Building without libfuse is only supported on Linux"); + #[cfg(not(feature = "libfuse"))] + { + println!("cargo:rustc-cfg=fuser_mount_impl=\"pure-rust\""); + } #[cfg(feature = "libfuse")] { - #[cfg(target_os = "macos")] - { + if cfg!(target_os = "macos") { if pkg_config::Config::new() .atleast_version("2.6.0") .probe("fuse") // for macFUSE 4.x .map_err(|e| eprintln!("{}", e)) .is_ok() { - println!("cargo:rustc-cfg=feature=\"libfuse2\""); + println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse2\""); println!("cargo:rustc-cfg=feature=\"macfuse-4-compat\""); } else { pkg_config::Config::new() @@ -20,11 +27,9 @@ fn main() { .probe("osxfuse") // for osxfuse 3.x .map_err(|e| eprintln!("{}", e)) .unwrap(); - println!("cargo:rustc-cfg=feature=\"libfuse2\""); + println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse2\""); } - } - #[cfg(not(target_os = "macos"))] - { + } else { // First try to link with libfuse3 if pkg_config::Config::new() .atleast_version("3.0.0") @@ -32,7 +37,7 @@ fn main() { .map_err(|e| eprintln!("{e}")) .is_ok() { - println!("cargo:rustc-cfg=feature=\"libfuse3\""); + println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse3\""); } else { // Fallback to libfuse pkg_config::Config::new() @@ -40,7 +45,7 @@ fn main() { .probe("fuse") .map_err(|e| eprintln!("{e}")) .unwrap(); - println!("cargo:rustc-cfg=feature=\"libfuse2\""); + println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse2\""); } } } diff --git a/examples/hello.rs b/examples/hello.rs index c41504957..6a69cbbe0 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -50,7 +50,7 @@ const HELLO_TXT_ATTR: FileAttr = FileAttr { struct HelloFS; impl Filesystem for HelloFS { - fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { if parent == 1 && name.to_str() == Some("hello.txt") { reply.entry(&TTL, &HELLO_TXT_ATTR, 0); } else { @@ -58,7 +58,7 @@ impl Filesystem for HelloFS { } } - fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { + fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { match ino { 1 => reply.attr(&TTL, &HELLO_DIR_ATTR), 2 => reply.attr(&TTL, &HELLO_TXT_ATTR), @@ -67,7 +67,7 @@ impl Filesystem for HelloFS { } fn read( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, @@ -85,7 +85,7 @@ impl Filesystem for HelloFS { } fn readdir( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, diff --git a/examples/ioctl.rs b/examples/ioctl.rs index d9c7cf343..d329d2d51 100644 --- a/examples/ioctl.rs +++ b/examples/ioctl.rs @@ -10,12 +10,13 @@ use fuser::{ use libc::{EINVAL, ENOENT}; use log::debug; use std::ffi::OsStr; +use std::sync::Mutex; use std::time::{Duration, UNIX_EPOCH}; const TTL: Duration = Duration::from_secs(1); // 1 second struct FiocFS { - content: Vec, + content: Mutex>, root_attr: FileAttr, fioc_file_attr: FileAttr, } @@ -62,7 +63,7 @@ impl FiocFS { }; Self { - content: vec![], + content: vec![].into(), root_attr, fioc_file_attr, } @@ -70,7 +71,7 @@ impl FiocFS { } impl Filesystem for FiocFS { - fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { if parent == 1 && name.to_str() == Some("fioc") { reply.entry(&TTL, &self.fioc_file_attr, 0); } else { @@ -78,7 +79,7 @@ impl Filesystem for FiocFS { } } - fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { + fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { match ino { 1 => reply.attr(&TTL, &self.root_attr), 2 => reply.attr(&TTL, &self.fioc_file_attr), @@ -87,7 +88,7 @@ impl Filesystem for FiocFS { } fn read( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, @@ -98,14 +99,15 @@ impl Filesystem for FiocFS { reply: ReplyData, ) { if ino == 2 { - reply.data(&self.content[offset as usize..]) + let content = self.content.lock().unwrap(); + reply.data(&content[offset as usize..]) } else { reply.error(ENOENT); } } fn readdir( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, @@ -133,7 +135,7 @@ impl Filesystem for FiocFS { } fn ioctl( - &mut self, + &self, _req: &Request<'_>, ino: u64, _fh: u64, @@ -153,12 +155,12 @@ impl Filesystem for FiocFS { match cmd.into() { FIOC_GET_SIZE => { - let size_bytes = self.content.len().to_ne_bytes(); + let size_bytes = self.content.lock().unwrap().len().to_ne_bytes(); reply.ioctl(0, &size_bytes); } FIOC_SET_SIZE => { let new_size = usize::from_ne_bytes(in_data.try_into().unwrap()); - self.content = vec![0_u8; new_size]; + *self.content.lock().unwrap() = vec![0_u8; new_size]; reply.ioctl(0, &[]); } _ => { diff --git a/examples/notify_inval_entry.rs b/examples/notify_inval_entry.rs index e62ea58b0..9b7770ee0 100644 --- a/examples/notify_inval_entry.rs +++ b/examples/notify_inval_entry.rs @@ -68,7 +68,7 @@ impl<'a> ClockFS<'a> { } impl<'a> Filesystem for ClockFS<'a> { - fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { if parent != FUSE_ROOT_ID || name != AsRef::::as_ref(&self.get_filename()) { reply.error(ENOENT); return; @@ -78,7 +78,7 @@ impl<'a> Filesystem for ClockFS<'a> { reply.entry(&self.timeout, &ClockFS::stat(ClockFS::FILE_INO).unwrap(), 0); } - fn forget(&mut self, _req: &Request, ino: u64, nlookup: u64) { + fn forget(&self, _req: &Request, ino: u64, nlookup: u64) { if ino == ClockFS::FILE_INO { let prev = self.lookup_cnt.fetch_sub(nlookup, SeqCst); assert!(prev >= nlookup); @@ -87,7 +87,7 @@ impl<'a> Filesystem for ClockFS<'a> { } } - fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { + fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { match ClockFS::stat(ino) { Some(a) => reply.attr(&self.timeout, &a), None => reply.error(ENOENT), @@ -95,7 +95,7 @@ impl<'a> Filesystem for ClockFS<'a> { } fn readdir( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, diff --git a/examples/notify_inval_inode.rs b/examples/notify_inval_inode.rs index 84f1418f9..c95284e73 100644 --- a/examples/notify_inval_inode.rs +++ b/examples/notify_inval_inode.rs @@ -67,7 +67,7 @@ impl<'a> ClockFS<'a> { } impl<'a> Filesystem for ClockFS<'a> { - fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { if parent != FUSE_ROOT_ID || name != AsRef::::as_ref(&Self::FILE_NAME) { reply.error(ENOENT); return; @@ -77,7 +77,7 @@ impl<'a> Filesystem for ClockFS<'a> { reply.entry(&Duration::MAX, &self.stat(ClockFS::FILE_INO).unwrap(), 0); } - fn forget(&mut self, _req: &Request, ino: u64, nlookup: u64) { + fn forget(&self, _req: &Request, ino: u64, nlookup: u64) { if ino == ClockFS::FILE_INO { let prev = self.lookup_cnt.fetch_sub(nlookup, SeqCst); assert!(prev >= nlookup); @@ -86,7 +86,7 @@ impl<'a> Filesystem for ClockFS<'a> { } } - fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { + fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { match self.stat(ino) { Some(a) => reply.attr(&Duration::MAX, &a), None => reply.error(ENOENT), @@ -94,7 +94,7 @@ impl<'a> Filesystem for ClockFS<'a> { } fn readdir( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, @@ -120,7 +120,7 @@ impl<'a> Filesystem for ClockFS<'a> { } } - fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { + fn open(&self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { if ino == FUSE_ROOT_ID { reply.error(EISDIR); } else if flags & libc::O_ACCMODE != libc::O_RDONLY { @@ -134,7 +134,7 @@ impl<'a> Filesystem for ClockFS<'a> { } fn read( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, diff --git a/examples/poll.rs b/examples/poll.rs index a86e0879b..6df475668 100644 --- a/examples/poll.rs +++ b/examples/poll.rs @@ -81,7 +81,7 @@ impl FSelFS { } impl fuser::Filesystem for FSelFS { - fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: fuser::ReplyEntry) { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: fuser::ReplyEntry) { if parent != FUSE_ROOT_ID || name.len() != 1 { reply.error(ENOENT); return; @@ -101,7 +101,7 @@ impl fuser::Filesystem for FSelFS { reply.entry(&Duration::ZERO, &self.get_data().filestat(idx), 0); } - fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: fuser::ReplyAttr) { + fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: fuser::ReplyAttr) { if ino == FUSE_ROOT_ID { let a = FileAttr { ino: FUSE_ROOT_ID, @@ -132,7 +132,7 @@ impl fuser::Filesystem for FSelFS { } fn readdir( - &mut self, + &self, _req: &Request, ino: u64, _fh: u64, @@ -169,7 +169,7 @@ impl fuser::Filesystem for FSelFS { reply.ok(); } - fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: fuser::ReplyOpen) { + fn open(&self, _req: &Request, ino: u64, flags: i32, reply: fuser::ReplyOpen) { let idx = FSelData::ino_to_idx(ino); if idx >= NUMFILES { reply.error(ENOENT); @@ -196,7 +196,7 @@ impl fuser::Filesystem for FSelFS { } fn release( - &mut self, + &self, _req: &Request, _ino: u64, fh: u64, @@ -215,7 +215,7 @@ impl fuser::Filesystem for FSelFS { } fn read( - &mut self, + &self, _req: &Request, _ino: u64, fh: u64, @@ -247,7 +247,7 @@ impl fuser::Filesystem for FSelFS { } fn poll( - &mut self, + &self, _req: &Request, _ino: u64, fh: u64, diff --git a/examples/simple.rs b/examples/simple.rs index 12469f389..ca4b008dc 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -478,7 +478,7 @@ impl SimpleFS { impl Filesystem for SimpleFS { fn init( - &mut self, + &self, _req: &Request, #[allow(unused_variables)] config: &mut KernelConfig, ) -> Result<(), c_int> { @@ -511,7 +511,7 @@ impl Filesystem for SimpleFS { Ok(()) } - fn lookup(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { if name.len() > MAX_NAME_LENGTH as usize { reply.error(libc::ENAMETOOLONG); return; @@ -535,9 +535,9 @@ impl Filesystem for SimpleFS { } } - fn forget(&mut self, _req: &Request, _ino: u64, _nlookup: u64) {} + fn forget(&self, _req: &Request, _ino: u64, _nlookup: u64) {} - fn getattr(&mut self, _req: &Request, inode: u64, _fh: Option, reply: ReplyAttr) { + fn getattr(&self, _req: &Request, inode: u64, _fh: Option, reply: ReplyAttr) { match self.get_inode(inode) { Ok(attrs) => reply.attr(&Duration::new(0, 0), &attrs.into()), Err(error_code) => reply.error(error_code), @@ -545,7 +545,7 @@ impl Filesystem for SimpleFS { } fn setattr( - &mut self, + &self, req: &Request, inode: u64, mode: Option, @@ -726,7 +726,7 @@ impl Filesystem for SimpleFS { return; } - fn readlink(&mut self, _req: &Request, inode: u64, reply: ReplyData) { + fn readlink(&self, _req: &Request, inode: u64, reply: ReplyData) { debug!("readlink() called on {:?}", inode); let path = self.content_path(inode); if let Ok(mut file) = File::open(path) { @@ -740,7 +740,7 @@ impl Filesystem for SimpleFS { } fn mknod( - &mut self, + &self, req: &Request, parent: u64, name: &OsStr, @@ -827,7 +827,7 @@ impl Filesystem for SimpleFS { } fn mkdir( - &mut self, + &self, req: &Request, parent: u64, name: &OsStr, @@ -900,7 +900,7 @@ impl Filesystem for SimpleFS { reply.entry(&Duration::new(0, 0), &attrs.into(), 0); } - fn unlink(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn unlink(&self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { debug!("unlink() called with {:?} {:?}", parent, name); let mut attrs = match self.lookup_name(parent, name) { Ok(attrs) => attrs, @@ -957,7 +957,7 @@ impl Filesystem for SimpleFS { reply.ok(); } - fn rmdir(&mut self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn rmdir(&self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { debug!("rmdir() called with {:?} {:?}", parent, name); let mut attrs = match self.lookup_name(parent, name) { Ok(attrs) => attrs, @@ -1019,7 +1019,7 @@ impl Filesystem for SimpleFS { } fn symlink( - &mut self, + &self, req: &Request, parent: u64, link_name: &OsStr, @@ -1089,7 +1089,7 @@ impl Filesystem for SimpleFS { } fn rename( - &mut self, + &self, req: &Request, parent: u64, name: &OsStr, @@ -1296,7 +1296,7 @@ impl Filesystem for SimpleFS { } fn link( - &mut self, + &self, req: &Request, inode: u64, new_parent: u64, @@ -1324,7 +1324,7 @@ impl Filesystem for SimpleFS { } } - fn open(&mut self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { + fn open(&self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { debug!("open() called for {:?}", inode); let (access_mask, read, write) = match flags & libc::O_ACCMODE { libc::O_RDONLY => { @@ -1373,7 +1373,7 @@ impl Filesystem for SimpleFS { } fn read( - &mut self, + &self, _req: &Request, inode: u64, fh: u64, @@ -1408,7 +1408,7 @@ impl Filesystem for SimpleFS { } fn write( - &mut self, + &self, _req: &Request, inode: u64, fh: u64, @@ -1453,7 +1453,7 @@ impl Filesystem for SimpleFS { } fn release( - &mut self, + &self, _req: &Request<'_>, inode: u64, _fh: u64, @@ -1468,7 +1468,7 @@ impl Filesystem for SimpleFS { reply.ok(); } - fn opendir(&mut self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { + fn opendir(&self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { debug!("opendir() called on {:?}", inode); let (access_mask, read, write) = match flags & libc::O_ACCMODE { libc::O_RDONLY => { @@ -1512,7 +1512,7 @@ impl Filesystem for SimpleFS { } fn readdir( - &mut self, + &self, _req: &Request, inode: u64, _fh: u64, @@ -1548,7 +1548,7 @@ impl Filesystem for SimpleFS { } fn releasedir( - &mut self, + &self, _req: &Request<'_>, inode: u64, _fh: u64, @@ -1561,7 +1561,7 @@ impl Filesystem for SimpleFS { reply.ok(); } - fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) { + fn statfs(&self, _req: &Request, _ino: u64, reply: ReplyStatfs) { warn!("statfs() implementation is a stub"); // TODO: real implementation of this reply.statfs( @@ -1577,7 +1577,7 @@ impl Filesystem for SimpleFS { } fn setxattr( - &mut self, + &self, request: &Request<'_>, inode: u64, key: &OsStr, @@ -1602,7 +1602,7 @@ impl Filesystem for SimpleFS { } fn getxattr( - &mut self, + &self, request: &Request<'_>, inode: u64, key: &OsStr, @@ -1634,7 +1634,7 @@ impl Filesystem for SimpleFS { } } - fn listxattr(&mut self, _req: &Request<'_>, inode: u64, size: u32, reply: ReplyXattr) { + fn listxattr(&self, _req: &Request<'_>, inode: u64, size: u32, reply: ReplyXattr) { if let Ok(attrs) = self.get_inode(inode) { let mut bytes = vec![]; // Convert to concatenated null-terminated strings @@ -1654,7 +1654,7 @@ impl Filesystem for SimpleFS { } } - fn removexattr(&mut self, request: &Request<'_>, inode: u64, key: &OsStr, reply: ReplyEmpty) { + fn removexattr(&self, request: &Request<'_>, inode: u64, key: &OsStr, reply: ReplyEmpty) { if let Ok(mut attrs) = self.get_inode(inode) { if let Err(error) = xattr_access_check(key.as_bytes(), libc::W_OK, &attrs, request) { reply.error(error); @@ -1676,7 +1676,7 @@ impl Filesystem for SimpleFS { } } - fn access(&mut self, req: &Request, inode: u64, mask: i32, reply: ReplyEmpty) { + fn access(&self, req: &Request, inode: u64, mask: i32, reply: ReplyEmpty) { debug!("access() called with {:?} {:?}", inode, mask); match self.get_inode(inode) { Ok(attr) => { @@ -1691,7 +1691,7 @@ impl Filesystem for SimpleFS { } fn create( - &mut self, + &self, req: &Request, parent: u64, name: &OsStr, @@ -1785,7 +1785,7 @@ impl Filesystem for SimpleFS { #[cfg(target_os = "linux")] fn fallocate( - &mut self, + &self, _req: &Request<'_>, inode: u64, _fh: u64, @@ -1815,7 +1815,7 @@ impl Filesystem for SimpleFS { } fn copy_file_range( - &mut self, + &self, _req: &Request<'_>, src_inode: u64, src_fh: u64, diff --git a/src/lib.rs b/src/lib.rs index 0fca009d0..09ca12ba7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ use crate::mnt::mount_options::check_option_conflicts; use crate::session::MAX_WRITE_SIZE; #[cfg(feature = "abi-7-16")] pub use ll::fuse_abi::fuse_forget_one; -pub use mnt::mount_options::MountOption; +pub use mnt::{Mount, mount_options::MountOption}; #[cfg(feature = "abi-7-11")] pub use notify::{Notifier, PollHandle}; #[cfg(feature = "abi-7-11")] @@ -295,16 +295,16 @@ pub trait Filesystem { /// Initialize filesystem. /// Called before any other filesystem method. /// The kernel module connection can be configured using the KernelConfig object - fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { + fn init(&self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { Ok(()) } /// Clean up filesystem. /// Called on filesystem exit. - fn destroy(&mut self) {} + fn destroy(&self) {} /// Look up a directory entry by name and get its attributes. - fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { + fn lookup(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { warn!( "[Not Implemented] lookup(parent: {:#x?}, name {:?})", parent, name @@ -319,19 +319,19 @@ pub trait Filesystem { /// each forget. The filesystem may ignore forget calls, if the inodes don't need to /// have a limited lifetime. On unmount it is not guaranteed, that all referenced /// inodes will receive a forget message. - fn forget(&mut self, _req: &Request<'_>, _ino: u64, _nlookup: u64) {} + fn forget(&self, _req: &Request<'_>, _ino: u64, _nlookup: u64) {} /// Like forget, but take multiple forget requests at once for performance. The default /// implementation will fallback to forget. #[cfg(feature = "abi-7-16")] - fn batch_forget(&mut self, req: &Request<'_>, nodes: &[fuse_forget_one]) { + fn batch_forget(&self, req: &Request<'_>, nodes: &[fuse_forget_one]) { for node in nodes { self.forget(req, node.nodeid, node.nlookup); } } /// Get file attributes. - fn getattr(&mut self, _req: &Request<'_>, ino: u64, fh: Option, reply: ReplyAttr) { + fn getattr(&self, _req: &Request<'_>, ino: u64, fh: Option, reply: ReplyAttr) { warn!( "[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", ino, fh @@ -341,7 +341,7 @@ pub trait Filesystem { /// Set file attributes. fn setattr( - &mut self, + &self, _req: &Request<'_>, ino: u64, mode: Option, @@ -367,7 +367,7 @@ pub trait Filesystem { } /// Read symbolic link. - fn readlink(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyData) { + fn readlink(&self, _req: &Request<'_>, ino: u64, reply: ReplyData) { debug!("[Not Implemented] readlink(ino: {:#x?})", ino); reply.error(ENOSYS); } @@ -375,7 +375,7 @@ pub trait Filesystem { /// Create file node. /// Create a regular file, character device, block device, fifo or socket node. fn mknod( - &mut self, + &self, _req: &Request<'_>, parent: u64, name: &OsStr, @@ -394,7 +394,7 @@ pub trait Filesystem { /// Create a directory. fn mkdir( - &mut self, + &self, _req: &Request<'_>, parent: u64, name: &OsStr, @@ -410,7 +410,7 @@ pub trait Filesystem { } /// Remove a file. - fn unlink(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn unlink(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { debug!( "[Not Implemented] unlink(parent: {:#x?}, name: {:?})", parent, name, @@ -419,7 +419,7 @@ pub trait Filesystem { } /// Remove a directory. - fn rmdir(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn rmdir(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { debug!( "[Not Implemented] rmdir(parent: {:#x?}, name: {:?})", parent, name, @@ -429,7 +429,7 @@ pub trait Filesystem { /// Create a symbolic link. fn symlink( - &mut self, + &self, _req: &Request<'_>, parent: u64, link_name: &OsStr, @@ -445,7 +445,7 @@ pub trait Filesystem { /// Rename a file. fn rename( - &mut self, + &self, _req: &Request<'_>, parent: u64, name: &OsStr, @@ -464,7 +464,7 @@ pub trait Filesystem { /// Create a hard link. fn link( - &mut self, + &self, _req: &Request<'_>, ino: u64, newparent: u64, @@ -486,7 +486,7 @@ pub trait Filesystem { /// 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(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { reply.opened(0, 0); } @@ -501,7 +501,7 @@ pub trait Filesystem { /// 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( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -532,7 +532,7 @@ pub trait Filesystem { /// 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( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -567,7 +567,7 @@ pub trait Filesystem { /// is not forced to flush pending writes. One reason to flush data, is if the /// filesystem wants to return write errors. If the filesystem supports file locking /// operations (setlk, getlk) it should remove all locks belonging to 'lock_owner'. - fn flush(&mut self, _req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { + fn flush(&self, _req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { debug!( "[Not Implemented] flush(ino: {:#x?}, fh: {}, lock_owner: {:?})", ino, fh, lock_owner @@ -584,7 +584,7 @@ pub trait Filesystem { /// if the open method didn't set any value. flags will contain the same flags as for /// open. fn release( - &mut self, + &self, _req: &Request<'_>, _ino: u64, _fh: u64, @@ -599,7 +599,7 @@ pub trait Filesystem { /// Synchronize file contents. /// If the datasync parameter is non-zero, then only the user data should be flushed, /// not the meta data. - fn fsync(&mut self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + fn fsync(&self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { debug!( "[Not Implemented] fsync(ino: {:#x?}, fh: {}, datasync: {})", ino, fh, datasync @@ -614,7 +614,7 @@ pub trait Filesystem { /// anything in fh, though that makes it impossible to implement standard conforming /// directory stream operations in case the contents of the directory can change /// between opendir and releasedir. - fn opendir(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + fn opendir(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { reply.opened(0, 0); } @@ -624,7 +624,7 @@ pub trait Filesystem { /// value set by the opendir method, or will be undefined if the opendir method /// didn't set any value. fn readdir( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -644,7 +644,7 @@ pub trait Filesystem { /// value set by the opendir method, or will be undefined if the opendir method /// didn't set any value. fn readdirplus( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -663,7 +663,7 @@ pub trait Filesystem { /// contain the value set by the opendir method, or will be undefined if the /// opendir method didn't set any value. fn releasedir( - &mut self, + &self, _req: &Request<'_>, _ino: u64, _fh: u64, @@ -678,7 +678,7 @@ pub trait Filesystem { /// be flushed, not the meta data. fh will contain the value set by the opendir /// method, or will be undefined if the opendir method didn't set any value. fn fsyncdir( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -693,13 +693,13 @@ pub trait Filesystem { } /// Get file system statistics. - fn statfs(&mut self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) { + fn statfs(&self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) { reply.statfs(0, 0, 0, 0, 0, 512, 255, 0); } /// Set an extended attribute. fn setxattr( - &mut self, + &self, _req: &Request<'_>, ino: u64, name: &OsStr, @@ -720,7 +720,7 @@ pub trait Filesystem { /// If `size` is not 0, and the value fits, send it with `reply.data()`, or /// `reply.error(ERANGE)` if it doesn't. fn getxattr( - &mut self, + &self, _req: &Request<'_>, ino: u64, name: &OsStr, @@ -738,7 +738,7 @@ pub trait Filesystem { /// If `size` is 0, the size of the value should be sent with `reply.size()`. /// If `size` is not 0, and the value fits, send it with `reply.data()`, or /// `reply.error(ERANGE)` if it doesn't. - fn listxattr(&mut self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) { + fn listxattr(&self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) { debug!( "[Not Implemented] listxattr(ino: {:#x?}, size: {})", ino, size @@ -747,7 +747,7 @@ pub trait Filesystem { } /// Remove an extended attribute. - fn removexattr(&mut self, _req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) { + fn removexattr(&self, _req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) { debug!( "[Not Implemented] removexattr(ino: {:#x?}, name: {:?})", ino, name @@ -759,7 +759,7 @@ pub trait Filesystem { /// This will be 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(&mut self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { + fn access(&self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { debug!("[Not Implemented] access(ino: {:#x?}, mask: {})", ino, mask); reply.error(ENOSYS); } @@ -775,7 +775,7 @@ pub trait Filesystem { /// implemented or under Linux kernel versions earlier than 2.6.15, the mknod() /// and open() methods will be called instead. fn create( - &mut self, + &self, _req: &Request<'_>, parent: u64, name: &OsStr, @@ -794,7 +794,7 @@ pub trait Filesystem { /// Test for a POSIX file lock. fn getlk( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -821,7 +821,7 @@ pub trait Filesystem { /// implemented, the kernel will still allow file locking to work locally. /// Hence these are only interesting for network filesystems and similar. fn setlk( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -844,7 +844,7 @@ pub trait Filesystem { /// 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(&mut self, _req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { + fn bmap(&self, _req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { debug!( "[Not Implemented] bmap(ino: {:#x?}, blocksize: {}, idx: {})", ino, blocksize, idx, @@ -854,7 +854,7 @@ pub trait Filesystem { /// control device fn ioctl( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -880,7 +880,7 @@ pub trait Filesystem { /// Poll for events #[cfg(feature = "abi-7-11")] fn poll( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -898,7 +898,7 @@ pub trait Filesystem { /// Preallocate or deallocate space to a file fn fallocate( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -917,7 +917,7 @@ pub trait Filesystem { /// Reposition read/write file offset fn lseek( - &mut self, + &self, _req: &Request<'_>, ino: u64, fh: u64, @@ -934,7 +934,7 @@ pub trait Filesystem { /// Copy the specified range from the source inode to the destination inode fn copy_file_range( - &mut self, + &self, _req: &Request<'_>, ino_in: u64, fh_in: u64, @@ -958,7 +958,7 @@ pub trait Filesystem { /// macOS only: Rename the volume. Set fuse_init_out.flags during init to /// FUSE_VOL_RENAME to enable #[cfg(target_os = "macos")] - fn setvolname(&mut self, _req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) { + fn setvolname(&self, _req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) { debug!("[Not Implemented] setvolname(name: {:?})", name); reply.error(ENOSYS); } @@ -966,7 +966,7 @@ pub trait Filesystem { /// macOS only (undocumented) #[cfg(target_os = "macos")] fn exchange( - &mut self, + &self, _req: &Request<'_>, parent: u64, name: &OsStr, @@ -986,7 +986,7 @@ pub trait Filesystem { /// macOS only: Query extended times (bkuptime and crtime). Set fuse_init_out.flags /// during init to FUSE_XTIMES to enable #[cfg(target_os = "macos")] - fn getxtimes(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyXTimes) { + fn getxtimes(&self, _req: &Request<'_>, ino: u64, reply: ReplyXTimes) { debug!("[Not Implemented] getxtimes(ino: {:#x?})", ino); reply.error(ENOSYS); } @@ -1017,7 +1017,7 @@ pub fn mount2>( options: &[MountOption], ) -> io::Result<()> { check_option_conflicts(options)?; - Session::new(filesystem, mountpoint.as_ref(), options).and_then(|mut se| se.run()) + Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.run()) } /// Mount the given filesystem to the given mountpoint. This function spawns diff --git a/src/ll/request.rs b/src/ll/request.rs index bd4647a9d..4c3274e3d 100644 --- a/src/ll/request.rs +++ b/src/ll/request.rs @@ -2156,11 +2156,15 @@ impl_request!(AnyRequest<'_>); impl<'a> AnyRequest<'a> { pub fn operation(&self) -> Result, RequestError> { // Parse/check opcode - let opcode = fuse_opcode::try_from(self.header.opcode) + let opcode = self.opcode() .map_err(|_: InvalidOpcodeError| RequestError::UnknownOperation(self.header.opcode))?; // Parse/check operation arguments op::parse(self.header, &opcode, self.data).ok_or(RequestError::InsufficientData) } + + pub fn opcode(&self) -> Result { + fuse_opcode::try_from(self.header.opcode) + } } impl<'a> fmt::Display for AnyRequest<'a> { diff --git a/src/mnt/fuse2.rs b/src/mnt/fuse2.rs index 6f3e391e3..8d6c2907d 100644 --- a/src/mnt/fuse2.rs +++ b/src/mnt/fuse2.rs @@ -18,11 +18,17 @@ fn ensure_last_os_error() -> io::Error { } } +/// An active FUSE mount. +/// +/// This struct manages the lifecycle of the mount, unmounting when dropped. #[derive(Debug)] pub struct Mount { mountpoint: CString, } impl Mount { + /// Mounts the filesystem at the given path, with the given options. + /// + /// Returns the mounted FUSE file descriptor along with a [Mount] for handling the mount lifecycle. pub fn new(mountpoint: &Path, options: &[MountOption]) -> io::Result<(Arc, Mount)> { let mountpoint = CString::new(mountpoint.as_os_str().as_bytes()).unwrap(); with_fuse_args(options, |args| { diff --git a/src/mnt/fuse2_sys.rs b/src/mnt/fuse2_sys.rs index d99597c9d..17368a0ef 100644 --- a/src/mnt/fuse2_sys.rs +++ b/src/mnt/fuse2_sys.rs @@ -16,7 +16,7 @@ pub struct fuse_args { pub allocated: c_int, } -#[cfg(feature = "libfuse2")] +#[cfg(fuser_mount_impl = "libfuse2")] extern "C" { // *_compat25 functions were introduced in FUSE 2.6 when function signatures changed. // Therefore, the minimum version requirement for *_compat25 functions is libfuse-2.6.0. diff --git a/src/mnt/fuse3.rs b/src/mnt/fuse3.rs index b7942b54a..c1ef1f4e8 100644 --- a/src/mnt/fuse3.rs +++ b/src/mnt/fuse3.rs @@ -22,11 +22,17 @@ fn ensure_last_os_error() -> io::Error { } } +/// An active FUSE mount. +/// +/// This struct manages the lifecycle of the mount, unmounting and destroying the session when dropped. #[derive(Debug)] pub struct Mount { fuse_session: *mut c_void, } impl Mount { + /// Mounts the filesystem at the given path, with the given options. + /// + /// Returns the mounted FUSE file descriptor along with a [Mount] for handling the mount lifecycle. pub fn new(mnt: &Path, options: &[MountOption]) -> io::Result<(Arc, Mount)> { let mnt = CString::new(mnt.as_os_str().as_bytes()).unwrap(); with_fuse_args(options, |args| { diff --git a/src/mnt/mod.rs b/src/mnt/mod.rs index 79d283cca..03dd3f085 100644 --- a/src/mnt/mod.rs +++ b/src/mnt/mod.rs @@ -2,24 +2,24 @@ //! //! Raw communication channel to the FUSE kernel driver. -#[cfg(feature = "libfuse2")] +#[cfg(fuser_mount_impl = "libfuse2")] mod fuse2; #[cfg(any(feature = "libfuse", test))] mod fuse2_sys; -#[cfg(feature = "libfuse3")] +#[cfg(fuser_mount_impl = "libfuse3")] mod fuse3; -#[cfg(feature = "libfuse3")] +#[cfg(fuser_mount_impl = "libfuse3")] mod fuse3_sys; -#[cfg(not(feature = "libfuse"))] +#[cfg(fuser_mount_impl = "pure-rust")] mod fuse_pure; pub mod mount_options; -#[cfg(any(feature = "libfuse", test))] +#[cfg(any(test, feature = "libfuse"))] use fuse2_sys::fuse_args; #[cfg(any(test, not(feature = "libfuse")))] use std::fs::File; -#[cfg(any(test, not(feature = "libfuse"), not(feature = "libfuse3")))] +#[cfg(any(test, fuser_mount_impl = "pure-rust", fuser_mount_impl = "libfuse2"))] use std::io; #[cfg(any(feature = "libfuse", test))] @@ -47,16 +47,16 @@ fn with_fuse_args T>(options: &[MountOption], f: F) }) } -#[cfg(feature = "libfuse2")] +#[cfg(fuser_mount_impl = "libfuse2")] pub use fuse2::Mount; -#[cfg(feature = "libfuse3")] +#[cfg(fuser_mount_impl = "libfuse3")] pub use fuse3::Mount; -#[cfg(not(feature = "libfuse"))] +#[cfg(fuser_mount_impl = "pure-rust")] pub use fuse_pure::Mount; -#[cfg(not(feature = "libfuse3"))] +#[cfg(not(fuser_mount_impl = "libfuse3"))] use std::ffi::CStr; -#[cfg(not(feature = "libfuse3"))] +#[cfg(not(fuser_mount_impl = "libfuse3"))] #[inline] fn libc_umount(mnt: &CStr) -> io::Result<()> { #[cfg(any( @@ -87,7 +87,7 @@ fn libc_umount(mnt: &CStr) -> io::Result<()> { /// Warning: This will return true if the filesystem has been detached (lazy unmounted), but not /// yet destroyed by the kernel. -#[cfg(any(test, not(feature = "libfuse")))] +#[cfg(any(test, fuser_mount_impl = "pure-rust"))] fn is_mounted(fuse_device: &File) -> bool { use libc::{poll, pollfd}; use std::os::unix::prelude::AsRawFd; diff --git a/src/request.rs b/src/request.rs index c2de185ac..42a646589 100644 --- a/src/request.rs +++ b/src/request.rs @@ -11,6 +11,7 @@ use std::convert::TryFrom; #[cfg(feature = "abi-7-28")] use std::convert::TryInto; use std::path::Path; +use std::sync::atomic::Ordering; use crate::channel::ChannelSender; use crate::ll::Request as _; @@ -52,7 +53,7 @@ impl<'a> Request<'a> { /// Dispatch request to the given filesystem. /// This calls the appropriate filesystem operation method for the /// request and sends back the returned reply to the kernel - pub(crate) fn dispatch(&self, se: &mut Session) { + pub(crate) fn dispatch(&self, se: &Session) { debug!("{}", self.request); let unique = self.request.unique(); @@ -70,7 +71,7 @@ impl<'a> Request<'a> { fn dispatch_req( &self, - se: &mut Session, + se: &Session, ) -> Result>, Errno> { let op = self.request.operation().map_err(|_| Errno::ENOSYS)?; // Implement allow_root & access check for auto_unmount @@ -150,8 +151,8 @@ impl<'a> Request<'a> { return Err(Errno::EPROTO); } // Remember ABI version supported by kernel - se.proto_major = v.major(); - se.proto_minor = v.minor(); + se.proto_major.store(v.major(), Ordering::SeqCst); + se.proto_minor.store(v.minor(), Ordering::SeqCst); let mut config = KernelConfig::new(x.capabilities(), x.max_readahead()); // Call filesystem init method and give it a chance to return an error @@ -170,22 +171,23 @@ impl<'a> Request<'a> { config.max_readahead, config.max_write ); - se.initialized = true; + se.initialized.store(true, Ordering::SeqCst); return Ok(Some(x.reply(&config))); } // Any operation is invalid before initialization - _ if !se.initialized => { + _ if !se.initialized.load(Ordering::SeqCst) => { warn!("Ignoring FUSE operation before init: {}", self.request); return Err(Errno::EIO); } // Filesystem destroyed ll::Operation::Destroy(x) => { - se.filesystem.destroy(); - se.destroyed = true; + if !se.destroyed.swap(true, Ordering::SeqCst) { + se.filesystem.destroy(); + } return Ok(Some(x.reply())); } // Any operation is invalid after destroy - _ if se.destroyed => { + _ if se.destroyed.load(Ordering::SeqCst) => { warn!("Ignoring FUSE operation after destroy: {}", self.request); return Err(Errno::EIO); } @@ -670,4 +672,14 @@ impl<'a> Request<'a> { pub fn pid(&self) -> u32 { self.request.pid() } + + /// Returns whether this is a forget request + pub fn is_forget(&self) -> bool { + match self.request.opcode() { + Ok(abi::fuse_opcode::FUSE_FORGET) => true, + #[cfg(feature = "abi-7-16")] + Ok(abi::fuse_opcode::FUSE_BATCH_FORGET) => true, + _ => false, + } + } } diff --git a/src/session.rs b/src/session.rs index 956befdc9..86daed561 100644 --- a/src/session.rs +++ b/src/session.rs @@ -11,6 +11,7 @@ use nix::unistd::geteuid; use std::fmt; use std::os::fd::{AsFd, BorrowedFd, OwnedFd}; use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicU32, AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; use std::{io, ops::DerefMut}; @@ -59,13 +60,13 @@ pub struct Session { /// User that launched the fuser process pub(crate) session_owner: u32, /// FUSE protocol major version - pub(crate) proto_major: u32, + pub(crate) proto_major: AtomicU32, /// FUSE protocol minor version - pub(crate) proto_minor: u32, + pub(crate) proto_minor: AtomicU32, /// True if the filesystem is initialized (init operation done) - pub(crate) initialized: bool, + pub(crate) initialized: AtomicBool, /// True if the filesystem was destroyed (destroy operation done) - pub(crate) destroyed: bool, + pub(crate) destroyed: AtomicBool, } impl AsFd for Session { @@ -113,10 +114,10 @@ impl Session { mount: Arc::new(Mutex::new(Some((mountpoint.to_owned(), mount)))), allowed, session_owner: geteuid().as_raw(), - proto_major: 0, - proto_minor: 0, - initialized: false, - destroyed: false, + proto_major: AtomicU32::new(0), + proto_minor: AtomicU32::new(0), + initialized: AtomicBool::new(false), + destroyed: AtomicBool::new(false), }) } @@ -130,18 +131,28 @@ impl Session { mount: Arc::new(Mutex::new(None)), allowed: acl, session_owner: geteuid().as_raw(), - proto_major: 0, - proto_minor: 0, - initialized: false, - destroyed: false, + proto_major: AtomicU32::new(0), + proto_minor: AtomicU32::new(0), + initialized: AtomicBool::new(false), + destroyed: AtomicBool::new(false), } } /// Run the session loop that receives kernel requests and dispatches them to method - /// calls into the filesystem. This read-dispatch-loop is non-concurrent to prevent - /// having multiple buffers (which take up much memory), but the filesystem methods - /// may run concurrent by spawning threads. - pub fn run(&mut self) -> io::Result<()> { + /// calls into the filesystem. + pub fn run(&self) -> io::Result<()> { + self.run_with_callbacks(|_| {}, |_| {}) + } + + /// Run the session loop that receives kernel requests and dispatches them to method + /// calls into the filesystem. + /// This version also notifies callers of kernel requests before and after they + /// are dispatched to the filesystem. + pub fn run_with_callbacks(&self, mut before_dispatch: FB, mut after_dispatch: FA) -> io::Result<()> + where + FB: FnMut(&Request<'_>), + FA: FnMut(&Request<'_>), + { // Buffer for receiving requests from the kernel. Only one is allocated and // it is reused immediately after dispatching to conserve memory and allocations. let mut buffer = vec![0; BUFFER_SIZE]; @@ -155,7 +166,11 @@ impl Session { match self.ch.receive(buf) { Ok(size) => match Request::new(self.ch.sender(), &buf[..size]) { // Dispatch request - Some(req) => req.dispatch(self), + Some(req) => { + before_dispatch(&req); + req.dispatch(self); + after_dispatch(&req); + }, // Quit loop on illegal request None => break, }, @@ -227,9 +242,8 @@ impl Session { impl Drop for Session { fn drop(&mut self) { - if !self.destroyed { + if !self.destroyed.swap(true, Ordering::SeqCst) { self.filesystem.destroy(); - self.destroyed = true; } if let Some((mountpoint, _mount)) = std::mem::take(&mut *self.mount.lock().unwrap()) { @@ -259,7 +273,6 @@ impl BackgroundSession { // Take the fuse_session, so that we can unmount it let mount = std::mem::take(&mut *se.mount.lock().unwrap()).map(|(_, mount)| mount); let guard = thread::spawn(move || { - let mut se = se; se.run() }); Ok(BackgroundSession {