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