diff --git a/vendor/fuser/CHANGELOG.md b/vendor/fuser/CHANGELOG.md index 38fddbfcc..63c0306a0 100644 --- a/vendor/fuser/CHANGELOG.md +++ b/vendor/fuser/CHANGELOG.md @@ -1,6 +1,10 @@ # FUSE for Rust - Changelog -## 0.12.0 - UNRELEASED +## 0.12.0 - 2022-12-13 +* Add method to `Session` to unmount non-`Send` `Filesystem`s + +## 0.11.1 - 2022-08-24 +* Improve an error message when using libfuse2 ## 0.11.0 - 2022-03-05 * Add `spawn_mount2()` diff --git a/vendor/fuser/Cargo.toml b/vendor/fuser/Cargo.toml index a3d9d5d0e..d7e1c5d91 100644 --- a/vendor/fuser/Cargo.toml +++ b/vendor/fuser/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fuser" -edition = "2021" -version = "0.11.0" +edition = "2018" +version = "0.12.0" authors = ["Christopher Berner "] description = "Filesystem in Userspace (FUSE) for Rust" documentation = "https://docs.rs/fuser" @@ -17,13 +17,10 @@ license = "MIT" travis-ci = { repository = "cberner/fuser" } [dependencies] -async-trait = "0.1.56" -crossbeam = "*" libc = "0.2.51" log = "0.4.6" memchr = "2" users = "0.11.0" -futures = "0.3.23" page_size = "0.4.2" serde = { version = "1.0.102", features = ["std", "derive"], optional = true } smallvec = "1.6.1" @@ -66,6 +63,3 @@ abi-7-28 = ["abi-7-27"] abi-7-29 = ["abi-7-28"] abi-7-30 = ["abi-7-29"] abi-7-31 = ["abi-7-30"] - -[lib] -bench = false diff --git a/vendor/fuser/README.md b/vendor/fuser/README.md index 335d1b8ed..9ad58d100 100644 --- a/vendor/fuser/README.md +++ b/vendor/fuser/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/vendor/fuser/deny.toml b/vendor/fuser/deny.toml index e5839cb7c..bf18fba3e 100644 --- a/vendor/fuser/deny.toml +++ b/vendor/fuser/deny.toml @@ -73,6 +73,7 @@ allow = [ "BSD-2-Clause", "BSD-3-Clause", "MIT", + "Unicode-DFS-2016", ] # List of explictly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses diff --git a/vendor/fuser/examples/hello.rs b/vendor/fuser/examples/hello.rs index ffcc89d87..8282c2e57 100644 --- a/vendor/fuser/examples/hello.rs +++ b/vendor/fuser/examples/hello.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use clap::{crate_version, Arg, Command}; use fuser::{ FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, @@ -50,9 +49,8 @@ const HELLO_TXT_ATTR: FileAttr = FileAttr { struct HelloFS; -#[async_trait] impl Filesystem for HelloFS { - async fn lookup(&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 { @@ -60,7 +58,7 @@ impl Filesystem for HelloFS { } } - async fn getattr(&self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { + fn getattr(&self, _req: &Request, ino: u64, reply: ReplyAttr) { match ino { 1 => reply.attr(&TTL, &HELLO_DIR_ATTR), 2 => reply.attr(&TTL, &HELLO_TXT_ATTR), @@ -68,9 +66,9 @@ impl Filesystem for HelloFS { } } - async fn read( + fn read( &self, - _req: &Request<'_>, + _req: &Request, ino: u64, _fh: u64, offset: i64, @@ -86,7 +84,14 @@ impl Filesystem for HelloFS { } } - async fn readdir(&self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory) { + fn readdir( + &self, + _req: &Request, + ino: u64, + _fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { if ino != 1 { reply.error(ENOENT); return; diff --git a/vendor/fuser/examples/simple.rs.TODO b/vendor/fuser/examples/simple.rs similarity index 99% rename from vendor/fuser/examples/simple.rs.TODO rename to vendor/fuser/examples/simple.rs index b6b6631b1..226c4fbee 100644 --- a/vendor/fuser/examples/simple.rs.TODO +++ b/vendor/fuser/examples/simple.rs @@ -1017,7 +1017,14 @@ impl Filesystem for SimpleFS { reply.ok(); } - fn symlink(&self, req: &Request, parent: u64, name: &OsStr, link: &Path, reply: ReplyEntry) { + fn symlink( + &self, + req: &Request, + parent: u64, + name: &OsStr, + link: &Path, + reply: ReplyEntry, + ) { debug!("symlink() called with {:?} {:?} {:?}", parent, name, link); let mut parent_attrs = match self.get_inode(parent) { Ok(attrs) => attrs, @@ -1076,7 +1083,6 @@ impl Filesystem for SimpleFS { reply.entry(&Duration::new(0, 0), &attrs.into(), 0); } - #[allow(unused_variables)] fn rename( &self, req: &Request, @@ -1536,7 +1542,14 @@ impl Filesystem for SimpleFS { reply.ok(); } - fn releasedir(&self, _req: &Request<'_>, inode: u64, _fh: u64, _flags: i32, reply: ReplyEmpty) { + fn releasedir( + &self, + _req: &Request<'_>, + inode: u64, + _fh: u64, + _flags: i32, + reply: ReplyEmpty, + ) { if let Ok(mut attrs) = self.get_inode(inode) { attrs.open_file_handles -= 1; } @@ -1903,7 +1916,6 @@ fn as_file_kind(mut mode: u32) -> FileKind { } } -#[allow(unused_variables)] fn get_groups(pid: u32) -> Vec { #[cfg(not(target_os = "macos"))] { diff --git a/vendor/fuser/osx_mount_tests.sh b/vendor/fuser/osx_mount_tests.sh index 2c78d8788..f03465e10 100755 --- a/vendor/fuser/osx_mount_tests.sh +++ b/vendor/fuser/osx_mount_tests.sh @@ -38,6 +38,8 @@ function run_test { } run_test --features=libfuse 'with libfuse' -run_test --features=libfuse 'with libfuse' --auto_unmount + +# TODO: re-enable this test. It seems to hang on OSX +#run_test --features=libfuse 'with libfuse' --auto_unmount export TEST_EXIT_STATUS=0 diff --git a/vendor/fuser/src/async_runtime.rs b/vendor/fuser/src/async_runtime.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/fuser/src/channel.rs b/vendor/fuser/src/channel.rs index 4923f1fc5..add5c831b 100644 --- a/vendor/fuser/src/channel.rs +++ b/vendor/fuser/src/channel.rs @@ -1,4 +1,3 @@ -use std::mem::MaybeUninit; use std::{fs::File, io, os::unix::prelude::AsRawFd, sync::Arc}; use libc::{c_int, c_void, size_t}; @@ -18,7 +17,7 @@ impl Channel { } /// Receives data up to the capacity of the given buffer (can block). - pub fn receive(&self, buffer: &mut [MaybeUninit]) -> io::Result { + pub fn receive(&self, buffer: &mut [u8]) -> io::Result { let rc = unsafe { libc::read( self.0.as_raw_fd(), diff --git a/vendor/fuser/src/lib.rs b/vendor/fuser/src/lib.rs index 9405ad0fa..15207252b 100644 --- a/vendor/fuser/src/lib.rs +++ b/vendor/fuser/src/lib.rs @@ -6,7 +6,6 @@ #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] -use async_trait::async_trait; use libc::{c_int, ENOSYS, EPERM}; use log::{debug, warn}; use mnt::mount_options::parse_options_from_args; @@ -37,13 +36,12 @@ pub use reply::{ ReplyStatfs, ReplyWrite, }; pub use request::Request; -pub use session::{BackgroundSession, Session}; +pub use session::{BackgroundSession, Session, SessionUnmounter}; #[cfg(feature = "abi-7-28")] use std::cmp::max; #[cfg(feature = "abi-7-13")] use std::cmp::min; -mod async_runtime; mod channel; mod ll; mod mnt; @@ -287,12 +285,11 @@ impl KernelConfig { /// implementations are provided here to get a mountable filesystem that does /// nothing. #[allow(clippy::too_many_arguments)] -#[async_trait] pub trait Filesystem { /// Initialize filesystem. /// Called before any other filesystem method. /// The kernel module connection can be configured using the KernelConfig object - async fn init(&self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { + fn init(&self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { Ok(()) } @@ -301,7 +298,7 @@ pub trait Filesystem { fn destroy(&self) {} /// Look up a directory entry by name and get its attributes. - async fn lookup(&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 @@ -316,25 +313,25 @@ 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. - async fn forget(&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")] - async fn batch_forget(&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).await; + self.forget(req, node.nodeid, node.nlookup); } } /// Get file attributes. - async fn getattr(&self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { + fn getattr(&self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { warn!("[Not Implemented] getattr(ino: {:#x?})", ino); reply.error(ENOSYS); } /// Set file attributes. - async fn setattr( + fn setattr( &self, _req: &Request<'_>, ino: u64, @@ -361,14 +358,14 @@ pub trait Filesystem { } /// Read symbolic link. - async fn readlink(&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); } /// Create file node. /// Create a regular file, character device, block device, fifo or socket node. - async fn mknod( + fn mknod( &self, _req: &Request<'_>, parent: u64, @@ -387,7 +384,7 @@ pub trait Filesystem { } /// Create a directory. - async fn mkdir( + fn mkdir( &self, _req: &Request<'_>, parent: u64, @@ -404,7 +401,7 @@ pub trait Filesystem { } /// Remove a file. - async fn unlink(&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, @@ -413,7 +410,7 @@ pub trait Filesystem { } /// Remove a directory. - async fn rmdir(&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, @@ -422,7 +419,7 @@ pub trait Filesystem { } /// Create a symbolic link. - async fn symlink( + fn symlink( &self, _req: &Request<'_>, parent: u64, @@ -438,7 +435,7 @@ pub trait Filesystem { } /// Rename a file. - async fn rename( + fn rename( &self, _req: &Request<'_>, parent: u64, @@ -457,7 +454,7 @@ pub trait Filesystem { } /// Create a hard link. - async fn link( + fn link( &self, _req: &Request<'_>, ino: u64, @@ -480,7 +477,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. - async fn open(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { reply.opened(0, 0); } @@ -494,7 +491,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 - async fn read( + fn read( &self, _req: &Request<'_>, ino: u64, @@ -525,7 +522,7 @@ pub trait Filesystem { /// is disabled /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 /// lock_owner: only supported with ABI >= 7.9 - async fn write( + fn write( &self, _req: &Request<'_>, ino: u64, @@ -561,14 +558,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'. - async fn flush( - &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 +574,7 @@ pub trait Filesystem { /// the release. fh will contain the value set by the open method, or will be undefined /// if the open method didn't set any value. flags will contain the same flags as for /// open. - async fn release( + fn release( &self, _req: &Request<'_>, _ino: u64, @@ -600,14 +590,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. - async fn fsync( - &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 @@ -622,7 +605,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. - async fn opendir(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + fn opendir(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { reply.opened(0, 0); } @@ -631,7 +614,7 @@ pub trait Filesystem { /// requested size. Send an empty buffer on end of stream. fh will contain the /// value set by the opendir method, or will be undefined if the opendir method /// didn't set any value. - async fn readdir( + fn readdir( &self, _req: &Request<'_>, ino: u64, @@ -651,7 +634,7 @@ pub trait Filesystem { /// requested size. Send an empty buffer on end of stream. fh will contain the /// value set by the opendir method, or will be undefined if the opendir method /// didn't set any value. - async fn readdirplus( + fn readdirplus( &self, _req: &Request<'_>, ino: u64, @@ -670,7 +653,7 @@ pub trait Filesystem { /// For every opendir call there will be exactly one releasedir call. fh will /// contain the value set by the opendir method, or will be undefined if the /// opendir method didn't set any value. - async fn releasedir( + fn releasedir( &self, _req: &Request<'_>, _ino: u64, @@ -685,7 +668,7 @@ pub trait Filesystem { /// If the datasync parameter is set, then only the directory contents should /// 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. - async fn fsyncdir( + fn fsyncdir( &self, _req: &Request<'_>, ino: u64, @@ -701,12 +684,12 @@ pub trait Filesystem { } /// Get file system statistics. - async fn statfs(&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. - async fn setxattr( + fn setxattr( &self, _req: &Request<'_>, ino: u64, @@ -727,7 +710,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. - async fn getxattr( + fn getxattr( &self, _req: &Request<'_>, ino: u64, @@ -746,7 +729,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. - async fn listxattr(&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 @@ -755,7 +738,7 @@ pub trait Filesystem { } /// Remove an extended attribute. - async fn removexattr(&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 @@ -767,7 +750,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 - async fn access(&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); } @@ -782,7 +765,7 @@ pub trait Filesystem { /// structure in for more details. If this method is not /// implemented or under Linux kernel versions earlier than 2.6.15, the mknod() /// and open() methods will be called instead. - async fn create( + fn create( &self, _req: &Request<'_>, parent: u64, @@ -801,7 +784,7 @@ pub trait Filesystem { } /// Test for a POSIX file lock. - async fn getlk( + fn getlk( &self, _req: &Request<'_>, ino: u64, @@ -828,7 +811,7 @@ pub trait Filesystem { /// used to fill in this field in getlk(). Note: if the locking methods are not /// implemented, the kernel will still allow file locking to work locally. /// Hence these are only interesting for network filesystems and similar. - async fn setlk( + fn setlk( &self, _req: &Request<'_>, ino: u64, @@ -852,7 +835,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 - async fn bmap(&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, @@ -861,7 +844,7 @@ pub trait Filesystem { } /// control device - async fn ioctl( + fn ioctl( &self, _req: &Request<'_>, ino: u64, @@ -886,7 +869,7 @@ pub trait Filesystem { } /// Preallocate or deallocate space to a file - async fn fallocate( + fn fallocate( &self, _req: &Request<'_>, ino: u64, @@ -905,7 +888,7 @@ pub trait Filesystem { } /// Reposition read/write file offset - async fn lseek( + fn lseek( &self, _req: &Request<'_>, ino: u64, @@ -922,7 +905,7 @@ pub trait Filesystem { } /// Copy the specified range from the source inode to the destination inode - async fn copy_file_range( + fn copy_file_range( &self, _req: &Request<'_>, ino_in: u64, @@ -947,14 +930,14 @@ 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")] - async fn setvolname(&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); } /// macOS only (undocumented) #[cfg(target_os = "macos")] - async fn exchange( + fn exchange( &self, _req: &Request<'_>, parent: u64, @@ -975,7 +958,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")] - async fn getxtimes(&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); } @@ -987,7 +970,7 @@ pub trait Filesystem { /// Note that you need to lead each option with a separate `"-o"` string. See /// `examples/hello.rs`. #[deprecated(note = "use mount2() instead")] -pub fn mount>( +pub fn mount>( filesystem: FS, mountpoint: P, options: &[&OsStr], @@ -1000,14 +983,13 @@ pub fn mount>( /// not return until the filesystem is unmounted. /// /// NOTE: This will eventually replace mount(), once the API is stable -pub fn mount2>( +pub fn mount2>( filesystem: FS, mountpoint: P, options: &[MountOption], ) -> io::Result<()> { check_option_conflicts(options)?; - let session = std::sync::Arc::new(Session::new(filesystem, mountpoint.as_ref(), options)?); - session.run() + Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.run()) } /// Mount the given filesystem to the given mountpoint. This function spawns @@ -1016,7 +998,7 @@ pub fn mount2>( /// to reference the mounted filesystem. If it's dropped, the filesystem will /// be unmounted. #[deprecated(note = "use spawn_mount2() instead")] -pub fn spawn_mount<'a, FS: Filesystem + Send + Sync + 'static + 'a, P: AsRef>( +pub fn spawn_mount<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef>( filesystem: FS, mountpoint: P, options: &[&OsStr], @@ -1036,7 +1018,7 @@ pub fn spawn_mount<'a, FS: Filesystem + Send + Sync + 'static + 'a, P: AsRef>( +pub fn spawn_mount2<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef>( filesystem: FS, mountpoint: P, options: &[MountOption], diff --git a/vendor/fuser/src/ll/reply.rs b/vendor/fuser/src/ll/reply.rs index aec19e3b6..34acee1e4 100644 --- a/vendor/fuser/src/ll/reply.rs +++ b/vendor/fuser/src/ll/reply.rs @@ -20,10 +20,11 @@ pub(crate) type ResponseBuf = SmallVec<[u8; INLINE_DATA_THRESHOLD]>; #[derive(Debug)] pub enum Response<'a> { Error(i32), - InlineData(ResponseBuf), - SliceData(&'a [u8]), + Data(ResponseBuf), + Slice(&'a [u8]), } +#[must_use] impl<'a> Response<'a> { pub(crate) fn with_iovec]) -> T, T>( &self, @@ -32,8 +33,8 @@ impl<'a> Response<'a> { ) -> T { let datalen = match &self { Response::Error(_) => 0, - Response::InlineData(v) => v.len(), - Response::SliceData(v) => v.len(), + Response::Data(v) => v.len(), + Response::Slice(d) => d.len(), }; let header = abi::fuse_out_header { unique: unique.0, @@ -49,8 +50,8 @@ impl<'a> Response<'a> { let mut v: SmallVec<[IoSlice<'_>; 3]> = smallvec![IoSlice::new(header.as_bytes())]; match &self { Response::Error(_) => {} - Response::InlineData(d) => v.push(IoSlice::new(d.as_ref())), - Response::SliceData(d) => v.push(IoSlice::new(d.as_ref())), + Response::Data(d) => v.push(IoSlice::new(d.as_ref())), + Response::Slice(d) => v.push(IoSlice::new(d.as_ref())), } f(&v) } @@ -64,12 +65,16 @@ impl<'a> Response<'a> { Self::Error(error.into()) } - pub(crate) fn new_owned_data>(data: T) -> Self { - Self::InlineData(data.as_ref().into()) + pub(crate) fn new_data + Into>>(data: T) -> Self { + Self::Data(if data.as_ref().len() <= INLINE_DATA_THRESHOLD { + data.as_ref().into() + } else { + data.into().into() + }) } - pub(crate) fn new_data(data: &'a [u8]) -> Self { - Self::SliceData(data) + pub(crate) fn new_slice(data: &'a [u8]) -> Self { + Self::Slice(data) } pub(crate) fn new_entry( @@ -218,12 +223,12 @@ impl<'a> Response<'a> { for x in data { v.extend_from_slice(x) } - Self::InlineData(v) + Self::Data(v) } fn new_directory(list: EntListBuf) -> Self { assert!(list.buf.len() <= list.max_size); - Self::InlineData(list.buf) + Self::Data(list.buf) } pub(crate) fn new_xattr_size(size: u32) -> Self { @@ -237,7 +242,7 @@ impl<'a> Response<'a> { } fn from_struct(data: &T) -> Self { - Self::InlineData(data.as_bytes().into()) + Self::Data(data.as_bytes().into()) } } @@ -413,7 +418,6 @@ impl DirEntList { #[derive(Debug)] pub struct DirEntryPlus> { - #[allow(unused)] ino: INodeNo, generation: Generation, offset: DirEntOffset, diff --git a/vendor/fuser/src/ll/request.rs b/vendor/fuser/src/ll/request.rs index 1c9a5da1e..e28d75262 100644 --- a/vendor/fuser/src/ll/request.rs +++ b/vendor/fuser/src/ll/request.rs @@ -989,7 +989,7 @@ mod op { #[cfg(feature = "abi-7-28")] reserved: [0; 8], }; - Response::new_owned_data(init.as_bytes()) + Response::new_data(init.as_bytes()) } } @@ -1340,7 +1340,6 @@ mod op { #[derive(Debug)] pub struct NotifyReply<'a> { header: &'a fuse_in_header, - #[allow(unused)] arg: &'a [u8], } #[cfg(feature = "abi-7-15")] @@ -1351,7 +1350,6 @@ mod op { #[derive(Debug)] pub struct BatchForget<'a> { header: &'a fuse_in_header, - #[allow(unused)] arg: &'a fuse_batch_forget_in, nodes: &'a [fuse_forget_one], } @@ -1589,7 +1587,6 @@ mod op { #[derive(Debug)] pub struct CuseInit<'a> { header: &'a fuse_in_header, - #[allow(unused)] arg: &'a fuse_init_in, } #[cfg(feature = "abi-7-12")] diff --git a/vendor/fuser/src/mnt/fuse2.rs b/vendor/fuser/src/mnt/fuse2.rs index cca8c093e..ad863360a 100644 --- a/vendor/fuser/src/mnt/fuse2.rs +++ b/vendor/fuser/src/mnt/fuse2.rs @@ -1,4 +1,4 @@ -use super::{fuse2_sys::*, with_fuse_args, MountOption}; +use super::{ensure_last_os_error, fuse2_sys::*, with_fuse_args, MountOption}; use log::warn; use std::{ ffi::CString, @@ -19,7 +19,7 @@ impl Mount { with_fuse_args(options, |args| { let fd = unsafe { fuse_mount_compat25(mountpoint.as_ptr(), args) }; if fd < 0 { - Err(io::Error::last_os_error()) + Err(ensure_last_os_error()) } else { let file = unsafe { File::from_raw_fd(fd) }; Ok((Arc::new(file), Mount { mountpoint })) diff --git a/vendor/fuser/src/mnt/fuse3.rs b/vendor/fuser/src/mnt/fuse3.rs index f30f7026d..289feef10 100644 --- a/vendor/fuser/src/mnt/fuse3.rs +++ b/vendor/fuser/src/mnt/fuse3.rs @@ -53,8 +53,4 @@ impl Drop for Mount { } } } - -// Safety: the pointer is never used except at Drop time, and at Drop time we know the pointer is -// only being used by a single thread. unsafe impl Send for Mount {} -unsafe impl Sync for Mount {} diff --git a/vendor/fuser/src/mnt/mod.rs b/vendor/fuser/src/mnt/mod.rs index 43250031b..205bde20d 100644 --- a/vendor/fuser/src/mnt/mod.rs +++ b/vendor/fuser/src/mnt/mod.rs @@ -118,7 +118,6 @@ fn is_mounted(fuse_device: &File) -> bool { } /// Ensures that an os error is never 0/Success -#[cfg(feature = "libfuse3")] fn ensure_last_os_error() -> io::Error { let err = io::Error::last_os_error(); match err.raw_os_error() { diff --git a/vendor/fuser/src/reply.rs b/vendor/fuser/src/reply.rs index 5137bcc35..30a682f62 100644 --- a/vendor/fuser/src/reply.rs +++ b/vendor/fuser/src/reply.rs @@ -148,7 +148,7 @@ impl Reply for ReplyData { impl ReplyData { /// Reply to a request with the given data pub fn data(self, data: &[u8]) { - self.reply.send_ll(&ll::Response::new_data(data)); + self.reply.send_ll(&ll::Response::new_slice(data)); } /// Reply to a request with the given error code diff --git a/vendor/fuser/src/request.rs b/vendor/fuser/src/request.rs index aa02e91c4..ba361a9a2 100644 --- a/vendor/fuser/src/request.rs +++ b/vendor/fuser/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 _; @@ -21,105 +22,21 @@ use crate::session::{Session, SessionACL}; use crate::Filesystem; use crate::{ll, KernelConfig}; -mod request_buffer { - use std::mem::MaybeUninit; - - use crate::ll::fuse_abi::fuse_in_header; - use crate::session::MAX_WRITE_SIZE; - - /// Size of the buffer for reading a request from the kernel. Since the kernel may send - /// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space. - const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096; - - /// This is a copy of the unstable `std::mem::MaybeUninit::slice_assume_init_ref` - unsafe fn slice_assume_init_ref(slice: &[MaybeUninit]) -> &[T] { - // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that - // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. - // The pointer obtained is valid since it refers to memory owned by `slice` which is a - // reference and thus guaranteed to be valid for reads. - &*(slice as *const [MaybeUninit] as *const [T]) - } - - /// This whole thing exists because `Box::new_uninit_slice` is not yet stable. - #[derive(Debug)] - pub(crate) struct FuseRequestBuffer(*mut [MaybeUninit]); - - impl FuseRequestBuffer { - pub(crate) fn allocate() -> Self { - let layout = Self::layout(); - // Safety: we know the `u8` layout has non-zero size, and we will handle the - // uninitialized memory below. - let ptr: *mut MaybeUninit = unsafe { std::alloc::alloc(layout) } as *mut _; - let slice = unsafe { std::slice::from_raw_parts_mut(ptr, BUFFER_SIZE) }; - Self(slice as *mut _) - } - - /// Safety: the caller must guarantee that at least `length` bytes of the underlying buffer - /// are initialized. - pub(crate) unsafe fn assume_init_ref(&self, length: usize) -> &[u8] { - let slice = &self[..length]; - slice_assume_init_ref(slice) - } - - const fn layout() -> std::alloc::Layout { - unsafe { - std::alloc::Layout::from_size_align_unchecked(BUFFER_SIZE, std::mem::align_of::()) - } - } - } - - impl std::ops::Deref for FuseRequestBuffer { - type Target = [MaybeUninit]; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.0 } - } - } - - impl std::ops::DerefMut for FuseRequestBuffer { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.0 } - } - } - - impl Drop for FuseRequestBuffer { - fn drop(&mut self) { - // Safety: we're guaranteed to be using the same layout as at construction time - unsafe { - std::alloc::dealloc(self.0 as *mut u8, Self::layout()); - } - } - } - unsafe impl Send for FuseRequestBuffer {} - unsafe impl Sync for FuseRequestBuffer {} -} - -pub(crate) use request_buffer::FuseRequestBuffer; - /// Request data structure #[derive(Debug)] pub struct Request<'a> { /// Channel sender for sending the reply ch: ChannelSender, + /// Request raw data + data: &'a [u8], /// Parsed request request: ll::AnyRequest<'a>, - /// Request raw data - #[allow(unused)] - data: FuseRequestBuffer, -} - -unsafe fn extend_lifetime<'old, 'new: 'old, T: 'new + ?Sized>(data: &'old T) -> &'new T { - &*(data as *const _) } impl<'a> Request<'a> { /// Create a new request from the given data - pub(crate) fn new(ch: ChannelSender, data: FuseRequestBuffer, initialized_size: usize) -> Option> { - let slice = unsafe { data.assume_init_ref(initialized_size) }; - // Safety: the slice we create here can't outlive its backing storage because `Request` - // drops the `request` field before the backing `data`. - let slice: &'a [u8] = unsafe { extend_lifetime(slice) }; - let request = match ll::AnyRequest::try_from(slice) { + pub(crate) fn new(ch: ChannelSender, data: &'a [u8]) -> Option> { + let request = match ll::AnyRequest::try_from(data) { Ok(request) => request, Err(err) => { error!("{}", err); @@ -133,11 +50,11 @@ 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) async fn dispatch(&self, se: &Session) { + pub(crate) fn dispatch(&self, se: &Session) { debug!("{}", self.request); let unique = self.request.unique(); - let res = match self.dispatch_req(se).await { + let res = match self.dispatch_req(se) { Ok(Some(resp)) => resp, Ok(None) => return, Err(errno) => self.request.reply_err(errno), @@ -149,7 +66,7 @@ impl<'a> Request<'a> { } } - async fn dispatch_req( + fn dispatch_req( &self, se: &Session, ) -> Result>, Errno> { @@ -231,16 +148,13 @@ impl<'a> Request<'a> { return Err(Errno::EPROTO); } // Remember ABI version supported by kernel - se.proto_major - .store(v.major(), std::sync::atomic::Ordering::SeqCst); - se.proto_minor - .store(v.minor(), std::sync::atomic::Ordering::SeqCst); + 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 se.filesystem .init(self, &mut config) - .await .map_err(Errno::from_i32)?; // Reply with our desired version and settings. If the kernel supports a @@ -254,24 +168,23 @@ impl<'a> Request<'a> { config.max_readahead, config.max_write ); - se.initialized - .store(true, std::sync::atomic::Ordering::SeqCst); + se.initialized.store(true, Ordering::SeqCst); return Ok(Some(x.reply(&config))); } // Any operation is invalid before initialization - _ if !se.initialized.load(std::sync::atomic::Ordering::SeqCst) => { + _ 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 - .store(true, std::sync::atomic::Ordering::SeqCst); + if !se.destroyed.swap(true, Ordering::SeqCst) { + se.filesystem.destroy(); + } return Ok(Some(x.reply())); } // Any operation is invalid after destroy - _ if se.destroyed.load(std::sync::atomic::Ordering::SeqCst) => { + _ if se.destroyed.load(Ordering::SeqCst) => { warn!("Ignoring FUSE operation after destroy: {}", self.request); return Err(Errno::EIO); } @@ -287,15 +200,15 @@ impl<'a> Request<'a> { self.request.nodeid().into(), x.name().as_ref(), self.reply(), - ).await; + ); } ll::Operation::Forget(x) => { se.filesystem - .forget(self, self.request.nodeid().into(), x.nlookup()).await; // no reply + .forget(self, self.request.nodeid().into(), x.nlookup()); // no reply } ll::Operation::GetAttr(_) => { se.filesystem - .getattr(self, self.request.nodeid().into(), self.reply()).await; + .getattr(self, self.request.nodeid().into(), self.reply()); } ll::Operation::SetAttr(x) => { se.filesystem.setattr( @@ -314,11 +227,11 @@ impl<'a> Request<'a> { x.bkuptime(), x.flags(), self.reply(), - ).await; + ); } ll::Operation::ReadLink(_) => { se.filesystem - .readlink(self, self.request.nodeid().into(), self.reply()).await; + .readlink(self, self.request.nodeid().into(), self.reply()); } ll::Operation::MkNod(x) => { se.filesystem.mknod( @@ -329,7 +242,7 @@ impl<'a> Request<'a> { x.umask(), x.rdev(), self.reply(), - ).await; + ); } ll::Operation::MkDir(x) => { se.filesystem.mkdir( @@ -339,7 +252,7 @@ impl<'a> Request<'a> { x.mode(), x.umask(), self.reply(), - ).await; + ); } ll::Operation::Unlink(x) => { se.filesystem.unlink( @@ -347,7 +260,7 @@ impl<'a> Request<'a> { self.request.nodeid().into(), x.name().as_ref(), self.reply(), - ).await; + ); } ll::Operation::RmDir(x) => { se.filesystem.rmdir( @@ -355,7 +268,7 @@ impl<'a> Request<'a> { self.request.nodeid().into(), x.name().as_ref(), self.reply(), - ).await; + ); } ll::Operation::SymLink(x) => { se.filesystem.symlink( @@ -364,7 +277,7 @@ impl<'a> Request<'a> { x.target().as_ref(), Path::new(x.link()), self.reply(), - ).await; + ); } ll::Operation::Rename(x) => { se.filesystem.rename( @@ -375,7 +288,7 @@ impl<'a> Request<'a> { x.dest().name.as_ref(), 0, self.reply(), - ).await; + ); } ll::Operation::Link(x) => { se.filesystem.link( @@ -384,11 +297,11 @@ impl<'a> Request<'a> { self.request.nodeid().into(), x.dest().name.as_ref(), self.reply(), - ).await; + ); } ll::Operation::Open(x) => { se.filesystem - .open(self, self.request.nodeid().into(), x.flags(), self.reply()).await; + .open(self, self.request.nodeid().into(), x.flags(), self.reply()); } ll::Operation::Read(x) => { se.filesystem.read( @@ -400,7 +313,7 @@ impl<'a> Request<'a> { x.flags(), x.lock_owner().map(|l| l.into()), self.reply(), - ).await; + ); } ll::Operation::Write(x) => { se.filesystem.write( @@ -413,7 +326,7 @@ impl<'a> Request<'a> { x.flags(), x.lock_owner().map(|l| l.into()), self.reply(), - ).await; + ); } ll::Operation::Flush(x) => { se.filesystem.flush( @@ -422,7 +335,7 @@ impl<'a> Request<'a> { x.file_handle().into(), x.lock_owner().into(), self.reply(), - ).await; + ); } ll::Operation::Release(x) => { se.filesystem.release( @@ -433,7 +346,7 @@ impl<'a> Request<'a> { x.lock_owner().map(|x| x.into()), x.flush(), self.reply(), - ).await; + ); } ll::Operation::FSync(x) => { se.filesystem.fsync( @@ -442,11 +355,11 @@ impl<'a> Request<'a> { x.file_handle().into(), x.fdatasync(), self.reply(), - ).await; + ); } ll::Operation::OpenDir(x) => { se.filesystem - .opendir(self, self.request.nodeid().into(), x.flags(), self.reply()).await; + .opendir(self, self.request.nodeid().into(), x.flags(), self.reply()); } ll::Operation::ReadDir(x) => { se.filesystem.readdir( @@ -459,7 +372,7 @@ impl<'a> Request<'a> { self.ch.clone(), x.size() as usize, ), - ).await; + ); } ll::Operation::ReleaseDir(x) => { se.filesystem.releasedir( @@ -468,7 +381,7 @@ impl<'a> Request<'a> { x.file_handle().into(), x.flags(), self.reply(), - ).await; + ); } ll::Operation::FSyncDir(x) => { se.filesystem.fsyncdir( @@ -477,11 +390,11 @@ impl<'a> Request<'a> { x.file_handle().into(), x.fdatasync(), self.reply(), - ).await; + ); } ll::Operation::StatFs(_) => { se.filesystem - .statfs(self, self.request.nodeid().into(), self.reply()).await; + .statfs(self, self.request.nodeid().into(), self.reply()); } ll::Operation::SetXAttr(x) => { se.filesystem.setxattr( @@ -492,7 +405,7 @@ impl<'a> Request<'a> { x.flags(), x.position(), self.reply(), - ).await; + ); } ll::Operation::GetXAttr(x) => { se.filesystem.getxattr( @@ -501,11 +414,11 @@ impl<'a> Request<'a> { x.name(), x.size_u32(), self.reply(), - ).await; + ); } ll::Operation::ListXAttr(x) => { se.filesystem - .listxattr(self, self.request.nodeid().into(), x.size(), self.reply()).await; + .listxattr(self, self.request.nodeid().into(), x.size(), self.reply()); } ll::Operation::RemoveXAttr(x) => { se.filesystem.removexattr( @@ -513,11 +426,11 @@ impl<'a> Request<'a> { self.request.nodeid().into(), x.name(), self.reply(), - ).await; + ); } ll::Operation::Access(x) => { se.filesystem - .access(self, self.request.nodeid().into(), x.mask(), self.reply()).await; + .access(self, self.request.nodeid().into(), x.mask(), self.reply()); } ll::Operation::Create(x) => { se.filesystem.create( @@ -528,7 +441,7 @@ impl<'a> Request<'a> { x.umask(), x.flags(), self.reply(), - ).await; + ); } ll::Operation::GetLk(x) => { se.filesystem.getlk( @@ -541,7 +454,7 @@ impl<'a> Request<'a> { x.lock().typ, x.lock().pid, self.reply(), - ).await; + ); } ll::Operation::SetLk(x) => { se.filesystem.setlk( @@ -555,7 +468,7 @@ impl<'a> Request<'a> { x.lock().pid, false, self.reply(), - ).await; + ); } ll::Operation::SetLkW(x) => { se.filesystem.setlk( @@ -569,7 +482,7 @@ impl<'a> Request<'a> { x.lock().pid, true, self.reply(), - ).await; + ); } ll::Operation::BMap(x) => { se.filesystem.bmap( @@ -578,7 +491,7 @@ impl<'a> Request<'a> { x.block_size(), x.block(), self.reply(), - ).await; + ); } #[cfg(feature = "abi-7-11")] @@ -595,7 +508,7 @@ impl<'a> Request<'a> { x.in_data(), x.out_size(), self.reply(), - ).await; + ); } } #[cfg(feature = "abi-7-11")] @@ -610,7 +523,7 @@ impl<'a> Request<'a> { } #[cfg(feature = "abi-7-16")] ll::Operation::BatchForget(x) => { - se.filesystem.batch_forget(self, x.nodes()).await; // no reply + se.filesystem.batch_forget(self, x.nodes()); // no reply } #[cfg(feature = "abi-7-19")] ll::Operation::FAllocate(x) => { @@ -622,7 +535,7 @@ impl<'a> Request<'a> { x.len(), x.mode(), self.reply(), - ).await; + ); } #[cfg(feature = "abi-7-21")] ll::Operation::ReadDirPlus(x) => { @@ -636,7 +549,7 @@ impl<'a> Request<'a> { self.ch.clone(), x.size() as usize, ), - ).await; + ); } #[cfg(feature = "abi-7-23")] ll::Operation::Rename2(x) => { @@ -648,7 +561,7 @@ impl<'a> Request<'a> { x.to().name.as_ref(), x.flags(), self.reply(), - ).await; + ); } #[cfg(feature = "abi-7-24")] ll::Operation::Lseek(x) => { @@ -659,7 +572,7 @@ impl<'a> Request<'a> { x.offset(), x.whence(), self.reply(), - ).await; + ); } #[cfg(feature = "abi-7-28")] ll::Operation::CopyFileRange(x) => { @@ -675,16 +588,16 @@ impl<'a> Request<'a> { x.len(), x.flags().try_into().unwrap(), self.reply(), - ).await; + ); } #[cfg(target_os = "macos")] ll::Operation::SetVolName(x) => { - se.filesystem.setvolname(self, x.name(), self.reply()).await; + se.filesystem.setvolname(self, x.name(), self.reply()); } #[cfg(target_os = "macos")] ll::Operation::GetXTimes(_) => { se.filesystem - .getxtimes(self, self.request.nodeid().into(), self.reply()).await; + .getxtimes(self, self.request.nodeid().into(), self.reply()); } #[cfg(target_os = "macos")] ll::Operation::Exchange(x) => { @@ -696,7 +609,7 @@ impl<'a> Request<'a> { x.to().name.as_ref(), x.options(), self.reply(), - ).await; + ); } #[cfg(feature = "abi-7-12")] diff --git a/vendor/fuser/src/session.rs b/vendor/fuser/src/session.rs index 04f10369d..43f624a56 100644 --- a/vendor/fuser/src/session.rs +++ b/vendor/fuser/src/session.rs @@ -9,11 +9,13 @@ use libc::{EAGAIN, EINTR, ENODEV, ENOENT}; use log::{info, warn}; use std::fmt; use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::sync::atomic::{AtomicU32, AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; use std::{io, ops::DerefMut}; -use crate::request::{Request, FuseRequestBuffer}; +use crate::ll::fuse_abi as abi; +use crate::request::Request; use crate::Filesystem; use crate::MountOption; use crate::{channel::Channel, mnt::Mount}; @@ -23,6 +25,10 @@ use crate::{channel::Channel, mnt::Mount}; /// and 128k on other systems. pub const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024; +/// Size of the buffer for reading a request from the kernel. Since the kernel may send +/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space. +const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096; + #[derive(Debug, Eq, PartialEq)] pub(crate) enum SessionACL { All, @@ -32,13 +38,13 @@ pub(crate) enum SessionACL { /// The session data structure #[derive(Debug)] -pub struct Session { +pub struct Session { /// Filesystem operation implementations pub(crate) filesystem: FS, /// Communication channel to the kernel driver ch: Channel, /// Handle to the mount. Dropping this unmounts. - mount: Option, + mount: Arc>>, /// Mount point mountpoint: PathBuf, /// Whether to restrict access to owner, root + owner, or unrestricted @@ -47,16 +53,16 @@ pub struct Session { /// User that launched the fuser process pub(crate) session_owner: u32, /// FUSE protocol major version - pub(crate) proto_major: std::sync::atomic::AtomicU32, + pub(crate) proto_major: AtomicU32, /// FUSE protocol minor version - pub(crate) proto_minor: std::sync::atomic::AtomicU32, + pub(crate) proto_minor: AtomicU32, /// True if the filesystem is initialized (init operation done) - pub(crate) initialized: std::sync::atomic::AtomicBool, + pub(crate) initialized: AtomicBool, /// True if the filesystem was destroyed (destroy operation done) - pub(crate) destroyed: std::sync::atomic::AtomicBool, + pub(crate) destroyed: AtomicBool, } -impl Session { +impl Session { /// Create a new session by mounting the given filesystem to the given mountpoint pub fn new( filesystem: FS, @@ -91,14 +97,14 @@ impl Session { Ok(Session { filesystem, ch, - mount: Some(mount), + mount: Arc::new(Mutex::new(Some(mount))), mountpoint: mountpoint.to_owned(), allowed, session_owner: unsafe { libc::geteuid() }, - proto_major: std::sync::atomic::AtomicU32::new(0), - proto_minor: std::sync::atomic::AtomicU32::new(0), - initialized: std::sync::atomic::AtomicBool::new(false), - destroyed: std::sync::atomic::AtomicBool::new(false), + proto_major: AtomicU32::new(0), + proto_minor: AtomicU32::new(0), + initialized: AtomicBool::new(false), + destroyed: AtomicBool::new(false), }) } @@ -108,25 +114,22 @@ impl Session { } /// 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(self: Arc) -> io::Result<()> - where - FS: 'static, - { - #[cfg(target_os = "linux")] - info!("new session thread with TID {}", unsafe { libc::syscall(libc::SYS_gettid) as libc::pid_t }); - let session = &*self; + /// calls into the filesystem. + pub fn run(&self) -> io::Result<()> { + // 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]; + let buf = aligned_sub_buf( + buffer.deref_mut(), + std::mem::align_of::(), + ); loop { // Read the next request from the given channel to kernel driver // The kernel driver makes sure that we get exactly one request per read - let mut buffer = FuseRequestBuffer::allocate(); - match self.ch.receive(buffer.deref_mut()) { - // TODO figure out the alignment stuff here ... need to chop `buffer` down to `size` + whatever padding came off the front - Ok(size) => match Request::new(self.ch.sender(), buffer, size) { + match self.ch.receive(buf) { + Ok(size) => match Request::new(self.ch.sender(), &buf[..size]) { // Dispatch request - Some(req) => futures::executor::block_on(async move { req.dispatch(session).await }), + Some(req) => req.dispatch(self), // Quit loop on illegal request None => break, }, @@ -149,23 +152,51 @@ impl Session { /// Unmount the filesystem pub fn unmount(&mut self) { - drop(std::mem::take(&mut self.mount)); + drop(std::mem::take(&mut *self.mount.lock().unwrap())); + } + + /// Returns a thread-safe object that can be used to unmount the Filesystem + pub fn unmount_callable(&mut self) -> SessionUnmounter { + SessionUnmounter { + mount: self.mount.clone(), + } + } +} + +#[derive(Debug)] +/// A thread-safe object that can be used to unmount a Filesystem +pub struct SessionUnmounter { + mount: Arc>>, +} + +impl SessionUnmounter { + /// Unmount the filesystem + pub fn unmount(&mut self) -> io::Result<()> { + drop(std::mem::take(&mut *self.mount.lock().unwrap())); + Ok(()) } } -impl Session { +fn aligned_sub_buf(buf: &mut [u8], alignment: usize) -> &mut [u8] { + let off = alignment - (buf.as_ptr() as usize) % alignment; + if off == alignment { + buf + } else { + &mut buf[off..] + } +} + +impl Session { /// Run the session loop in a background thread pub fn spawn(self) -> io::Result { BackgroundSession::new(self) } } -impl Drop for Session { +impl Drop for Session { fn drop(&mut self) { - if !self.destroyed.load(std::sync::atomic::Ordering::SeqCst) { + if !self.destroyed.swap(true, Ordering::SeqCst) { self.filesystem.destroy(); - self.destroyed - .store(true, std::sync::atomic::Ordering::SeqCst); } info!("Unmounted {}", self.mountpoint().display()); } @@ -176,7 +207,7 @@ pub struct BackgroundSession { /// Path of the mounted filesystem pub mountpoint: PathBuf, /// Thread guard of the background session - pub guards: Vec>>, + pub guard: JoinHandle>, /// Ensures the filesystem is unmounted when the session ends _mount: Mount, } @@ -185,57 +216,31 @@ impl BackgroundSession { /// Create a new background session for the given session by running its /// session loop in a background thread. If the returned handle is dropped, /// the filesystem is unmounted and the given session ends. - pub fn new( - mut se: Session, + pub fn new( + se: Session, ) -> io::Result { let mountpoint = se.mountpoint().to_path_buf(); // Take the fuse_session, so that we can unmount it - let mount = std::mem::take(&mut se.mount); + let mount = std::mem::take(&mut *se.mount.lock().unwrap()); let mount = mount.ok_or_else(|| io::Error::from_raw_os_error(libc::ENODEV))?; - let se = Arc::new(se); - let guard = thread::spawn(move || se.run()); + let guard = thread::spawn(move || { + se.run() + }); Ok(BackgroundSession { mountpoint, - guards: vec![guard], + guard, _mount: mount, }) } - - /// Like `new` but multithreaded - pub fn new_multi_thread( - mut se: Session, - thread_count: usize, - ) -> io::Result { - assert!(thread_count >= 1); - let mountpoint = se.mountpoint().to_path_buf(); - // Take the fuse_session, so that we can unmount it - let mount = std::mem::take(&mut se.mount); - let mount = mount.ok_or_else(|| io::Error::from_raw_os_error(libc::ENODEV))?; - let se = Arc::new(se); - let guards = (0..thread_count) - .map(|_| { - let se = Arc::clone(&se); - thread::spawn(move || se.run()) - }) - .collect(); - Ok(BackgroundSession { - mountpoint, - guards, - _mount: mount, - }) - } - /// Unmount the filesystem and join the background thread. - pub async fn join(self) { + pub fn join(self) { let Self { mountpoint: _, - guards, + guard, _mount, } = self; drop(_mount); - for guard in guards { - guard.join().unwrap().unwrap(); - } + guard.join().unwrap().unwrap(); } } diff --git a/vendor/fuser/tests/integration_tests.rs b/vendor/fuser/tests/integration_tests.rs new file mode 100644 index 000000000..0ed8c804b --- /dev/null +++ b/vendor/fuser/tests/integration_tests.rs @@ -0,0 +1,23 @@ +use fuser::{Filesystem, Session}; +use std::rc::Rc; +use std::thread; +use std::time::Duration; +use tempfile::TempDir; + +#[test] +#[cfg(target_os = "linux")] +fn unmount_no_send() { + // Rc to make this !Send + struct NoSendFS(Rc<()>); + + impl Filesystem for NoSendFS {} + + let tmpdir: TempDir = tempfile::tempdir().unwrap(); + let mut session = Session::new(NoSendFS(Rc::new(())), tmpdir.path(), &[]).unwrap(); + let mut unmounter = session.unmount_callable(); + thread::spawn(move || { + thread::sleep(Duration::from_secs(1)); + unmounter.unmount().unwrap(); + }); + session.run().unwrap(); +}