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::{
10 MountFlags, MoveMountFlags, OpenTreeFlags, UnmountFlags, move_mount, open_tree, unmount,
11};
12
13#[derive(Debug)]
18pub struct MountGuard(std::path::PathBuf);
19
20impl MountGuard {
21 pub fn mount(
24 dev: &str,
25 path: std::path::PathBuf,
26 fstype: &str,
27 flags: MountFlags,
28 data: Option<&std::ffi::CStr>,
29 ) -> Result<Self> {
30 rustix::mount::mount(dev, &path, fstype, flags, data)
31 .with_context(|| format!("Mounting {} at {}", dev, path.display()))?;
32 Ok(Self(path))
33 }
34}
35
36impl std::ops::Deref for MountGuard {
37 type Target = Path;
38 fn deref(&self) -> &Path {
39 &self.0
40 }
41}
42
43impl Drop for MountGuard {
44 fn drop(&mut self) {
45 if let Err(e) = unmount(&self.0, UnmountFlags::empty()) {
46 tracing::error!("Failed to unmount {}: {e:?}", self.0.display());
50 }
51 }
52}
53
54#[derive(Debug)]
56pub struct MountpointTempdir(tempfile::TempDir);
57
58impl std::ops::Deref for MountpointTempdir {
59 type Target = tempfile::TempDir;
60 fn deref(&self) -> &tempfile::TempDir {
61 &self.0
62 }
63}
64
65impl MountpointTempdir {
66 fn new() -> Result<Self> {
67 let mut tmpdir = tempfile::TempDir::new()?;
68 tmpdir.disable_cleanup(true); Ok(Self(tmpdir))
70 }
71}
72
73impl Drop for MountpointTempdir {
74 fn drop(&mut self) {
75 if let Err(e) = std::fs::remove_dir(self.path()) {
78 tracing::warn!(
79 "Failed to remove tmpdir at {}: {e:?}",
80 self.path().display()
81 )
82 }
83 }
84}
85
86#[derive(Debug)]
88pub struct TempMount {
89 pub dir: MountpointTempdir,
91 pub fd: Dir,
93}
94
95impl TempMount {
96 #[context("Mounting {dev}")]
98 pub fn mount_dev(
99 dev: &str,
100 fstype: &str,
101 flags: MountFlags,
102 data: Option<&std::ffi::CStr>,
103 ) -> Result<Self> {
104 let tempdir = MountpointTempdir::new()?;
105
106 let utf8path = Utf8Path::from_path(tempdir.path())
107 .ok_or(anyhow::anyhow!("Failed to convert path to UTF-8 Path"))?;
108
109 rustix::mount::mount(dev, utf8path.as_std_path(), fstype, flags, data)?;
110
111 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
112 .with_context(|| format!("Opening {:?}", tempdir.path()));
113
114 let fd = match fd {
115 Ok(fd) => fd,
116 Err(e) => {
117 unmount(tempdir.path(), UnmountFlags::DETACH)?;
118 return Err(e)?;
119 }
120 };
121
122 Ok(Self { dir: tempdir, fd })
123 }
124
125 #[context("Cloning existing mount at {source_target}")]
136 pub fn clone_existing_mount(source_target: &Utf8Path) -> Result<Self> {
137 let tempdir = MountpointTempdir::new()?;
138
139 let cloned = open_tree(
140 rustix::fs::CWD,
141 source_target.as_std_path(),
142 OpenTreeFlags::OPEN_TREE_CLOEXEC | OpenTreeFlags::OPEN_TREE_CLONE,
143 )
144 .with_context(|| format!("open_tree({source_target})"))?;
145 move_mount(
146 cloned.as_fd(),
147 "",
148 rustix::fs::CWD,
149 tempdir.path(),
150 MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
151 )
152 .context("move_mount")?;
153
154 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
155 .with_context(|| format!("Opening {:?}", tempdir.path()));
156
157 let fd = match fd {
158 Ok(fd) => fd,
159 Err(e) => {
160 unmount(tempdir.path(), UnmountFlags::DETACH)?;
161 return Err(e)?;
162 }
163 };
164
165 Ok(Self { dir: tempdir, fd })
166 }
167
168 #[context("Mounting fd")]
170 pub fn mount_fd(mnt_fd: impl AsFd) -> Result<Self> {
171 let tempdir = MountpointTempdir::new()?;
172
173 move_mount(
174 mnt_fd.as_fd(),
175 "",
176 rustix::fs::CWD,
177 tempdir.path(),
178 MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
179 )
180 .context("move_mount")?;
181
182 let fd = Dir::open_ambient_dir(tempdir.path(), ambient_authority())
183 .with_context(|| format!("Opening {:?}", tempdir.path()));
184
185 let fd = match fd {
186 Ok(fd) => fd,
187 Err(e) => {
188 unmount(tempdir.path(), UnmountFlags::DETACH)?;
189 return Err(e)?;
190 }
191 };
192
193 Ok(Self { dir: tempdir, fd })
194 }
195}
196
197impl Drop for TempMount {
198 fn drop(&mut self) {
199 match unmount(self.dir.path(), UnmountFlags::DETACH) {
200 Ok(_) => {}
201 Err(e) => tracing::warn!("Failed to unmount tempdir: {e:?}"),
202 }
203 }
204}