//! Memory mapping helpers. use std::convert::TryFrom; use std::mem::MaybeUninit; use std::num::NonZeroUsize; use std::os::unix::io::RawFd; use std::{io, mem}; use nix::sys::mman; use proxmox_lang::io_format_err; use crate::error::SysError; pub struct Mmap { data: *mut T, len: usize, } unsafe impl Send for Mmap where T: Send {} unsafe impl Sync for Mmap where T: Sync {} impl Mmap { /// Map a file into memory. /// /// # Safety /// /// `fd` must refer to a valid file descriptor. pub unsafe fn map_fd( fd: RawFd, ofs: u64, count: usize, prot: mman::ProtFlags, flags: mman::MapFlags, ) -> io::Result { let byte_len = NonZeroUsize::new(count * mem::size_of::()) .ok_or_else(|| io_format_err!("mapped length must not be zero"))?; // libc::size_t vs usize #[allow(clippy::useless_conversion)] let data = mman::mmap( None, byte_len, prot, flags, fd, libc::off_t::try_from(ofs).map_err(io::Error::other)?, ) .map_err(SysError::into_io_error)?; Ok(Self { data: data as *mut T, len: count, }) } } impl std::ops::Deref for Mmap { type Target = [T]; #[inline] fn deref(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.data, self.len) } } } impl std::ops::DerefMut for Mmap { #[inline] fn deref_mut(&mut self) -> &mut [T] { unsafe { std::slice::from_raw_parts_mut(self.data, self.len) } } } impl Drop for Mmap { fn drop(&mut self) { unsafe { // In theory this can fail if too many memory mappings are already present and // unmapping a smaller region inside a bigger one, causing it to become split into 2 // regions. But then we have bigger problems already anyway, so we'll just ignore this. let _ = mman::munmap( self.data as *mut libc::c_void, self.len * mem::size_of::(), ); } } } impl<'a, T> IntoIterator for &'a Mmap { type Item = &'a T; type IntoIter = <&'a [T] as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { <&'a [T] as IntoIterator>::into_iter(self) } } impl Mmap> { /// Converts to `Mmap`. /// /// # Safety /// /// It is up to the caller to ensure this is safe, see /// [`MaybeUninit::assume_init`](std::mem::MaybeUninit::assume_init). pub unsafe fn assume_init(self) -> Mmap { let out = Mmap { data: self.data as *mut T, len: self.len, }; std::mem::forget(self); out } }