use crate::sealing;
use rustix::fs::{MemfdFlags, SealFlags};
use std::fs;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[derive(Clone, Debug)]
pub struct MemfdOptions {
allow_sealing: bool,
cloexec: bool,
hugetlb: Option<HugetlbSize>,
}
impl MemfdOptions {
pub const fn new() -> Self {
Self {
allow_sealing: false,
cloexec: true,
hugetlb: None,
}
}
pub const fn allow_sealing(mut self, value: bool) -> Self {
self.allow_sealing = value;
self
}
pub const fn close_on_exec(mut self, value: bool) -> Self {
self.cloexec = value;
self
}
pub const fn hugetlb(mut self, size: Option<HugetlbSize>) -> Self {
self.hugetlb = size;
self
}
fn bitflags(&self) -> MemfdFlags {
let mut bits = MemfdFlags::empty();
if self.allow_sealing {
bits |= MemfdFlags::ALLOW_SEALING;
}
if self.cloexec {
bits |= MemfdFlags::CLOEXEC;
}
if let Some(ref hugetlb) = self.hugetlb {
bits |= hugetlb.bitflags();
bits |= MemfdFlags::HUGETLB;
}
bits
}
pub fn create<T: AsRef<str>>(&self, name: T) -> Result<Memfd, crate::Error> {
let flags = self.bitflags();
let fd = rustix::fs::memfd_create(name.as_ref(), flags)
.map_err(Into::into)
.map_err(crate::Error::Create)?;
Ok(Memfd { file: fd.into() })
}
}
impl Default for MemfdOptions {
fn default() -> Self {
Self::new()
}
}
#[derive(Copy, Clone, Debug)]
pub enum HugetlbSize {
Huge64KB,
Huge512KB,
Huge1MB,
Huge2MB,
Huge8MB,
Huge16MB,
Huge256MB,
Huge1GB,
Huge2GB,
Huge16GB,
}
impl HugetlbSize {
const fn bitflags(self) -> MemfdFlags {
match self {
Self::Huge64KB => MemfdFlags::HUGE_64KB,
Self::Huge512KB => MemfdFlags::HUGE_512KB,
Self::Huge1MB => MemfdFlags::HUGE_1MB,
Self::Huge2MB => MemfdFlags::HUGE_2MB,
Self::Huge8MB => MemfdFlags::HUGE_8MB,
Self::Huge16MB => MemfdFlags::HUGE_16MB,
Self::Huge256MB => MemfdFlags::HUGE_256MB,
Self::Huge1GB => MemfdFlags::HUGE_1GB,
Self::Huge2GB => MemfdFlags::HUGE_2GB,
Self::Huge16GB => MemfdFlags::HUGE_16GB,
}
}
}
#[derive(Debug)]
pub struct Memfd {
file: fs::File,
}
impl Memfd {
pub fn try_from_fd<F>(fd: F) -> Result<Self, F>
where
F: AsRawFd + IntoRawFd,
{
if is_memfd(&fd) {
let file = unsafe { fs::File::from_raw_fd(fd.into_raw_fd()) };
Ok(Self { file })
} else {
Err(fd)
}
}
pub fn try_from_file(file: fs::File) -> Result<Self, fs::File> {
Self::try_from_fd(file)
}
pub const fn as_file(&self) -> &fs::File {
&self.file
}
pub fn into_file(self) -> fs::File {
self.file
}
pub fn seals(&self) -> Result<sealing::SealsHashSet, crate::Error> {
let flags = Self::file_get_seals(&self.file)?;
Ok(sealing::bitflags_to_seals(flags))
}
pub fn add_seal(&self, seal: sealing::FileSeal) -> Result<(), crate::Error> {
let flags = seal.bitflags();
self.add_seal_flags(flags)
}
pub fn add_seals<'a>(
&self,
seals: impl IntoIterator<Item = &'a sealing::FileSeal>,
) -> Result<(), crate::Error> {
let flags = sealing::seals_to_bitflags(seals);
self.add_seal_flags(flags)
}
fn add_seal_flags(&self, flags: rustix::fs::SealFlags) -> Result<(), crate::Error> {
rustix::fs::fcntl_add_seals(&self.file, flags)
.map_err(Into::into)
.map_err(crate::Error::AddSeals)?;
Ok(())
}
fn file_get_seals(fp: &fs::File) -> Result<SealFlags, crate::Error> {
let r = rustix::fs::fcntl_get_seals(fp)
.map_err(Into::into)
.map_err(crate::Error::GetSeals)?;
Ok(r)
}
}
impl FromRawFd for Memfd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
let file = fs::File::from_raw_fd(fd);
Self { file }
}
}
impl AsRawFd for Memfd {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl IntoRawFd for Memfd {
fn into_raw_fd(self) -> RawFd {
self.into_file().into_raw_fd()
}
}
fn is_memfd<F: AsRawFd>(fd: &F) -> bool {
let fd = unsafe { rustix::fd::BorrowedFd::borrow_raw(fd.as_raw_fd()) };
rustix::fs::fcntl_get_seals(fd).is_ok()
}