From 872ea74421438ac2bd357908fa823da973e6cbc3 Mon Sep 17 00:00:00 2001 From: Avril Date: Fri, 20 May 2022 19:37:56 +0100 Subject: [PATCH] It seems creating HUGETLB memory files either just doesn"t work or changes their behaviour so that any (or at least, small arbitrary) writing or fallocate()ing to them fails... Read up on MFD_HUGETLB more then re-do a test like `memfd_create_wrapper()` to find out why... and if it depends on the MAP_HUGE_ mask, and if so, find one that works... (We know when masks are invalid, since the error message is different. The masks collected via `get_masks()` *are* valid for this system, they just prevent the fd from being any way useful.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for collect's current commit: Blessing − 吉 --- src/memfile.rs | 18 ++++++++++- src/memfile/hp.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/memfile.rs b/src/memfile.rs index 709ad51..5e69f29 100644 --- a/src/memfile.rs +++ b/src/memfile.rs @@ -279,8 +279,24 @@ impl RawFile opt.borrow().open(path).map(Into::into) } + /// Allocates `size` bytes for this file. + /// + /// # Note + /// This does not *extend* the file's capacity, it is instead similar to `fs::File::set_len()`. + #[cfg_attr(feature="logging", instrument(err))] + pub fn allocate_size(&mut self, size: u64) -> io::Result<()> + { + use libc::{ fallocate, off_t}; + if_trace!(trace!("attempting fallocate({}, 0, 0, {size}) (max offset: {})", self.0.get(), off_t::MAX)); + match unsafe { fallocate(self.0.get(), 0, 0, if cfg!(debug_assertions) { + size.try_into().map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Offset larger than max offset size"))? + } else { size as off_t }) } { //XXX is this biteise AND check needed? fallocate() should already error if the size is negative with these parameters, no? + -1 => Err(io::Error::last_os_error()), + _ => Ok(()) + } + } + /// 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 { diff --git a/src/memfile/hp.rs b/src/memfile/hp.rs index a877c29..cad511e 100644 --- a/src/memfile/hp.rs +++ b/src/memfile/hp.rs @@ -187,8 +187,9 @@ impl Mask { /// Create a function that acts as `memfd_create()` with *only* this mask applied to it. /// - /// The `flags` argument is erased. To pass arbitrary flags to `memfd_create()`, use `memfd_create_wrapper_flags()` - pub const fn memfd_create_wrapper(self) -> impl Fn (*const libc::c_char) -> c_int + /// The `flags` argument is erased. To pass arbitrary flags to `memfd_create()`, use `memfd_create_raw_wrapper_flags()` + #[inline(always)] + pub const fn memfd_create_raw_wrapper(self) -> impl Fn (*const libc::c_char) -> c_int { use libc::memfd_create; move |path| { @@ -199,7 +200,8 @@ impl Mask { } /// Create a function that acts as `memfd_create()` with this mask applied to it. - pub const fn memfd_create_wrapper_flags(self) -> impl Fn (*const libc::c_char, c_uint) -> c_int + #[inline(always)] + pub const fn memfd_create_raw_wrapper_flags(self) -> impl Fn (*const libc::c_char, c_uint) -> c_int { use libc::memfd_create; move |path, flag| { @@ -208,6 +210,38 @@ impl Mask { } } } + + /// Create a function that acts as safe `memfd_create()` wrapper with this mask applied to it. + /// + /// The `flags` argument is erased. To pass arbitrary flags to `memfd_create()`, use `memfd_create_wrapper_flags()` + /// # Returns + /// A RAII-guarded wrapper over the memory-file, or the `errno` in an `Err(io::Error)` if the operation failed. + #[inline] + pub const fn memfd_create_wrapper(self) -> impl Fn(*const libc::c_char) -> io::Result + { + let memfd_create = self.memfd_create_raw_wrapper(); + move |path| { + match memfd_create(path) { + -1 => Err(io::Error::last_os_error()), + fd => Ok(super::RawFile::take_ownership_of_unchecked(fd)) + } + } + } + + /// Create a function that acts as safe `memfd_create()` wrapper with this mask applied to it. + /// # Returns + /// A RAII-guarded wrapper over the memory-file, or the `errno` in an `Err(io::Error)` if the operation failed. + #[inline] + pub const fn memfd_create_wrapper_flags(self) -> impl Fn(*const libc::c_char, c_uint) -> io::Result + { + let memfd_create = self.memfd_create_raw_wrapper_flags(); + move |path, flags| { + match memfd_create(path, flags) { + -1 => Err(io::Error::last_os_error()), + fd => Ok(super::RawFile::take_ownership_of_unchecked(fd)) + } + } + } } impl TryFrom for Mask @@ -504,5 +538,46 @@ mod tests (masks > 0).then(|| drop(println!("Found {masks} masks on system"))).ok_or(eyre!("Found no masks")) } + + //#[test] TODO: XXX: AAAAA: system does not support huge-page memfd_create() allocations!?!?!?!? + // TODO: Or am I missing something here? Does it pre-allocate? What is this? + fn memfd_create_wrapper() -> eyre::Result<()> + { + //crate::init()?; + + use std::ffi::CString; + let name = CString::new(Vec::from_iter(b"memfd_create_wrapper() test".into_iter().copied())).unwrap(); + let mask = super::get_masks()?.next().ok_or(eyre!("No masks found"))?.wrap_err("Failed to extract mask")?; + eprintln!("Using mask: {mask:x} ({mask:b})"); + let create = mask.memfd_create_wrapper_flags(); + let buf = { + let mut buf = vec![0; name.as_bytes_with_nul().len()]; + println!("Allocated {} bytes for buffer", buf.len()); + let mut file: fs::File = { + let mut file = unsafe {super::RawFile::from_raw_fd( libc::memfd_create(name.as_ptr(), super::MEMFD_CREATE_FLAGS | mask.mask()) ) };//.wrap_err(eyre!("Failed to create file"))?; + println!("Created file {file:?}"); + file.allocate_size(buf.len() as u64).wrap_err(eyre!("fallocate() failed"))?; + println!("Set file-size to {}", buf.len()); + file + }.into(); + + use std::io::{Read, Write, Seek}; + + + println!("Writing {} bytes {:?}...", name.as_bytes_with_nul().len(), name.as_bytes_with_nul()); + file.write_all(name.as_bytes_with_nul()).wrap_err(eyre!("Writing failed"))?; + println!("Seeking back to 0..."); + file.seek(std::io::SeekFrom::Start(0)).wrap_err(eyre!("Seeking failed"))?; + println!("Reading {} bytes...", buf.len()); + file.read_exact(&mut buf[..]).wrap_err(eyre!("Reading failed"))?; + + println!("Read {} bytes into: {:?}", buf.len(), buf); + + buf + }; + assert_eq!(CString::from_vec_with_nul(buf).expect("Invalid contents read"), name); + + Ok(()) + } } }