use std::{
io::Result,
os::fd::{AsFd, BorrowedFd, OwnedFd},
};
#[cfg(not(feature = "pre-6.15"))]
pub fn overlayfs_set_fd(fs_fd: BorrowedFd, key: &str, fd: BorrowedFd) -> rustix::io::Result<()> {
rustix::mount::fsconfig_set_fd(fs_fd, key, fd)
}
#[cfg(not(feature = "rhel9"))]
pub fn overlayfs_set_lower_and_data_fds(
fs_fd: impl AsFd,
lower: impl AsFd,
data: Option<impl AsFd>,
) -> rustix::io::Result<()> {
overlayfs_set_fd(fs_fd.as_fd(), "lowerdir+", lower.as_fd())?;
if let Some(data) = data {
overlayfs_set_fd(fs_fd.as_fd(), "datadir+", data.as_fd())?;
}
Ok(())
}
#[cfg(not(feature = "rhel9"))]
pub fn make_erofs_mountable(image: OwnedFd) -> Result<OwnedFd> {
Ok(image)
}
#[cfg(not(feature = "pre-6.15"))]
pub fn prepare_mount(mnt_fd: OwnedFd) -> Result<impl AsFd> {
Ok(mnt_fd)
}
#[cfg(feature = "pre-6.15")]
#[cfg(not(feature = "rhel9"))]
pub fn overlayfs_set_fd(fs_fd: BorrowedFd, key: &str, fd: BorrowedFd) -> rustix::io::Result<()> {
use rustix::fs::{openat, Mode, OFlags};
use rustix::mount::fsconfig_set_fd;
fsconfig_set_fd(
fs_fd,
key,
openat(
fd,
".",
OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
Mode::empty(),
)?
.as_fd(),
)
}
#[cfg(feature = "rhel9")]
pub fn overlayfs_set_fd(fs_fd: BorrowedFd, key: &str, fd: BorrowedFd) -> rustix::io::Result<()> {
rustix::mount::fsconfig_set_string(fs_fd, key, crate::util::proc_self_fd(&fd))
}
#[cfg(feature = "rhel9")]
pub fn overlayfs_set_lower_and_data_fds(
fs_fd: impl AsFd,
lower: impl AsFd,
data: Option<impl AsFd>,
) -> rustix::io::Result<()> {
use std::os::fd::AsRawFd;
let lower_fd = lower.as_fd().as_raw_fd().to_string();
let arg = if let Some(data) = data {
let data_fd = data.as_fd().as_raw_fd().to_string();
format!("/proc/self/fd/{lower_fd}::/proc/self/fd/{data_fd}")
} else {
format!("/proc/self/fd/{lower_fd}")
};
rustix::mount::fsconfig_set_string(fs_fd.as_fd(), "lowerdir", arg)
}
#[cfg(feature = "pre-6.15")]
pub fn prepare_mount(mnt_fd: OwnedFd) -> Result<impl AsFd> {
tmpmount::TmpMount::mount(mnt_fd)
}
#[cfg(feature = "rhel9")]
pub fn make_erofs_mountable(image: OwnedFd) -> Result<OwnedFd> {
loopback::loopify(image)
}
#[cfg(feature = "pre-6.15")]
mod tmpmount {
use std::{
io::Result,
os::fd::{AsFd, BorrowedFd, OwnedFd},
};
use rustix::fs::{open, Mode, OFlags};
use rustix::mount::{move_mount, unmount, MoveMountFlags, UnmountFlags};
pub(super) struct TmpMount {
dir: tempfile::TempDir,
fd: OwnedFd,
}
impl TmpMount {
pub(super) fn mount(mnt_fd: OwnedFd) -> Result<impl AsFd> {
let tmp = tempfile::TempDir::new()?;
move_mount(
mnt_fd.as_fd(),
"",
rustix::fs::CWD,
tmp.path(),
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
)?;
let fd = open(
tmp.path(),
OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC,
Mode::empty(),
)?;
Ok(TmpMount { dir: tmp, fd })
}
}
impl AsFd for TmpMount {
fn as_fd(&self) -> BorrowedFd {
self.fd.as_fd()
}
}
impl Drop for TmpMount {
fn drop(&mut self) {
let _ = unmount(self.dir.path(), UnmountFlags::DETACH);
}
}
}
#[cfg(feature = "rhel9")]
mod loopback {
#![allow(unsafe_code)]
use std::{
io::Result,
os::fd::{AsFd, AsRawFd, OwnedFd},
};
use rustix::fs::{open, Mode, OFlags};
struct LoopCtlGetFree;
unsafe impl rustix::ioctl::Ioctl for LoopCtlGetFree {
type Output = std::ffi::c_int;
const IS_MUTATING: bool = false;
fn opcode(&self) -> u32 {
LOOP_CTL_GET_FREE
}
fn as_ptr(&mut self) -> *mut std::ffi::c_void {
std::ptr::null_mut()
}
unsafe fn output_from_ptr(
out: rustix::ioctl::IoctlOutput,
_ptr: *mut std::ffi::c_void,
) -> rustix::io::Result<std::ffi::c_int> {
Ok(out)
}
}
const LO_NAME_SIZE: usize = 64;
const LO_KEY_SIZE: usize = 32;
#[derive(Default)]
#[repr(C)]
struct LoopInfo {
lo_device: u64,
lo_inode: u64,
lo_rdevice: u64,
lo_offset: u64,
lo_sizelimit: u64,
lo_number: u32,
lo_encrypt_type: u32,
lo_encrypt_key_size: u32,
lo_flags: u32,
lo_file_name: ([u8; LO_NAME_SIZE / 2], [u8; LO_NAME_SIZE / 2]),
lo_crypt_name: ([u8; LO_NAME_SIZE / 2], [u8; LO_NAME_SIZE / 2]),
lo_encrypt_key: [u8; LO_KEY_SIZE],
lo_init: [u64; 2],
}
#[derive(Default)]
#[repr(C)]
struct LoopConfig {
fd: u32,
block_size: u32,
info: LoopInfo,
reserved: [u64; 8],
}
const LOOP_CTL_GET_FREE: u32 = 0x4C82;
const LOOP_CONFIGURE: u32 = 0x4C0A;
const LO_FLAGS_READ_ONLY: u32 = 1;
const LO_FLAGS_AUTOCLEAR: u32 = 4;
const LO_FLAGS_DIRECT_IO: u32 = 16;
pub fn loopify(image: OwnedFd) -> Result<OwnedFd> {
let control = open(
"/dev/loop-control",
OFlags::RDWR | OFlags::CLOEXEC,
Mode::empty(),
)?;
let index = unsafe { rustix::ioctl::ioctl(&control, LoopCtlGetFree {})? };
let fd = open(
format!("/dev/loop{index}"),
OFlags::RDWR | OFlags::CLOEXEC,
Mode::empty(),
)?;
let config = LoopConfig {
fd: image.as_fd().as_raw_fd() as u32,
block_size: 4096,
info: LoopInfo {
lo_flags: LO_FLAGS_READ_ONLY | LO_FLAGS_AUTOCLEAR | LO_FLAGS_DIRECT_IO,
..LoopInfo::default()
},
..LoopConfig::default()
};
unsafe {
rustix::ioctl::ioctl(
&fd,
rustix::ioctl::Setter::<{ LOOP_CONFIGURE }, LoopConfig>::new(config),
)?;
};
Ok(fd)
}
}