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