diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/memmap/src/windows.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/memmap/src/windows.rs')
-rw-r--r-- | third_party/rust/memmap/src/windows.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/third_party/rust/memmap/src/windows.rs b/third_party/rust/memmap/src/windows.rs new file mode 100644 index 0000000000..d8aa99d255 --- /dev/null +++ b/third_party/rust/memmap/src/windows.rs @@ -0,0 +1,300 @@ +use std::fs::File; +use std::os::raw::c_void; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::{io, mem, ptr}; + +use winapi::shared::basetsd::SIZE_T; +use winapi::shared::minwindef::DWORD; +use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; +use winapi::um::memoryapi::{ + CreateFileMappingW, FlushViewOfFile, MapViewOfFile, UnmapViewOfFile, VirtualProtect, + FILE_MAP_ALL_ACCESS, FILE_MAP_COPY, FILE_MAP_EXECUTE, FILE_MAP_READ, FILE_MAP_WRITE, +}; +use winapi::um::sysinfoapi::GetSystemInfo; +use winapi::um::winnt::{ + PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, + PAGE_READWRITE, PAGE_WRITECOPY, +}; + +pub struct MmapInner { + file: Option<File>, + ptr: *mut c_void, + len: usize, + copy: bool, +} + +impl MmapInner { + /// Creates a new `MmapInner`. + /// + /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls. + pub fn new( + file: &File, + protect: DWORD, + access: DWORD, + offset: u64, + len: usize, + copy: bool, + ) -> io::Result<MmapInner> { + let alignment = offset % allocation_granularity() as u64; + let aligned_offset = offset - alignment as u64; + let aligned_len = len + alignment as usize; + + unsafe { + let handle = CreateFileMappingW( + file.as_raw_handle(), + ptr::null_mut(), + protect, + 0, + 0, + ptr::null(), + ); + if handle == ptr::null_mut() { + return Err(io::Error::last_os_error()); + } + + let ptr = MapViewOfFile( + handle, + access, + (aligned_offset >> 16 >> 16) as DWORD, + (aligned_offset & 0xffffffff) as DWORD, + aligned_len as SIZE_T, + ); + CloseHandle(handle); + + if ptr == ptr::null_mut() { + Err(io::Error::last_os_error()) + } else { + Ok(MmapInner { + file: Some(file.try_clone()?), + ptr: ptr.offset(alignment as isize), + len: len as usize, + copy: copy, + }) + } + } + } + + pub fn map(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); + let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); + let mut access = FILE_MAP_READ; + let protection = match (write, exec) { + (true, true) => { + access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE; + PAGE_EXECUTE_READWRITE + } + (true, false) => { + access |= FILE_MAP_WRITE; + PAGE_READWRITE + } + (false, true) => { + access |= FILE_MAP_EXECUTE; + PAGE_EXECUTE_READ + } + (false, false) => PAGE_READONLY, + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; + if write || exec { + inner.make_read_only()?; + } + Ok(inner) + } + + pub fn map_exec(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); + let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE; + let protection = if write { + access |= FILE_MAP_WRITE; + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; + if write { + inner.make_exec()?; + } + Ok(inner) + } + + pub fn map_mut(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); + let mut access = FILE_MAP_READ | FILE_MAP_WRITE; + let protection = if exec { + access |= FILE_MAP_EXECUTE; + PAGE_EXECUTE_READWRITE + } else { + PAGE_READWRITE + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; + if exec { + inner.make_mut()?; + } + Ok(inner) + } + + pub fn map_copy(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READWRITE); + let mut access = FILE_MAP_COPY; + let protection = if exec { + access |= FILE_MAP_EXECUTE; + PAGE_EXECUTE_WRITECOPY + } else { + PAGE_WRITECOPY + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, true)?; + if exec { + inner.make_mut()?; + } + Ok(inner) + } + + pub fn map_anon(len: usize, _stack: bool) -> io::Result<MmapInner> { + unsafe { + // Create a mapping and view with maximum access permissions, then use `VirtualProtect` + // to set the actual `Protection`. This way, we can set more permissive protection later + // on. + // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx + + let handle = CreateFileMappingW( + INVALID_HANDLE_VALUE, + ptr::null_mut(), + PAGE_EXECUTE_READWRITE, + (len >> 16 >> 16) as DWORD, + (len & 0xffffffff) as DWORD, + ptr::null(), + ); + if handle == ptr::null_mut() { + return Err(io::Error::last_os_error()); + } + let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE; + let ptr = MapViewOfFile(handle, access, 0, 0, len as SIZE_T); + CloseHandle(handle); + + if ptr == ptr::null_mut() { + return Err(io::Error::last_os_error()); + } + + let mut old = 0; + let result = VirtualProtect(ptr, len as SIZE_T, PAGE_READWRITE, &mut old); + if result != 0 { + Ok(MmapInner { + file: None, + ptr: ptr, + len: len as usize, + copy: false, + }) + } else { + Err(io::Error::last_os_error()) + } + } + } + + pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { + self.flush_async(offset, len)?; + if let Some(ref file) = self.file { + file.sync_data()?; + } + Ok(()) + } + + pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { + let result = unsafe { FlushViewOfFile(self.ptr.offset(offset as isize), len as SIZE_T) }; + if result != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { + unsafe { + let alignment = self.ptr as usize % allocation_granularity(); + let ptr = self.ptr.offset(-(alignment as isize)); + let aligned_len = self.len as SIZE_T + alignment as SIZE_T; + + let mut old = 0; + let result = VirtualProtect(ptr, aligned_len, protect, &mut old); + + if result != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + } + + pub fn make_read_only(&mut self) -> io::Result<()> { + self.virtual_protect(PAGE_READONLY) + } + + pub fn make_exec(&mut self) -> io::Result<()> { + if self.copy { + self.virtual_protect(PAGE_EXECUTE_WRITECOPY) + } else { + self.virtual_protect(PAGE_EXECUTE_READ) + } + } + + pub fn make_mut(&mut self) -> io::Result<()> { + if self.copy { + self.virtual_protect(PAGE_WRITECOPY) + } else { + self.virtual_protect(PAGE_READWRITE) + } + } + + #[inline] + pub fn ptr(&self) -> *const u8 { + self.ptr as *const u8 + } + + #[inline] + pub fn mut_ptr(&mut self) -> *mut u8 { + self.ptr as *mut u8 + } + + #[inline] + pub fn len(&self) -> usize { + self.len + } +} + +impl Drop for MmapInner { + fn drop(&mut self) { + let alignment = self.ptr as usize % allocation_granularity(); + unsafe { + let ptr = self.ptr.offset(-(alignment as isize)); + assert!( + UnmapViewOfFile(ptr) != 0, + "unable to unmap mmap: {}", + io::Error::last_os_error() + ); + } + } +} + +unsafe impl Sync for MmapInner {} +unsafe impl Send for MmapInner {} + +fn protection_supported(handle: RawHandle, protection: DWORD) -> bool { + unsafe { + let handle = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null()); + if handle == ptr::null_mut() { + return false; + } + CloseHandle(handle); + true + } +} + +fn allocation_granularity() -> usize { + unsafe { + let mut info = mem::zeroed(); + GetSystemInfo(&mut info); + return info.dwAllocationGranularity as usize; + } +} |