From 881b489680b6316cf5e8f270b4ef9f0f10609d9e Mon Sep 17 00:00:00 2001 From: Xu Jihui Date: Thu, 4 Sep 2025 14:59:59 +0800 Subject: [PATCH 1/3] feat(fusedev): add `try_with_writer` on `FuseSession`; update `with_writer` docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduce `try_with_writer` that builds a `FuseDevWriter` and returns the closure’s `Result`, converting `super::Error` via `From`. - Return `SessionFailure("invalid fuse session")` when `self.file` is absent. - Tweak `with_writer` doc to clarify it passes a writer to the given closure. example(passthrough): update example to show how to use try_with_writer --- src/transport/fusedev/linux_session.rs | 18 +++++++++++++++++- tests/passthrough/src/main.rs | 18 ++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/transport/fusedev/linux_session.rs b/src/transport/fusedev/linux_session.rs index bcc600ab..16885bc4 100644 --- a/src/transport/fusedev/linux_session.rs +++ b/src/transport/fusedev/linux_session.rs @@ -238,7 +238,7 @@ impl FuseSession { } } - /// Create a new fuse message channel with a specific buffer size. + /// Create a new fuse message writer and pass it to the given closure. pub fn with_writer(&mut self, f: F) where F: FnOnce(FuseDevWriter), @@ -251,6 +251,22 @@ impl FuseSession { } } + /// Create a new fuse message writer and pass it to the given closure. and return the result from the closure. + pub fn try_with_writer(&mut self, f: F) -> std::result::Result + where + F: FnOnce(FuseDevWriter) -> std::result::Result, + E: From, + { + if let Some(file) = &self.file { + let fd = file.as_raw_fd(); + let mut buf = vec![0x0u8; self.bufsize]; + let writer = FuseDevWriter::new(fd, &mut buf)?; + f(writer) + } else { + Err(SessionFailure("invalid fuse session".into()).into()) + } + } + /// Wake channel loop and exit pub fn wake(&self) -> Result<()> { let wakers = self diff --git a/tests/passthrough/src/main.rs b/tests/passthrough/src/main.rs index b7326b82..70d5fdee 100644 --- a/tests/passthrough/src/main.rs +++ b/tests/passthrough/src/main.rs @@ -23,6 +23,17 @@ pub struct Daemon { session: Option, } +pub enum PassthroughFsError { + FuseError(fuse_backend_rs::Error), + TransportError(fuse_backend_rs::transport::Error), +} + +impl From for PassthroughFsError { + fn from(e: fuse_backend_rs::transport::Error) -> Self { + PassthroughFsError::TransportError(e) + } +} + #[allow(dead_code)] impl Daemon { /// Creates a fusedev daemon instance @@ -59,11 +70,10 @@ impl Daemon { FuseSession::new(Path::new(&self.mountpoint), "testpassthrough", "", false).unwrap(); se.mount().unwrap(); - se.with_writer(|writer| { + se.try_with_writer(|writer| { self.server - .notify_resend(writer) - .unwrap_or_else(|e| println!("failed to send resend notification {}", e)); - }); + .notify_resend(writer).map_err(PassthroughFsError::FuseError) + }).map_err(|_| Error::from_raw_os_error(libc::EINVAL))?; for _ in 0..self.thread_cnt { let mut server = FuseServer { From eac176d0422ee7f08e2dd54cc05949b8d76b7d74 Mon Sep 17 00:00:00 2001 From: Xu Jihui Date: Fri, 5 Sep 2025 11:10:32 +0800 Subject: [PATCH 2/3] refactor: Extract writer methods to FuseSessionExt trait To reduce code duplication between the Linux and macOS FuseSession implementations, this change extracts the with_writer and try_with_writer helper methods into a new extension trait, FuseSessionExt. The trait provides default implementations for these methods, which rely on file() and bufsize() accessor methods that the concrete session types must provide. Both linux_session::FuseSession and macos_session::FuseSession now implement this trait, allowing them to share the common logic. --- src/transport/fusedev/linux_session.rs | 41 ++++++++------------------ src/transport/fusedev/macos_session.rs | 10 +++++++ src/transport/fusedev/mod.rs | 40 +++++++++++++++++++++++++ src/transport/mod.rs | 2 +- tests/passthrough/src/main.rs | 1 + 5 files changed, 64 insertions(+), 30 deletions(-) diff --git a/src/transport/fusedev/linux_session.rs b/src/transport/fusedev/linux_session.rs index 16885bc4..16a22bc3 100644 --- a/src/transport/fusedev/linux_session.rs +++ b/src/transport/fusedev/linux_session.rs @@ -24,6 +24,8 @@ use nix::poll::{poll, PollFd, PollFlags}; use nix::sys::epoll::{epoll_ctl, EpollEvent, EpollFlags, EpollOp}; use nix::unistd::{getgid, getuid, read}; +use crate::transport::fusedev::FuseSessionExt; + use super::{ super::pagesize, Error::{IoError, SessionFailure}, @@ -238,35 +240,6 @@ impl FuseSession { } } - /// Create a new fuse message writer and pass it to the given closure. - pub fn with_writer(&mut self, f: F) - where - F: FnOnce(FuseDevWriter), - { - if let Some(file) = &self.file { - let fd = file.as_raw_fd(); - let mut buf = vec![0x0u8; self.bufsize]; - let writer = FuseDevWriter::new(fd, &mut buf).unwrap(); - f(writer); - } - } - - /// Create a new fuse message writer and pass it to the given closure. and return the result from the closure. - pub fn try_with_writer(&mut self, f: F) -> std::result::Result - where - F: FnOnce(FuseDevWriter) -> std::result::Result, - E: From, - { - if let Some(file) = &self.file { - let fd = file.as_raw_fd(); - let mut buf = vec![0x0u8; self.bufsize]; - let writer = FuseDevWriter::new(fd, &mut buf)?; - f(writer) - } else { - Err(SessionFailure("invalid fuse session".into()).into()) - } - } - /// Wake channel loop and exit pub fn wake(&self) -> Result<()> { let wakers = self @@ -297,6 +270,16 @@ impl Drop for FuseSession { } } +impl FuseSessionExt for FuseSession { + fn file(&self) -> Option<&File> { + self.file.as_ref() + } + + fn bufsize(&self) -> usize { + self.bufsize + } +} + /// A fuse channel abstraction. /// /// Each session can hold multiple channels. diff --git a/src/transport/fusedev/macos_session.rs b/src/transport/fusedev/macos_session.rs index fa786ebc..a0f93d9d 100644 --- a/src/transport/fusedev/macos_session.rs +++ b/src/transport/fusedev/macos_session.rs @@ -203,6 +203,16 @@ impl Drop for FuseSession { } } +impl FuseSessionExt for FuseSession { + fn file(&self) -> Option<&File> { + self.file.as_ref() + } + + fn bufsize(&self) -> usize { + self.bufsize + } +} + /// A fuse channel abstruction. Each session can hold multiple channels. pub struct FuseChannel { file: File, diff --git a/src/transport/fusedev/mod.rs b/src/transport/fusedev/mod.rs index b9161db1..887e1c88 100644 --- a/src/transport/fusedev/mod.rs +++ b/src/transport/fusedev/mod.rs @@ -11,6 +11,7 @@ use std::collections::VecDeque; use std::io::{self, IoSlice, Write}; use std::marker::PhantomData; use std::mem::ManuallyDrop; +use std::os::fd::AsRawFd; use std::os::unix::io::RawFd; use nix::sys::uio::writev; @@ -334,6 +335,45 @@ impl Write for FuseDevWriter<'_, S> { } } +/// Extension trait for FuseSession to provide helper methods. +pub trait FuseSessionExt { + + /// Get the underlying file of the fuse session. + fn file(&self) -> Option<&std::fs::File>; + + /// Get the buffer size of the fuse session. + fn bufsize(&self) -> usize; + + /// Create a new fuse message writer and pass it to the given closure. + fn with_writer(&mut self, f: F) + where + F: FnOnce(FuseDevWriter), + { + if let Some(file) = self.file() { + let fd = file.as_raw_fd(); + let mut buf = vec![0x0u8; self.bufsize()]; + let writer = FuseDevWriter::new(fd, &mut buf).unwrap(); + f(writer); + } + } + + /// Create a new fuse message writer and pass it to the given closure. and return the result from the closure. + fn try_with_writer(&mut self, f: F) -> std::result::Result + where + F: FnOnce(FuseDevWriter) -> std::result::Result, + E: From, + { + if let Some(file) = self.file() { + let fd = file.as_raw_fd(); + let mut buf = vec![0x0u8; self.bufsize()]; + let writer = FuseDevWriter::new(fd, &mut buf)?; + f(writer) + } else { + Err(Error::SessionFailure("invalid fuse session".into()).into()) + } + } +} + #[cfg(feature = "async-io")] mod async_io { use super::*; diff --git a/src/transport/mod.rs b/src/transport/mod.rs index ef61080a..0835d40c 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -42,7 +42,7 @@ mod virtiofs; pub use self::fs_cache_req_handler::FsCacheReqHandler; #[cfg(feature = "fusedev")] -pub use self::fusedev::{FuseBuf, FuseChannel, FuseDevWriter, FuseSession}; +pub use self::fusedev::{FuseBuf, FuseChannel, FuseDevWriter, FuseSession, FuseSessionExt}; #[cfg(feature = "virtiofs")] pub use self::virtiofs::VirtioFsWriter; diff --git a/tests/passthrough/src/main.rs b/tests/passthrough/src/main.rs index 70d5fdee..138d7ab0 100644 --- a/tests/passthrough/src/main.rs +++ b/tests/passthrough/src/main.rs @@ -1,3 +1,4 @@ +use fuse_backend_rs::transport::FuseSessionExt as _; use log::{error, info, warn, LevelFilter}; use std::env; use std::fs; From a8f811af148d287976d50e3bc0eb4edefdcea4a5 Mon Sep 17 00:00:00 2001 From: Xu Jihui Date: Fri, 5 Sep 2025 11:14:22 +0800 Subject: [PATCH 3/3] feat(macos): Implement FuseSessionExt for FuseSession --- src/transport/fusedev/linux_session.rs | 3 +-- src/transport/fusedev/macos_session.rs | 1 + src/transport/fusedev/mod.rs | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/transport/fusedev/linux_session.rs b/src/transport/fusedev/linux_session.rs index 16a22bc3..0283241a 100644 --- a/src/transport/fusedev/linux_session.rs +++ b/src/transport/fusedev/linux_session.rs @@ -16,6 +16,7 @@ use std::os::unix::net::UnixStream; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use crate::transport::fusedev::FuseSessionExt; use mio::{Events, Poll, Token, Waker}; use nix::errno::Errno; use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag}; @@ -24,8 +25,6 @@ use nix::poll::{poll, PollFd, PollFlags}; use nix::sys::epoll::{epoll_ctl, EpollEvent, EpollFlags, EpollOp}; use nix::unistd::{getgid, getuid, read}; -use crate::transport::fusedev::FuseSessionExt; - use super::{ super::pagesize, Error::{IoError, SessionFailure}, diff --git a/src/transport/fusedev/macos_session.rs b/src/transport/fusedev/macos_session.rs index a0f93d9d..0c4a850e 100644 --- a/src/transport/fusedev/macos_session.rs +++ b/src/transport/fusedev/macos_session.rs @@ -35,6 +35,7 @@ use super::{ Error::IoError, Error::SessionFailure, FuseBuf, FuseDevWriter, Reader, Result, FUSE_HEADER_SIZE, FUSE_KERN_BUF_PAGES, }; +use crate::transport::fusedev::FuseSessionExt; use crate::transport::pagesize; const OSXFUSE_MOUNT_PROG: &str = "/Library/Filesystems/macfuse.fs/Contents/Resources/mount_macfuse"; diff --git a/src/transport/fusedev/mod.rs b/src/transport/fusedev/mod.rs index 887e1c88..051a4c8f 100644 --- a/src/transport/fusedev/mod.rs +++ b/src/transport/fusedev/mod.rs @@ -337,7 +337,6 @@ impl Write for FuseDevWriter<'_, S> { /// Extension trait for FuseSession to provide helper methods. pub trait FuseSessionExt { - /// Get the underlying file of the fuse session. fn file(&self) -> Option<&std::fs::File>;