use std::fs::{File, OpenOptions};
use std::io::ErrorKind;
use std::os::windows::{fs::OpenOptionsExt, io::AsRawHandle};
use std::path::PathBuf;
use crate::{log::*, ShmemConf};
use win_sys::*;
use crate::ShmemError;
#[derive(Clone, Default)]
pub struct ShmemConfExt {
allow_raw: bool,
}
impl ShmemConf {
pub fn allow_raw(mut self, allow: bool) -> Self {
self.ext.allow_raw = allow;
self
}
}
pub struct MapData {
owner: bool,
pub view: ViewOfFile,
#[allow(dead_code)]
file_map: FileMapping,
#[allow(dead_code)]
persistent_file: Option<File>,
pub unique_id: String,
pub map_size: usize,
}
impl Drop for MapData {
fn drop(&mut self) {
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).0)
.create(false)
.attributes((FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE).0)
.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
}
pub fn as_mut_ptr(&self) -> *mut u8 {
self.view.as_mut_ptr() as _
}
}
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");
if path.is_dir() {
return Ok(path);
}
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 _)),
}
}
fn new_map(
unique_id: &str,
mut map_size: usize,
create: bool,
allow_raw: bool,
) -> Result<MapData, ShmemError> {
let mut file_path = get_tmp_dir()?;
file_path.push(unique_id.trim_start_matches('/'));
debug!(
"{} persistent_file at {}",
if create { "Creating" } else { "Openning" },
file_path.to_string_lossy()
);
let mut opt = OpenOptions::new();
opt.read(true)
.write(true)
.share_mode((FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE).0)
.attributes((FILE_ATTRIBUTE_TEMPORARY).0);
if create {
opt.create_new(true);
} else {
opt.create(false);
};
let mut persistent_file = None;
let map_h = match opt.open(&file_path) {
Ok(f) => {
debug!(
"{} memory mapping",
if create { "Creating" } else { "Openning" },
);
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;
trace!(
"CreateFileMapping({:?}, NULL, {:X}, {}, {}, '{}')",
HANDLE(f.as_raw_handle() as _),
PAGE_READWRITE.0,
high_size,
low_size,
unique_id,
);
match CreateFileMapping(
HANDLE(f.as_raw_handle() as _),
None,
PAGE_READWRITE,
high_size,
low_size,
unique_id,
) {
Ok(v) => {
persistent_file = Some(f);
v
}
Err(e) => {
let err_code = e.win32_error().unwrap();
return if err_code == ERROR_ALREADY_EXISTS {
Err(ShmemError::MappingIdExists)
} else {
Err(if create {
ShmemError::MapCreateFailed(err_code.0)
} else {
ShmemError::MapOpenFailed(err_code.0)
})
};
}
}
}
Err(e) if e.kind() == ErrorKind::AlreadyExists => return Err(ShmemError::MappingIdExists),
Err(e) => {
if create {
return Err(ShmemError::MapCreateFailed(e.raw_os_error().unwrap() as _));
} else if !allow_raw {
return Err(ShmemError::MapOpenFailed(ERROR_FILE_NOT_FOUND.0));
}
trace!(
"OpenFileMappingW({:?}, {}, '{}')",
FILE_MAP_ALL_ACCESS,
false,
unique_id,
);
match OpenFileMapping(FILE_MAP_ALL_ACCESS, false, unique_id) {
Ok(h) => h,
Err(e) => {
return Err(ShmemError::MapOpenFailed(e.win32_error().unwrap().0));
}
}
}
};
trace!("0x{:X}", map_h);
debug!("Loading mapping into address space");
trace!(
"MapViewOfFile(0x{:X}, {:X}, 0, 0, 0)",
map_h,
(FILE_MAP_READ | FILE_MAP_WRITE).0,
);
let map_ptr = match MapViewOfFile(map_h.as_handle(), FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0) {
Ok(v) => v,
Err(e) => {
return Err(if create {
ShmemError::MapCreateFailed(e.win32_error().unwrap().0)
} else {
ShmemError::MapOpenFailed(e.win32_error().unwrap().0)
})
}
};
trace!("\t{:p}", map_ptr);
if !create {
let mut info = MEMORY_BASIC_INFORMATION::default();
if let Err(e) = VirtualQuery(map_ptr.as_mut_ptr(), &mut info) {
return Err(ShmemError::UnknownOsError(e.win32_error().unwrap().0));
}
map_size = info.RegionSize;
}
Ok(MapData {
owner: create,
file_map: map_h,
persistent_file,
unique_id: unique_id.to_string(),
map_size,
view: map_ptr,
})
}
pub fn create_mapping(unique_id: &str, map_size: usize) -> Result<MapData, ShmemError> {
new_map(unique_id, map_size, true, false)
}
pub fn open_mapping(
unique_id: &str,
map_size: usize,
ext: &ShmemConfExt,
) -> Result<MapData, ShmemError> {
new_map(unique_id, map_size, false, ext.allow_raw)
}