Skip to content

Commit b97dae7

Browse files
authored
Allow wrapping an existing /dev/fuse file descriptor (#304)
This is important for container runtimes, which need to do a special namespace mount dance. Fixes #300.
1 parent bbba3db commit b97dae7

File tree

3 files changed

+55
-27
lines changed

3 files changed

+55
-27
lines changed

src/channel.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
use std::{fs::File, io, os::unix::prelude::AsRawFd, sync::Arc};
1+
use std::{
2+
fs::File,
3+
io,
4+
os::{
5+
fd::{AsFd, BorrowedFd},
6+
unix::prelude::AsRawFd,
7+
},
8+
sync::Arc,
9+
};
210

311
use libc::{c_int, c_void, size_t};
412

@@ -8,6 +16,12 @@ use crate::reply::ReplySender;
816
#[derive(Debug)]
917
pub struct Channel(Arc<File>);
1018

19+
impl AsFd for Channel {
20+
fn as_fd(&self) -> BorrowedFd<'_> {
21+
self.0.as_fd()
22+
}
23+
}
24+
1125
impl Channel {
1226
/// Create a new communication channel to the kernel driver by mounting the
1327
/// given path. The kernel driver will delegate filesystem operations of

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub use reply::{
4040
ReplyStatfs, ReplyWrite,
4141
};
4242
pub use request::Request;
43-
pub use session::{BackgroundSession, Session, SessionUnmounter};
43+
pub use session::{BackgroundSession, Session, SessionACL, SessionUnmounter};
4444
#[cfg(feature = "abi-7-28")]
4545
use std::cmp::max;
4646
#[cfg(feature = "abi-7-13")]

src/session.rs

+39-25
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
99
use log::{info, warn};
1010
use nix::unistd::geteuid;
1111
use std::fmt;
12+
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
1213
use std::path::{Path, PathBuf};
1314
use std::sync::{Arc, Mutex};
1415
use std::thread::{self, JoinHandle};
@@ -31,10 +32,15 @@ pub const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024;
3132
/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space.
3233
const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096;
3334

34-
#[derive(Debug, Eq, PartialEq)]
35-
pub(crate) enum SessionACL {
35+
#[derive(Default, Debug, Eq, PartialEq)]
36+
/// How requests should be filtered based on the calling UID.
37+
pub enum SessionACL {
38+
/// Allow requests from any user. Corresponds to the `allow_other` mount option.
3639
All,
40+
/// Allow requests from root. Corresponds to the `allow_root` mount option.
3741
RootAndOwner,
42+
/// Allow requests from the owning UID. This is FUSE's default mode of operation.
43+
#[default]
3844
Owner,
3945
}
4046

@@ -46,9 +52,7 @@ pub struct Session<FS: Filesystem> {
4652
/// Communication channel to the kernel driver
4753
pub(crate) ch: Channel,
4854
/// Handle to the mount. Dropping this unmounts.
49-
mount: Arc<Mutex<Option<Mount>>>,
50-
/// Mount point
51-
mountpoint: PathBuf,
55+
mount: Arc<Mutex<Option<(PathBuf, Mount)>>>,
5256
/// Whether to restrict access to owner, root + owner, or unrestricted
5357
/// Used to implement allow_root and auto_unmount
5458
pub(crate) allowed: SessionACL,
@@ -64,6 +68,12 @@ pub struct Session<FS: Filesystem> {
6468
pub(crate) destroyed: bool,
6569
}
6670

71+
impl<FS: Filesystem> AsFd for Session<FS> {
72+
fn as_fd(&self) -> BorrowedFd<'_> {
73+
self.ch.as_fd()
74+
}
75+
}
76+
6777
impl<FS: Filesystem> Session<FS> {
6878
/// Create a new session by mounting the given filesystem to the given mountpoint
6979
pub fn new<P: AsRef<Path>>(
@@ -100,8 +110,7 @@ impl<FS: Filesystem> Session<FS> {
100110
Ok(Session {
101111
filesystem,
102112
ch,
103-
mount: Arc::new(Mutex::new(Some(mount))),
104-
mountpoint: mountpoint.to_owned(),
113+
mount: Arc::new(Mutex::new(Some((mountpoint.to_owned(), mount)))),
105114
allowed,
106115
session_owner: geteuid().as_raw(),
107116
proto_major: 0,
@@ -111,9 +120,21 @@ impl<FS: Filesystem> Session<FS> {
111120
})
112121
}
113122

114-
/// Return path of the mounted filesystem
115-
pub fn mountpoint(&self) -> &Path {
116-
&self.mountpoint
123+
/// Wrap an existing /dev/fuse file descriptor. This doesn't mount the
124+
/// filesystem anywhere; that must be done separately.
125+
pub fn from_fd(filesystem: FS, fd: OwnedFd, acl: SessionACL) -> Self {
126+
let ch = Channel::new(Arc::new(fd.into()));
127+
Session {
128+
filesystem,
129+
ch,
130+
mount: Arc::new(Mutex::new(None)),
131+
allowed: acl,
132+
session_owner: geteuid().as_raw(),
133+
proto_major: 0,
134+
proto_minor: 0,
135+
initialized: false,
136+
destroyed: false,
137+
}
117138
}
118139

119140
/// Run the session loop that receives kernel requests and dispatches them to method
@@ -177,7 +198,7 @@ impl<FS: Filesystem> Session<FS> {
177198
#[derive(Debug)]
178199
/// A thread-safe object that can be used to unmount a Filesystem
179200
pub struct SessionUnmounter {
180-
mount: Arc<Mutex<Option<Mount>>>,
201+
mount: Arc<Mutex<Option<(PathBuf, Mount)>>>,
181202
}
182203

183204
impl SessionUnmounter {
@@ -210,40 +231,38 @@ impl<FS: Filesystem> Drop for Session<FS> {
210231
self.filesystem.destroy();
211232
self.destroyed = true;
212233
}
213-
info!("Unmounted {}", self.mountpoint().display());
234+
235+
if let Some((mountpoint, _mount)) = std::mem::take(&mut *self.mount.lock().unwrap()) {
236+
info!("unmounting session at {}", mountpoint.display());
237+
}
214238
}
215239
}
216240

217241
/// The background session data structure
218242
pub struct BackgroundSession {
219-
/// Path of the mounted filesystem
220-
pub mountpoint: PathBuf,
221243
/// Thread guard of the background session
222244
pub guard: JoinHandle<io::Result<()>>,
223245
/// Object for creating Notifiers for client use
224246
#[cfg(feature = "abi-7-11")]
225247
sender: ChannelSender,
226248
/// Ensures the filesystem is unmounted when the session ends
227-
_mount: Mount,
249+
_mount: Option<Mount>,
228250
}
229251

230252
impl BackgroundSession {
231253
/// Create a new background session for the given session by running its
232254
/// session loop in a background thread. If the returned handle is dropped,
233255
/// the filesystem is unmounted and the given session ends.
234256
pub fn new<FS: Filesystem + Send + 'static>(se: Session<FS>) -> io::Result<BackgroundSession> {
235-
let mountpoint = se.mountpoint().to_path_buf();
236257
#[cfg(feature = "abi-7-11")]
237258
let sender = se.ch.sender();
238259
// Take the fuse_session, so that we can unmount it
239-
let mount = std::mem::take(&mut *se.mount.lock().unwrap());
240-
let mount = mount.ok_or_else(|| io::Error::from_raw_os_error(libc::ENODEV))?;
260+
let mount = std::mem::take(&mut *se.mount.lock().unwrap()).map(|(_, mount)| mount);
241261
let guard = thread::spawn(move || {
242262
let mut se = se;
243263
se.run()
244264
});
245265
Ok(BackgroundSession {
246-
mountpoint,
247266
guard,
248267
#[cfg(feature = "abi-7-11")]
249268
sender,
@@ -253,7 +272,6 @@ impl BackgroundSession {
253272
/// Unmount the filesystem and join the background thread.
254273
pub fn join(self) {
255274
let Self {
256-
mountpoint: _,
257275
guard,
258276
#[cfg(feature = "abi-7-11")]
259277
sender: _,
@@ -274,10 +292,6 @@ impl BackgroundSession {
274292
// thread_scoped::JoinGuard
275293
impl fmt::Debug for BackgroundSession {
276294
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
277-
write!(
278-
f,
279-
"BackgroundSession {{ mountpoint: {:?}, guard: JoinGuard<()> }}",
280-
self.mountpoint
281-
)
295+
write!(f, "BackgroundSession {{ guard: JoinGuard<()> }}",)
282296
}
283297
}

0 commit comments

Comments
 (0)