#![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 aligned pointer that is valid for `slice::from_raw_parts::` with `len == 0`. /// /// This aligns the pointer to `allocation_granularity()` or 1 if unknown. fn empty_slice_ptr() -> *mut c_void { let align = allocation_granularity().max(1); unsafe { mem::transmute(align) } } pub struct MmapInner { handle: Option, 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 { 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 { 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 { 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 { 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 { 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 { 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 { // 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 { // 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()) } }