1use std::os::fd::AsFd;
2
3use anyhow::{Context, Result};
4
5use camino::Utf8Path;
6use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
7use fn_error_context::context;
8use rustix::mount::{MountFlags, MoveMountFlags, UnmountFlags, move_mount, unmount};
9
10pub struct TempMount {
11 pub dir: tempfile::TempDir,
12 pub fd: Dir,
13}
14
15impl TempMount {
16 #[context("Mounting {dev}")]
18 pub fn mount_dev(
19 dev: &str,
20 fstype: &str,
21 flags: MountFlags,
22 data: Option<&std::ffi::CStr>,
23 ) -> Result<Self> {
24 let tempdir = tempfile::TempDir::new()?;
25
26 let utf8path = Utf8Path::from_path(tempdir.path())
27 .ok_or(anyhow::anyhow!("Failed to convert path to UTF-8 Path"))?;
28
29 rustix::mount::mount(dev, utf8path.as_std_path(), fstype, flags, data)?;
30
31 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
32 .with_context(|| format!("Opening {:?}", tempdir.path()));
33
34 let fd = match fd {
35 Ok(fd) => fd,
36 Err(e) => {
37 unmount(tempdir.path(), UnmountFlags::DETACH)?;
38 Err(e)?
39 }
40 };
41
42 Ok(Self { dir: tempdir, fd })
43 }
44
45 #[context("Mounting fd")]
47 pub fn mount_fd(mnt_fd: impl AsFd) -> Result<Self> {
48 let tempdir = tempfile::TempDir::new()?;
49
50 move_mount(
51 mnt_fd.as_fd(),
52 "",
53 rustix::fs::CWD,
54 tempdir.path(),
55 MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
56 )
57 .context("move_mount")?;
58
59 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
60 .with_context(|| format!("Opening {:?}", tempdir.path()));
61
62 let fd = match fd {
63 Ok(fd) => fd,
64 Err(e) => {
65 unmount(tempdir.path(), UnmountFlags::DETACH)?;
66 Err(e)?
67 }
68 };
69
70 Ok(Self { dir: tempdir, fd })
71 }
72}
73
74impl Drop for TempMount {
75 fn drop(&mut self) {
76 match unmount(self.dir.path(), UnmountFlags::DETACH) {
77 Ok(_) => {}
78 Err(e) => tracing::warn!("Failed to unmount tempdir: {e:?}"),
79 }
80 }
81}