]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
1 | #![allow(non_camel_case_types)] |
2 | #![allow(non_snake_case)] | |
3 | ||
4 | use std::fs::File; | |
5 | use std::os::raw::c_void; | |
6 | use std::os::windows::io::{AsRawHandle, RawHandle}; | |
7 | use std::{io, mem, ptr}; | |
8 | ||
9 | type BOOL = i32; | |
10 | type WORD = u16; | |
11 | type DWORD = u32; | |
12 | type WCHAR = u16; | |
13 | type HANDLE = *mut c_void; | |
14 | type LPVOID = *mut c_void; | |
15 | type LPCVOID = *const c_void; | |
16 | type ULONG_PTR = usize; | |
17 | type SIZE_T = ULONG_PTR; | |
18 | type LPCWSTR = *const WCHAR; | |
19 | type PDWORD = *mut DWORD; | |
20 | type DWORD_PTR = ULONG_PTR; | |
21 | type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; | |
22 | type LPSYSTEM_INFO = *mut SYSTEM_INFO; | |
23 | ||
24 | const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE; | |
25 | ||
26 | const STANDARD_RIGHTS_REQUIRED: DWORD = 0x000F0000; | |
27 | ||
28 | const SECTION_QUERY: DWORD = 0x0001; | |
29 | const SECTION_MAP_WRITE: DWORD = 0x0002; | |
30 | const SECTION_MAP_READ: DWORD = 0x0004; | |
31 | const SECTION_MAP_EXECUTE: DWORD = 0x0008; | |
32 | const SECTION_EXTEND_SIZE: DWORD = 0x0010; | |
33 | const SECTION_MAP_EXECUTE_EXPLICIT: DWORD = 0x0020; | |
34 | const SECTION_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | |
35 | | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE; | |
36 | ||
37 | const PAGE_READONLY: DWORD = 0x02; | |
38 | const PAGE_READWRITE: DWORD = 0x04; | |
39 | const PAGE_WRITECOPY: DWORD = 0x08; | |
40 | const PAGE_EXECUTE_READ: DWORD = 0x20; | |
41 | const PAGE_EXECUTE_READWRITE: DWORD = 0x40; | |
42 | const PAGE_EXECUTE_WRITECOPY: DWORD = 0x80; | |
43 | ||
44 | const FILE_MAP_WRITE: DWORD = SECTION_MAP_WRITE; | |
45 | const FILE_MAP_READ: DWORD = SECTION_MAP_READ; | |
46 | const FILE_MAP_ALL_ACCESS: DWORD = SECTION_ALL_ACCESS; | |
47 | const FILE_MAP_EXECUTE: DWORD = SECTION_MAP_EXECUTE_EXPLICIT; | |
48 | const FILE_MAP_COPY: DWORD = 0x00000001; | |
49 | ||
50 | #[repr(C)] | |
51 | #[derive(Clone, Copy)] | |
52 | struct SECURITY_ATTRIBUTES { | |
53 | nLength: DWORD, | |
54 | lpSecurityDescriptor: LPVOID, | |
55 | bInheritHandle: BOOL, | |
56 | } | |
57 | ||
58 | #[repr(C)] | |
59 | #[derive(Clone, Copy)] | |
60 | struct SYSTEM_INFO_u_s { | |
61 | wProcessorArchitecture: WORD, | |
62 | wReserved: WORD, | |
63 | } | |
64 | ||
65 | #[repr(C)] | |
66 | #[derive(Clone, Copy)] | |
67 | struct SYSTEM_INFO_u([u32; 1]); | |
68 | ||
69 | #[repr(C)] | |
70 | #[derive(Clone, Copy)] | |
71 | struct SYSTEM_INFO { | |
72 | u: SYSTEM_INFO_u, | |
73 | dwPageSize: DWORD, | |
74 | lpMinimumApplicationAddress: LPVOID, | |
75 | lpMaximumApplicationAddress: LPVOID, | |
76 | dwActiveProcessorMask: DWORD_PTR, | |
77 | dwNumberOfProcessors: DWORD, | |
78 | dwProcessorType: DWORD, | |
79 | dwAllocationGranularity: DWORD, | |
80 | wProcessorLevel: WORD, | |
81 | wProcessorRevision: WORD, | |
82 | } | |
83 | ||
84 | extern "system" { | |
85 | fn CloseHandle( | |
86 | hObject: HANDLE, | |
87 | ) -> BOOL; | |
88 | ||
89 | fn CreateFileMappingW( | |
90 | hFile: HANDLE, | |
91 | lpFileMappingAttributes: LPSECURITY_ATTRIBUTES, | |
92 | flProtect: DWORD, | |
93 | dwMaximumSizeHigh: DWORD, | |
94 | dwMaximumSizeLow: DWORD, | |
95 | lpName: LPCWSTR, | |
96 | ) -> HANDLE; | |
97 | ||
98 | fn FlushViewOfFile( | |
99 | lpBaseAddress: LPCVOID, | |
100 | dwNumberOfBytesToFlush: SIZE_T, | |
101 | ) -> BOOL; | |
102 | ||
103 | fn UnmapViewOfFile( | |
104 | lpBaseAddress: LPCVOID, | |
105 | ) -> BOOL; | |
106 | ||
107 | fn MapViewOfFile( | |
108 | hFileMappingObject: HANDLE, | |
109 | dwDesiredAccess: DWORD, | |
110 | dwFileOffsetHigh: DWORD, | |
111 | dwFileOffsetLow: DWORD, | |
112 | dwNumberOfBytesToMap: SIZE_T, | |
113 | ) -> LPVOID; | |
114 | ||
115 | fn VirtualProtect( | |
116 | lpAddress: LPVOID, | |
117 | dwSize: SIZE_T, | |
118 | flNewProtect: DWORD, | |
119 | lpflOldProtect: PDWORD, | |
120 | ) -> BOOL; | |
121 | ||
122 | fn GetSystemInfo( | |
123 | lpSystemInfo: LPSYSTEM_INFO, | |
124 | ); | |
125 | } | |
126 | ||
127 | pub struct MmapInner { | |
128 | file: Option<File>, | |
129 | ptr: *mut c_void, | |
130 | len: usize, | |
131 | copy: bool, | |
132 | } | |
133 | ||
134 | impl MmapInner { | |
135 | /// Creates a new `MmapInner`. | |
136 | /// | |
137 | /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls. | |
138 | pub fn new( | |
139 | file: &File, | |
140 | protect: DWORD, | |
141 | access: DWORD, | |
142 | offset: u64, | |
143 | len: usize, | |
144 | copy: bool, | |
145 | ) -> io::Result<MmapInner> { | |
146 | let alignment = offset % allocation_granularity() as u64; | |
147 | let aligned_offset = offset - alignment as u64; | |
148 | let aligned_len = len + alignment as usize; | |
149 | ||
150 | unsafe { | |
151 | let handle = CreateFileMappingW( | |
152 | file.as_raw_handle(), | |
153 | ptr::null_mut(), | |
154 | protect, | |
155 | 0, | |
156 | 0, | |
157 | ptr::null(), | |
158 | ); | |
159 | if handle == ptr::null_mut() { | |
160 | return Err(io::Error::last_os_error()); | |
161 | } | |
162 | ||
163 | let ptr = MapViewOfFile( | |
164 | handle, | |
165 | access, | |
166 | (aligned_offset >> 16 >> 16) as DWORD, | |
167 | (aligned_offset & 0xffffffff) as DWORD, | |
168 | aligned_len as SIZE_T, | |
169 | ); | |
170 | CloseHandle(handle); | |
171 | ||
172 | if ptr == ptr::null_mut() { | |
173 | Err(io::Error::last_os_error()) | |
174 | } else { | |
175 | Ok(MmapInner { | |
176 | file: Some(file.try_clone()?), | |
177 | ptr: ptr.offset(alignment as isize), | |
178 | len: len as usize, | |
179 | copy: copy, | |
180 | }) | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | pub fn map(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { | |
186 | let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); | |
187 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); | |
188 | let mut access = FILE_MAP_READ; | |
189 | let protection = match (write, exec) { | |
190 | (true, true) => { | |
191 | access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE; | |
192 | PAGE_EXECUTE_READWRITE | |
193 | } | |
194 | (true, false) => { | |
195 | access |= FILE_MAP_WRITE; | |
196 | PAGE_READWRITE | |
197 | } | |
198 | (false, true) => { | |
199 | access |= FILE_MAP_EXECUTE; | |
200 | PAGE_EXECUTE_READ | |
201 | } | |
202 | (false, false) => PAGE_READONLY, | |
203 | }; | |
204 | ||
205 | let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; | |
206 | if write || exec { | |
207 | inner.make_read_only()?; | |
208 | } | |
209 | Ok(inner) | |
210 | } | |
211 | ||
212 | pub fn map_exec(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { | |
213 | let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); | |
214 | let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE; | |
215 | let protection = if write { | |
216 | access |= FILE_MAP_WRITE; | |
217 | PAGE_EXECUTE_READWRITE | |
218 | } else { | |
219 | PAGE_EXECUTE_READ | |
220 | }; | |
221 | ||
222 | let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; | |
223 | if write { | |
224 | inner.make_exec()?; | |
225 | } | |
226 | Ok(inner) | |
227 | } | |
228 | ||
229 | pub fn map_mut(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { | |
230 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); | |
231 | let mut access = FILE_MAP_READ | FILE_MAP_WRITE; | |
232 | let protection = if exec { | |
233 | access |= FILE_MAP_EXECUTE; | |
234 | PAGE_EXECUTE_READWRITE | |
235 | } else { | |
236 | PAGE_READWRITE | |
237 | }; | |
238 | ||
239 | let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; | |
240 | if exec { | |
241 | inner.make_mut()?; | |
242 | } | |
243 | Ok(inner) | |
244 | } | |
245 | ||
246 | pub fn map_copy(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { | |
247 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READWRITE); | |
248 | let mut access = FILE_MAP_COPY; | |
249 | let protection = if exec { | |
250 | access |= FILE_MAP_EXECUTE; | |
251 | PAGE_EXECUTE_WRITECOPY | |
252 | } else { | |
253 | PAGE_WRITECOPY | |
254 | }; | |
255 | ||
256 | let mut inner = MmapInner::new(file, protection, access, offset, len, true)?; | |
257 | if exec { | |
258 | inner.make_mut()?; | |
259 | } | |
260 | Ok(inner) | |
261 | } | |
262 | ||
263 | pub fn map_copy_read_only(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { | |
264 | let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); | |
265 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); | |
266 | let mut access = FILE_MAP_COPY; | |
267 | let protection = if exec { | |
268 | access |= FILE_MAP_EXECUTE; | |
269 | PAGE_EXECUTE_WRITECOPY | |
270 | } else { | |
271 | PAGE_WRITECOPY | |
272 | }; | |
273 | ||
274 | let mut inner = MmapInner::new(file, protection, access, offset, len, true)?; | |
275 | if write || exec { | |
276 | inner.make_read_only()?; | |
277 | } | |
278 | Ok(inner) | |
279 | } | |
280 | ||
281 | pub fn map_anon(len: usize, _stack: bool) -> io::Result<MmapInner> { | |
282 | unsafe { | |
283 | // Create a mapping and view with maximum access permissions, then use `VirtualProtect` | |
284 | // to set the actual `Protection`. This way, we can set more permissive protection later | |
285 | // on. | |
286 | // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx | |
287 | ||
288 | let handle = CreateFileMappingW( | |
289 | INVALID_HANDLE_VALUE, | |
290 | ptr::null_mut(), | |
291 | PAGE_EXECUTE_READWRITE, | |
292 | (len >> 16 >> 16) as DWORD, | |
293 | (len & 0xffffffff) as DWORD, | |
294 | ptr::null(), | |
295 | ); | |
296 | if handle == ptr::null_mut() { | |
297 | return Err(io::Error::last_os_error()); | |
298 | } | |
299 | let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE; | |
300 | let ptr = MapViewOfFile(handle, access, 0, 0, len as SIZE_T); | |
301 | CloseHandle(handle); | |
302 | ||
303 | if ptr == ptr::null_mut() { | |
304 | return Err(io::Error::last_os_error()); | |
305 | } | |
306 | ||
307 | let mut old = 0; | |
308 | let result = VirtualProtect(ptr, len as SIZE_T, PAGE_READWRITE, &mut old); | |
309 | if result != 0 { | |
310 | Ok(MmapInner { | |
311 | file: None, | |
312 | ptr: ptr, | |
313 | len: len as usize, | |
314 | copy: false, | |
315 | }) | |
316 | } else { | |
317 | Err(io::Error::last_os_error()) | |
318 | } | |
319 | } | |
320 | } | |
321 | ||
322 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { | |
323 | self.flush_async(offset, len)?; | |
324 | if let Some(ref file) = self.file { | |
325 | file.sync_data()?; | |
326 | } | |
327 | Ok(()) | |
328 | } | |
329 | ||
330 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { | |
331 | let result = unsafe { FlushViewOfFile(self.ptr.offset(offset as isize), len as SIZE_T) }; | |
332 | if result != 0 { | |
333 | Ok(()) | |
334 | } else { | |
335 | Err(io::Error::last_os_error()) | |
336 | } | |
337 | } | |
338 | ||
339 | fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { | |
340 | unsafe { | |
341 | let alignment = self.ptr as usize % allocation_granularity(); | |
342 | let ptr = self.ptr.offset(-(alignment as isize)); | |
343 | let aligned_len = self.len as SIZE_T + alignment as SIZE_T; | |
344 | ||
345 | let mut old = 0; | |
346 | let result = VirtualProtect(ptr, aligned_len, protect, &mut old); | |
347 | ||
348 | if result != 0 { | |
349 | Ok(()) | |
350 | } else { | |
351 | Err(io::Error::last_os_error()) | |
352 | } | |
353 | } | |
354 | } | |
355 | ||
356 | pub fn make_read_only(&mut self) -> io::Result<()> { | |
357 | self.virtual_protect(PAGE_READONLY) | |
358 | } | |
359 | ||
360 | pub fn make_exec(&mut self) -> io::Result<()> { | |
361 | if self.copy { | |
362 | self.virtual_protect(PAGE_EXECUTE_WRITECOPY) | |
363 | } else { | |
364 | self.virtual_protect(PAGE_EXECUTE_READ) | |
365 | } | |
366 | } | |
367 | ||
368 | pub fn make_mut(&mut self) -> io::Result<()> { | |
369 | if self.copy { | |
370 | self.virtual_protect(PAGE_WRITECOPY) | |
371 | } else { | |
372 | self.virtual_protect(PAGE_READWRITE) | |
373 | } | |
374 | } | |
375 | ||
376 | #[inline] | |
377 | pub fn ptr(&self) -> *const u8 { | |
378 | self.ptr as *const u8 | |
379 | } | |
380 | ||
381 | #[inline] | |
382 | pub fn mut_ptr(&mut self) -> *mut u8 { | |
383 | self.ptr as *mut u8 | |
384 | } | |
385 | ||
386 | #[inline] | |
387 | pub fn len(&self) -> usize { | |
388 | self.len | |
389 | } | |
390 | } | |
391 | ||
392 | impl Drop for MmapInner { | |
393 | fn drop(&mut self) { | |
394 | let alignment = self.ptr as usize % allocation_granularity(); | |
395 | unsafe { | |
396 | let ptr = self.ptr.offset(-(alignment as isize)); | |
397 | assert!( | |
398 | UnmapViewOfFile(ptr) != 0, | |
399 | "unable to unmap mmap: {}", | |
400 | io::Error::last_os_error() | |
401 | ); | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | unsafe impl Sync for MmapInner {} | |
407 | unsafe impl Send for MmapInner {} | |
408 | ||
409 | fn protection_supported(handle: RawHandle, protection: DWORD) -> bool { | |
410 | unsafe { | |
411 | let handle = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null()); | |
412 | if handle == ptr::null_mut() { | |
413 | return false; | |
414 | } | |
415 | CloseHandle(handle); | |
416 | true | |
417 | } | |
418 | } | |
419 | ||
420 | fn allocation_granularity() -> usize { | |
421 | unsafe { | |
422 | let mut info = mem::zeroed(); | |
423 | GetSystemInfo(&mut info); | |
424 | return info.dwAllocationGranularity as usize; | |
425 | } | |
426 | } |