use std::fmt;
use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::ptr::NonNull;
const MFD_NOEXEC_SEAL: libc::c_uint = 0x0008;
#[derive(Debug)]
pub enum LinuxError {
Os {
operation: &'static str,
code: i32,
},
InvalidSize(usize),
InvalidCapability,
}
impl fmt::Display for LinuxError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Os { operation, code } => {
write!(formatter, "Linux {operation} failed with errno {code}")
}
Self::InvalidSize(size) => write!(formatter, "invalid Linux mapping size {size}"),
Self::InvalidCapability => {
formatter.write_str("Linux returned an unrepresentable mapping")
}
}
}
}
impl std::error::Error for LinuxError {}
pub struct QuiescentRegion {
fd: OwnedFd,
mapping: Mapping,
logical_len: usize,
}
impl QuiescentRegion {
pub fn new(logical_len: usize) -> Result<Self, LinuxError> {
let len = page_align(logical_len)?;
let raw = unsafe {
libc::memfd_create(c"native-ipc".as_ptr(), libc::MFD_CLOEXEC | MFD_NOEXEC_SEAL)
};
if raw < 0 {
return Err(last_os("memfd_create"));
}
let fd = unsafe { OwnedFd::from_raw_fd(raw) };
if unsafe { libc::ftruncate(fd.as_raw_fd(), len as libc::off_t) } != 0 {
return Err(last_os("ftruncate"));
}
let mapping = Mapping::map(fd.as_raw_fd(), len)?;
unsafe { std::ptr::write_bytes(mapping.base.as_ptr(), 0, len) };
mapping.advise()?;
Ok(Self {
fd,
mapping,
logical_len,
})
}
pub const fn len(&self) -> usize {
self.mapping.len
}
pub const fn logical_len(&self) -> usize {
self.logical_len
}
pub fn as_bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.mapping.base.as_ptr(), self.mapping.len) }
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.mapping.base.as_ptr(), self.mapping.len) }
}
pub(crate) fn into_vnext_unmapped_parts(self) -> (OwnedFd, usize, usize) {
let Self {
fd,
mapping,
logical_len,
} = self;
let mapped_len = mapping.len;
drop(mapping);
(fd, logical_len, mapped_len)
}
pub(crate) fn as_raw_fd_for_vnext(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
struct Mapping {
base: NonNull<u8>,
len: usize,
}
impl Mapping {
fn map(fd: RawFd, len: usize) -> Result<Self, LinuxError> {
let pointer = unsafe {
libc::mmap(
std::ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
)
};
if pointer == libc::MAP_FAILED {
return Err(last_os("mmap"));
}
let Some(base) = NonNull::new(pointer.cast()) else {
let _ = unsafe { libc::munmap(pointer, len) };
return Err(LinuxError::InvalidCapability);
};
Ok(Self { base, len })
}
fn advise(&self) -> Result<(), LinuxError> {
for advice in [libc::MADV_DONTDUMP, libc::MADV_DONTFORK] {
if unsafe { libc::madvise(self.base.as_ptr().cast(), self.len, advice) } != 0 {
return Err(last_os("madvise"));
}
}
Ok(())
}
}
impl Drop for Mapping {
fn drop(&mut self) {
let _ = unsafe { libc::munmap(self.base.as_ptr().cast(), self.len) };
}
}
fn page_align(size: usize) -> Result<usize, LinuxError> {
if size == 0 {
return Err(LinuxError::InvalidSize(size));
}
let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if page <= 0 {
return Err(last_os("sysconf(_SC_PAGESIZE)"));
}
let page = page as usize;
size.checked_add(page - 1)
.map(|value| value & !(page - 1))
.filter(|value| *value <= isize::MAX as usize)
.ok_or(LinuxError::InvalidSize(size))
}
fn last_os(operation: &'static str) -> LinuxError {
LinuxError::Os {
operation,
code: io::Error::last_os_error().raw_os_error().unwrap_or(-1),
}
}
#[cfg(test)]
#[path = "linux_test.rs"]
mod tests;