diff options
Diffstat (limited to 'third_party/rust/memmap2/src/windows.rs')
-rw-r--r-- | third_party/rust/memmap2/src/windows.rs | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/third_party/rust/memmap2/src/windows.rs b/third_party/rust/memmap2/src/windows.rs new file mode 100644 index 0000000000..8a870955d2 --- /dev/null +++ b/third_party/rust/memmap2/src/windows.rs @@ -0,0 +1,524 @@ +#![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::<u8>` 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<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, + _huge: Option<u8>, + ) -> 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()) + } +} |