From 3593110b787a3159ae114b80d04e79248ae55833 Mon Sep 17 00:00:00 2001 From: Yusuke Sasaki Date: Sun, 2 Nov 2025 23:50:42 +0900 Subject: [PATCH 1/3] add signal handling to examples --- Cargo.toml | 1 + examples/basic.rs | 43 +++++++----- examples/heartbeat.rs | 11 +++- examples/heartbeat_entry.rs | 11 +++- examples/hello.rs | 127 ++++++++++++++++++++---------------- examples/poll.rs | 11 +++- src/mount.rs | 5 +- 7 files changed, 125 insertions(+), 84 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e00a399..db39e462 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ bytes = "1.10.1" chrono = "0.4.41" dashmap = "6.1" pico-args = "0.5" +signal-hook = "0.3.18" slab = ">=0.4.11" tempfile = "3.23.0" tokio = { version = ">=1.8.4", features = [ "macros", "fs", "io-util", "net", "rt-multi-thread", "sync", "time" ] } diff --git a/examples/basic.rs b/examples/basic.rs index 81f9a09f..17f866f5 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,5 +1,6 @@ #![forbid(unsafe_code)] +use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, op::{self, Operation}, @@ -14,7 +15,8 @@ use rustix::{ io::Errno, process::{getgid, getuid}, }; -use std::{io, path::PathBuf, time::Duration}; +use signal_hook::iterator::Signals; +use std::{io, path::PathBuf, thread, time::Duration}; const CONTENT: &[u8] = b"Hello from FUSE!\n"; @@ -30,21 +32,30 @@ fn main() -> Result<()> { let (session, device, mount) = polyfuse::connect(mountpoint, MountOptions::new(), KernelConfig::new())?; - // Receive an incoming FUSE request from the kernel. - let mut buf = session.new_fallback_buffer(); - while session.recv_request(&device, &mut buf)? { - let (req, op, _remains) = session.decode(&device, &mut buf)?; - match op { - // Dispatch your callbacks to the supported operations... - Operation::Getattr(op) => getattr(req, op)?, - Operation::Read(op) => read(req, op)?, - - // Or annotate that the operation is not supported. - _ => req.reply_error(Errno::NOSYS)?, - }; - } - - mount.unmount()?; + thread::scope(|scope| -> Result<()> { + let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; + scope.spawn(move || { + if let Some(_sig) = signals.forever().next() { + let _ = mount.unmount(); + } + }); + + // Receive an incoming FUSE request from the kernel. + let mut buf = session.new_fallback_buffer(); + while session.recv_request(&device, &mut buf)? { + let (req, op, _remains) = session.decode(&device, &mut buf)?; + match op { + // Dispatch your callbacks to the supported operations... + Operation::Getattr(op) => getattr(req, op)?, + Operation::Read(op) => read(req, op)?, + + // Or annotate that the operation is not supported. + _ => req.reply_error(Errno::NOSYS)?, + }; + } + + Ok(()) + })?; Ok(()) } diff --git a/examples/heartbeat.rs b/examples/heartbeat.rs index 86ed79e0..4bf44416 100644 --- a/examples/heartbeat.rs +++ b/examples/heartbeat.rs @@ -8,6 +8,7 @@ #![deny(clippy::unimplemented)] #![forbid(unsafe_code)] +use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, notify::Notifier as _, @@ -22,6 +23,7 @@ use anyhow::{anyhow, ensure, Context as _, Result}; use chrono::Local; use dashmap::DashMap; use rustix::{io::Errno, param::page_size}; +use signal_hook::iterator::Signals; use std::{ io::{self, prelude::*}, path::PathBuf, @@ -51,6 +53,13 @@ fn main() -> Result<()> { let conn = &conn; let session = &session; thread::scope(|scope| -> Result<()> { + let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; + scope.spawn(move || { + if let Some(_sig) = signals.forever().next() { + let _ = mount.unmount(); + } + }); + // Spawn a task that beats the heart. scope.spawn({ let fs = fs.clone(); @@ -127,8 +136,6 @@ fn main() -> Result<()> { Ok(()) })?; - mount.unmount()?; - Ok(()) } diff --git a/examples/heartbeat_entry.rs b/examples/heartbeat_entry.rs index 2b13391f..b87a52da 100644 --- a/examples/heartbeat_entry.rs +++ b/examples/heartbeat_entry.rs @@ -8,6 +8,7 @@ #![deny(clippy::unimplemented, clippy::todo)] #![forbid(unsafe_code)] +use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, notify::Notifier as _, @@ -21,6 +22,7 @@ use polyfuse::{ use anyhow::{ensure, Context as _, Result}; use chrono::Local; use rustix::io::Errno; +use signal_hook::iterator::Signals; use std::{ io, mem, os::unix::prelude::*, @@ -66,6 +68,13 @@ fn main() -> Result<()> { let session = &session; let conn = &conn; thread::scope(|scope| -> Result<()> { + let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; + scope.spawn(move || { + if let Some(_sig) = signals.forever().next() { + let _ = mount.unmount(); + } + }); + // spawn heartbeat thread. scope.spawn({ let fs = fs.clone(); @@ -152,8 +161,6 @@ fn main() -> Result<()> { Ok(()) })?; - mount.unmount()?; - Ok(()) } diff --git a/examples/hello.rs b/examples/hello.rs index d8c4a839..637f599f 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -2,6 +2,7 @@ #![deny(clippy::unimplemented)] #![forbid(unsafe_code)] +use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, op::Operation, @@ -16,7 +17,8 @@ use rustix::{ io::Errno, process::{getgid, getuid}, }; -use std::{os::unix::prelude::*, path::PathBuf, time::Duration}; +use signal_hook::iterator::Signals; +use std::{os::unix::prelude::*, path::PathBuf, thread, time::Duration}; const TTL: Duration = Duration::from_secs(60 * 60 * 24 * 365); const HELLO_INO: NodeID = match NodeID::from_raw(2) { @@ -39,78 +41,87 @@ fn main() -> Result<()> { let fs = Hello::new(); - let mut buf = session.new_splice_buffer()?; - while session.recv_request(&conn, &mut buf)? { - let (req, op, _remains) = session.decode(&conn, &mut buf)?; - match op { - Operation::Lookup(op) => match op.parent { - NodeID::ROOT if op.name.as_bytes() == HELLO_FILENAME.as_bytes() => { - req.reply_entry(Some(HELLO_INO), &fs.hello_attr(), 0, Some(TTL), Some(TTL))? - } - _ => req.reply_error(Errno::NOENT)?, - }, - - Operation::Getattr(op) => { - let attr = match op.ino { - NodeID::ROOT => fs.root_attr(), - HELLO_INO => fs.hello_attr(), - _ => Err(Errno::NOENT)?, - }; - req.reply_attr(&attr, Some(TTL))?; + thread::scope(|scope| -> Result<()> { + let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; + scope.spawn(move || { + if let Some(_sig) = signals.forever().next() { + let _ = mount.unmount(); } - - Operation::Read(op) => { - match op.ino { - HELLO_INO => (), - NodeID::ROOT => { - req.reply_error(Errno::ISDIR)?; - continue; - } - _ => { - req.reply_error(Errno::NOENT)?; - continue; + }); + + let mut buf = session.new_splice_buffer()?; + while session.recv_request(&conn, &mut buf)? { + let (req, op, _remains) = session.decode(&conn, &mut buf)?; + match op { + Operation::Lookup(op) => match op.parent { + NodeID::ROOT if op.name.as_bytes() == HELLO_FILENAME.as_bytes() => { + req.reply_entry(Some(HELLO_INO), &fs.hello_attr(), 0, Some(TTL), Some(TTL))? } + _ => req.reply_error(Errno::NOENT)?, + }, + + Operation::Getattr(op) => { + let attr = match op.ino { + NodeID::ROOT => fs.root_attr(), + HELLO_INO => fs.hello_attr(), + _ => Err(Errno::NOENT)?, + }; + req.reply_attr(&attr, Some(TTL))?; } - let mut data: &[u8] = &[]; + Operation::Read(op) => { + match op.ino { + HELLO_INO => (), + NodeID::ROOT => { + req.reply_error(Errno::ISDIR)?; + continue; + } + _ => { + req.reply_error(Errno::NOENT)?; + continue; + } + } - let offset = op.offset as usize; - if offset < HELLO_CONTENT.len() { - let size = op.size as usize; - data = &HELLO_CONTENT[offset..]; - data = &data[..std::cmp::min(data.len(), size)]; - } + let mut data: &[u8] = &[]; - req.reply_bytes(data)?; - } + let offset = op.offset as usize; + if offset < HELLO_CONTENT.len() { + let size = op.size as usize; + data = &HELLO_CONTENT[offset..]; + data = &data[..std::cmp::min(data.len(), size)]; + } - Operation::Readdir(op) => { - if op.ino != NodeID::ROOT { - req.reply_error(Errno::NOTDIR)?; - continue; + req.reply_bytes(data)?; } - let mut buf = DirEntryBuf::new(op.size as usize); - for (i, entry) in fs.dir_entries().skip(op.offset as usize) { - let full = buf.push_entry( - entry.name.as_ref(), // - entry.ino, - entry.typ, - i + 1, - ); - if full { - break; + Operation::Readdir(op) => { + if op.ino != NodeID::ROOT { + req.reply_error(Errno::NOTDIR)?; + continue; + } + + let mut buf = DirEntryBuf::new(op.size as usize); + for (i, entry) in fs.dir_entries().skip(op.offset as usize) { + let full = buf.push_entry( + entry.name.as_ref(), // + entry.ino, + entry.typ, + i + 1, + ); + if full { + break; + } } + + req.reply_dir(&buf)?; } - req.reply_dir(&buf)?; + _ => req.reply_error(Errno::NOSYS)?, } - - _ => req.reply_error(Errno::NOSYS)?, } - } - mount.unmount()?; + Ok(()) + })?; Ok(()) } diff --git a/examples/poll.rs b/examples/poll.rs index 95df086a..5cb0eba0 100644 --- a/examples/poll.rs +++ b/examples/poll.rs @@ -1,3 +1,4 @@ +use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, notify::Notifier as _, @@ -14,6 +15,7 @@ use rustix::{ io::Errno, process::{getgid, getuid}, }; +use signal_hook::iterator::Signals; use std::{ collections::HashMap, path::PathBuf, @@ -49,6 +51,13 @@ fn main() -> Result<()> { let conn = &conn; let session = &session; thread::scope(|scope| -> Result<()> { + let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; + scope.spawn(move || { + if let Some(_sig) = signals.forever().next() { + let _ = mount.unmount(); + } + }); + let mut buf = session.new_splice_buffer()?; while session.recv_request(conn, &mut buf)? { let (req, op, _remains) = session.decode(conn, &mut buf)?; @@ -170,8 +179,6 @@ fn main() -> Result<()> { Ok(()) })?; - mount.unmount()?; - Ok(()) } diff --git a/src/mount.rs b/src/mount.rs index dd9c19bd..acddc1ba 100644 --- a/src/mount.rs +++ b/src/mount.rs @@ -182,10 +182,7 @@ impl Mount { match mem::replace(&mut self.kind, MountKind::Gone) { MountKind::Priv(mount) => mount.unmount(&self.mountpoint, &self.mountopts), MountKind::Unpriv(mount) => mount.unmount(&self.mountpoint, &self.mountopts), - MountKind::Gone => { - tracing::warn!("unmounted twice"); - Ok(()) - } + MountKind::Gone => Ok(()), } } } From c1b0beddb8e690f8ea3bce701f33843da696ba0d Mon Sep 17 00:00:00 2001 From: Yusuke Sasaki Date: Mon, 3 Nov 2025 00:14:06 +0900 Subject: [PATCH 2/3] reorder use statements + remove redundant allocation --- examples/basic.rs | 2 +- examples/heartbeat.rs | 22 ++++++++++------------ examples/heartbeat_entry.rs | 19 +++++-------------- examples/hello.rs | 2 +- examples/poll.rs | 7 +++++-- 5 files changed, 22 insertions(+), 30 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 17f866f5..7a4f2fcb 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_code)] -use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, op::{self, Operation}, @@ -11,6 +10,7 @@ use polyfuse::{ }; use anyhow::{ensure, Context as _, Result}; +use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{ io::Errno, process::{getgid, getuid}, diff --git a/examples/heartbeat.rs b/examples/heartbeat.rs index 4bf44416..645ad4d8 100644 --- a/examples/heartbeat.rs +++ b/examples/heartbeat.rs @@ -8,7 +8,6 @@ #![deny(clippy::unimplemented)] #![forbid(unsafe_code)] -use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, notify::Notifier as _, @@ -22,12 +21,13 @@ use polyfuse::{ use anyhow::{anyhow, ensure, Context as _, Result}; use chrono::Local; use dashmap::DashMap; +use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{io::Errno, param::page_size}; use signal_hook::iterator::Signals; use std::{ io::{self, prelude::*}, path::PathBuf, - sync::{Arc, Mutex}, + sync::Mutex, thread, time::Duration, }; @@ -45,13 +45,14 @@ fn main() -> Result<()> { let mountpoint: PathBuf = args.opt_free_from_str()?.context("missing mountpoint")?; ensure!(mountpoint.is_file(), "mountpoint must be a regular file"); - let fs = Arc::new(Heartbeat::new(kind, update_interval)); + let fs = Heartbeat::new(kind, update_interval); let (session, conn, mount) = polyfuse::connect(mountpoint, MountOptions::new(), KernelConfig::new())?; let conn = &conn; let session = &session; + let fs = &fs; thread::scope(|scope| -> Result<()> { let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; scope.spawn(move || { @@ -61,15 +62,12 @@ fn main() -> Result<()> { }); // Spawn a task that beats the heart. - scope.spawn({ - let fs = fs.clone(); - move || -> Result<()> { - loop { - tracing::info!("heartbeat"); - fs.update_content(); - fs.notify(session, conn)?; - thread::sleep(fs.update_interval); - } + scope.spawn(move || -> Result<()> { + loop { + tracing::info!("heartbeat"); + fs.update_content(); + fs.notify(session, conn)?; + thread::sleep(fs.update_interval); } }); diff --git a/examples/heartbeat_entry.rs b/examples/heartbeat_entry.rs index b87a52da..07d9fa9b 100644 --- a/examples/heartbeat_entry.rs +++ b/examples/heartbeat_entry.rs @@ -8,7 +8,6 @@ #![deny(clippy::unimplemented, clippy::todo)] #![forbid(unsafe_code)] -use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, notify::Notifier as _, @@ -21,16 +20,10 @@ use polyfuse::{ use anyhow::{ensure, Context as _, Result}; use chrono::Local; +use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::io::Errno; use signal_hook::iterator::Signals; -use std::{ - io, mem, - os::unix::prelude::*, - path::PathBuf, - sync::{Arc, Mutex}, - thread, - time::Duration, -}; +use std::{io, mem, os::unix::prelude::*, path::PathBuf, sync::Mutex, thread, time::Duration}; const FILE_INO: NodeID = match NodeID::from_raw(2) { Some(ino) => ino, @@ -60,13 +53,14 @@ fn main() -> Result<()> { let mountpoint: PathBuf = args.opt_free_from_str()?.context("missing mountpoint")?; ensure!(mountpoint.is_dir(), "mountpoint must be a directory"); - let fs = Arc::new(Heartbeat::new(ttl, update_interval, no_notify)); + let fs = Heartbeat::new(ttl, update_interval, no_notify); let (session, conn, mount) = polyfuse::connect(mountpoint, MountOptions::new(), KernelConfig::new())?; let session = &session; let conn = &conn; + let fs = &fs; thread::scope(|scope| -> Result<()> { let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; scope.spawn(move || { @@ -76,10 +70,7 @@ fn main() -> Result<()> { }); // spawn heartbeat thread. - scope.spawn({ - let fs = fs.clone(); - move || fs.heartbeat(session, conn) - }); + scope.spawn(move || fs.heartbeat(session, conn)); let mut buf = session.new_splice_buffer()?; while session.recv_request(conn, &mut buf)? { diff --git a/examples/hello.rs b/examples/hello.rs index 637f599f..40d67a4d 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -2,7 +2,6 @@ #![deny(clippy::unimplemented)] #![forbid(unsafe_code)] -use libc::{SIGHUP, SIGINT, SIGTERM}; use polyfuse::{ mount::MountOptions, op::Operation, @@ -12,6 +11,7 @@ use polyfuse::{ }; use anyhow::{ensure, Context as _, Result}; +use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{ fs::{Gid, Uid}, io::Errno, diff --git a/examples/poll.rs b/examples/poll.rs index 5cb0eba0..de65f290 100644 --- a/examples/poll.rs +++ b/examples/poll.rs @@ -1,4 +1,5 @@ -use libc::{SIGHUP, SIGINT, SIGTERM}; +#![forbid(unsafe_code)] + use polyfuse::{ mount::MountOptions, notify::Notifier as _, @@ -11,6 +12,7 @@ use polyfuse::{ }; use anyhow::{ensure, Context as _, Result}; +use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{ io::Errno, process::{getgid, getuid}, @@ -40,7 +42,7 @@ fn main() -> Result<()> { .unwrap_or(5), ); - let fs = Arc::new(PollFS::new(wakeup_interval)); + let fs = PollFS::new(wakeup_interval); let mountpoint: PathBuf = args.opt_free_from_str()?.context("missing mountpoint")?; ensure!(mountpoint.is_file(), "mountpoint must be a regular file"); @@ -50,6 +52,7 @@ fn main() -> Result<()> { let conn = &conn; let session = &session; + let fs = &fs; thread::scope(|scope| -> Result<()> { let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; scope.spawn(move || { From b3cb1ba05896da8cef0cbff554a3a7bd1d6e058b Mon Sep 17 00:00:00 2001 From: Yusuke Sasaki Date: Mon, 3 Nov 2025 02:04:39 +0900 Subject: [PATCH 3/3] add signal handling utilities to Mount --- Cargo.toml | 4 ++-- examples/basic.rs | 12 ++++-------- examples/heartbeat.rs | 12 ++++-------- examples/heartbeat_entry.rs | 12 ++++-------- examples/hello.rs | 12 ++++-------- examples/poll.rs | 12 ++++-------- src/mount.rs | 29 +++++++++++++++++++++-------- 7 files changed, 43 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db39e462..ead224bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ either = "1" libc = "0.2" num_cpus = "1.17" polyfuse-kernel = { version = "0.3.0-dev", path = "crates/polyfuse-kernel" } -rustix = { version = "1", features = [ "fs", "mount", "net", "param", "pipe", "process", "thread", "use-libc" ] } +rustix = { version = "1", features = [ "fs", "mount", "net", "param", "pipe", "process", "io_uring", "thread", "use-libc" ] } +signal-hook = "0.3.18" thiserror = "2" tracing = "0.1" zerocopy = "0.8.26" @@ -34,7 +35,6 @@ bytes = "1.10.1" chrono = "0.4.41" dashmap = "6.1" pico-args = "0.5" -signal-hook = "0.3.18" slab = ">=0.4.11" tempfile = "3.23.0" tokio = { version = ">=1.8.4", features = [ "macros", "fs", "io-util", "net", "rt-multi-thread", "sync", "time" ] } diff --git a/examples/basic.rs b/examples/basic.rs index 7a4f2fcb..433b04d7 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -10,12 +10,10 @@ use polyfuse::{ }; use anyhow::{ensure, Context as _, Result}; -use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{ io::Errno, process::{getgid, getuid}, }; -use signal_hook::iterator::Signals; use std::{io, path::PathBuf, thread, time::Duration}; const CONTENT: &[u8] = b"Hello from FUSE!\n"; @@ -33,12 +31,8 @@ fn main() -> Result<()> { polyfuse::connect(mountpoint, MountOptions::new(), KernelConfig::new())?; thread::scope(|scope| -> Result<()> { - let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; - scope.spawn(move || { - if let Some(_sig) = signals.forever().next() { - let _ = mount.unmount(); - } - }); + let mount_handle = mount.handle(); + scope.spawn(move || mount.unmount_after_interrupted()); // Receive an incoming FUSE request from the kernel. let mut buf = session.new_fallback_buffer(); @@ -54,6 +48,8 @@ fn main() -> Result<()> { }; } + mount_handle.close(); + Ok(()) })?; diff --git a/examples/heartbeat.rs b/examples/heartbeat.rs index 645ad4d8..d6db9719 100644 --- a/examples/heartbeat.rs +++ b/examples/heartbeat.rs @@ -21,9 +21,7 @@ use polyfuse::{ use anyhow::{anyhow, ensure, Context as _, Result}; use chrono::Local; use dashmap::DashMap; -use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{io::Errno, param::page_size}; -use signal_hook::iterator::Signals; use std::{ io::{self, prelude::*}, path::PathBuf, @@ -54,12 +52,8 @@ fn main() -> Result<()> { let session = &session; let fs = &fs; thread::scope(|scope| -> Result<()> { - let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; - scope.spawn(move || { - if let Some(_sig) = signals.forever().next() { - let _ = mount.unmount(); - } - }); + let mount_handle = mount.handle(); + scope.spawn(move || mount.unmount_after_interrupted()); // Spawn a task that beats the heart. scope.spawn(move || -> Result<()> { @@ -131,6 +125,8 @@ fn main() -> Result<()> { } } + mount_handle.close(); + Ok(()) })?; diff --git a/examples/heartbeat_entry.rs b/examples/heartbeat_entry.rs index 07d9fa9b..aa25e2d1 100644 --- a/examples/heartbeat_entry.rs +++ b/examples/heartbeat_entry.rs @@ -20,9 +20,7 @@ use polyfuse::{ use anyhow::{ensure, Context as _, Result}; use chrono::Local; -use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::io::Errno; -use signal_hook::iterator::Signals; use std::{io, mem, os::unix::prelude::*, path::PathBuf, sync::Mutex, thread, time::Duration}; const FILE_INO: NodeID = match NodeID::from_raw(2) { @@ -62,12 +60,8 @@ fn main() -> Result<()> { let conn = &conn; let fs = &fs; thread::scope(|scope| -> Result<()> { - let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; - scope.spawn(move || { - if let Some(_sig) = signals.forever().next() { - let _ = mount.unmount(); - } - }); + let mount_handle = mount.handle(); + scope.spawn(move || mount.unmount_after_interrupted()); // spawn heartbeat thread. scope.spawn(move || fs.heartbeat(session, conn)); @@ -149,6 +143,8 @@ fn main() -> Result<()> { } } + mount_handle.close(); + Ok(()) })?; diff --git a/examples/hello.rs b/examples/hello.rs index 40d67a4d..7224ab4a 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -11,13 +11,11 @@ use polyfuse::{ }; use anyhow::{ensure, Context as _, Result}; -use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{ fs::{Gid, Uid}, io::Errno, process::{getgid, getuid}, }; -use signal_hook::iterator::Signals; use std::{os::unix::prelude::*, path::PathBuf, thread, time::Duration}; const TTL: Duration = Duration::from_secs(60 * 60 * 24 * 365); @@ -42,12 +40,8 @@ fn main() -> Result<()> { let fs = Hello::new(); thread::scope(|scope| -> Result<()> { - let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; - scope.spawn(move || { - if let Some(_sig) = signals.forever().next() { - let _ = mount.unmount(); - } - }); + let mount_handle = mount.handle(); + scope.spawn(move || mount.unmount_after_interrupted()); let mut buf = session.new_splice_buffer()?; while session.recv_request(&conn, &mut buf)? { @@ -120,6 +114,8 @@ fn main() -> Result<()> { } } + mount_handle.close(); + Ok(()) })?; diff --git a/examples/poll.rs b/examples/poll.rs index de65f290..770c6da5 100644 --- a/examples/poll.rs +++ b/examples/poll.rs @@ -12,12 +12,10 @@ use polyfuse::{ }; use anyhow::{ensure, Context as _, Result}; -use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{ io::Errno, process::{getgid, getuid}, }; -use signal_hook::iterator::Signals; use std::{ collections::HashMap, path::PathBuf, @@ -54,12 +52,8 @@ fn main() -> Result<()> { let session = &session; let fs = &fs; thread::scope(|scope| -> Result<()> { - let mut signals = Signals::new([SIGTERM, SIGHUP, SIGINT])?; - scope.spawn(move || { - if let Some(_sig) = signals.forever().next() { - let _ = mount.unmount(); - } - }); + let mount_handle = mount.handle(); + scope.spawn(move || mount.unmount_after_interrupted()); let mut buf = session.new_splice_buffer()?; while session.recv_request(conn, &mut buf)? { @@ -179,6 +173,8 @@ fn main() -> Result<()> { } } + mount_handle.close(); + Ok(()) })?; diff --git a/src/mount.rs b/src/mount.rs index acddc1ba..c53797c3 100644 --- a/src/mount.rs +++ b/src/mount.rs @@ -3,7 +3,9 @@ mod unpriv_mount; use self::{priv_mount::PrivMount, unpriv_mount::UnprivMount}; use bitflags::{bitflags, bitflags_match}; +use libc::{SIGHUP, SIGINT, SIGTERM}; use rustix::{io::Errno, mount::MountFlags}; +use signal_hook::iterator::Signals; use std::{borrow::Cow, io, mem, os::fd::OwnedFd, path::Path}; // refs: @@ -148,6 +150,7 @@ pub struct Mount { kind: MountKind, mountpoint: Cow<'static, Path>, mountopts: MountOptions, + signals: Signals, } #[derive(Debug)] @@ -174,8 +177,21 @@ impl Mount { } #[inline] - pub fn unmount(mut self) -> io::Result<()> { - self.unmount_() + pub fn handle(&self) -> signal_hook::iterator::Handle { + self.signals.handle() + } + + #[inline] + pub fn unmount_after_interrupted(mut self) -> io::Result<()> { + match self.signals.wait().next() { + Some(sig) => tracing::debug!( + "caught the interruption signal: {:?}", + rustix::io_uring::Signal::from_named_raw(sig) + ), + None => tracing::debug!("caught the closing event from the handle"), + } + self.unmount_()?; + Ok(()) } fn unmount_(&mut self) -> io::Result<()> { @@ -187,12 +203,6 @@ impl Mount { } } -impl Drop for Mount { - fn drop(&mut self) { - let _ = self.unmount_(); - } -} - pub(crate) fn mount( mountpoint: Cow<'static, Path>, mut mountopts: MountOptions, @@ -225,12 +235,15 @@ pub(crate) fn mount( }, }; + let signals = Signals::new([SIGHUP, SIGTERM, SIGINT])?; + Ok(( fd, Mount { kind, mountpoint, mountopts, + signals, }, )) }