use std::{
io::{Read, Seek, SeekFrom, Write},
os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd},
};
use anyhow::{bail, Result};
use memfd::{FileSeal, Memfd, MemfdOptions};
pub struct Mfd(Memfd);
pub enum Seals {
Unsealed,
Readable,
Writable,
}
impl Mfd {
pub fn create(name: &str) -> Result<Self> {
let opts = MemfdOptions::default().allow_sealing(true);
let mfd = opts.create(name)?;
Ok(Self(mfd))
}
pub fn read_all(&mut self) -> Result<Vec<u8>> {
self.0.as_file().seek(SeekFrom::Start(0))?;
let mut buf: Vec<u8> = Vec::new();
self.0.as_file().read_to_end(&mut buf)?;
Ok(buf)
}
pub fn write(&mut self, data: &[u8]) -> Result<()> {
self.0.as_file().seek(SeekFrom::Start(0))?;
self.0.as_file().set_len(0)?;
self.0.as_file().write_all(data)?;
Ok(())
}
pub fn finalize(&mut self, seals: Seals) -> Result<()> {
let file_seals: Vec<FileSeal> = match seals {
Seals::Unsealed => vec![],
Seals::Readable => vec![
FileSeal::SealShrink,
FileSeal::SealGrow,
FileSeal::SealWrite,
FileSeal::SealSeal,
],
Seals::Writable => vec![FileSeal::SealSeal],
};
for seal in file_seals {
self.0.add_seal(seal)?;
}
Ok(())
}
pub fn from_fd(fd: OwnedFd) -> Result<Self> {
let fd = match Memfd::try_from_fd(fd) {
Ok(memfd) => memfd,
Err(_) => bail!("cannot get Memfd from RawFd"),
};
Ok(Self(fd))
}
}
impl AsFd for Mfd {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_file().as_fd()
}
}
impl AsRawFd for Mfd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mfd() {
let mut mfd = Mfd::create("test").unwrap();
mfd.write("Hello, world!".as_bytes()).unwrap();
mfd.finalize(Seals::Readable).unwrap();
assert_eq!(mfd.read_all().unwrap(), "Hello, world!".as_bytes());
}
}