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