From bc1128819819b32e92b30ecdda1a305bee1ba0d9 Mon Sep 17 00:00:00 2001 From: Christopher Berner Date: Thu, 21 Nov 2024 21:02:30 -0800 Subject: [PATCH 01/12] Updates to CI config --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 629dce657..5eecc2172 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,14 +16,28 @@ jobs: steps: - uses: actions/checkout@v4 + + - name: Cache + id: rust-cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '.github/workflows/*.yml') }} + - name: Install packages run: | sudo apt update sudo apt install -y ${{ matrix.libfuse }} build-essential - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - target: x86_64-unknown-linux-musl + - name: Install Rust + #if: steps.rust-cache.outputs.cache-hit != 'true' + run: | + rustup target add x86_64-unknown-linux-musl - name: Run tests run: | @@ -36,18 +50,34 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 + + - name: Cache + id: rust-cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '.github/workflows/*.yml') }} + - name: Install packages run: | sudo apt update sudo apt install -y libfuse-dev libfuse3-dev build-essential - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - components: rustfmt, clippy + - name: Install Rust + #if: steps.rust-cache.outputs.cache-hit != 'true' + run: | + rustup toolchain install 1.81 + rustup component add rustfmt + rustup component add clippy - - uses: taiki-e/install-action@v2 - with: - tool: cargo-deny@0.14 + - name: Install cargo-deny + #if: steps.rust-cache.outputs.cache-hit != 'true' + run: cargo +1.81 install --force --version 0.16.2 cargo-deny --locked - name: Run tests run: INTERACTIVE="" make pre From d675c07ecb8e826467d53dc00b45a67c731d5b85 Mon Sep 17 00:00:00 2001 From: Christopher Berner Date: Fri, 22 Nov 2024 17:25:18 -0800 Subject: [PATCH 02/12] Enable caching in CI --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5eecc2172..bc1c38eea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: sudo apt install -y ${{ matrix.libfuse }} build-essential - name: Install Rust - #if: steps.rust-cache.outputs.cache-hit != 'true' + if: steps.rust-cache.outputs.cache-hit != 'true' run: | rustup target add x86_64-unknown-linux-musl @@ -69,14 +69,14 @@ jobs: sudo apt install -y libfuse-dev libfuse3-dev build-essential - name: Install Rust - #if: steps.rust-cache.outputs.cache-hit != 'true' + if: steps.rust-cache.outputs.cache-hit != 'true' run: | rustup toolchain install 1.81 rustup component add rustfmt rustup component add clippy - name: Install cargo-deny - #if: steps.rust-cache.outputs.cache-hit != 'true' + if: steps.rust-cache.outputs.cache-hit != 'true' run: cargo +1.81 install --force --version 0.16.2 cargo-deny --locked - name: Run tests From 998bb952ff6ae091d76858c634d7b22b805f0339 Mon Sep 17 00:00:00 2001 From: Firelight Flagboy Date: Wed, 27 Nov 2024 16:17:54 +0100 Subject: [PATCH 03/12] Fix cache concurrency issue Using the same cache key across multiple jobs can cause issues if we make assumption about it's content (like installing tools if not hit on the cache). --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc1c38eea..0bd8647f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,8 +26,9 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ + ~/.rustup/toolchains/ target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '.github/workflows/*.yml') }} + key: ${{ runner.os }}-cargo-compile-v1-${{ matrix.libfuse }}-${{ matrix.features }}-${{ hashFiles('**/Cargo.toml', '.github/workflows/*.yml') }} - name: Install packages run: | @@ -60,8 +61,9 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ + ~/.rustup/toolchains/ target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '.github/workflows/*.yml') }} + key: ${{ runner.os }}-cargo-ci-v1-${{ hashFiles('**/Cargo.toml', '.github/workflows/*.yml') }} - name: Install packages run: | From a2200c8d6110386bd569bca9da180394ca2a891b Mon Sep 17 00:00:00 2001 From: Firelight Flagboy Date: Wed, 27 Nov 2024 15:56:35 +0100 Subject: [PATCH 04/12] Fix bugeous `crtime` value on macOS During certain operation, macOS use some helper that send request to the mountpoint with `crtime` set to `0xffff83da4f80`. That value correspond to `-2_082_844_800u64` which is the difference between the date 1904-01-01 and 1970-01-01 because macOS epoch start at 1904 and not 1970. Fix #217 --- src/ll/request.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ll/request.rs b/src/ll/request.rs index 7b57c7595..5f6961e9b 100644 --- a/src/ll/request.rs +++ b/src/ll/request.rs @@ -427,6 +427,10 @@ mod op { #[cfg(target_os = "macos")] match self.arg.valid & FATTR_CRTIME { 0 => None, + // During certain operation, macOS use some helper that send request to the mountpoint with `crtime` set to 0xffffffff83da4f80. + // That value correspond to `-2_082_844_800u64` which is the difference between the date 1904-01-01 and 1970-01-01 because macOS epoch start at 1904 and not 1970. + // https://github.com/macfuse/macfuse/issues/1042 + _ if self.arg.crtime == 0xffffffff83da4f80 => None, _ => Some( SystemTime::UNIX_EPOCH + Duration::new(self.arg.crtime, self.arg.crtimensec), ), From 0fc144367882be4f5681765e7ad4612147eb63a3 Mon Sep 17 00:00:00 2001 From: Christopher Berner Date: Wed, 27 Nov 2024 16:57:50 -0800 Subject: [PATCH 05/12] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 347680644..e66b018a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # FUSE for Rust - Changelog +## 0.15.1 - 2024-11-27 +* Fix crtime related panic that could occur on MacOS. See PR #322 for details. + ## 0.15.0 - 2024-10-25 * Add file handle argument to `getattr()` * Change `poll()` to take a `PollHandle` instead of a `u64` From 168705a63a0c47b6cd330291e4a0dc1d14317cd0 Mon Sep 17 00:00:00 2001 From: Christopher Berner Date: Wed, 27 Nov 2024 16:58:00 -0800 Subject: [PATCH 06/12] Bump version to 0.15.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 93befc3c0..2febf448b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ license = "MIT" repository = "https://github.com/cberner/fuser" documentation = "https://docs.rs/fuser" homepage = "https://github.com/cberner/fuser" -version = "0.15.0" +version = "0.15.1" edition = "2021" readme = "README.md" authors = ["Christopher Berner "] From f74d4912392b16fe270ece6035508d8673dde154 Mon Sep 17 00:00:00 2001 From: James Bornholt Date: Mon, 13 Mar 2023 14:46:28 +0000 Subject: [PATCH 07/12] Add Fuser README note about fork Co-authored-by: Daniel Carl Jones --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 6b4919bcc..3807c7201 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ +## Fork of fuser for Mountpoint + +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. + +### Fork Maintenance + +This fork should be maintained in the `fuser/fork` branch of the [awslabs/mountpoint-s3 repository on GitHub](https://github.com/awslabs/mountpoint-s3/tree/main/vendor/fuser). + +To pull in new changes from upstream, you should fetch and rebase the changes locally and then force push to the `fuser/fork` branch (- not ideal!). +Once the `fuser/fork` branch is as desired, create a new branch from Mountpoint's `main` branch. +Run the [`vendor-fuser.sh` script](https://github.com/awslabs/mountpoint-s3/blob/main/vendor-fuser.sh). +You should open a pull request with the new commit created by the script. +This pull request is where the changes are reviewed. + +If you wish to add new divergent changes to this fork of Fuser, +open a pull request on the Mountpoint repository branching from the `fuser/fork` branch and use `fuser/fork` as the base branch when creating the pull request. + +--- + # FUSE (Filesystem in Userspace) for Rust ![CI](https://github.com/cberner/fuser/actions/workflows/ci.yml/badge.svg) From 2ec10396793fd8575ea603ff2262f3fe2d0f3564 Mon Sep 17 00:00:00 2001 From: James Bornholt Date: Mon, 13 Mar 2023 04:03:39 +0000 Subject: [PATCH 08/12] Make `Filesystem` trait use interior mutability Currently the Filesystem trait forces only a single request dispatch loop, with the idea that a filesystem that wants to be concurrent can just spawn its own threads and dispatch operations on them itself. This works, but it's bad for performance, because it forces every request to bounce across threads. Instead, let's make the Filesystem trait use interior mutability. This allows us to spawn multiple dispatch threads that each block on the FUSE device, read a request, and immediately dispatch it without the bounce. Unfortunately there's not a good way for a trait to be variant in mutability, so this is a breaking API change. We'll need to think more carefully about how to upstream this change. Signed-off-by: James Bornholt --- examples/hello.rs | 8 +-- examples/ioctl.rs | 22 +++++---- examples/notify_inval_entry.rs | 8 +-- examples/notify_inval_inode.rs | 12 ++--- examples/poll.rs | 14 +++--- examples/simple.rs | 58 +++++++++++----------- src/lib.rs | 90 +++++++++++++++++----------------- src/request.rs | 20 ++++---- src/session.rs | 35 ++++++------- 9 files changed, 134 insertions(+), 133 deletions(-) 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 0c0928555..6a6fdc3ff 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, @@ -1300,7 +1300,7 @@ impl Filesystem for SimpleFS { } fn link( - &mut self, + &self, req: &Request, inode: u64, new_parent: u64, @@ -1328,7 +1328,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 => { @@ -1377,7 +1377,7 @@ impl Filesystem for SimpleFS { } fn read( - &mut self, + &self, _req: &Request, inode: u64, fh: u64, @@ -1412,7 +1412,7 @@ impl Filesystem for SimpleFS { } fn write( - &mut self, + &self, _req: &Request, inode: u64, fh: u64, @@ -1457,7 +1457,7 @@ impl Filesystem for SimpleFS { } fn release( - &mut self, + &self, _req: &Request<'_>, inode: u64, _fh: u64, @@ -1472,7 +1472,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 => { @@ -1516,7 +1516,7 @@ impl Filesystem for SimpleFS { } fn readdir( - &mut self, + &self, _req: &Request, inode: u64, _fh: u64, @@ -1552,7 +1552,7 @@ impl Filesystem for SimpleFS { } fn releasedir( - &mut self, + &self, _req: &Request<'_>, inode: u64, _fh: u64, @@ -1565,7 +1565,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( @@ -1581,7 +1581,7 @@ impl Filesystem for SimpleFS { } fn setxattr( - &mut self, + &self, request: &Request<'_>, inode: u64, key: &OsStr, @@ -1606,7 +1606,7 @@ impl Filesystem for SimpleFS { } fn getxattr( - &mut self, + &self, request: &Request<'_>, inode: u64, key: &OsStr, @@ -1638,7 +1638,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 @@ -1658,7 +1658,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); @@ -1680,7 +1680,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) => { @@ -1695,7 +1695,7 @@ impl Filesystem for SimpleFS { } fn create( - &mut self, + &self, req: &Request, parent: u64, name: &OsStr, @@ -1789,7 +1789,7 @@ impl Filesystem for SimpleFS { #[cfg(target_os = "linux")] fn fallocate( - &mut self, + &self, _req: &Request<'_>, inode: u64, _fh: u64, @@ -1819,7 +1819,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..9005973ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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/request.rs b/src/request.rs index e25a4c646..b22b56389 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); } diff --git a/src/session.rs b/src/session.rs index 956befdc9..9418f504c 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,16 @@ 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<()> { // 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]; @@ -227,9 +226,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 +257,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 { From 3cda4ae4828badf7269c99beb7b39ad2a5459631 Mon Sep 17 00:00:00 2001 From: Alessandro Passaro Date: Wed, 26 Jul 2023 11:21:15 +0100 Subject: [PATCH 09/12] Expose forget requests Add `is_forget()` method to `Request`. Signed-off-by: Alessandro Passaro --- src/ll/request.rs | 6 +++++- src/request.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ll/request.rs b/src/ll/request.rs index 5f6961e9b..ceeff9510 100644 --- a/src/ll/request.rs +++ b/src/ll/request.rs @@ -2166,11 +2166,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/request.rs b/src/request.rs index b22b56389..8036af798 100644 --- a/src/request.rs +++ b/src/request.rs @@ -672,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, + } + } } From db089f215727efaebbf393c6a425499d0309a893 Mon Sep 17 00:00:00 2001 From: Alessandro Passaro Date: Wed, 26 Jul 2023 11:21:32 +0100 Subject: [PATCH 10/12] Add callbacks to a fuse session run Introduce `Session::run_with_callbacks()` to notify callers before and after kernel messages are dispatched. Signed-off-by: Alessandro Passaro --- src/session.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/session.rs b/src/session.rs index 9418f504c..86daed561 100644 --- a/src/session.rs +++ b/src/session.rs @@ -141,6 +141,18 @@ impl Session { /// Run the session loop that receives kernel requests and dispatches them to method /// 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]; @@ -154,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, }, From e34ef86e0af5dd25400309da8f3c42e8fb5a28f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Varl=C4=B1?= Date: Tue, 5 Nov 2024 10:15:05 +0000 Subject: [PATCH 11/12] Make `mnt::Mount` public (#1098) ## Description of change fuser v0.15.0 added support for creating sessions from FUSE fd. I'm working on a PR to add this support to Mountpoint. We still need to open the FUSE device and call `mount` syscall in order to test this new behavior, and all this logic is already exists with `mnt::Mount`. We could just copy the logic from `mnt::Mount` - but since we already have this fork, making this change seemed fine to me. Maybe we can also consider upstreaming it. ## Does this change impact existing behavior? No ## Does this change need a changelog entry in any of the crates? No --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and I agree to the terms of the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). Signed-off-by: Burak Varli --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9005973ad..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")] From 1713543724fa7081ff90edbd1bbc9e2215867463 Mon Sep 17 00:00:00 2001 From: Daniel Carl Jones Date: Wed, 6 Nov 2024 17:44:56 +0000 Subject: [PATCH 12/12] Add missing documentation for fuser Mount structs (#1105) Our fuser fork emits a lot of warnings during builds, impacting our pull requests. This change addresses missing documentation on one struct in fuser (which was introduced by our change in https://github.com/awslabs/mountpoint-s3/pull/1098). This change is a good candidate to be contributed to upstream. Once contributed, this commit can be removed. Relevant issues: N/A No. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and I agree to the terms of the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). --------- Signed-off-by: Daniel Carl Jones --- src/mnt/fuse2.rs | 6 ++++++ src/mnt/fuse3.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/mnt/fuse2.rs b/src/mnt/fuse2.rs index 5ce8fe175..ccdf0d819 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/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| {