async_fuser/passthrough.rs
1use std::os::fd::AsFd;
2use std::os::unix::io::AsRawFd;
3use std::sync::Arc;
4use std::sync::Weak;
5
6use log::error;
7
8use crate::dev_fuse::DevFuse;
9use crate::ll::ioctl::fuse_backing_map;
10use crate::ll::ioctl::fuse_dev_ioc_backing_close;
11use crate::ll::ioctl::fuse_dev_ioc_backing_open;
12
13/// A reference to a previously opened fd intended to be used for passthrough
14///
15/// You can create these via [`ReplyOpen::open_backing()`](crate::ReplyOpen::open_backing)
16/// and send them via [`ReplyOpen::opened_passthrough()`](crate::ReplyOpen::opened_passthrough).
17///
18/// When working with backing IDs you need to ensure that they live "long enough". A good practice
19/// is to create them in the [`Filesystem::open()`](crate::Filesystem::open) impl,
20/// store them in the struct of your Filesystem impl, then drop them in the
21/// [`Filesystem::release()`](crate::Filesystem::release) impl. Dropping them immediately after
22/// sending them in the `Filesystem::open()` impl can lead to the kernel returning EIO when userspace
23/// attempts to access the file.
24///
25/// This is implemented as a safe wrapper around the `backing_id` field of the `fuse_backing_map`
26/// struct used by the ioctls involved in fd passthrough. It is created by performing a
27/// `FUSE_DEV_IOC_BACKING_OPEN` ioctl on an fd and has a Drop trait impl which makes a matching
28/// `FUSE_DEV_IOC_BACKING_CLOSE` call. It holds a weak reference on the fuse channel to allow it to
29/// make that call (if the channel hasn't already been closed).
30#[derive(Debug)]
31pub struct BackingId {
32 pub(crate) channel: Weak<DevFuse>,
33 /// The `backing_id` field passed to and from the kernel
34 pub(crate) backing_id: u32,
35}
36
37impl BackingId {
38 /// Creates a new backing file reference for the given file descriptor.
39 ///
40 /// Usually, you will want to use [`ReplyOpen::open_backing()`](crate::ReplyOpen::open_backing)
41 /// instead, since this method will return a raw `backing_id` value instead of a managed
42 /// `BackingId` wrapper. As such you must manage the lifetime of the backing file yourself.
43 ///
44 /// This method is useful if you want to open a backing file reference without access to a reply
45 /// object.
46 pub fn create_raw(fuse_dev: impl AsFd, fd: impl AsFd) -> std::io::Result<u32> {
47 if !cfg!(target_os = "linux") {
48 return Err(std::io::Error::new(
49 std::io::ErrorKind::Other,
50 "backing IDs are only supported on Linux",
51 ));
52 }
53
54 let map = fuse_backing_map {
55 fd: fd.as_fd().as_raw_fd() as u32,
56 flags: 0,
57 padding: 0,
58 };
59 let id = unsafe { fuse_dev_ioc_backing_open(fuse_dev.as_fd().as_raw_fd(), &map) }?;
60
61 Ok(id as u32)
62 }
63
64 pub(crate) fn create(channel: &Arc<DevFuse>, fd: impl AsFd) -> std::io::Result<Self> {
65 Ok(Self {
66 channel: Arc::downgrade(channel),
67 backing_id: Self::create_raw(channel, fd)?,
68 })
69 }
70
71 pub(crate) unsafe fn wrap_raw(channel: &Arc<DevFuse>, id: u32) -> Self {
72 Self {
73 channel: Arc::downgrade(channel),
74 backing_id: id,
75 }
76 }
77
78 /// Converts this backing file reference into the raw `backing_id` value as returned by the kernel.
79 ///
80 /// This method transfers ownership of the backing file to the caller, who must invoke the
81 /// `FUSE_DEV_IOC_BACKING_CLOSE` themselves once they wish to close the backing file.
82 ///
83 /// The returned ID may subsequently be reopened using
84 /// [`ReplyOpen::wrap_backing()`](crate::ReplyOpen::wrap_backing).
85 pub fn into_raw(mut self) -> u32 {
86 let id = self.backing_id;
87 drop(std::mem::take(&mut self.channel));
88 std::mem::forget(self);
89 id
90 }
91}
92
93impl Drop for BackingId {
94 fn drop(&mut self) {
95 if let Some(ch) = self.channel.upgrade() {
96 if let Err(e) = unsafe { fuse_dev_ioc_backing_close(ch.as_raw_fd(), &self.backing_id) }
97 {
98 error!("Failed to close backing fd: {e}");
99 }
100 }
101 }
102}