bootc_internal_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
10/// RAII wrapper for a temporary mount that is automatically unmounted on drop.
11#[derive(Debug)]
12pub struct TempMount {
13    /// The backing temporary directory.
14    pub dir: tempfile::TempDir,
15    /// An open handle to the mounted directory.
16    pub fd: Dir,
17}
18
19impl TempMount {
20    /// Mount device/partition on a tempdir which will be automatically unmounted on drop
21    #[context("Mounting {dev}")]
22    pub fn mount_dev(
23        dev: &str,
24        fstype: &str,
25        flags: MountFlags,
26        data: Option<&std::ffi::CStr>,
27    ) -> Result<Self> {
28        let tempdir = tempfile::TempDir::new()?;
29
30        let utf8path = Utf8Path::from_path(tempdir.path())
31            .ok_or(anyhow::anyhow!("Failed to convert path to UTF-8 Path"))?;
32
33        rustix::mount::mount(dev, utf8path.as_std_path(), fstype, flags, data)?;
34
35        let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
36            .with_context(|| format!("Opening {:?}", tempdir.path()));
37
38        let fd = match fd {
39            Ok(fd) => fd,
40            Err(e) => {
41                unmount(tempdir.path(), UnmountFlags::DETACH)?;
42                Err(e)?
43            }
44        };
45
46        Ok(Self { dir: tempdir, fd })
47    }
48
49    /// Mount and fd acquired with `open_tree` like syscall
50    #[context("Mounting fd")]
51    pub fn mount_fd(mnt_fd: impl AsFd) -> Result<Self> {
52        let tempdir = tempfile::TempDir::new()?;
53
54        move_mount(
55            mnt_fd.as_fd(),
56            "",
57            rustix::fs::CWD,
58            tempdir.path(),
59            MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
60        )
61        .context("move_mount")?;
62
63        let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
64            .with_context(|| format!("Opening {:?}", tempdir.path()));
65
66        let fd = match fd {
67            Ok(fd) => fd,
68            Err(e) => {
69                unmount(tempdir.path(), UnmountFlags::DETACH)?;
70                Err(e)?
71            }
72        };
73
74        Ok(Self { dir: tempdir, fd })
75    }
76}
77
78impl Drop for TempMount {
79    fn drop(&mut self) {
80        match unmount(self.dir.path(), UnmountFlags::DETACH) {
81            Ok(_) => {}
82            Err(e) => tracing::warn!("Failed to unmount tempdir: {e:?}"),
83        }
84    }
85}