diff --git a/Cargo.toml b/Cargo.toml index 671a13f..ed743dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/main.rs b/src/main.rs index 022092b..fa557bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -82,10 +82,10 @@ struct StackStr(usize, std::mem::MaybeUninit<[u8; MAXLEN]>) impl StackStr { - #[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(()) +} diff --git a/src/memfile.rs b/src/memfile.rs index 9907f23..f41559c 100644 --- a/src/memfile.rs +++ b/src/memfile.rs @@ -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 +{ + if_trace!(debug!("Attempting to allocate {size} bytes of contiguous physical memory for memory file named {:?}", name.unwrap_or(""))); + 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 + { + 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 { lazy_static! { @@ -160,19 +191,29 @@ 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 + 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 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 = 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(()) + } +} diff --git a/src/memfile/error.rs b/src/memfile/error.rs index 662cfad..87ee37e 100644 --- a/src/memfile/error.rs +++ b/src/memfile/error.rs @@ -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, libc::c_uint), /// `fallocate()` call - Allocate(fd::RawFileDescriptor, usize), + Allocate(Option, 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(, {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({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})"),