use std::{ffi::c_void, fs::File, io, path::Path};
use super::error::UserDmpError;
#[derive(Debug)]
pub struct MappingFile<'a> {
pub buffer: &'a [u8],
pub address: *mut c_void,
}
impl<'a> MappingFile<'a> {
pub fn new(path: impl AsRef<Path>) -> Result<Self, UserDmpError> {
let file = File::open(path)?;
let (buffer, address) = map::map_file(file)?;
Ok(Self { buffer, address })
}
pub fn cursor(&self) -> io::Cursor<&'a [u8]> {
io::Cursor::new(self.buffer)
}
}
impl Drop for MappingFile<'_> {
fn drop(&mut self) {
if !self.address.is_null() {
#[cfg(windows)]
{
use windows_sys::Win32::System::Memory::{MEMORY_MAPPED_VIEW_ADDRESS, UnmapViewOfFile};
let address = MEMORY_MAPPED_VIEW_ADDRESS { Value: self.address };
unsafe {
UnmapViewOfFile(address);
}
}
#[cfg(unix)]
{
unsafe {
libc::munmap(self.address, self.buffer.len());
}
}
}
}
}
mod map {
use std::{ffi::c_void, ptr, slice};
use super::{File, UserDmpError};
#[cfg(windows)]
pub fn map_file(file: File) -> Result<(&'static [u8], *mut c_void), UserDmpError> {
use std::os::windows::io::AsRawHandle;
use windows_sys::Win32::{
Foundation::CloseHandle,
System::Memory::{CreateFileMappingA, FILE_MAP_READ, MapViewOfFile, PAGE_READONLY},
};
let h_file = file.as_raw_handle();
let h_mapping = unsafe { CreateFileMappingA(h_file, ptr::null_mut(), PAGE_READONLY, 0, 0, ptr::null_mut()) };
if h_mapping.is_null() {
return Err(UserDmpError::CreateFileMappingError);
}
let size = file.metadata()?.len() as usize;
let base_address = unsafe { MapViewOfFile(h_mapping, FILE_MAP_READ, 0, 0, size) };
if base_address.Value.is_null() {
unsafe { CloseHandle(h_mapping) };
return Err(UserDmpError::MapViewOfFileError);
}
unsafe { CloseHandle(h_mapping) };
unsafe { Ok((slice::from_raw_parts(base_address.Value as *const u8, size), base_address.Value)) }
}
#[cfg(unix)]
pub fn map_file(file: File) -> Result<(&'static [u8], *mut c_void), UserDmpError> {
use std::os::unix::io::AsRawFd;
use libc::{MAP_FAILED, MAP_SHARED, PROT_READ, mmap};
let fd = file.as_raw_fd();
let size = file.metadata()?.len() as usize;
let base_address = unsafe { mmap(ptr::null_mut(), size, PROT_READ, MAP_SHARED, fd, 0) };
if base_address == MAP_FAILED {
return Err(UserDmpError::MmapError);
}
unsafe { Ok((slice::from_raw_parts(base_address as *const u8, size), base_address)) }
}
}