use ::winapi::{
shared::{ntdef::NULL, winerror::ERROR_ALREADY_EXISTS},
um::{
errhandlingapi::GetLastError,
handleapi::CloseHandle,
memoryapi::{
CreateFileMappingW, MapViewOfFile, UnmapViewOfFile, VirtualQuery, FILE_MAP_READ,
FILE_MAP_WRITE,
},
winbase::FILE_FLAG_DELETE_ON_CLOSE,
winnt::{
DELETE, FILE_ATTRIBUTE_TEMPORARY, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
GENERIC_READ, GENERIC_WRITE, HANDLE, MEMORY_BASIC_INFORMATION, PAGE_READWRITE,
},
},
};
#[allow(unused_imports)]
use super::log::*;
use crate::ShmemError;
use std::ffi::OsStr;
use std::fs::{File, OpenOptions};
use std::io::ErrorKind;
use std::iter::once;
use std::mem::size_of;
use std::os::windows::{ffi::OsStrExt, fs::OpenOptionsExt, io::AsRawHandle};
use std::path::PathBuf;
use std::ptr::null_mut;
pub struct MapData {
owner: bool,
map_handle: HANDLE,
persistent_file: File,
pub unique_id: String,
pub map_size: usize,
pub map_ptr: *mut u8,
}
impl Drop for MapData {
fn drop(&mut self) {
if self.map_ptr as *mut _ != NULL {
trace!("UnmapViewOfFile(map_ptr:{:p})", self.map_ptr);
unsafe {
UnmapViewOfFile(self.map_ptr as *mut _);
}
}
if self.map_handle as *mut _ != NULL {
trace!("CloseHandle(map_handle:{:p})", self.map_handle);
unsafe {
CloseHandle(self.map_handle);
}
}
if self.owner {
let mut base_path = get_tmp_dir().unwrap();
let file_path = base_path.join(self.unique_id.trim_start_matches('/'));
debug!("Setting mapping to delete after everyone has closed it");
match OpenOptions::new()
.access_mode(GENERIC_READ | GENERIC_WRITE | DELETE)
.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
.create(false)
.attributes(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE)
.open(&file_path)
{
Ok(_) => {
base_path.push(&format!(
"{}_deleted",
self.unique_id.trim_start_matches('/')
));
debug!(
"Renaming {} to {}",
file_path.to_string_lossy(),
base_path.to_string_lossy()
);
if let Err(_e) = std::fs::rename(&file_path, &base_path) {
debug!(
"Failed to rename persistent_file {} : {}",
file_path.to_string_lossy(),
_e
);
}
}
Err(_e) => {
debug!(
"Failed to set DELETE_ON_CLOSE on persistent_file {} : {}",
file_path.to_string_lossy(),
_e
);
}
};
}
}
}
impl MapData {
pub fn set_owner(&mut self, is_owner: bool) -> bool {
let prev_val = self.owner;
self.owner = is_owner;
prev_val
}
}
fn get_tmp_dir() -> Result<PathBuf, ShmemError> {
debug!("Getting & creating shared_memory-rs temp dir");
let mut path = std::env::temp_dir();
path.push("shared_memory-rs");
match std::fs::create_dir_all(path.as_path()) {
Ok(_) => Ok(path),
Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(path),
Err(e) => Err(ShmemError::UnknownOsError(e.raw_os_error().unwrap() as _)),
}
}
pub fn create_mapping(unique_id: &str, map_size: usize) -> Result<MapData, ShmemError> {
let mut file_path = get_tmp_dir()?;
file_path.push(unique_id.trim_start_matches('/'));
debug!(
"Creating persistent_file at {}",
file_path.to_string_lossy()
);
let persistent_file = match OpenOptions::new()
.read(true)
.write(true)
.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
.create_new(true)
.attributes(FILE_ATTRIBUTE_TEMPORARY)
.open(&file_path)
{
Ok(f) => f,
Err(e) if e.kind() == ErrorKind::AlreadyExists => return Err(ShmemError::MappingIdExists),
Err(e) => return Err(ShmemError::MapCreateFailed(e.raw_os_error().unwrap() as _)),
};
let mut new_map: MapData = MapData {
owner: true,
persistent_file,
unique_id: String::from(unique_id),
map_handle: NULL,
map_size,
map_ptr: null_mut(),
};
debug!("Creating memory mapping");
let high_size: u32 = ((map_size as u64 & 0xFFFF_FFFF_0000_0000_u64) >> 32) as u32;
let low_size: u32 = (map_size as u64 & 0xFFFF_FFFF_u64) as u32;
let unique_id: Vec<u16> = OsStr::new(unique_id).encode_wide().chain(once(0)).collect();
new_map.map_handle = unsafe {
CreateFileMappingW(
new_map.persistent_file.as_raw_handle() as _,
null_mut(),
PAGE_READWRITE,
high_size,
low_size,
unique_id.as_ptr(),
)
};
let last_error = unsafe { GetLastError() };
trace!(
"CreateFileMappingW({:p}, NULL, {:X}, {}, {}, '{}') == {:p}",
new_map.persistent_file.as_raw_handle(),
PAGE_READWRITE,
high_size,
low_size,
new_map.unique_id,
new_map.map_handle
);
if new_map.map_handle == NULL {
return Err(ShmemError::MapCreateFailed(last_error));
} else if last_error == ERROR_ALREADY_EXISTS {
return Err(ShmemError::MappingIdExists);
}
debug!("Loading mapping into address space");
new_map.map_ptr =
unsafe { MapViewOfFile(new_map.map_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0) } as _;
let last_error = unsafe { GetLastError() };
trace!(
"MapViewOfFile({:p}, {:X}, 0, 0, 0) == {:p}",
new_map.map_handle,
FILE_MAP_READ | FILE_MAP_WRITE,
new_map.map_ptr
);
if new_map.map_ptr.is_null() {
return Err(ShmemError::MapCreateFailed(last_error));
}
Ok(new_map)
}
pub fn open_mapping(unique_id: &str, map_size: usize) -> Result<MapData, ShmemError> {
let mut file_path = get_tmp_dir()?;
file_path.push(unique_id.trim_start_matches('/'));
debug!(
"Openning persistent_file at {}",
file_path.to_string_lossy()
);
let persistent_file = match OpenOptions::new()
.access_mode(GENERIC_READ | GENERIC_WRITE)
.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
.create(false)
.attributes(FILE_ATTRIBUTE_TEMPORARY)
.open(&file_path)
{
Ok(f) => f,
Err(e) if e.kind() == ErrorKind::AlreadyExists => return Err(ShmemError::MappingIdExists),
Err(e) => return Err(ShmemError::MapOpenFailed(e.raw_os_error().unwrap() as _)),
};
let mut new_map: MapData = MapData {
owner: false,
persistent_file,
unique_id: String::from(unique_id),
map_handle: NULL,
map_size,
map_ptr: null_mut(),
};
debug!("Openning memory mapping");
let high_size: u32 = ((map_size as u64 & 0xFFFF_FFFF_0000_0000_u64) >> 32) as u32;
let low_size: u32 = (map_size as u64 & 0xFFFF_FFFF_u64) as u32;
let unique_id: Vec<u16> = OsStr::new(unique_id).encode_wide().chain(once(0)).collect();
new_map.map_handle = unsafe {
CreateFileMappingW(
new_map.persistent_file.as_raw_handle() as _,
null_mut(),
PAGE_READWRITE,
high_size,
low_size,
unique_id.as_ptr(),
)
};
let last_error = unsafe { GetLastError() };
trace!(
"CreateFileMappingW({:p}, NULL, {:X}, {}, {}, '{}') == {:p}",
new_map.persistent_file.as_raw_handle(),
PAGE_READWRITE,
high_size,
low_size,
new_map.unique_id,
new_map.map_handle
);
if new_map.map_handle == NULL {
return Err(ShmemError::MapCreateFailed(last_error));
}
debug!("Loading mapping into address space");
new_map.map_ptr =
unsafe { MapViewOfFile(new_map.map_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0) } as _;
let last_error = unsafe { GetLastError() };
trace!(
"MapViewOfFile({:p}, {:X}, 0, 0, 0) == {:p}",
new_map.map_handle,
FILE_MAP_READ | FILE_MAP_WRITE,
new_map.map_ptr
);
if new_map.map_ptr.is_null() {
return Err(ShmemError::MapOpenFailed(last_error));
}
new_map.map_size = unsafe {
let mut mem_ba: MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION {
BaseAddress: null_mut(),
AllocationBase: null_mut(),
AllocationProtect: 0,
RegionSize: 0,
State: 0,
Protect: 0,
Type: 0,
};
let ret_val = VirtualQuery(
new_map.map_ptr as *const _,
&mut mem_ba as *mut _,
size_of::<MEMORY_BASIC_INFORMATION>(),
);
if ret_val == 0 {
let last_error = GetLastError();
return Err(ShmemError::UnknownOsError(last_error));
}
mem_ba.RegionSize
};
Ok(new_map)
}