use std::ffi::CStr;
use std::fs::File;
use std::os::fd::{BorrowedFd, AsFd, OwnedFd};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
mod sys;
mod seal;
pub use seal::{Seal, Seals};
#[derive(Debug)]
pub struct MemFile {
file: File,
}
impl MemFile {
pub fn create(name: &str, options: CreateOptions) -> std::io::Result<Self> {
let file = sys::memfd_create(name, options.as_flags())?;
Ok(Self { file })
}
pub fn create_cstr(name: &CStr, options: CreateOptions) -> std::io::Result<Self> {
let file = sys::memfd_create_cstr(name, options.as_flags())?;
Ok(Self { file })
}
pub fn create_default(name: &str) -> std::io::Result<Self> {
Self::create(name, CreateOptions::default())
}
pub fn create_sealable(name: &str) -> std::io::Result<Self> {
Self::create(name, CreateOptions::new().allow_sealing(true))
}
pub fn try_clone(&self) -> std::io::Result<Self> {
let file = self.file.try_clone()?;
Ok(Self { file })
}
pub fn from_fd(fd: OwnedFd) -> Result<Self, FromFdError> {
match sys::memfd_get_seals(fd.as_raw_fd()) {
Err(error) => Err(FromFdError { error, fd }),
Ok(_) => {
let file = File::from(fd);
Ok(Self { file })
}
}
}
pub fn into_fd(self) -> OwnedFd {
self.file.into()
}
pub fn as_fd(&self) -> BorrowedFd<'_> {
self.file.as_fd()
}
pub fn into_file(self) -> std::fs::File {
self.file
}
pub fn metadata(&self) -> std::io::Result<std::fs::Metadata> {
self.file.metadata()
}
pub fn set_len(&self, size: u64) -> std::io::Result<()> {
self.file.set_len(size)
}
pub fn get_seals(&self) -> std::io::Result<Seals> {
let seals = sys::memfd_get_seals(self.as_raw_fd())?;
Ok(Seals::from_bits_truncate(seals as u32))
}
pub fn add_seal(&self, seal: Seal) -> std::io::Result<()> {
self.add_seals(seal.into())
}
pub fn add_seals(&self, seals: Seals) -> std::io::Result<()> {
sys::memfd_add_seals(self.as_raw_fd(), seals.bits() as std::os::raw::c_int)
}
}
impl From<MemFile> for OwnedFd {
fn from(value: MemFile) -> Self {
value.file.into()
}
}
impl TryFrom<OwnedFd> for MemFile {
type Error = FromFdError;
fn try_from(value: OwnedFd) -> Result<Self, Self::Error> {
MemFile::from_fd(value)
}
}
impl AsFd for MemFile {
fn as_fd(&self) -> BorrowedFd<'_> {
MemFile::as_fd(self)
}
}
impl FromRawFd for MemFile {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
let file = File::from_raw_fd(fd);
Self { file }
}
}
impl AsRawFd for MemFile {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl IntoRawFd for MemFile {
fn into_raw_fd(self) -> RawFd {
self.file.into_raw_fd()
}
}
impl std::os::unix::fs::FileExt for MemFile {
fn read_at(&self, buf: &mut [u8], offset: u64) -> std::io::Result<usize> {
self.file.read_at(buf, offset)
}
fn write_at(&self, buf: &[u8], offset: u64) -> std::io::Result<usize> {
self.file.write_at(buf, offset)
}
}
impl std::io::Write for MemFile {
fn flush(&mut self) -> std::io::Result<()> {
self.file.flush()
}
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.file.write(buf)
}
}
impl std::io::Read for MemFile {
fn read(&mut self, buf: &mut[u8]) -> std::io::Result<usize> {
self.file.read(buf)
}
}
impl std::io::Seek for MemFile {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
self.file.seek(pos)
}
}
impl From<MemFile> for std::process::Stdio {
fn from(other: MemFile) -> Self {
other.file.into()
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct CreateOptions {
allow_sealing: bool,
huge_table: Option<HugeTlb>,
}
impl CreateOptions {
pub fn new() -> Self {
Self::default()
}
pub fn create(&self, name: &str) -> std::io::Result<MemFile> {
MemFile::create(name, *self)
}
pub fn create_cstr(&self, name: &CStr) -> std::io::Result<MemFile> {
MemFile::create_cstr(name, *self)
}
pub fn allow_sealing(mut self, value: bool) -> Self {
self.allow_sealing = value;
self
}
pub fn huge_tlb(mut self, value: impl Into<Option<HugeTlb>>) -> Self {
self.huge_table = value.into();
self
}
fn as_flags(&self) -> std::os::raw::c_int {
let mut flags = sys::flags::MFD_CLOEXEC;
if self.allow_sealing {
flags |= sys::flags::MFD_ALLOW_SEALING;
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
if let Some(size) = self.huge_table {
flags |= sys::flags::MFD_HUGETLB | size as u32 as std::os::raw::c_int;
}
flags
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[repr(u32)]
#[non_exhaustive]
pub enum HugeTlb {
Huge64KB = sys::flags::MFD_HUGE_64KB as u32,
Huge512KB = sys::flags::MFD_HUGE_512KB as u32,
Huge1MB = sys::flags::MFD_HUGE_1MB as u32,
Huge2MB = sys::flags::MFD_HUGE_2MB as u32,
Huge8MB = sys::flags::MFD_HUGE_8MB as u32,
Huge16MB = sys::flags::MFD_HUGE_16MB as u32,
Huge32MB = sys::flags::MFD_HUGE_32MB as u32,
Huge256MB = sys::flags::MFD_HUGE_256MB as u32,
Huge512MB = sys::flags::MFD_HUGE_512MB as u32,
Huge1GB = sys::flags::MFD_HUGE_1GB as u32,
Huge2GB = sys::flags::MFD_HUGE_2GB as u32,
Huge16GB = sys::flags::MFD_HUGE_16GB as u32,
}
pub struct FromFdError {
error: std::io::Error,
fd: OwnedFd,
}
impl FromFdError {
pub fn error(&self) -> &std::io::Error {
&self.error
}
pub fn fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
pub fn into_parts(self) -> (std::io::Error, OwnedFd) {
(self.error, self.fd)
}
pub fn into_error(self) -> std::io::Error {
self.error
}
pub fn into_fd(self) -> OwnedFd {
self.fd
}
}
impl From<FromFdError> for std::io::Error {
fn from(other: FromFdError) -> Self {
other.into_error()
}
}