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]
name = "collect"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["jemalloc", "memfile", "logging", "tracing/release_max_level_warn"]
default = ["jemalloc", "logging", "tracing/release_max_level_warn"] #, "memfile" ]
# TODO: mmap, memfd_create() ver
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>
{
#[inline]
pub const fn new() -> Self
{
Self(0, std::mem::MaybeUninit::uninit())
#[inline]
pub const fn new() -> Self
{
Self(0, std::mem::MaybeUninit::uninit())
}
#[inline(always)]
@ -220,10 +220,11 @@ fn init() -> eyre::Result<()>
color_eyre::install()
}
#[cfg_attr(tracing, instrument(err))]
fn main() -> eyre::Result<()> {
init()?;
if_trace!(debug!("initialised"));
#[cfg_attr(feature="logging", instrument(err))]
#[inline]
fn non_map_work() -> eyre::Result<()>
{
if_trace!(trace!("strategy: allocated buffer"));
let (bytes, read) = {
let stdin = io::stdin();
@ -254,3 +255,33 @@ fn main() -> eyre::Result<()> {
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)]
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
{
/// 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.
///
/// # Note
@ -146,6 +175,8 @@ impl RawFile
}
/// 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>
{
lazy_static! {
@ -161,18 +192,28 @@ impl RawFile
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
#[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
{
($errcon:literal, $expr:expr, $step:expr) => {
//if_trace!(debug!("attempting systemcall"));
match unsafe {
$expr
} {
$errcon => Err($step),
$errcon => {
if_trace!(warn!("systemcall failed: {}", error::raw_errno()));
Err($step)
},
x => Ok(x)
}
}
}
if_trace!(trace!("copying {rname:p} `{rname}' (sz: {}) -> nul-terminated {:p}", rname.len(), bname));
let bname = {
unsafe {
std::ptr::copy_nonoverlapping(rname.as_ptr(), bname.as_mut_ptr(), rname.len());
@ -186,11 +227,10 @@ impl RawFile
attempt_call!(-1
, fallocate(fd.0.get(), 0, 0, len.try_into()
.map_err(|_| Allocate(fd.fileno().clone(), len))?)
, Allocate(fd.fileno().clone(), len))?;
.map_err(|_| Allocate(None, len))?)
, Allocate(Some(fd.fileno().clone()), len))?;
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
{
#[inline]
@ -311,3 +343,29 @@ impl IntoRawFd for RawFile
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 std::{fmt, error};
pub(super) fn raw_errno() -> libc::c_int
{
unsafe { *libc::__errno_location() }
}
/// The kind of duplicate fd syscall that was attempted
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
pub enum DuplicateKind
@ -117,7 +122,7 @@ pub enum MemfileCreationStep
/// `memfd_create()` call
Create(Option<String>, libc::c_uint),
/// `fallocate()` call
Allocate(fd::RawFileDescriptor, usize),
Allocate(Option<fd::RawFileDescriptor>, usize),
/// `mmap()` call
Map {
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, flags) => write!(f, "memfd_create(<unbound>, {flags})"),
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: 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})"),

Loading…
Cancel
Save