| #![allow(non_camel_case_types)] |
| #![allow(non_snake_case)] |
| |
| use std::fs::File; |
| use std::mem::ManuallyDrop; |
| use std::os::raw::c_void; |
| use std::os::windows::io::{FromRawHandle, RawHandle}; |
| use std::{io, mem, ptr}; |
| |
| type BOOL = i32; |
| type WORD = u16; |
| type DWORD = u32; |
| type WCHAR = u16; |
| type HANDLE = *mut c_void; |
| type LPHANDLE = *mut HANDLE; |
| type LPVOID = *mut c_void; |
| type LPCVOID = *const c_void; |
| type ULONG_PTR = usize; |
| type SIZE_T = ULONG_PTR; |
| type LPCWSTR = *const WCHAR; |
| type PDWORD = *mut DWORD; |
| type DWORD_PTR = ULONG_PTR; |
| type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; |
| type LPSYSTEM_INFO = *mut SYSTEM_INFO; |
| |
| const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE; |
| |
| const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002; |
| |
| const STANDARD_RIGHTS_REQUIRED: DWORD = 0x000F0000; |
| |
| const SECTION_QUERY: DWORD = 0x0001; |
| const SECTION_MAP_WRITE: DWORD = 0x0002; |
| const SECTION_MAP_READ: DWORD = 0x0004; |
| const SECTION_MAP_EXECUTE: DWORD = 0x0008; |
| const SECTION_EXTEND_SIZE: DWORD = 0x0010; |
| const SECTION_MAP_EXECUTE_EXPLICIT: DWORD = 0x0020; |
| const SECTION_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED |
| | SECTION_QUERY |
| | SECTION_MAP_WRITE |
| | SECTION_MAP_READ |
| | SECTION_MAP_EXECUTE |
| | SECTION_EXTEND_SIZE; |
| |
| const PAGE_READONLY: DWORD = 0x02; |
| const PAGE_READWRITE: DWORD = 0x04; |
| const PAGE_WRITECOPY: DWORD = 0x08; |
| const PAGE_EXECUTE_READ: DWORD = 0x20; |
| const PAGE_EXECUTE_READWRITE: DWORD = 0x40; |
| const PAGE_EXECUTE_WRITECOPY: DWORD = 0x80; |
| |
| const FILE_MAP_WRITE: DWORD = SECTION_MAP_WRITE; |
| const FILE_MAP_READ: DWORD = SECTION_MAP_READ; |
| const FILE_MAP_ALL_ACCESS: DWORD = SECTION_ALL_ACCESS; |
| const FILE_MAP_EXECUTE: DWORD = SECTION_MAP_EXECUTE_EXPLICIT; |
| const FILE_MAP_COPY: DWORD = 0x00000001; |
| |
| #[repr(C)] |
| struct SECURITY_ATTRIBUTES { |
| nLength: DWORD, |
| lpSecurityDescriptor: LPVOID, |
| bInheritHandle: BOOL, |
| } |
| |
| #[repr(C)] |
| struct SYSTEM_INFO { |
| wProcessorArchitecture: WORD, |
| wReserved: WORD, |
| dwPageSize: DWORD, |
| lpMinimumApplicationAddress: LPVOID, |
| lpMaximumApplicationAddress: LPVOID, |
| dwActiveProcessorMask: DWORD_PTR, |
| dwNumberOfProcessors: DWORD, |
| dwProcessorType: DWORD, |
| dwAllocationGranularity: DWORD, |
| wProcessorLevel: WORD, |
| wProcessorRevision: WORD, |
| } |
| |
| #[repr(C)] |
| #[derive(Copy, Clone)] |
| pub struct FILETIME { |
| pub dwLowDateTime: DWORD, |
| pub dwHighDateTime: DWORD, |
| } |
| |
| extern "system" { |
| fn GetCurrentProcess() -> HANDLE; |
| |
| fn CloseHandle(hObject: HANDLE) -> BOOL; |
| |
| fn DuplicateHandle( |
| hSourceProcessHandle: HANDLE, |
| hSourceHandle: HANDLE, |
| hTargetProcessHandle: HANDLE, |
| lpTargetHandle: LPHANDLE, |
| dwDesiredAccess: DWORD, |
| bInheritHandle: BOOL, |
| dwOptions: DWORD, |
| ) -> BOOL; |
| |
| fn CreateFileMappingW( |
| hFile: HANDLE, |
| lpFileMappingAttributes: LPSECURITY_ATTRIBUTES, |
| flProtect: DWORD, |
| dwMaximumSizeHigh: DWORD, |
| dwMaximumSizeLow: DWORD, |
| lpName: LPCWSTR, |
| ) -> HANDLE; |
| |
| fn FlushFileBuffers(hFile: HANDLE) -> BOOL; |
| |
| fn FlushViewOfFile(lpBaseAddress: LPCVOID, dwNumberOfBytesToFlush: SIZE_T) -> BOOL; |
| |
| fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL; |
| |
| fn MapViewOfFile( |
| hFileMappingObject: HANDLE, |
| dwDesiredAccess: DWORD, |
| dwFileOffsetHigh: DWORD, |
| dwFileOffsetLow: DWORD, |
| dwNumberOfBytesToMap: SIZE_T, |
| ) -> LPVOID; |
| |
| fn VirtualProtect( |
| lpAddress: LPVOID, |
| dwSize: SIZE_T, |
| flNewProtect: DWORD, |
| lpflOldProtect: PDWORD, |
| ) -> BOOL; |
| |
| fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO); |
| } |
| |
| /// Returns a fixed pointer that is valid for `slice::from_raw_parts::<u8>` with `len == 0`. |
| fn empty_slice_ptr() -> *mut c_void { |
| std::ptr::NonNull::<u8>::dangling().cast().as_ptr() |
| } |
| |
| pub struct MmapInner { |
| handle: Option<RawHandle>, |
| 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( |
| handle: RawHandle, |
| 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; |
| if aligned_len == 0 { |
| // `CreateFileMappingW` documents: |
| // |
| // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw |
| // > An attempt to map a file with a length of 0 (zero) fails with an error code |
| // > of ERROR_FILE_INVALID. Applications should test for files with a length of 0 |
| // > (zero) and reject those files. |
| // |
| // For such files, don’t create a mapping at all and use a marker pointer instead. |
| return Ok(MmapInner { |
| handle: None, |
| ptr: empty_slice_ptr(), |
| len: 0, |
| copy, |
| }); |
| } |
| |
| unsafe { |
| let mapping = CreateFileMappingW(handle, ptr::null_mut(), protect, 0, 0, ptr::null()); |
| if mapping.is_null() { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let ptr = MapViewOfFile( |
| mapping, |
| access, |
| (aligned_offset >> 16 >> 16) as DWORD, |
| (aligned_offset & 0xffffffff) as DWORD, |
| aligned_len as SIZE_T, |
| ); |
| CloseHandle(mapping); |
| if ptr.is_null() { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let mut new_handle = 0 as RawHandle; |
| let cur_proc = GetCurrentProcess(); |
| let ok = DuplicateHandle( |
| cur_proc, |
| handle, |
| cur_proc, |
| &mut new_handle, |
| 0, |
| 0, |
| DUPLICATE_SAME_ACCESS, |
| ); |
| if ok == 0 { |
| UnmapViewOfFile(ptr); |
| return Err(io::Error::last_os_error()); |
| } |
| |
| Ok(MmapInner { |
| handle: Some(new_handle), |
| ptr: ptr.offset(alignment as isize), |
| len: len as usize, |
| copy, |
| }) |
| } |
| } |
| |
| pub fn map( |
| len: usize, |
| handle: RawHandle, |
| offset: u64, |
| _populate: bool, |
| ) -> io::Result<MmapInner> { |
| let write = protection_supported(handle, PAGE_READWRITE); |
| let exec = protection_supported(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(handle, protection, access, offset, len, false)?; |
| if write || exec { |
| inner.make_read_only()?; |
| } |
| Ok(inner) |
| } |
| |
| pub fn map_exec( |
| len: usize, |
| handle: RawHandle, |
| offset: u64, |
| _populate: bool, |
| ) -> io::Result<MmapInner> { |
| let write = protection_supported(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(handle, protection, access, offset, len, false)?; |
| if write { |
| inner.make_exec()?; |
| } |
| Ok(inner) |
| } |
| |
| pub fn map_mut( |
| len: usize, |
| handle: RawHandle, |
| offset: u64, |
| _populate: bool, |
| ) -> io::Result<MmapInner> { |
| let exec = protection_supported(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(handle, protection, access, offset, len, false)?; |
| if exec { |
| inner.make_mut()?; |
| } |
| Ok(inner) |
| } |
| |
| pub fn map_copy( |
| len: usize, |
| handle: RawHandle, |
| offset: u64, |
| _populate: bool, |
| ) -> io::Result<MmapInner> { |
| let exec = protection_supported(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(handle, protection, access, offset, len, true)?; |
| if exec { |
| inner.make_mut()?; |
| } |
| Ok(inner) |
| } |
| |
| pub fn map_copy_read_only( |
| len: usize, |
| handle: RawHandle, |
| offset: u64, |
| _populate: bool, |
| ) -> io::Result<MmapInner> { |
| let write = protection_supported(handle, PAGE_READWRITE); |
| let exec = protection_supported(handle, PAGE_EXECUTE_READ); |
| 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(handle, protection, access, offset, len, true)?; |
| if write || exec { |
| inner.make_read_only()?; |
| } |
| Ok(inner) |
| } |
| |
| pub fn map_anon(len: usize, _stack: bool, _populate: bool) -> io::Result<MmapInner> { |
| // Ensure a non-zero length for the underlying mapping |
| let mapped_len = len.max(1); |
| 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 mapping = CreateFileMappingW( |
| INVALID_HANDLE_VALUE, |
| ptr::null_mut(), |
| PAGE_EXECUTE_READWRITE, |
| (mapped_len >> 16 >> 16) as DWORD, |
| (mapped_len & 0xffffffff) as DWORD, |
| ptr::null(), |
| ); |
| if mapping.is_null() { |
| return Err(io::Error::last_os_error()); |
| } |
| let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE; |
| let ptr = MapViewOfFile(mapping, access, 0, 0, mapped_len as SIZE_T); |
| CloseHandle(mapping); |
| |
| if ptr.is_null() { |
| return Err(io::Error::last_os_error()); |
| } |
| |
| let mut old = 0; |
| let result = VirtualProtect(ptr, mapped_len as SIZE_T, PAGE_READWRITE, &mut old); |
| if result != 0 { |
| Ok(MmapInner { |
| handle: None, |
| 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(handle) = self.handle { |
| let ok = unsafe { FlushFileBuffers(handle) }; |
| if ok == 0 { |
| return Err(io::Error::last_os_error()); |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { |
| if self.ptr == empty_slice_ptr() { |
| return Ok(()); |
| } |
| let result = unsafe { FlushViewOfFile(self.ptr.add(offset), len as SIZE_T) }; |
| if result != 0 { |
| Ok(()) |
| } else { |
| Err(io::Error::last_os_error()) |
| } |
| } |
| |
| fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { |
| if self.ptr == empty_slice_ptr() { |
| return Ok(()); |
| } |
| 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) { |
| if self.ptr == empty_slice_ptr() { |
| return; |
| } |
| let alignment = self.ptr as usize % allocation_granularity(); |
| // Any errors during unmapping/closing are ignored as the only way |
| // to report them would be through panicking which is highly discouraged |
| // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97 |
| unsafe { |
| let ptr = self.ptr.offset(-(alignment as isize)); |
| UnmapViewOfFile(ptr); |
| |
| if let Some(handle) = self.handle { |
| CloseHandle(handle); |
| } |
| } |
| } |
| } |
| |
| unsafe impl Sync for MmapInner {} |
| unsafe impl Send for MmapInner {} |
| |
| fn protection_supported(handle: RawHandle, protection: DWORD) -> bool { |
| unsafe { |
| let mapping = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null()); |
| if mapping.is_null() { |
| return false; |
| } |
| CloseHandle(mapping); |
| true |
| } |
| } |
| |
| fn allocation_granularity() -> usize { |
| unsafe { |
| let mut info = mem::zeroed(); |
| GetSystemInfo(&mut info); |
| info.dwAllocationGranularity as usize |
| } |
| } |
| |
| pub fn file_len(handle: RawHandle) -> io::Result<u64> { |
| // SAFETY: We must not close the passed-in fd by dropping the File we create, |
| // we ensure this by immediately wrapping it in a ManuallyDrop. |
| unsafe { |
| let file = ManuallyDrop::new(File::from_raw_handle(handle)); |
| Ok(file.metadata()?.len()) |
| } |
| } |