@ -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 < CStr > , 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 < MemoryFile > 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 < Self >
/// 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 < Self >
{
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 < Self >
{
// 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 < Self >
{
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 < str > ) -> io ::Result < Self >
{
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 < Self >
{
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 < str > , hugetlb : MapHugeFlag ) -> io ::Result < Self >
{
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 < str > , size : usize ) -> io ::Result < Self >
{
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 < str > , size : usize , hugetlb : MapHugeFlag ) -> io ::Result < Self >
{
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 < str > , hugetlb : MapHugeFlag ) -> io ::Result < Self >
{
let name : Box < CStr > = 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.
}