1 use std::{io, mem, ptr};
2 use std::fs::File;
3 use std::os::raw::c_void;
4 use std::os::windows::io::{AsRawHandle, RawHandle};
5 
6 use winapi::shared::basetsd::SIZE_T;
7 use winapi::shared::minwindef::DWORD;
8 use winapi::um::memoryapi::{
9     FILE_MAP_ALL_ACCESS,
10     FILE_MAP_COPY,
11     FILE_MAP_EXECUTE,
12     FILE_MAP_READ,
13     FILE_MAP_WRITE,
14     CreateFileMappingW,
15     VirtualProtect,
16     MapViewOfFile,
17     UnmapViewOfFile,
18     FlushViewOfFile,
19 };
20 use winapi::um::handleapi::{
21     CloseHandle,
22     INVALID_HANDLE_VALUE,
23 };
24 use winapi::um::sysinfoapi::GetSystemInfo;
25 use winapi::um::winnt::{
26     PAGE_EXECUTE_READ,
27     PAGE_EXECUTE_READWRITE,
28     PAGE_EXECUTE_WRITECOPY,
29     PAGE_READONLY,
30     PAGE_READWRITE,
31     PAGE_WRITECOPY,
32 };
33 
34 pub struct MmapInner {
35     file: Option<File>,
36     ptr: *mut c_void,
37     len: usize,
38     copy: bool,
39 }
40 
41 impl MmapInner {
42     /// Creates a new `MmapInner`.
43     ///
44     /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls.
new( file: &File, protect: DWORD, access: DWORD, offset: usize, len: usize, copy: bool, ) -> io::Result<MmapInner>45     pub fn new(
46         file: &File,
47         protect: DWORD,
48         access: DWORD,
49         offset: usize,
50         len: usize,
51         copy: bool,
52     ) -> io::Result<MmapInner> {
53         let alignment = offset % allocation_granularity();
54         let aligned_offset = offset - alignment;
55         let aligned_len = len + alignment;
56 
57         unsafe {
58             let handle = CreateFileMappingW(
59                 file.as_raw_handle(),
60                 ptr::null_mut(),
61                 protect,
62                 0,
63                 0,
64                 ptr::null(),
65             );
66             if handle == ptr::null_mut() {
67                 return Err(io::Error::last_os_error());
68             }
69 
70             let ptr = MapViewOfFile(
71                 handle,
72                 access,
73                 (aligned_offset >> 16 >> 16) as DWORD,
74                 (aligned_offset & 0xffffffff) as DWORD,
75                 aligned_len as SIZE_T,
76             );
77             CloseHandle(handle);
78 
79             if ptr == ptr::null_mut() {
80                 Err(io::Error::last_os_error())
81             } else {
82                 Ok(MmapInner {
83                     file: Some(file.try_clone()?),
84                     ptr: ptr.offset(alignment as isize),
85                     len: len as usize,
86                     copy: copy,
87                 })
88             }
89         }
90     }
91 
map(len: usize, file: &File, offset: usize) -> io::Result<MmapInner>92     pub fn map(len: usize, file: &File, offset: usize) -> io::Result<MmapInner> {
93         let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE);
94         let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ);
95         let mut access = FILE_MAP_READ;
96         let protection = match (write, exec) {
97             (true, true) => {
98                 access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE;
99                 PAGE_EXECUTE_READWRITE
100             }
101             (true, false) => {
102                 access |= FILE_MAP_WRITE;
103                 PAGE_READWRITE
104             }
105             (false, true) => {
106                 access |= FILE_MAP_EXECUTE;
107                 PAGE_EXECUTE_READ
108             }
109             (false, false) => PAGE_READONLY,
110         };
111 
112         let mut inner = MmapInner::new(file, protection, access, offset, len, false)?;
113         if write || exec {
114             inner.make_read_only()?;
115         }
116         Ok(inner)
117     }
118 
map_exec(len: usize, file: &File, offset: usize) -> io::Result<MmapInner>119     pub fn map_exec(len: usize, file: &File, offset: usize) -> io::Result<MmapInner> {
120         let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE);
121         let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE;
122         let protection = if write {
123             access |= FILE_MAP_WRITE;
124             PAGE_EXECUTE_READWRITE
125         } else {
126             PAGE_EXECUTE_READ
127         };
128 
129         let mut inner = MmapInner::new(file, protection, access, offset, len, false)?;
130         if write {
131             inner.make_exec()?;
132         }
133         Ok(inner)
134     }
135 
map_mut(len: usize, file: &File, offset: usize) -> io::Result<MmapInner>136     pub fn map_mut(len: usize, file: &File, offset: usize) -> io::Result<MmapInner> {
137         let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ);
138         let mut access = FILE_MAP_READ | FILE_MAP_WRITE;
139         let protection = if exec {
140             access |= FILE_MAP_EXECUTE;
141             PAGE_EXECUTE_READWRITE
142         } else {
143             PAGE_READWRITE
144         };
145 
146         let mut inner = MmapInner::new(file, protection, access, offset, len, false)?;
147         if exec {
148             inner.make_mut()?;
149         }
150         Ok(inner)
151     }
152 
map_copy(len: usize, file: &File, offset: usize) -> io::Result<MmapInner>153     pub fn map_copy(len: usize, file: &File, offset: usize) -> io::Result<MmapInner> {
154         let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READWRITE);
155         let mut access = FILE_MAP_COPY;
156         let protection = if exec {
157             access |= FILE_MAP_EXECUTE;
158             PAGE_EXECUTE_WRITECOPY
159         } else {
160             PAGE_WRITECOPY
161         };
162 
163         let mut inner = MmapInner::new(file, protection, access, offset, len, true)?;
164         if exec {
165             inner.make_mut()?;
166         }
167         Ok(inner)
168     }
169 
map_anon(len: usize, _stack: bool) -> io::Result<MmapInner>170     pub fn map_anon(len: usize, _stack: bool) -> io::Result<MmapInner> {
171         unsafe {
172             // Create a mapping and view with maximum access permissions, then use `VirtualProtect`
173             // to set the actual `Protection`. This way, we can set more permissive protection later
174             // on.
175             // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx
176 
177             let handle = CreateFileMappingW(
178                 INVALID_HANDLE_VALUE,
179                 ptr::null_mut(),
180                 PAGE_EXECUTE_READWRITE,
181                 (len >> 16 >> 16) as DWORD,
182                 (len & 0xffffffff) as DWORD,
183                 ptr::null(),
184             );
185             if handle == ptr::null_mut() {
186                 return Err(io::Error::last_os_error());
187             }
188             let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
189             let ptr = MapViewOfFile(handle, access, 0, 0, len as SIZE_T);
190             CloseHandle(handle);
191 
192             if ptr == ptr::null_mut() {
193                 return Err(io::Error::last_os_error());
194             }
195 
196             let mut old = 0;
197             let result = VirtualProtect(
198                 ptr,
199                 len as SIZE_T,
200                 PAGE_READWRITE,
201                 &mut old,
202             );
203             if result != 0 {
204                 Ok(MmapInner {
205                     file: None,
206                     ptr: ptr,
207                     len: len as usize,
208                     copy: false,
209                 })
210             } else {
211                 Err(io::Error::last_os_error())
212             }
213         }
214     }
215 
flush(&self, offset: usize, len: usize) -> io::Result<()>216     pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
217         self.flush_async(offset, len)?;
218         if let Some(ref file) = self.file {
219             file.sync_data()?;
220         }
221         Ok(())
222     }
223 
flush_async(&self, offset: usize, len: usize) -> io::Result<()>224     pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
225         let result = unsafe { FlushViewOfFile(self.ptr.offset(offset as isize), len as SIZE_T) };
226         if result != 0 {
227             Ok(())
228         } else {
229             Err(io::Error::last_os_error())
230         }
231     }
232 
virtual_protect(&mut self, protect: DWORD) -> io::Result<()>233     fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> {
234         unsafe {
235             let alignment = self.ptr as usize % allocation_granularity();
236             let ptr = self.ptr.offset(-(alignment as isize));
237             let aligned_len = self.len as SIZE_T + alignment as SIZE_T;
238 
239             let mut old = 0;
240             let result = VirtualProtect(ptr, aligned_len, protect, &mut old);
241 
242             if result != 0 {
243                 Ok(())
244             } else {
245                 Err(io::Error::last_os_error())
246             }
247         }
248     }
249 
make_read_only(&mut self) -> io::Result<()>250     pub fn make_read_only(&mut self) -> io::Result<()> {
251         self.virtual_protect(PAGE_READONLY)
252     }
253 
make_exec(&mut self) -> io::Result<()>254     pub fn make_exec(&mut self) -> io::Result<()> {
255         if self.copy {
256             self.virtual_protect(PAGE_EXECUTE_WRITECOPY)
257         } else {
258             self.virtual_protect(PAGE_EXECUTE_READ)
259         }
260     }
261 
make_mut(&mut self) -> io::Result<()>262     pub fn make_mut(&mut self) -> io::Result<()> {
263         if self.copy {
264             self.virtual_protect(PAGE_WRITECOPY)
265         } else {
266             self.virtual_protect(PAGE_READWRITE)
267         }
268     }
269 
270     #[inline]
ptr(&self) -> *const u8271     pub fn ptr(&self) -> *const u8 {
272         self.ptr as *const u8
273     }
274 
275     #[inline]
mut_ptr(&mut self) -> *mut u8276     pub fn mut_ptr(&mut self) -> *mut u8 {
277         self.ptr as *mut u8
278     }
279 
280     #[inline]
len(&self) -> usize281     pub fn len(&self) -> usize {
282         self.len
283     }
284 }
285 
286 impl Drop for MmapInner {
drop(&mut self)287     fn drop(&mut self) {
288         let alignment = self.ptr as usize % allocation_granularity();
289         unsafe {
290             let ptr = self.ptr.offset(-(alignment as isize));
291             assert!(UnmapViewOfFile(ptr) != 0,
292                     "unable to unmap mmap: {}",
293                     io::Error::last_os_error());
294         }
295     }
296 }
297 
298 unsafe impl Sync for MmapInner {}
299 unsafe impl Send for MmapInner {}
300 
protection_supported(handle: RawHandle, protection: DWORD) -> bool301 fn protection_supported(handle: RawHandle, protection: DWORD) -> bool {
302     unsafe {
303         let handle = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null());
304         if handle == ptr::null_mut() {
305             return false;
306         }
307         CloseHandle(handle);
308         true
309     }
310 }
311 
allocation_granularity() -> usize312 fn allocation_granularity() -> usize {
313     unsafe {
314         let mut info = mem::zeroed();
315         GetSystemInfo(&mut info);
316         return info.dwAllocationGranularity as usize;
317     }
318 }
319