mapped_file/file/
memory.rs

1//! Provides physical in-memory file descriptors.
2//!
3//! This can be useful for temporary buffers where a file descriptor is required.
4//! Huge-pages can also be used for this memory.
5use super::*;
6use libc::{
7    c_uint,
8    memfd_create,
9    MFD_CLOEXEC,
10    MFD_HUGETLB,
11
12    ftruncate,
13};
14use std::{
15    ffi::CStr,
16    borrow::{
17	Borrow,
18	BorrowMut,
19    },
20    ops,
21};
22use hugetlb::{
23    MapHugeFlag,
24    HugePage,
25};
26
27static UNNAMED: &'static CStr = unsafe {
28    CStr::from_bytes_with_nul_unchecked(b"<unnamed memory file>\0")
29};
30
31const DEFAULT_FLAGS: c_uint = MFD_CLOEXEC;
32
33#[inline(always)]
34//XXX: Is the static bound required here?
35/// Create a raw, unmanaged, memory file with these flags and this name.
36///
37/// # Safety
38/// The reference obtained by `name` must not move as long as the `Ok()` result is alive.
39pub unsafe fn create_raw(name: impl AsRef<CStr>, flags: c_uint) -> io::Result<UnmanagedFD> 
40{
41    UnmanagedFD::new_raw(memfd_create(name.as_ref().as_ptr(), flags)).ok_or_else(|| io::Error::last_os_error())
42}
43
44/// A physical-memory backed file
45#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
46#[repr(transparent)]
47pub struct MemoryFile(ManagedFD);
48
49/// A named, physical-memory backed file
50#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
51pub struct NamedMemoryFile(Box<CStr>, MemoryFile);
52
53impl Borrow<MemoryFile> for NamedMemoryFile
54{
55    #[inline] 
56    fn borrow(&self) -> &MemoryFile {
57	&self.1
58    }
59}
60impl BorrowMut<MemoryFile> for NamedMemoryFile
61{
62    #[inline] 
63    fn borrow_mut(&mut self) -> &mut MemoryFile {
64	&mut self.1
65    }
66}
67impl ops::DerefMut for NamedMemoryFile
68{
69    fn deref_mut(&mut self) -> &mut Self::Target {
70	&mut self.1
71    }
72}
73impl ops::Deref for NamedMemoryFile
74{
75    type Target = MemoryFile;
76    #[inline] 
77    fn deref(&self) -> &Self::Target {
78	&self.1
79    }
80}
81
82//impl `MemoryFile` (memfd_create() fd wrapper)
83impl MemoryFile
84{
85    /// Create a new, empty, memory file with no name and no flags.
86    pub fn new() -> io::Result<Self>
87    {
88	let managed = unsafe {
89	    match memfd_create(UNNAMED.as_ptr(), DEFAULT_FLAGS) {
90		-1 => return Err(io::Error::last_os_error()),
91		fd => ManagedFD::take_unchecked(fd),
92	    }
93	};
94	Ok(Self(managed))
95    }
96    #[inline] 
97    pub fn resize(&mut self, value: usize) -> io::Result<()>
98    {
99	if 0 == unsafe { ftruncate(self.as_raw_fd(), value.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?) } {
100	    Ok(())
101	} else {
102	    Err(io::Error::last_os_error())
103	}
104    }
105    
106    pub fn with_hugetlb(hugetlb: MapHugeFlag) -> io::Result<Self>
107    {
108	unsafe { create_raw(UNNAMED, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) }
109	.map(ManagedFD::take)
110	    .map(Self)
111    }
112
113    pub fn with_size(size: usize) -> io::Result<Self>
114    {
115	let mut this = Self(unsafe { create_raw(UNNAMED, DEFAULT_FLAGS) }.map(ManagedFD::take)?);
116	this.resize(size)?;
117	Ok(this)
118    }
119
120    #[inline] 
121    pub fn with_size_hugetlb(size: usize, hugetlb: MapHugeFlag) -> io::Result<Self>
122    {
123	let mut this = Self::with_hugetlb(hugetlb)?;
124	this.resize(size)?;
125	Ok(this)
126    }
127}
128
129fn alloc_cstring(string: &str) -> std::ffi::CString
130{
131    #[cold]
132    fn _contains_nul(mut bytes: Vec<u8>) -> std::ffi::CString
133    {
134	// SAFETY: We know this will only be called if byte `0` is in `bytes` (**before** the final element)
135	let len = unsafe {
136	    memchr::memchr(0, &bytes[..]).unwrap_unchecked()
137	};
138	bytes.truncate(len);
139	// SAFETY: We have truncated the vector to end on the *first* instance of the `0` byte in `bytes`.
140	unsafe {
141	    std::ffi::CString::from_vec_with_nul_unchecked(bytes)
142	}
143    }
144    let mut bytes = Vec::with_capacity(string.len()+1);
145    bytes.extend_from_slice(string.as_bytes());
146    bytes.push(0);
147    match std::ffi::CString::from_vec_with_nul(bytes) {
148	Ok(v) => v,
149	Err(cn) => {
150	    _contains_nul(cn.into_bytes())
151	}
152    }
153}
154
155impl NamedMemoryFile
156{
157    #[inline] 
158    pub fn new(name: impl AsRef<str>) -> io::Result<Self>
159    {
160	let name: Box<CStr> = alloc_cstring(name.as_ref()).into();
161	let managed = unsafe {
162	    match memfd_create(name.as_ptr(), DEFAULT_FLAGS) {
163		-1 => return Err(io::Error::last_os_error()),
164		fd => ManagedFD::take_unchecked(fd),
165	    }
166	};
167	Ok(Self(name, MemoryFile(managed)))
168    }
169
170    pub fn with_hugetlb(name: impl AsRef<str>, hugetlb: MapHugeFlag) -> io::Result<Self>
171    {
172	let name: Box<CStr> = alloc_cstring(name.as_ref()).into();
173	let memfd = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) }
174			       .map(ManagedFD::take)?);
175	Ok(Self(name, memfd))
176    }
177
178    pub fn with_size(name: impl AsRef<str>, size: usize) -> io::Result<Self>
179    {
180	let name: Box<CStr> = alloc_cstring(name.as_ref()).into();
181	let mut this = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS) }.map(ManagedFD::take)?);
182	this.resize(size)?;
183	Ok(Self(name, this))
184    }
185
186    #[inline] 
187    pub fn with_size_hugetlb(name: impl AsRef<str>, size: usize, hugetlb: MapHugeFlag) -> io::Result<Self>
188    {
189	let mut this = Self::with_hugetlb(name, hugetlb)?;
190	this.resize(size)?;
191	Ok(this)
192    }
193}
194
195impl AsRawFd for MemoryFile
196{
197    #[inline] 
198    fn as_raw_fd(&self) -> RawFd {
199	self.0.as_raw_fd()
200    }
201}
202
203impl FromRawFd for MemoryFile
204{
205    #[inline] 
206    unsafe fn from_raw_fd(fd: RawFd) -> Self {
207	Self(ManagedFD::from_raw_fd(fd))
208    }
209}
210
211impl IntoRawFd for MemoryFile
212{
213    #[inline]
214    fn into_raw_fd(self) -> RawFd {
215	self.0.into_raw_fd()
216    }
217}
218
219impl From<MemoryFile> for ManagedFD
220{
221    #[inline] 
222    fn from(from: MemoryFile) -> Self
223    {
224	from.0
225    }
226}
227
228impl From<MemoryFile> for std::fs::File
229{
230    #[inline] 
231    fn from(from: MemoryFile) -> Self
232    {
233	from.0.into()
234    }
235}
236
237raw::impl_io_for_fd!(MemoryFile => .0.as_raw_fd());