You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
475 lines
12 KiB
475 lines
12 KiB
|
|
#[macro_use] extern crate lazy_static;
|
|
|
|
use libc::{
|
|
mmap,
|
|
MAP_FAILED,
|
|
};
|
|
|
|
use std::{
|
|
os::unix::prelude::*,
|
|
ops,
|
|
mem,
|
|
ptr::{
|
|
self,
|
|
NonNull,
|
|
},
|
|
io,
|
|
fmt, error,
|
|
|
|
borrow::{
|
|
Borrow, BorrowMut,
|
|
}
|
|
};
|
|
|
|
mod ffi;
|
|
use ffi::c_try;
|
|
|
|
pub mod hugetlb;
|
|
pub mod file;
|
|
|
|
use hugetlb::{
|
|
HugePage,
|
|
MapHugeFlag,
|
|
};
|
|
|
|
mod uniq;
|
|
use uniq::UniqueSlice;
|
|
|
|
mod flags;
|
|
pub use flags::*;
|
|
|
|
pub mod err;
|
|
use err::{
|
|
os_error,
|
|
opaque,
|
|
};
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
#[repr(transparent)]
|
|
struct MappedSlice(UniqueSlice<u8>);
|
|
|
|
impl ops::Drop for MappedSlice
|
|
{
|
|
#[inline]
|
|
fn drop(&mut self)
|
|
{
|
|
unsafe {
|
|
libc::munmap(self.0.as_mut_ptr() as *mut _, self.0.len());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A memory mapping over file `T`.
|
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
pub struct MappedFile<T>
|
|
{
|
|
file: T,
|
|
map: MappedSlice,
|
|
}
|
|
|
|
impl<T: AsRawFd> MappedFile<T> {
|
|
/// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and mapping flags provided by `flags`.
|
|
/// # Mapping flags
|
|
/// The trait `MapFlags` is used to allow user-defined configurations of `mmap()`, but the `Flags` enum should usually be used for this, or `()`, which behaves the same as `Flags::default()`.
|
|
///
|
|
/// # Returns
|
|
/// If `mmap()` fails, then the current `errno` is returned alongside the `file` that was passed in, otherwise, a new mapping is
|
|
/// constructed over `file`, and that is returned.
|
|
///
|
|
/// # Panics
|
|
/// If `mmap()` succeeds, but returns an invalid address (e.g. 0)
|
|
pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result<Self, TryNewError<T>>
|
|
{
|
|
#[inline(never)]
|
|
#[cold]
|
|
fn _panic_invalid_address() -> !
|
|
{
|
|
panic!("Invalid/unsupported address returned from mmap()")
|
|
}
|
|
const NULL: *mut libc::c_void = ptr::null_mut();
|
|
let fd = file.as_raw_fd();
|
|
let slice = match unsafe {
|
|
mmap(ptr::null_mut(), len, perm.get_prot(), flags.get_mmap_flags(), fd, 0)
|
|
} {
|
|
MAP_FAILED => return Err(TryNewError::wrap_last_error(file)),
|
|
NULL => _panic_invalid_address(),
|
|
ptr => unsafe {
|
|
UniqueSlice {
|
|
mem: NonNull::new_unchecked(ptr as *mut u8),
|
|
end: match NonNull::new((ptr as *mut u8).add(len)) {
|
|
Some(n) => n,
|
|
_ => _panic_invalid_address(),
|
|
},
|
|
}
|
|
},
|
|
};
|
|
Ok(Self {
|
|
file,
|
|
map: MappedSlice(slice)
|
|
})
|
|
}
|
|
|
|
/// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and
|
|
/// mapping flags provided by `flags`.
|
|
///
|
|
/// # Returns
|
|
/// If `mmap()` fails, then the current `errno` set by `mmap()` is returned, otherwise, a new mapping is
|
|
/// constructed over `file`, and that is returned.
|
|
/// If `mmap()` fails, `file` is dropped. To retain `file`, use `try_new()`.
|
|
///
|
|
/// # Panics
|
|
/// If `mmap()` succeeds, but returns an invalid address (e.g. 0)
|
|
#[inline]
|
|
pub fn new(file: T, len: usize, perm: Perm, flags: Flags) -> io::Result<Self>
|
|
{
|
|
Self::try_new(file, len, perm, flags).map_err(Into::into)
|
|
}
|
|
|
|
/// Sync the mapped memory to the backing file store via `msync()`.
|
|
///
|
|
/// If this is a private mapping, or is mapped over a private file descriptor that does not refer to on-disk persistent storage, syncing the data is usually pointless.
|
|
///
|
|
/// # Returns
|
|
/// If `msync()` fails.
|
|
pub fn flush(&mut self, flush: Flush) -> io::Result<()>
|
|
{
|
|
use libc::msync;
|
|
match unsafe {
|
|
msync(self.map.0.as_mut_ptr() as *mut _, self.map.0.len(), flush.get_ms())
|
|
} {
|
|
0 => Ok(()),
|
|
_ => Err(io::Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
/// Replace the mapped file object with another that aliases the same file descriptor.
|
|
///
|
|
/// # Warning
|
|
/// * The old file object is *not* dropped to prevent the file descriptor being closed. (see `replace_inner_unchecked()`).
|
|
/// If `T` contains other resources, this can cause a memory leak.
|
|
///
|
|
/// # Panics
|
|
/// If `other`'s `AsRawFd` impl *does not* alias the already contained `T`'s.
|
|
pub fn replace_inner<U: AsRawFd>(self, other: U) -> MappedFile<U>
|
|
{
|
|
assert_eq!(self.file.as_raw_fd(), other.as_raw_fd(), "File descriptors must alias");
|
|
unsafe {
|
|
let (this, file) = self.replace_inner_unchecked(other);
|
|
mem::forget(file);
|
|
this
|
|
}
|
|
}
|
|
|
|
/// Unmap the memory contained in `T` and return it.
|
|
/// Before the memory is unmapped, it is `msync()`'d according to `flush`.
|
|
///
|
|
/// # Panics
|
|
/// If `msync()` fails.
|
|
#[inline]
|
|
pub fn into_inner_synced(mut self, flush: Flush) -> T
|
|
{
|
|
self.flush(flush).expect("Failed to sync data");
|
|
drop(self.map);
|
|
self.file
|
|
}
|
|
}
|
|
|
|
impl<T> MappedFile<T> {
|
|
|
|
|
|
#[inline(always)]
|
|
fn raw_parts(&self) -> (*mut u8, usize)
|
|
{
|
|
(self.map.0.mem.as_ptr(), self.map.0.len())
|
|
}
|
|
|
|
/// Set advise according to `adv`, and optionally advise the kernel on if the memory will be needed or not.
|
|
pub fn advise(&mut self, adv: Advice, needed: Option<bool>) -> io::Result<()>
|
|
{
|
|
use libc::{
|
|
madvise,
|
|
MADV_WILLNEED,
|
|
MADV_DONTNEED
|
|
};
|
|
let (addr, len) = self.raw_parts();
|
|
match unsafe { madvise(addr as *mut _, len, adv.get_madv() | needed.map(|n| n.then(|| MADV_WILLNEED).unwrap_or(MADV_DONTNEED)).unwrap_or(0)) } {
|
|
0 => Ok(()),
|
|
_ => Err(io::Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
/// With advice, used as a builder-pattern alternative for `advise()`.
|
|
///
|
|
/// # Returns
|
|
/// If `madvise()` fails, then the `io::Error` along with the previous instance is returned.
|
|
#[inline(always)]
|
|
pub fn try_with_advice(mut self, adv: Advice, needed: Option<bool>) -> Result<Self, TryNewError<Self>>
|
|
{
|
|
match self.advise(adv, needed) {
|
|
Ok(_) => Ok(self),
|
|
Err(error) => Err(TryNewError {
|
|
error: Box::new(error),
|
|
value: self,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// With advice, used as a builder-pattern alternative for `advise()`.
|
|
///
|
|
/// # Returns
|
|
/// If `madvise()` fails, then the mapping is dropped and the error is returned. To keep the previous instance if the call failes, use `try_with_advice()`.
|
|
#[inline]
|
|
pub fn with_advice(self, adv: Advice, needed: Option<bool>) -> io::Result<Self>
|
|
{
|
|
self.try_with_advice(adv, needed).map_err(Into::into)
|
|
}
|
|
|
|
/// Replace the inner file with another without checking static or dynamic bounding.
|
|
/// This function is extremely unsafe if the following conditions are not met in entirity.
|
|
///
|
|
/// # Safety
|
|
/// * `U` and `T` **must** have an `AsRawFd::as_raw_fd()` impl that returns the same `RawFd` value unconditionally.
|
|
/// * The returned `T` in the tuple **must not** attempt close the file descriptor while the returned `MappedFile<U>` in the tuple is alive.
|
|
/// * The returned values **should not** *both* attempt to close the file descriptor when dropped. To prevent the `MappedFile<U>` from attempting to close the file descriptor, use `MappedFile::into_inner()` and ensure `U` does not close the file descriptor while `T` is alive. Alternatively, use a mechanism of `T` to prevent it from closing the file descriptor while `U` is alive.
|
|
#[inline(always)]
|
|
pub unsafe fn replace_inner_unchecked<U>(self, other: U) -> (MappedFile<U>, T)
|
|
{
|
|
let MappedFile{ file, map } = self;
|
|
(MappedFile {
|
|
file: other,
|
|
map
|
|
}, file)
|
|
}
|
|
|
|
/// Unmap the memory contained in `T` and return it.
|
|
///
|
|
/// # Warning
|
|
/// If the map is shared, or refers to a persistent file on disk, you should call `flush()`
|
|
/// first or use `into_inner_synced()`
|
|
#[inline]
|
|
pub fn into_inner(self) -> T
|
|
{
|
|
drop(self.map);
|
|
self.file
|
|
}
|
|
|
|
/// The size of the mapped memory
|
|
#[inline]
|
|
pub fn len(&self) -> usize
|
|
{
|
|
self.map.0.len()
|
|
}
|
|
|
|
/// Get a slice of the mapped memory
|
|
#[inline]
|
|
pub fn as_slice(&self) -> &[u8]
|
|
{
|
|
&self.map.0[..]
|
|
}
|
|
|
|
/// Get a mutable slice of the mapped memory
|
|
#[inline]
|
|
pub fn as_slice_mut(&mut self) -> &mut [u8]
|
|
{
|
|
&mut self.map.0[..]
|
|
}
|
|
|
|
/// Get a raw slice of the mapped memory
|
|
#[inline]
|
|
pub fn as_raw_slice(&self) -> *const [u8]
|
|
{
|
|
self.map.0.as_raw_slice()
|
|
}
|
|
|
|
/// Get a raw mutable slice of the mapped memory
|
|
#[inline]
|
|
pub fn as_raw_slice_mut(&mut self) -> *mut [u8]
|
|
{
|
|
self.map.0.as_raw_slice_mut()
|
|
}
|
|
|
|
/// Checks if the mapping dangles (i.e. `len() == 0`.)
|
|
#[inline]
|
|
pub fn is_empty(&self) -> bool
|
|
{
|
|
self.map.0.is_empty()
|
|
}
|
|
}
|
|
|
|
/// Error returned when mapping operation fails
|
|
pub struct TryNewError<T: ?Sized>
|
|
{
|
|
error: Box<io::Error>,
|
|
value: T
|
|
}
|
|
|
|
impl<T:?Sized> error::Error for TryNewError<T>
|
|
{
|
|
#[inline]
|
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
Some(self.error.as_ref())
|
|
}
|
|
}
|
|
|
|
impl<T:?Sized> fmt::Display for TryNewError<T>
|
|
{
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "error in mapping of type {}", std::any::type_name::<T>())
|
|
}
|
|
}
|
|
|
|
impl<T:?Sized> fmt::Debug for TryNewError<T>
|
|
{
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
f.debug_struct("TryNewError")
|
|
.field("error", &self.error)
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> TryNewError<T>
|
|
{
|
|
/// A reference to the value
|
|
#[inline]
|
|
pub fn value(&self) -> &T
|
|
{
|
|
&self.value
|
|
}
|
|
/// A mutable reference to the value
|
|
#[inline]
|
|
pub fn value_mut(&mut self) -> &mut T
|
|
{
|
|
&mut self.value
|
|
}
|
|
/// A reference to the IO error
|
|
#[inline]
|
|
pub fn error(&self) -> &io::Error
|
|
{
|
|
&self.error
|
|
}
|
|
/// Consume a boxed instance and return the boxed IO error.
|
|
#[inline]
|
|
pub fn into_error_box(self: Box<Self>) -> Box<io::Error>
|
|
{
|
|
self.error
|
|
}
|
|
}
|
|
|
|
impl<T> TryNewError<T>
|
|
{
|
|
#[inline]
|
|
fn wrap_last_error(value: T) -> Self
|
|
{
|
|
Self {
|
|
error: Box::new(io::Error::last_os_error()),
|
|
value,
|
|
}
|
|
}
|
|
/// Consume into the contained value
|
|
#[inline]
|
|
pub fn into_inner(self) -> T
|
|
{
|
|
self.value
|
|
}
|
|
|
|
/// Consume into the IO error
|
|
#[inline]
|
|
pub fn into_error(self) -> io::Error
|
|
{
|
|
*self.error
|
|
}
|
|
/// Consume into the value and the error.
|
|
#[inline]
|
|
pub fn into_parts(self) -> (T, io::Error)
|
|
{
|
|
(self.value, *self.error)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> From<Box<TryNewError<T>>> for io::Error
|
|
{
|
|
#[inline]
|
|
fn from(from: Box<TryNewError<T>>) -> Self
|
|
{
|
|
*from.error
|
|
}
|
|
}
|
|
|
|
impl<T> From<TryNewError<T>> for io::Error
|
|
{
|
|
#[inline]
|
|
fn from(from: TryNewError<T>) -> Self
|
|
{
|
|
from.into_error()
|
|
}
|
|
}
|
|
|
|
impl<T: AsRawFd> Borrow<T> for MappedFile<T>
|
|
{
|
|
#[inline]
|
|
fn borrow(&self) -> &T
|
|
{
|
|
&self.file
|
|
}
|
|
}
|
|
|
|
impl<T> Borrow<[u8]> for MappedFile<T>
|
|
{
|
|
#[inline]
|
|
fn borrow(&self) -> &[u8]
|
|
{
|
|
self.as_slice()
|
|
}
|
|
}
|
|
|
|
impl<T> BorrowMut<[u8]> for MappedFile<T>
|
|
{
|
|
#[inline]
|
|
fn borrow_mut(&mut self) -> &mut [u8]
|
|
{
|
|
self.as_slice_mut()
|
|
}
|
|
}
|
|
|
|
impl<T> ops::Deref for MappedFile<T>
|
|
{
|
|
type Target= [u8];
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target
|
|
{
|
|
self.as_slice()
|
|
}
|
|
}
|
|
impl<T> ops::DerefMut for MappedFile<T>
|
|
{
|
|
#[inline]
|
|
fn deref_mut(&mut self) -> &mut Self::Target
|
|
{
|
|
self.as_slice_mut()
|
|
}
|
|
}
|
|
|
|
/// Used for anonymous mappings with `MappedFile`.
|
|
///
|
|
/// # Safety
|
|
/// The `AsRawFd` impl of this structure always returns `-1`. It should only be used with `MappedFile`, as this is an invlalid file descriptor in all other contexts.
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
|
pub struct Anonymous;
|
|
|
|
impl AsRawFd for Anonymous
|
|
{
|
|
#[inline(always)]
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
-1
|
|
}
|
|
}
|
|
|
|
//TODO: Continue copying from `utf8encode` at the //TODO (cont.) line
|
|
|