memfile: Disambiguated memfile errors resulting in `fallocate()` sizes being too large or the call itself failing.

Fortune for collect's current commit: Half blessing − 半吉
safe-memfd
Avril 3 years ago
parent b3dbb30deb
commit edb57c7c95
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,12 +1,12 @@
[package] [package]
name = "collect" name = "collect"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
default = ["jemalloc", "memfile", "logging", "tracing/release_max_level_warn"] default = ["jemalloc", "logging", "tracing/release_max_level_warn"] #, "memfile" ]
# TODO: mmap, memfd_create() ver # TODO: mmap, memfd_create() ver
memfile = ["bitflags", "lazy_static", "stackalloc"] memfile = ["bitflags", "lazy_static", "stackalloc"]

@ -82,10 +82,10 @@ struct StackStr<const MAXLEN: usize>(usize, std::mem::MaybeUninit<[u8; MAXLEN]>)
impl<const SZ: usize> StackStr<SZ> impl<const SZ: usize> StackStr<SZ>
{ {
#[inline] #[inline]
pub const fn new() -> Self pub const fn new() -> Self
{ {
Self(0, std::mem::MaybeUninit::uninit()) Self(0, std::mem::MaybeUninit::uninit())
} }
#[inline(always)] #[inline(always)]
@ -220,10 +220,11 @@ fn init() -> eyre::Result<()>
color_eyre::install() color_eyre::install()
} }
#[cfg_attr(tracing, instrument(err))] #[cfg_attr(feature="logging", instrument(err))]
fn main() -> eyre::Result<()> { #[inline]
init()?; fn non_map_work() -> eyre::Result<()>
if_trace!(debug!("initialised")); {
if_trace!(trace!("strategy: allocated buffer"));
let (bytes, read) = { let (bytes, read) = {
let stdin = io::stdin(); let stdin = io::stdin();
@ -254,3 +255,33 @@ fn main() -> eyre::Result<()> {
Ok(()) Ok(())
} }
#[cfg_attr(feature="logging", instrument(err))]
#[inline]
#[cfg(feature="memfile")]
fn map_work() -> eyre::Result<()>
{
if_trace!(trace!("strategy: mapped memory file"));
let file = memfile::create_memfile(Some("this is a test file"), 4096)?;
unimplemented!("Feature not yet implemented")
}
#[cfg_attr(feature="logging", instrument(err))]
fn main() -> eyre::Result<()> {
init()?;
if_trace!(debug!("initialised"));
cfg_if!{
if #[cfg(feature="memfile")] {
map_work()
.wrap_err(eyre!("Operation failed").with_note(|| "With mapped memfd algorithm"))?;
} else {
non_map_work()
.wrap_err(eyre!("Operation failed").with_note(|| "With alloc-buf (non-mapped) algorithm"))?;
}
}
Ok(())
}

@ -24,6 +24,25 @@ const MEMFD_CREATE_FLAGS: libc::c_uint = libc::MFD_CLOEXEC;
#[repr(transparent)] #[repr(transparent)]
pub struct RawFile(fd::RawFileDescriptor); pub struct RawFile(fd::RawFileDescriptor);
/// Create an in-memory `File`, with an optional name
#[cfg_attr(feature="logging", instrument(level="info", err))]
pub fn create_memfile(name: Option<&str>, size: usize) -> eyre::Result<fs::File>
{
if_trace!(debug!("Attempting to allocate {size} bytes of contiguous physical memory for memory file named {:?}", name.unwrap_or("<unbound>")));
RawFile::open_mem(name, size).map(Into::into)
.wrap_err(eyre!("Failed to open in-memory file")
.with_section(move || format!("{:?}", name).header("Proposed name"))
.with_section(|| size.header("Requested physical memory buffer size")))
}
impl Clone for RawFile
{
#[inline]
fn clone(&self) -> Self {
self.try_clone().expect("failed to duplicate raw fd")
}
}
impl RawFile impl RawFile
{ {
/// Get the raw fd for this raw file /// Get the raw fd for this raw file
@ -97,6 +116,16 @@ impl RawFile
} }
} }
/// Attempt to duplicate this raw file
pub fn try_clone(&self) -> Result<Self, error::DuplicateError>
{
match unsafe { libc::dup(self.0.get()) }
{
-1 => Err(error::DuplicateError::new_dup(self)),
fd => Ok(Self::take_ownership_of_unchecked(fd))
}
}
/// Consume a managed file into a raw file, attempting to synchronise it first. /// Consume a managed file into a raw file, attempting to synchronise it first.
/// ///
/// # Note /// # Note
@ -146,6 +175,8 @@ impl RawFile
} }
/// Open a new in-memory (W+R) file with an optional name and a fixed size. /// Open a new in-memory (W+R) file with an optional name and a fixed size.
#[cfg_attr(feature="logging", instrument(err))]
pub fn open_mem(name: Option<&str>, len: usize) -> Result<Self, error::MemfileError> pub fn open_mem(name: Option<&str>, len: usize) -> Result<Self, error::MemfileError>
{ {
lazy_static! { lazy_static! {
@ -160,19 +191,29 @@ impl RawFile
let rname = name.unwrap_or(&DEFAULT_NAME); let rname = name.unwrap_or(&DEFAULT_NAME);
stackalloc::alloca_zeroed(rname.len()+1, move |bname| { //XXX: Isn't the whole point of making `name` `&'static` that I don't know if `memfd_create()` requires static-lifetime name strings? TODO: Check this stackalloc::alloca_zeroed(rname.len()+1, move |bname| { //XXX: Isn't the whole point of making `name` `&'static` that I don't know if `memfd_create()` requires static-lifetime name strings? TODO: Check this
#[cfg(feature="logging")]
let _span = info_span!("stack_name_cpy", size = bname.len());
#[cfg(feature="logging")]
let _span_lock = _span.enter();
macro_rules! attempt_call macro_rules! attempt_call
{ {
($errcon:literal, $expr:expr, $step:expr) => { ($errcon:literal, $expr:expr, $step:expr) => {
//if_trace!(debug!("attempting systemcall"));
match unsafe { match unsafe {
$expr $expr
} { } {
$errcon => Err($step), $errcon => {
if_trace!(warn!("systemcall failed: {}", error::raw_errno()));
Err($step)
},
x => Ok(x) x => Ok(x)
} }
} }
} }
if_trace!(trace!("copying {rname:p} `{rname}' (sz: {}) -> nul-terminated {:p}", rname.len(), bname));
let bname = { let bname = {
unsafe { unsafe {
std::ptr::copy_nonoverlapping(rname.as_ptr(), bname.as_mut_ptr(), rname.len()); std::ptr::copy_nonoverlapping(rname.as_ptr(), bname.as_mut_ptr(), rname.len());
@ -186,11 +227,10 @@ impl RawFile
attempt_call!(-1 attempt_call!(-1
, fallocate(fd.0.get(), 0, 0, len.try_into() , fallocate(fd.0.get(), 0, 0, len.try_into()
.map_err(|_| Allocate(fd.fileno().clone(), len))?) .map_err(|_| Allocate(None, len))?)
, Allocate(fd.fileno().clone(), len))?; , Allocate(Some(fd.fileno().clone()), len))?;
Ok(fd) Ok(fd)
}) })
} }
} }
@ -268,14 +308,6 @@ impl From<RawFile> for fs::File
} }
} }
impl Clone for RawFile
{
#[inline]
fn clone(&self) -> Self {
unsafe { Self::from_raw_fd(libc::dup(self.0.get())) }
}
}
impl ops::Drop for RawFile impl ops::Drop for RawFile
{ {
#[inline] #[inline]
@ -311,3 +343,29 @@ impl IntoRawFd for RawFile
fd fd
} }
} }
#[cfg(test)]
mod tests
{
use super::*;
#[test]
fn memory_mapping() -> eyre::Result<()>
{
use std::io::*;
const STRING: &[u8] = b"Hello world!";
let mut file = {
let mut file = RawFile::open_mem(None, 4096)?;
file.write_all(STRING)?;
let mut file = fs::File::from(file);
file.seek(SeekFrom::Start(0))?;
file
};
let v: Vec<u8> = stackalloc::alloca_zeroed(STRING.len(), |buf| {
file.read_exact(buf).map(|_| buf.into())
})?;
assert_eq!(v.len(), STRING.len(), "Invalid read size.");
assert_eq!(&v[..], &STRING[..], "Invalid read data.");
Ok(())
}
}

