bootc_internal_mount/
tempmount.rs1use std::os::fd::AsFd;
2use std::path::Path;
3
4use anyhow::{Context, Result};
5
6use camino::Utf8Path;
7use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
8use fn_error_context::context;
9use rustix::mount::{MountFlags, MoveMountFlags, UnmountFlags, move_mount, unmount};
10
11#[derive(Debug)]
16pub struct MountGuard(std::path::PathBuf);
17
18impl MountGuard {
19 pub fn mount(
22 dev: &str,
23 path: std::path::PathBuf,
24 fstype: &str,
25 flags: MountFlags,
26 data: Option<&std::ffi::CStr>,
27 ) -> Result<Self> {
28 rustix::mount::mount(dev, &path, fstype, flags, data)
29 .with_context(|| format!("Mounting {} at {}", dev, path.display()))?;
30 Ok(Self(path))
31 }
32}
33
34impl std::ops::Deref for MountGuard {
35 type Target = Path;
36 fn deref(&self) -> &Path {
37 &self.0
38 }
39}
40
41impl Drop for MountGuard {
42 fn drop(&mut self) {
43 if let Err(e) = unmount(&self.0, UnmountFlags::empty()) {
44 tracing::error!("Failed to unmount {}: {e:?}", self.0.display());
48 }
49 }
50}
51
52#[derive(Debug)]
54pub struct MountpointTempdir(tempfile::TempDir);
55
56impl std::ops::Deref for MountpointTempdir {
57 type Target = tempfile::TempDir;
58 fn deref(&self) -> &tempfile::TempDir {
59 &self.0
60 }
61}
62
63impl MountpointTempdir {
64 fn new() -> Result<Self> {
65 let mut tmpdir = tempfile::TempDir::new()?;
66 tmpdir.disable_cleanup(true); Ok(Self(tmpdir))
68 }
69}
70
71impl Drop for MountpointTempdir {
72 fn drop(&mut self) {
73 if let Err(e) = std::fs::remove_dir(self.path()) {
76 tracing::warn!(
77 "Failed to remove tmpdir at {}: {e:?}",
78 self.path().display()
79 )
80 }
81 }
82}
83
84#[derive(Debug)]
86pub struct TempMount {
87 pub dir: MountpointTempdir,
89 pub fd: Dir,
91}
92
93impl TempMount {
94 #[context("Mounting {dev}")]
96 pub fn mount_dev(
97 dev: &str,
98 fstype: &str,
99 flags: MountFlags,
100 data: Option<&std::ffi::CStr>,
101 ) -> Result<Self> {
102 let tempdir = MountpointTempdir::new()?;
103
104 let utf8path = Utf8Path::from_path(tempdir.path())
105 .ok_or(anyhow::anyhow!("Failed to convert path to UTF-8 Path"))?;
106
107 rustix::mount::mount(dev, utf8path.as_std_path(), fstype, flags, data)?;
108
109 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
110 .with_context(|| format!("Opening {:?}", tempdir.path()));
111
112 let fd = match fd {
113 Ok(fd) => fd,
114 Err(e) => {
115 unmount(tempdir.path(), UnmountFlags::DETACH)?;
116 return Err(e)?;
117 }
118 };
119
120 Ok(Self { dir: tempdir, fd })
121 }
122
123 #[context("Mounting fd")]
125 pub fn mount_fd(mnt_fd: impl AsFd) -> Result<Self> {
126 let tempdir = MountpointTempdir::new()?;
127
128 move_mount(
129 mnt_fd.as_fd(),
130 "",
131 rustix::fs::CWD,
132 tempdir.path(),
133 MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
134 )
135 .context("move_mount")?;
136
137 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
138 .with_context(|| format!("Opening {:?}", tempdir.path()));
139
140 let fd = match fd {
141 Ok(fd) => fd,
142 Err(e) => {
143 unmount(tempdir.path(), UnmountFlags::DETACH)?;
144 return Err(e)?;
145 }
146 };
147
148 Ok(Self { dir: tempdir, fd })
149 }
150}
151
152impl Drop for TempMount {
153 fn drop(&mut self) {
154 match unmount(self.dir.path(), UnmountFlags::DETACH) {
155 Ok(_) => {}
156 Err(e) => tracing::warn!("Failed to unmount tempdir: {e:?}"),
157 }
158 }
159}