diff options
Diffstat (limited to '')
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/passthrough.rs | 9 | ||||
| -rw-r--r-- | src/fuse/mount.rs | 37 | ||||
| -rw-r--r-- | src/fuse/session.rs | 57 | ||||
| -rw-r--r-- | src/util.rs | 10 |
5 files changed, 93 insertions, 22 deletions
@@ -26,5 +26,5 @@ tokio = { version = "1.15.0", features = ["rt", "net", "macros", "sync"] } clap = "2.34.0" env_logger = "0.9.0" futures-util = "0.3.19" -tokio = { version = "1.15.0", features = ["fs", "io-util", "rt-multi-thread"] } +tokio = { version = "1.15.0", features = ["fs", "io-util", "rt-multi-thread", "signal"] } uuid = "0.8.2" diff --git a/examples/passthrough.rs b/examples/passthrough.rs index c25d728..5ecb2e8 100644 --- a/examples/passthrough.rs +++ b/examples/passthrough.rs @@ -389,5 +389,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { } }; - Ok(Runtime::new()?.block_on(main_loop(session, fs))?) + let result = Runtime::new()?.block_on(async move { + tokio::select! { + result = main_loop(session, fs) => result, + _ = tokio::signal::ctrl_c() => Ok(()), + } + }); + + Ok(result?) } diff --git a/src/fuse/mount.rs b/src/fuse/mount.rs index 9b03961..c924a9a 100644 --- a/src/fuse/mount.rs +++ b/src/fuse/mount.rs @@ -6,6 +6,7 @@ use std::{ io::{AsRawFd, RawFd}, net::UnixStream, }, + path::{Path, PathBuf}, process::Command, }; @@ -97,7 +98,7 @@ impl<O: AsRef<OsStr>> Extend<O> for Options { pub fn mount_sync<M>(mountpoint: M, options: &Options) -> Result<Start, MountError> where - M: AsRef<OsStr>, + M: AsRef<Path> + Into<PathBuf>, { let (left_side, right_side) = UnixStream::pair()?; @@ -112,24 +113,18 @@ where ) .unwrap(); - let mut command = Command::new("fusermount3"); + let mut command = Command::new(FUSERMOUNT_CMD); if !options.0.is_empty() { - command.args(&[ - OsStr::new("-o"), - &options.0, - OsStr::new("--"), - mountpoint.as_ref(), - ]); - } else { - command.arg(mountpoint); - }; + command.args(&[OsStr::new("-o"), &options.0]); + } + command.args(&[OsStr::new("--"), mountpoint.as_ref().as_ref()]); let mut fusermount = command.env("_FUSE_COMMFD", right_fd.to_string()).spawn()?; // recvmsg() should fail if fusermount exits (last open fd is closed) drop(right_side); - let session_fd = (|| { + let session_fd = { let mut buffer = cmsg_space!(RawFd); let message = recvmsg( left_side.as_raw_fd(), @@ -145,10 +140,10 @@ where }; session_fd.ok_or(MountError::Fusermount) - })(); + }; match session_fd { - Ok(session_fd) => Ok(Start::new(DumbFd(session_fd))), + Ok(session_fd) => Ok(Start::new(DumbFd(session_fd), mountpoint.into())), Err(error) => { drop(left_side); @@ -157,3 +152,17 @@ where } } } + +pub(crate) fn unmount_sync<M: AsRef<OsStr>>(mountpoint: M) -> Result<(), MountError> { + let status = Command::new(FUSERMOUNT_CMD) + .args(&[OsStr::new("-zuq"), OsStr::new("--"), mountpoint.as_ref()]) + .status()?; + + if status.success() { + Ok(()) + } else { + Err(MountError::Fusermount) + } +} + +const FUSERMOUNT_CMD: &str = "fusermount3"; diff --git a/src/fuse/session.rs b/src/fuse/session.rs index d7b6501..55fedcb 100644 --- a/src/fuse/session.rs +++ b/src/fuse/session.rs @@ -4,6 +4,7 @@ use std::{ marker::PhantomData, ops::ControlFlow, os::unix::io::{IntoRawFd, RawFd}, + path::PathBuf, sync::{Arc, Mutex}, }; @@ -22,6 +23,7 @@ use bytemuck::bytes_of; use smallvec::SmallVec; use crate::{ + mount::{unmount_sync, MountError}, proto::{self, InHeader, Structured}, util::{page_size, DumbFd, OutputChain}, Errno, FuseError, FuseResult, @@ -34,6 +36,7 @@ use super::{ pub struct Start { session_fd: DumbFd, + mountpoint: PathBuf, } pub struct Session { @@ -42,6 +45,7 @@ pub struct Session { buffers: Mutex<Vec<Buffer>>, buffer_semaphore: Arc<Semaphore>, buffer_pages: usize, + mountpoint: Mutex<Option<PathBuf>>, } pub struct Endpoint<'a> { @@ -93,6 +97,15 @@ impl Session { } } + pub fn unmount_sync(&self) -> Result<(), MountError> { + let mountpoint = self.mountpoint.lock().unwrap().take(); + if let Some(mountpoint) = &mountpoint { + unmount_sync(mountpoint)?; + } + + Ok(()) + } + pub(crate) fn ok(&self, unique: u64, output: OutputChain<'_>) -> FuseResult<()> { self.send(unique, 0, output) } @@ -212,6 +225,24 @@ impl Session { } } +impl Drop for Start { + fn drop(&mut self) { + if !self.mountpoint.as_os_str().is_empty() { + let _ = unmount_sync(&self.mountpoint); + } + } +} + +impl Drop for Session { + fn drop(&mut self) { + if let Some(mountpoint) = self.mountpoint.get_mut().unwrap().take() { + let _ = unmount_sync(&mountpoint); + } + + drop(DumbFd(*self.session_fd.get_ref())); // Close + } +} + impl<'o> Dispatch<'o> { pub fn op(self) -> Op<'o> { use Dispatch::*; @@ -253,7 +284,11 @@ impl Endpoint<'_> { let mut readable = tokio::select! { readable = session_fd.readable() => readable?, - _ = session_fd.writable() => return Ok(ControlFlow::Break(())), + + _ = session_fd.writable() => { + self.session.mountpoint.lock().unwrap().take(); + return Ok(ControlFlow::Break(())); + } }; let mut read = |fd: &AsyncFd<RawFd>| read(*fd.get_ref(), buffer); @@ -264,6 +299,7 @@ impl Endpoint<'_> { match result { // Interrupted + //TODO: libfuse docs say that this has some side effects Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue, result => break result, @@ -330,11 +366,12 @@ impl Endpoint<'_> { } impl Start { - pub async fn start<F>(self, mut init: F) -> FuseResult<Arc<Session>> + pub async fn start<F>(mut self, mut init: F) -> FuseResult<Arc<Session>> where F: FnOnce(Op<'_, ops::Init>) -> Done<'_>, { - let session_fd = self.session_fd.into_raw_fd(); + let mountpoint = std::mem::take(&mut self.mountpoint); + let session_fd = self.session_fd.take().into_raw_fd(); let flags = OFlag::O_NONBLOCK | OFlag::O_LARGEFILE; fcntl(session_fd, FcntlArg::F_SETFL(flags)).unwrap(); @@ -353,6 +390,7 @@ impl Start { buffers: Mutex::new(buffers), buffer_semaphore: Arc::new(Semaphore::new(buffer_count)), buffer_pages, + mountpoint: Mutex::new(Some(mountpoint)), }; let mut init_buffer = session.buffers.get_mut().unwrap().pop().unwrap(); @@ -368,8 +406,17 @@ impl Start { } } - pub(crate) fn new(session_fd: DumbFd) -> Self { - Start { session_fd } + pub fn unmount_sync(mut self) -> Result<(), MountError> { + // This prevents Start::drop() from unmounting a second time + let mountpoint = std::mem::take(&mut self.mountpoint); + unmount_sync(&mountpoint) + } + + pub(crate) fn new(session_fd: DumbFd, mountpoint: PathBuf) -> Self { + Start { + session_fd, + mountpoint, + } } } diff --git a/src/util.rs b/src/util.rs index c21442f..5da19c8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -29,6 +29,12 @@ pub struct OutputChain<'a> { pub struct OutputChainIter<'a>(Option<&'a OutputChain<'a>>); +impl DumbFd { + pub fn take(&mut self) -> DumbFd { + DumbFd(std::mem::replace(&mut self.0, -1)) + } +} + impl IntoRawFd for DumbFd { fn into_raw_fd(self) -> RawFd { let fd = self.0; @@ -39,7 +45,9 @@ impl IntoRawFd for DumbFd { impl Drop for DumbFd { fn drop(&mut self) { - let _ = close(self.0); + if !self.0.is_negative() { + let _ = close(self.0); + } } } |
