Added basic creation methods for `MemoryFile` (and `NamedMemoryFile`.)

Fortune for mapped-file's current commit: Small blessing − 小吉
master
Avril 2 years ago
parent c1f1d00af2
commit 13165dfbe5
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -24,6 +24,38 @@ impl Clone for ManagedFD {
} }
} }
impl ManagedFD
{
#[inline]
pub const unsafe fn take_unchecked(fd: RawFd) -> Self
{
Self(UnmanagedFD::new_unchecked(fd))
}
#[inline]
pub const fn take_raw(fd: RawFd) -> Self
{
assert!(fd>=0, "Invalid file descriptor");
unsafe {
Self::take_unchecked(fd)
}
}
#[inline]
pub const fn take(fd: UnmanagedFD) -> Self
{
Self(fd)
}
#[inline]
pub fn detach(self) -> UnmanagedFD
{
let v = self.0.clone();
std::mem::forget(self);
v
}
}
impl ops::Drop for ManagedFD impl ops::Drop for ManagedFD
{ {
fn drop(&mut self) { fn drop(&mut self) {

@ -4,17 +4,193 @@
//! Huge-pages can also be used for this memory. //! Huge-pages can also be used for this memory.
use super::*; use super::*;
use libc::{ use libc::{
c_uint,
memfd_create, memfd_create,
MFD_CLOEXEC, MFD_CLOEXEC,
MFD_HUGETLB, MFD_HUGETLB,
ftruncate,
};
use std::{
ffi::CStr,
borrow::{
Borrow,
BorrowMut,
},
ops,
};
use hugetlb::{
MapHugeFlag,
HugePage,
};
static UNNAMED: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(b"<unnamed memory file>\0")
}; };
const DEFAULT_FLAGS: c_uint = MFD_CLOEXEC;
#[inline(always)]
//XXX: Is the static bound required here?
/// Create a raw, unmanaged, memory file with these flags and this name.
///
/// # Safety
/// The reference obtained by `name` must not move as long as the `Ok()` result is alive.
pub unsafe fn create_raw(name: impl AsRef<CStr>, flags: c_uint) -> io::Result<UnmanagedFD>
{
UnmanagedFD::new_raw(memfd_create(name.as_ref().as_ptr(), flags)).ok_or_else(|| io::Error::last_os_error())
}
/// A physical-memory backed file /// A physical-memory backed file
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] #[repr(transparent)]
pub struct MemoryFile(ManagedFD); pub struct MemoryFile(ManagedFD);
/// A named, physical-memory backed file
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NamedMemoryFile(Box<CStr>, MemoryFile);
impl Borrow<MemoryFile> for NamedMemoryFile
{
#[inline]
fn borrow(&self) -> &MemoryFile {
&self.1
}
}
impl BorrowMut<MemoryFile> for NamedMemoryFile
{
#[inline]
fn borrow_mut(&mut self) -> &mut MemoryFile {
&mut self.1
}
}
impl ops::DerefMut for NamedMemoryFile
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}
impl ops::Deref for NamedMemoryFile
{
type Target = MemoryFile;
#[inline]
fn deref(&self) -> &Self::Target {
&self.1
}
}
//TODO: impl `MemoryFile` (memfd_create() fd wrapper) //TODO: 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>
{
let managed = unsafe {
match memfd_create(UNNAMED.as_ptr(), DEFAULT_FLAGS) {
-1 => return Err(io::Error::last_os_error()),
fd => ManagedFD::take_unchecked(fd),
}
};
Ok(Self(managed))
}
#[inline]
pub fn resize(&mut self, value: usize) -> io::Result<()>
{
if 0 == unsafe { ftruncate(self.as_raw_fd(), value.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?) } {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
pub fn with_hugetlb(hugetlb: MapHugeFlag) -> io::Result<Self>
{
unsafe { create_raw(UNNAMED, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) }
.map(ManagedFD::take)
.map(Self)
}
pub fn with_size(size: usize) -> io::Result<Self>
{
let mut this = Self(unsafe { create_raw(UNNAMED, DEFAULT_FLAGS) }.map(ManagedFD::take)?);
this.resize(size)?;
Ok(this)
}
#[inline]
pub fn with_size_hugetlb(size: usize, hugetlb: MapHugeFlag) -> io::Result<Self>
{
let mut this = Self::with_hugetlb(hugetlb)?;
this.resize(size)?;
Ok(this)
}
}
fn alloc_cstring(string: &str) -> std::ffi::CString
{
#[cold]
fn _contains_nul(mut bytes: Vec<u8>) -> std::ffi::CString
{
// SAFETY: We know this will only be called if byte `0` is in `bytes` (**before** the final element)
let len = unsafe {
memchr::memchr(0, &bytes[..]).unwrap_unchecked()
};
bytes.truncate(len);
// SAFETY: We have truncated the vector to end on the *first* instance of the `0` byte in `bytes`.
unsafe {
std::ffi::CString::from_vec_with_nul_unchecked(bytes)
}
}
let mut bytes = Vec::with_capacity(string.len()+1);
bytes.extend_from_slice(string.as_bytes());
bytes.push(0);
match std::ffi::CString::from_vec_with_nul(bytes) {
Ok(v) => v,
Err(cn) => {
_contains_nul(cn.into_bytes())
}
}
}
impl NamedMemoryFile
{
#[inline]
pub fn new(name: impl AsRef<str>) -> io::Result<Self>
{
let name: Box<CStr> = alloc_cstring(name.as_ref()).into();
let managed = unsafe {
match memfd_create(name.as_ptr(), DEFAULT_FLAGS) {
-1 => return Err(io::Error::last_os_error()),
fd => ManagedFD::take_unchecked(fd),
}
};
Ok(Self(name, MemoryFile(managed)))
}
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)) }
.map(ManagedFD::take)?);
Ok(Self(name, memfd))
}
pub fn with_size(name: impl AsRef<str>, size: usize) -> io::Result<Self>
{
let name: Box<CStr> = alloc_cstring(name.as_ref()).into();
let mut this = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS) }.map(ManagedFD::take)?);
this.resize(size)?;
Ok(Self(name, this))
}
#[inline]
pub fn with_size_hugetlb(name: impl AsRef<str>, size: usize, hugetlb: MapHugeFlag) -> io::Result<Self>
{
let mut this = Self::with_hugetlb(name, hugetlb)?;
this.resize(size)?;
Ok(this)
}
}
impl AsRawFd for MemoryFile impl AsRawFd for MemoryFile
{ {

@ -14,6 +14,15 @@ impl UnmanagedFD {
Self(alias.as_raw_fd().into()) Self(alias.as_raw_fd().into())
} }
#[inline]
pub const fn new_raw(raw: RawFd) -> Option<Self>
{
match NonNegativeI32::new(raw) {
Some(x) => Some(Self(x)),
None => None,
}
}
#[inline] #[inline]
pub(super) const fn new_or_panic(raw: RawFd) -> Self pub(super) const fn new_or_panic(raw: RawFd) -> Self
{ {

@ -6,6 +6,7 @@ use std::{
num::NonZeroUsize, num::NonZeroUsize,
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
fmt, error,
}; };
use libc::{ use libc::{
c_int, c_int,
@ -20,6 +21,32 @@ pub const HUGEPAGE_LOCATION: &'static str = "/sys/kernel/mm/hugepages/";
#[repr(transparent)] #[repr(transparent)]
pub struct MapHugeFlag(c_int); pub struct MapHugeFlag(c_int);
/// Error for when `HugePage::compute_huge()` fails.
#[derive(Debug)]
pub struct HugePageCalcErr(());
impl TryFrom<HugePage> for MapHugeFlag
{
type Error = HugePageCalcErr;
#[inline]
fn try_from(from: HugePage) -> Result<Self, Self::Error>
{
from.compute_huge().ok_or(HugePageCalcErr(()))
}
}
impl error::Error for HugePageCalcErr{}
impl fmt::Display for HugePageCalcErr
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
f.write_str("Invalid huge-page specification")
}
}
impl Default for MapHugeFlag impl Default for MapHugeFlag
{ {
#[inline] #[inline]

Loading…
Cancel
Save