bootc_mount/
tempmount.rs

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    /// Mount device/partition on a tempdir which will be automatically unmounted on drop
17    #[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    /// Mount and fd acquired with `open_tree` like syscall
46    #[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}