Skip to content

Commit

Permalink
Allow wrapping an existing /dev/fuse file descriptor
Browse files Browse the repository at this point in the history
This is important for container runtimes, which need to do a special
namespace mount dance.

Fixes cberner#300.
  • Loading branch information
colinmarc committed Oct 14, 2024
1 parent 7f338a9 commit 8efb0dd
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 22 deletions.
16 changes: 15 additions & 1 deletion src/channel.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
use std::{fs::File, io, os::unix::prelude::AsRawFd, sync::Arc};
use std::{
fs::File,
io,
os::{
fd::{AsFd, BorrowedFd},
unix::prelude::AsRawFd,
},
sync::Arc,
};

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

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

impl AsFd for Channel {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}

impl Channel {
/// Create a new communication channel to the kernel driver by mounting the
/// given path. The kernel driver will delegate filesystem operations of
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub use reply::{
ReplyStatfs, ReplyWrite,
};
pub use request::Request;
pub use session::{BackgroundSession, Session, SessionUnmounter};
pub use session::{BackgroundSession, Session, SessionACL, SessionUnmounter};
#[cfg(feature = "abi-7-28")]
use std::cmp::max;
#[cfg(feature = "abi-7-13")]
Expand Down
51 changes: 31 additions & 20 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use log::{info, warn};
use nix::unistd::geteuid;
use std::fmt;
use std::path::{Path, PathBuf};
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
use std::{io, ops::DerefMut};
Expand All @@ -31,10 +32,15 @@ pub const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024;
/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space.
const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096;

#[derive(Debug, Eq, PartialEq)]
pub(crate) enum SessionACL {
#[derive(Default, Debug, Eq, PartialEq)]
/// How requests should be filtered based on the calling UID.
pub enum SessionACL {
/// Allow requests from any user. Corresponds to the `allow_other` mount option.
All,
/// Allow requests from root. Corresponds to the `allow_root` mount option.
RootAndOwner,
/// Allow requests from the owning UID. This is FUSE's default mode of operation.
#[default]
Owner,
}

Expand All @@ -47,8 +53,6 @@ pub struct Session<FS: Filesystem> {
ch: Channel,
/// Handle to the mount. Dropping this unmounts.
mount: Arc<Mutex<Option<Mount>>>,
/// Mount point
mountpoint: PathBuf,
/// Whether to restrict access to owner, root + owner, or unrestricted
/// Used to implement allow_root and auto_unmount
pub(crate) allowed: SessionACL,
Expand All @@ -64,6 +68,12 @@ pub struct Session<FS: Filesystem> {
pub(crate) destroyed: bool,
}

impl<FS: Filesystem> AsFd for Session<FS> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.ch.as_fd()
}
}

impl<FS: Filesystem> Session<FS> {
/// Create a new session by mounting the given filesystem to the given mountpoint
pub fn new<P: AsRef<Path>>(
Expand Down Expand Up @@ -101,7 +111,6 @@ impl<FS: Filesystem> Session<FS> {
filesystem,
ch,
mount: Arc::new(Mutex::new(Some(mount))),
mountpoint: mountpoint.to_owned(),
allowed,
session_owner: geteuid().as_raw(),
proto_major: 0,
Expand All @@ -111,9 +120,21 @@ impl<FS: Filesystem> Session<FS> {
})
}

/// Return path of the mounted filesystem
pub fn mountpoint(&self) -> &Path {
&self.mountpoint
/// Wrap an existing /dev/fuse file descriptor. This doesn't mount the
/// filesystem anywhere; that must be done separately.
pub fn from_fd(filesystem: FS, fd: OwnedFd, acl: SessionACL) -> Self {
let ch = Channel::new(Arc::new(fd.into()));
Session {
filesystem,
ch,
mount: Arc::new(Mutex::new(None)),
allowed: acl,
session_owner: geteuid().as_raw(),
proto_major: 0,
proto_minor: 0,
initialized: false,
destroyed: false,
}
}

/// Run the session loop that receives kernel requests and dispatches them to method
Expand Down Expand Up @@ -210,14 +231,11 @@ impl<FS: Filesystem> Drop for Session<FS> {
self.filesystem.destroy();
self.destroyed = true;
}
info!("Unmounted {}", self.mountpoint().display());
}
}

/// The background session data structure
pub struct BackgroundSession {
/// Path of the mounted filesystem
pub mountpoint: PathBuf,
/// Thread guard of the background session
pub guard: JoinHandle<io::Result<()>>,
/// Object for creating Notifiers for client use
Expand All @@ -232,7 +250,6 @@ impl BackgroundSession {
/// session loop in a background thread. If the returned handle is dropped,
/// the filesystem is unmounted and the given session ends.
pub fn new<FS: Filesystem + Send + 'static>(se: Session<FS>) -> io::Result<BackgroundSession> {
let mountpoint = se.mountpoint().to_path_buf();
#[cfg(feature = "abi-7-11")]
let sender = se.ch.sender();
// Take the fuse_session, so that we can unmount it
Expand All @@ -243,7 +260,6 @@ impl BackgroundSession {
se.run()
});
Ok(BackgroundSession {
mountpoint,
guard,
#[cfg(feature = "abi-7-11")]
sender,
Expand All @@ -253,7 +269,6 @@ impl BackgroundSession {
/// Unmount the filesystem and join the background thread.
pub fn join(self) {
let Self {
mountpoint: _,
guard,
#[cfg(feature = "abi-7-11")]
sender: _,
Expand All @@ -274,10 +289,6 @@ impl BackgroundSession {
// thread_scoped::JoinGuard
impl fmt::Debug for BackgroundSession {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"BackgroundSession {{ mountpoint: {:?}, guard: JoinGuard<()> }}",
self.mountpoint
)
write!(f, "BackgroundSession {{ guard: JoinGuard<()> }}",)
}
}

0 comments on commit 8efb0dd

Please sign in to comment.