@ -2,6 +2,11 @@
use super::*; use super::*;
use std::{fmt, error}; use std::{fmt, error};
pub(super) fn raw_errno() -> libc::c_int
{
unsafe { *libc::__errno_location() }
}
/// The kind of duplicate fd syscall that was attempted /// The kind of duplicate fd syscall that was attempted
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
pub enum DuplicateKind pub enum DuplicateKind
@ -117,7 +122,7 @@ pub enum MemfileCreationStep
/// `memfd_create()` call /// `memfd_create()` call
Create(Option<String>, libc::c_uint), Create(Option<String>, libc::c_uint),
/// `fallocate()` call /// `fallocate()` call
Allocate(fd::RawFileDescriptor, usize), Allocate(Option<fd::RawFileDescriptor>, usize),
/// `mmap()` call /// `mmap()` call
Map { Map {
addr: usize, addr: usize,
@ -144,7 +149,8 @@ impl fmt::Display for MemfileCreationStep
Self::Create(None, 0 | MEMFD_CREATE_FLAGS) => f.write_str("memfd_create()"), Self::Create(None, 0 | MEMFD_CREATE_FLAGS) => f.write_str("memfd_create()"),
Self::Create(None, flags) => write!(f, "memfd_create(<unbound>, {flags})"), Self::Create(None, flags) => write!(f, "memfd_create(<unbound>, {flags})"),
Self::Create(Some(name), flag) => write!(f, "memfd_create({name}, {flag})"), Self::Create(Some(name), flag) => write!(f, "memfd_create({name}, {flag})"),
Self::Allocate(fd, size) => write!(f, "fallocate({fd}, 0, 0, {size})"), Self::Allocate(None, size) => write!(f, "checked_cast<off_t>({size})"),
Self::Allocate(Some(fd), size) => write!(f, "fallocate({fd}, 0, 0, {size})"),
Self::Map{ addr: 0, size, prot, flags, fd: Some(fd), offset } => write!(f, "mmap(NULL, {size}, {prot:?}, {flags}, {fd}, {offset})"), Self::Map{ addr: 0, size, prot, flags, fd: Some(fd), offset } => write!(f, "mmap(NULL, {size}, {prot:?}, {flags}, {fd}, {offset})"),
Self::Map{ addr: 0, size, prot, flags, fd: None, offset } => write!(f, "mmap(NULL, {size}, {prot:?}, {flags}, -1, {offset})"), Self::Map{ addr: 0, size, prot, flags, fd: None, offset } => write!(f, "mmap(NULL, {size}, {prot:?}, {flags}, -1, {offset})"),
Self::Map{ addr, size, prot, flags, fd: Some(fd), offset } => write!(f, "mmap(0x{addr:x}, {size}, {prot:?}, {flags}, {fd}, {offset})"), Self::Map{ addr, size, prot, flags, fd: Some(fd), offset } => write!(f, "mmap(0x{addr:x}, {size}, {prot:?}, {flags}, {fd}, {offset})"),

Loading…
Cancel
Save