From ee28043069199513cd71966deb220d2fc94105ec Mon Sep 17 00:00:00 2001 From: Avril Date: Sun, 9 Mar 2025 22:15:48 +0000 Subject: [PATCH] Pre-release 0.0.9-r0: Started `NamedMemoryFile` refactor into `MemoryFile`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for mapped-file's current commit: Blessing − 吉 --- Cargo.toml | 5 +- src/file/memory.rs | 150 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 146 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff07d26..968f3be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "mapped-file" description = "Construct a memory mapping over any file object" keywords = ["unix", "mmap", "generic", "file", "fd"] -version = "0.0.8-r1" +version = "0.0.9-r0" edition = "2021" repository="https://github.com/notflan/mapped-file" license="MIT" @@ -10,7 +10,7 @@ license="MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["default-cloexec"] +default = ["default-cloexec", "file"] # Add support for file-descriptor operations other than simply mapping # TODO: XXX: Incomplete, will be enabled by default when complete @@ -26,3 +26,4 @@ default-cloexec = [] lazy_static = "1.4.0" libc = "0.2.132" memchr = "2.5.0" +stackalloc = "1.2.1" diff --git a/src/file/memory.rs b/src/file/memory.rs index 1c14231..86bec92 100644 --- a/src/file/memory.rs +++ b/src/file/memory.rs @@ -2,6 +2,8 @@ //! //! This can be useful for temporary buffers where a file descriptor is required. //! Huge-pages can also be used for this memory. +#![allow(deprecated)] + use super::*; use libc::{ c_uint, @@ -12,10 +14,14 @@ use libc::{ ftruncate, }; use std::{ - ffi::CStr, + ffi::{CStr, CString}, borrow::{ Borrow, BorrowMut, + Cow, + }, + path::{ + Path, PathBuf, }, ops, }; @@ -48,8 +54,69 @@ pub struct MemoryFile(ManagedFD); /// A named, physical-memory backed file #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[deprecated(note = "Use `MemoryFile::named[_with_size]()` instead.")] pub struct NamedMemoryFile(Box, MemoryFile); +impl NamedMemoryFile +{ + pub(crate) const PATH_PREFIX: &'static str = "/proc/self/fd/"; + + /// Get the name of the memfd which will be `/proc/self/fd/memfd:{}` (where `{}` is `.name()`.) + #[inline] + pub fn name(&self) -> &CStr + { + self.0.as_ref() + } + + /// Get the absolute path (in `/proc/self/fd/`) of this memfd. + #[inline] + pub fn get_path(&self) -> PathBuf + { + use std::ffi::OsStr; + /// Name prefix within `PATH_PREFIX`. + const PREFIX: &[u8; 6] = b"memfd:"; + + // Alloc space for `PREFIX`+`self.0.len()` to create last element in path. + stackalloc::alloca_zeroed(self.0.count_bytes() + PREFIX.len(), move |name| { + // Write `memfd:` to `name[..6]` + name[..PREFIX.len()].copy_from_slice(&PREFIX[..]); + // Write name to `name+6` + name[PREFIX.len()..].copy_from_slice(self.0.to_bytes()); + + // for (&src, dest) in PREFIX.into_iter().chain(self.0.to_bytes().iter()).zip(&mut name[..]) { + // *dest = src; + // } + + debug_assert_ne!(name.last().unwrap(), &0, "Invalid stack based path-builder string"); + + Path::new(Self::PATH_PREFIX).join(OsStr::from_bytes(&name[..])) + }) + } + + /// Consume into the internal `MemoryFile`. + #[inline] + pub fn into_inner(self) -> MemoryFile + { + self.1 + } +} + +impl AsRawFd for NamedMemoryFile +{ + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.1.as_raw_fd() + } +} + +impl IntoRawFd for NamedMemoryFile +{ + #[inline] + fn into_raw_fd(self) -> RawFd { + self.1.into_raw_fd() + } +} + impl Borrow for NamedMemoryFile { #[inline] @@ -79,20 +146,43 @@ impl ops::Deref for NamedMemoryFile } } +#[cold] +fn last_os_error() -> io::Error +{ + io::Error::last_os_error() +} + //impl `MemoryFile` (memfd_create() fd wrapper) impl MemoryFile { - /// Create a new, empty, memory file with no name and no flags. - pub fn new() -> io::Result + + /// Create a new, empty, memory file with the specified raw C-string pointer name and the default set of flags. + /// # Safety + /// + /// `name` must be a: + /// - Non-null, aligned, valid pointer to: + /// - A nul-terminated, non-wide C string. + /// - Which must **not** contain a `/`. + #[inline(always)] + pub(super) unsafe fn new_raw_named(name: *const std::ffi::c_char, flags: c_uint) -> io::Result { let managed = unsafe { - match memfd_create(UNNAMED.as_ptr(), DEFAULT_FLAGS) { - -1 => return Err(io::Error::last_os_error()), + match memfd_create(name, flags) { + -1 => return Err(last_os_error()), fd => ManagedFD::take_unchecked(fd), } }; Ok(Self(managed)) } + + /// Create a new, empty, memory file with no user-provided name and the default set of flags. + pub fn new() -> io::Result + { + // SAFETY: `UNNAMED` is a constant that fits the constraints required for `name`. + unsafe { + Self::new_raw_named(UNNAMED.as_ptr(), DEFAULT_FLAGS) + } + } #[inline] pub fn resize(&mut self, value: usize) -> io::Result<()> { @@ -105,7 +195,7 @@ impl MemoryFile pub fn with_hugetlb(hugetlb: MapHugeFlag) -> io::Result { - unsafe { create_raw(UNNAMED, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) } + unsafe { create_raw(UNNAMED, DEFAULT_FLAGS | MFD_HUGETLB | (hugetlb.get_mask() as c_uint)) } .map(ManagedFD::take) .map(Self) } @@ -124,6 +214,50 @@ impl MemoryFile this.resize(size)?; Ok(this) } + + /// Create a new, empty, memory file with the specified raw C-string pointer name and the default set of flags. + pub fn new_named(name: impl AsRef) -> io::Result + { + Self::raw_with_str_name(name.as_ref(), DEFAULT_FLAGS) + } + + #[inline(always)] + pub(crate) fn raw_with_str_name(name: &str, flags: c_uint) -> io::Result + { + stackalloc::alloca_zeroed(name.len()+1, move |cname| { + cname[..name.len()].copy_from_slice(name.as_bytes()); + debug_assert_ne!(cname[name.len()], 0, "Copied name not nul-terminated for `memfd_create()` call."); + + // SAFETY: We have initialised `cname[..]`, and we know the final byte will be + unsafe { + Self::new_raw_named(cname.as_ptr() as *const _, flags) + } + }) + } + + /// Create a new, empty, memory file with the specified raw C-string pointer name and the `MEMFD_HUGETLB` flag setup as provided as `hugetlb`. + pub fn new_named_with_hugetlb(name: impl AsRef, hugetlb: MapHugeFlag) -> io::Result + { + Self::raw_with_str_name(name.as_ref(), DEFAULT_FLAGS | MFD_HUGETLB | (hugetlb.get_mask() as c_uint)) + } + + /// Create a new memory file with the specified raw C-string pointer name and the default set of flags. + /// Then resize it via `ftruncate()` to `size` bytes. + pub fn new_named_with_size(name: impl AsRef, size: usize) -> io::Result + { + let mut this = Self::raw_with_str_name(name.as_ref(), DEFAULT_FLAGS)?; + this.resize(size)?; + Ok(this) + } + + /// Create a new memory file with the specified raw C-string pointer name and the `MEMFD_HUGETLB` flag setup as provided as `hugetlb`. + /// Then resize it via `ftruncate()` to `size` bytes. + pub fn new_named_with_size_hugetlb(name: impl AsRef, size: usize, hugetlb: MapHugeFlag) -> io::Result + { + let mut this = Self::raw_with_str_name(name.as_ref(), DEFAULT_FLAGS | MFD_HUGETLB | (hugetlb.get_mask() as c_uint))?; + this.resize(size)?; + Ok(this) + } } fn alloc_cstring(string: &str) -> std::ffi::CString @@ -170,7 +304,7 @@ impl NamedMemoryFile pub fn with_hugetlb(name: impl AsRef, hugetlb: MapHugeFlag) -> io::Result { let name: Box = alloc_cstring(name.as_ref()).into(); - let memfd = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) } + let memfd = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS | MFD_HUGETLB | (hugetlb.get_mask() as c_uint)) } .map(ManagedFD::take)?); Ok(Self(name, memfd)) } @@ -245,4 +379,6 @@ mod test { assert_eq!(super::DEFAULT_FLAGS, cfg!(feature="default-cloexec").then(|| super::MFD_CLOEXEC).unwrap_or_default(), "Compile-time default creation flags are not in accordance with provided global crate configuration"); } + + //TODO: Test if `NamedMemoryFile.get_path()` works properly. }