1use std::{
8 io::Result,
9 os::fd::{AsFd, BorrowedFd, OwnedFd},
10};
11
12#[cfg(not(feature = "pre-6.15"))]
24pub fn overlayfs_set_fd(fs_fd: BorrowedFd, key: &str, fd: BorrowedFd) -> rustix::io::Result<()> {
25 rustix::mount::fsconfig_set_fd(fs_fd, key, fd)
26}
27
28#[cfg(not(feature = "rhel9"))]
35pub fn overlayfs_set_lower_and_data_fds(
36 fs_fd: impl AsFd,
37 lower: impl AsFd,
38 data: Option<impl AsFd>,
39) -> rustix::io::Result<()> {
40 overlayfs_set_fd(fs_fd.as_fd(), "lowerdir+", lower.as_fd())?;
41 if let Some(data) = data {
42 overlayfs_set_fd(fs_fd.as_fd(), "datadir+", data.as_fd())?;
43 }
44 Ok(())
45}
46
47#[cfg(not(feature = "rhel9"))]
50pub fn make_erofs_mountable(image: OwnedFd) -> Result<OwnedFd> {
51 Ok(image)
52}
53
54#[cfg(not(feature = "pre-6.15"))]
60pub fn prepare_mount(mnt_fd: OwnedFd) -> Result<impl AsFd> {
61 Ok(mnt_fd)
62}
63
64#[cfg(feature = "pre-6.15")]
70#[cfg(not(feature = "rhel9"))]
71pub fn overlayfs_set_fd(fs_fd: BorrowedFd, key: &str, fd: BorrowedFd) -> rustix::io::Result<()> {
72 use rustix::fs::{openat, Mode, OFlags};
73 use rustix::mount::fsconfig_set_fd;
74
75 fsconfig_set_fd(
77 fs_fd,
78 key,
79 openat(
80 fd,
81 ".",
82 OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
83 Mode::empty(),
84 )?
85 .as_fd(),
86 )
87}
88
89#[cfg(feature = "rhel9")]
94pub fn overlayfs_set_fd(fs_fd: BorrowedFd, key: &str, fd: BorrowedFd) -> rustix::io::Result<()> {
95 rustix::mount::fsconfig_set_string(fs_fd, key, crate::util::proc_self_fd(fd))
96}
97
98#[cfg(feature = "rhel9")]
104pub fn overlayfs_set_lower_and_data_fds(
105 fs_fd: impl AsFd,
106 lower: impl AsFd,
107 data: Option<impl AsFd>,
108) -> rustix::io::Result<()> {
109 use std::os::fd::AsRawFd;
110
111 let lower_fd = lower.as_fd().as_raw_fd().to_string();
112 let arg = if let Some(data) = data {
113 let data_fd = data.as_fd().as_raw_fd().to_string();
114 format!("/proc/self/fd/{lower_fd}::/proc/self/fd/{data_fd}")
115 } else {
116 format!("/proc/self/fd/{lower_fd}")
117 };
118 rustix::mount::fsconfig_set_string(fs_fd.as_fd(), "lowerdir", arg)
119}
120
121#[cfg(feature = "pre-6.15")]
127pub fn prepare_mount(mnt_fd: OwnedFd) -> Result<impl AsFd> {
128 tmpmount::TmpMount::mount(mnt_fd)
129}
130
131#[cfg(feature = "rhel9")]
136pub fn make_erofs_mountable(image: OwnedFd) -> Result<OwnedFd> {
137 loopback::loopify(image)
138}
139
140#[cfg(feature = "pre-6.15")]
147mod tmpmount {
148 use std::{
149 io::Result,
150 os::fd::{AsFd, BorrowedFd, OwnedFd},
151 };
152
153 use rustix::fs::{open, Mode, OFlags};
154 use rustix::mount::{move_mount, unmount, MoveMountFlags, UnmountFlags};
155
156 pub(super) struct TmpMount {
157 dir: tempfile::TempDir,
158 fd: OwnedFd,
159 }
160
161 impl TmpMount {
162 pub(super) fn mount(mnt_fd: OwnedFd) -> Result<impl AsFd> {
163 let tmp = tempfile::TempDir::new()?;
164 move_mount(
165 mnt_fd.as_fd(),
166 "",
167 rustix::fs::CWD,
168 tmp.path(),
169 MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
170 )?;
171 let fd = open(
172 tmp.path(),
173 OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC,
174 Mode::empty(),
175 )?;
176 Ok(TmpMount { dir: tmp, fd })
177 }
178 }
179
180 impl AsFd for TmpMount {
181 fn as_fd(&self) -> BorrowedFd<'_> {
182 self.fd.as_fd()
183 }
184 }
185
186 impl Drop for TmpMount {
187 fn drop(&mut self) {
188 let _ = unmount(self.dir.path(), UnmountFlags::DETACH);
189 }
190 }
191}
192
193#[cfg(feature = "rhel9")]
195mod loopback {
196 #![allow(unsafe_code)]
197 use std::{
198 io::Result,
199 os::fd::{AsFd, AsRawFd, OwnedFd},
200 };
201
202 use rustix::fs::{open, Mode, OFlags};
203
204 struct LoopCtlGetFree;
205
206 unsafe impl rustix::ioctl::Ioctl for LoopCtlGetFree {
209 type Output = std::ffi::c_int;
210
211 const IS_MUTATING: bool = false;
212
213 fn opcode(&self) -> rustix::ioctl::Opcode {
214 LOOP_CTL_GET_FREE
215 }
216
217 fn as_ptr(&mut self) -> *mut std::ffi::c_void {
218 std::ptr::null_mut()
219 }
220
221 unsafe fn output_from_ptr(
222 out: rustix::ioctl::IoctlOutput,
223 _ptr: *mut std::ffi::c_void,
224 ) -> rustix::io::Result<std::ffi::c_int> {
225 Ok(out)
226 }
227 }
228
229 const LO_NAME_SIZE: usize = 64;
230 const LO_KEY_SIZE: usize = 32;
231
232 #[derive(Default)]
233 #[repr(C)]
234 struct LoopInfo {
235 lo_device: u64,
236 lo_inode: u64,
237 lo_rdevice: u64,
238 lo_offset: u64,
239 lo_sizelimit: u64,
240 lo_number: u32,
241 lo_encrypt_type: u32,
242 lo_encrypt_key_size: u32,
243 lo_flags: u32,
244 lo_file_name: ([u8; LO_NAME_SIZE / 2], [u8; LO_NAME_SIZE / 2]),
246 lo_crypt_name: ([u8; LO_NAME_SIZE / 2], [u8; LO_NAME_SIZE / 2]),
247 lo_encrypt_key: [u8; LO_KEY_SIZE],
248 lo_init: [u64; 2],
249 }
250
251 #[derive(Default)]
252 #[repr(C)]
253 struct LoopConfig {
254 fd: u32,
255 block_size: u32,
256 info: LoopInfo,
257 reserved: [u64; 8],
258 }
259
260 const LOOP_CTL_GET_FREE: rustix::ioctl::Opcode = 0x4C82;
261 const LOOP_CONFIGURE: rustix::ioctl::Opcode = 0x4C0A;
262 const LO_FLAGS_READ_ONLY: u32 = 1;
263 const LO_FLAGS_AUTOCLEAR: u32 = 4;
264 const LO_FLAGS_DIRECT_IO: u32 = 16;
265
266 pub fn loopify(image: OwnedFd) -> Result<OwnedFd> {
267 let control = open(
268 "/dev/loop-control",
269 OFlags::RDWR | OFlags::CLOEXEC,
270 Mode::empty(),
271 )?;
272 let index = unsafe { rustix::ioctl::ioctl(&control, LoopCtlGetFree {})? };
273 let fd = open(
274 format!("/dev/loop{index}"),
275 OFlags::RDWR | OFlags::CLOEXEC,
276 Mode::empty(),
277 )?;
278 let config = LoopConfig {
279 fd: image.as_fd().as_raw_fd() as u32,
280 block_size: 4096,
281 info: LoopInfo {
282 lo_flags: LO_FLAGS_READ_ONLY | LO_FLAGS_AUTOCLEAR | LO_FLAGS_DIRECT_IO,
283 ..LoopInfo::default()
284 },
285 ..LoopConfig::default()
286 };
287 unsafe {
288 rustix::ioctl::ioctl(
289 &fd,
290 rustix::ioctl::Setter::<{ LOOP_CONFIGURE }, LoopConfig>::new(config),
291 )?;
292 };
293 Ok(fd)
294 }
295}