1use std::os::unix::io::RawFd;
4
5use anyhow::{anyhow, Context};
6use clippy_utilities::Cast;
7use nix::fcntl::{self, FcntlArg, FdFlag, OFlag};
8use nix::ioctl_read;
9use nix::sys::stat::Mode;
10use nix::unistd::close;
11
12use super::file_system::FileSystem;
13use super::session::Session;
14
15#[derive(Debug)]
17pub struct Channel {
18 chan_fd: RawFd,
20}
21
22impl Channel {
23 #[allow(dead_code)]
25 pub async fn new<F: FileSystem + Send + Sync + 'static>(
26 session: &Session<F>,
27 ) -> anyhow::Result<Self> {
28 let devname = "/dev/fuse";
29 let clonefd = tokio::task::spawn_blocking(move || {
30 fcntl::open(devname, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
31 })
32 .await?;
33
34 let clonefd = match clonefd {
35 Err(err) => {
36 return Err(anyhow!("fuse: failed to open {:?}: {:?}", devname, err));
37 }
38 Ok(fd) => fd,
39 };
40
41 if let Err(err) = tokio::task::spawn_blocking(move || {
42 fcntl::fcntl(clonefd, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC))
43 })
44 .await?
45 {
46 return Err(anyhow!(
47 "fuse: failed to set clonefd to FD_CLOEXEC: {:?}",
48 err,
49 ));
50 }
51
52 ioctl_read!(clone, 229, 0, u32);
53 let masterfd = session.dev_fd();
54 let mut masterfd_u32 = masterfd.cast();
55 let res = tokio::task::spawn_blocking(move || unsafe { clone(clonefd, &mut masterfd_u32) })
56 .await?;
57 if let Err(err) = res {
58 close(clonefd).context("fuse: failed to close clone device")?;
59 return Err(anyhow!("fuse: failed to clone device fd: {:?}", err,));
60 }
61
62 Ok(Self { chan_fd: clonefd })
63 }
64
65 #[allow(dead_code)]
67 #[must_use]
68 pub const fn fd(&self) -> RawFd {
69 self.chan_fd
70 }
71}