1#![allow(
2 clippy::not_unsafe_ptr_arg_deref,
3 reason = "These helpers are thin wrappers around raw Windows mapping APIs."
4)]
5
6use std::io;
7
8#[cfg(unix)]
9pub use libc::{
10 MADV_DONTNEED, MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, MADV_WILLNEED, PROT_EXEC, PROT_READ,
11 PROT_WRITE,
12};
13
14#[cfg(unix)]
15bitflags::bitflags! {
16 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
17 pub struct MapFlags: libc::c_int {
18 const MAP_SHARED = libc::MAP_SHARED;
19 const MAP_PRIVATE = libc::MAP_PRIVATE;
20 const MAP_ANON = libc::MAP_ANON;
21 const MAP_ANONYMOUS = libc::MAP_ANONYMOUS;
22
23 #[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))]
24 const MAP_DENYWRITE = libc::MAP_DENYWRITE;
25 #[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))]
26 const MAP_EXECUTABLE = libc::MAP_EXECUTABLE;
27 #[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))]
28 const MAP_POPULATE = libc::MAP_POPULATE;
29
30 #[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "netbsd"))]
31 const MAP_STACK = libc::MAP_STACK;
32
33 #[cfg(target_vendor = "apple")]
34 const MAP_NORESERVE = libc::MAP_NORESERVE;
35 #[cfg(target_vendor = "apple")]
36 const MAP_NOEXTEND = libc::MAP_NOEXTEND;
37 #[cfg(target_vendor = "apple")]
38 const MAP_HASSEMAPHORE = libc::MAP_HASSEMAPHORE;
39 #[cfg(target_vendor = "apple")]
40 const MAP_NOCACHE = libc::MAP_NOCACHE;
41 #[cfg(target_vendor = "apple")]
42 const MAP_JIT = libc::MAP_JIT;
43 #[cfg(target_vendor = "apple")]
45 const MAP_RESILIENT_CODESIGN = 0x2000;
46 #[cfg(target_vendor = "apple")]
47 const MAP_RESILIENT_MEDIA = 0x4000;
48 #[cfg(target_vendor = "apple")]
49 const MAP_32BIT = 0x8000;
50 #[cfg(target_vendor = "apple")]
51 const MAP_TRANSLATED_ALLOW_EXECUTE = 0x20000;
52 #[cfg(target_vendor = "apple")]
53 const MAP_UNIX03 = 0x40000;
54 #[cfg(target_vendor = "apple")]
55 const MAP_TPRO = 0x80000;
56 }
57}
58
59#[cfg(unix)]
60macro_rules! map_flag_consts {
61 ($($name:ident),+ $(,)?) => {
62 $(pub const $name: libc::c_int = MapFlags::$name.bits();)+
63 };
64}
65
66#[cfg(unix)]
67map_flag_consts! {
68 MAP_SHARED, MAP_PRIVATE, MAP_ANON, MAP_ANONYMOUS,
69}
70
71#[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))]
72map_flag_consts! {
73 MAP_DENYWRITE, MAP_EXECUTABLE, MAP_POPULATE,
74}
75
76#[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "netbsd"))]
77map_flag_consts! {
78 MAP_STACK,
79}
80
81#[cfg(target_vendor = "apple")]
82map_flag_consts! {
83 MAP_HASSEMAPHORE, MAP_JIT, MAP_NOCACHE, MAP_NOEXTEND, MAP_NORESERVE,
84 MAP_RESILIENT_CODESIGN, MAP_RESILIENT_MEDIA, MAP_32BIT,
85 MAP_TRANSLATED_ALLOW_EXECUTE, MAP_UNIX03, MAP_TPRO,
86}
87
88#[cfg(target_os = "macos")]
89pub use libc::{MADV_FREE_REUSABLE, MADV_FREE_REUSE};
90
91#[cfg(any(
92 target_os = "android",
93 target_os = "dragonfly",
94 target_os = "fuchsia",
95 target_os = "freebsd",
96 target_os = "linux",
97 target_os = "netbsd",
98 target_os = "openbsd",
99 target_vendor = "apple"
100))]
101pub use libc::MADV_FREE;
102
103#[cfg(target_os = "linux")]
104pub use libc::{
105 MADV_DODUMP, MADV_DOFORK, MADV_DONTDUMP, MADV_DONTFORK, MADV_HUGEPAGE, MADV_HWPOISON,
106 MADV_MERGEABLE, MADV_NOHUGEPAGE, MADV_REMOVE, MADV_UNMERGEABLE,
107};
108
109#[cfg(any(
110 target_os = "android",
111 all(
112 target_os = "linux",
113 any(
114 target_arch = "aarch64",
115 target_arch = "arm",
116 target_arch = "powerpc",
117 target_arch = "powerpc64",
118 target_arch = "s390x",
119 target_arch = "x86",
120 target_arch = "x86_64",
121 target_arch = "sparc64"
122 )
123 )
124))]
125pub use libc::MADV_SOFT_OFFLINE;
126
127#[cfg(target_os = "freebsd")]
128pub use libc::{MADV_AUTOSYNC, MADV_CORE, MADV_NOCORE, MADV_NOSYNC, MADV_PROTECT};
129
130pub use libc::EOVERFLOW;
131
132#[cfg(windows)]
133use crate::windows::{CheckWin32Bool, HandleToOwned};
134#[cfg(unix)]
135use crate::{crt_fd, fileutils, posix};
136use memmap2::{Mmap, MmapMut, MmapOptions};
137#[cfg(windows)]
138use std::os::windows::io::{AsRawHandle, IntoRawHandle};
139#[cfg(windows)]
140use windows_sys::Win32::{
141 Foundation::{
142 CloseHandle, DUPLICATE_SAME_ACCESS, DuplicateHandle, GetLastError, HANDLE,
143 INVALID_HANDLE_VALUE,
144 },
145 Storage::FileSystem::{FILE_BEGIN, GetFileSize, SetEndOfFile, SetFilePointerEx},
146 System::{
147 Memory::{
148 CreateFileMappingW, FILE_MAP_COPY, FILE_MAP_READ, FILE_MAP_WRITE, FlushViewOfFile,
149 MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile, PAGE_READONLY, PAGE_READWRITE,
150 PAGE_WRITECOPY, UnmapViewOfFile,
151 },
152 Threading::GetCurrentProcess,
153 },
154};
155
156#[cfg(windows)]
157pub type Handle = HANDLE;
158#[cfg(windows)]
159pub const INVALID_HANDLE: Handle = INVALID_HANDLE_VALUE;
160
161#[derive(Clone, Copy, Debug, Eq, PartialEq)]
162pub enum AccessMode {
163 Default = 0,
164 Read = 1,
165 Write = 2,
166 Copy = 3,
167}
168
169#[cfg(windows)]
170#[derive(Debug)]
171pub struct NamedMmap {
172 map_handle: Handle,
173 view_ptr: *mut u8,
174 len: usize,
175}
176
177#[derive(Debug)]
178pub enum MappedFile {
179 Read(Mmap),
180 Write(MmapMut),
181}
182
183impl MappedFile {
184 pub fn as_slice(&self) -> &[u8] {
185 match self {
186 Self::Read(mmap) => &mmap[..],
187 Self::Write(mmap) => &mmap[..],
188 }
189 }
190
191 pub fn as_mut_slice(&mut self) -> &mut [u8] {
192 match self {
193 Self::Read(_) => panic!("mmap can't modify a readonly memory map."),
194 Self::Write(mmap) => &mut mmap[..],
195 }
196 }
197
198 pub fn as_ptr(&self) -> *const u8 {
199 match self {
200 Self::Read(mmap) => mmap.as_ptr(),
201 Self::Write(mmap) => mmap.as_ptr(),
202 }
203 }
204
205 pub fn flush_range(&self, offset: usize, size: usize) -> io::Result<()> {
206 match self {
207 Self::Read(_) => Ok(()),
208 Self::Write(mmap) => mmap.flush_range(offset, size),
209 }
210 }
211
212 #[cfg(all(unix, not(target_os = "redox")))]
213 pub fn madvise_range(&self, start: usize, length: usize, advice: i32) -> io::Result<()> {
214 let ptr = unsafe { self.as_ptr().add(start) };
215 posix::madvise(ptr as usize, length, advice)
216 }
217}
218
219#[cfg(windows)]
220unsafe impl Send for NamedMmap {}
221#[cfg(windows)]
222unsafe impl Sync for NamedMmap {}
223
224#[cfg(windows)]
225impl NamedMmap {
226 pub fn as_slice(&self) -> &[u8] {
227 unsafe { core::slice::from_raw_parts(self.view_ptr, self.len) }
228 }
229
230 pub fn as_mut_slice(&mut self) -> &mut [u8] {
231 unsafe { core::slice::from_raw_parts_mut(self.view_ptr, self.len) }
232 }
233
234 pub fn ptr_at(&self, offset: usize) -> *const core::ffi::c_void {
235 unsafe { self.view_ptr.add(offset) as *const _ }
236 }
237
238 pub fn flush_range(&self, offset: usize, size: usize) -> io::Result<()> {
239 flush_view(self.ptr_at(offset), size)
240 }
241}
242
243#[cfg(windows)]
244impl Drop for NamedMmap {
245 fn drop(&mut self) {
246 unsafe {
247 if !self.view_ptr.is_null() {
248 UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
249 Value: self.view_ptr as *mut _,
250 });
251 }
252 if !self.map_handle.is_null() {
253 CloseHandle(self.map_handle);
254 }
255 }
256 }
257}
258
259#[cfg(windows)]
260pub fn duplicate_handle(handle: Handle) -> io::Result<Handle> {
261 let mut new_handle: Handle = INVALID_HANDLE;
262 unsafe {
263 DuplicateHandle(
264 GetCurrentProcess(),
265 handle,
266 GetCurrentProcess(),
267 &mut new_handle,
268 0,
269 0,
270 DUPLICATE_SAME_ACCESS,
271 )
272 }
273 .check_win32_bool()?;
274 Ok(new_handle)
275}
276
277#[cfg(windows)]
278pub fn get_file_len(handle: Handle) -> io::Result<i64> {
279 let mut high: u32 = 0;
280 let low = unsafe { GetFileSize(handle, &mut high) };
281 if low == u32::MAX {
282 let err = io::Error::last_os_error();
283 if err.raw_os_error() != Some(0) {
284 return Err(err);
285 }
286 }
287 Ok(((high as i64) << 32) | (low as i64))
288}
289
290#[cfg(unix)]
291pub fn file_len(fd: crt_fd::Borrowed<'_>) -> io::Result<i64> {
292 #[allow(clippy::useless_conversion, reason = "needed for 32-bit platforms")]
293 Ok(fileutils::fstat(fd)?.st_size.into())
294}
295
296#[cfg(unix)]
297pub fn prepare_file_mapping(fd: crt_fd::Borrowed<'_>) {
298 #[cfg(target_os = "macos")]
299 {
300 let _ = posix::full_fsync(fd.into());
301 }
302 #[cfg(not(target_os = "macos"))]
303 {
304 let _ = fd;
305 }
306}
307
308#[cfg(windows)]
309pub fn is_invalid_handle_value(handle: isize) -> bool {
310 handle == INVALID_HANDLE as isize
311}
312
313#[cfg(windows)]
314pub fn extend_file(handle: Handle, size: i64) -> io::Result<()> {
315 unsafe { SetFilePointerEx(handle, size, core::ptr::null_mut(), FILE_BEGIN) }
316 .check_win32_bool()?;
317 unsafe { SetEndOfFile(handle) }.check_win32_bool()
318}
319
320#[cfg(unix)]
321pub fn close_descriptor(fd: i32) {
322 if fd >= 0 {
323 let _ = crt_fd::close(unsafe { crt_fd::Owned::from_raw(fd) });
324 }
325}
326
327#[cfg(windows)]
328pub fn close_handle(handle: Handle) {
329 unsafe { CloseHandle(handle) };
330}
331
332#[cfg(windows)]
333pub fn flush_view(ptr: *const core::ffi::c_void, size: usize) -> io::Result<()> {
334 unsafe { FlushViewOfFile(ptr, size) }.check_win32_bool()
335}
336
337#[cfg(windows)]
338pub fn last_error() -> u32 {
339 unsafe { GetLastError() }
340}
341
342#[cfg(windows)]
343pub fn create_named_mapping(
344 file_handle: Handle,
345 tag: &str,
346 access: AccessMode,
347 offset: i64,
348 map_size: usize,
349) -> io::Result<NamedMmap> {
350 let (fl_protect, desired_access) = match access {
351 AccessMode::Default | AccessMode::Write => (PAGE_READWRITE, FILE_MAP_WRITE),
352 AccessMode::Read => (PAGE_READONLY, FILE_MAP_READ),
353 AccessMode::Copy => (PAGE_WRITECOPY, FILE_MAP_COPY),
354 };
355
356 let total_size = (offset as u64)
357 .checked_add(map_size as u64)
358 .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
359 let size_hi = (total_size >> 32) as u32;
360 let size_lo = total_size as u32;
361 let tag_wide: Vec<u16> = tag.encode_utf16().chain(core::iter::once(0)).collect();
362
363 let map_handle = unsafe {
364 CreateFileMappingW(
365 file_handle,
366 core::ptr::null(),
367 fl_protect,
368 size_hi,
369 size_lo,
370 tag_wide.as_ptr(),
371 )
372 }
373 .into_owned()
374 .ok_or_else(io::Error::last_os_error)?;
375
376 let off_hi = (offset as u64 >> 32) as u32;
377 let off_lo = offset as u32;
378 let view = unsafe {
379 MapViewOfFile(
380 map_handle.as_raw_handle() as Handle,
381 desired_access,
382 off_hi,
383 off_lo,
384 map_size,
385 )
386 };
387 if view.Value.is_null() {
388 return Err(io::Error::last_os_error());
390 }
391
392 Ok(NamedMmap {
393 map_handle: map_handle.into_raw_handle() as Handle,
394 view_ptr: view.Value as *mut u8,
395 len: map_size,
396 })
397}
398
399#[cfg(unix)]
400pub fn map_anon(size: usize) -> io::Result<MappedFile> {
401 let mut mmap_opt = MmapOptions::new();
402 mmap_opt.len(size).map_anon().map(MappedFile::Write)
403}
404
405#[cfg(windows)]
406pub fn map_anon(size: usize) -> io::Result<MappedFile> {
407 let mut mmap_opt = MmapOptions::new();
408 mmap_opt.len(size).map_anon().map(MappedFile::Write)
409}
410
411#[cfg(unix)]
412pub fn map_file(
413 fd: crt_fd::Borrowed<'_>,
414 offset: i64,
415 size: usize,
416 access: AccessMode,
417) -> io::Result<(crt_fd::Owned, MappedFile)> {
418 let new_fd: crt_fd::Owned = posix::dup_noninheritable(fd.into())?.into();
419 let mut mmap_opt = MmapOptions::new();
420 let mmap_opt = mmap_opt.offset(offset as u64).len(size);
421
422 let mapped = match access {
423 AccessMode::Default | AccessMode::Write => {
424 unsafe { mmap_opt.map_mut(&new_fd) }.map(MappedFile::Write)?
425 }
426 AccessMode::Read => unsafe { mmap_opt.map(&new_fd) }.map(MappedFile::Read)?,
427 AccessMode::Copy => unsafe { mmap_opt.map_copy(&new_fd) }.map(MappedFile::Write)?,
428 };
429
430 Ok((new_fd, mapped))
431}
432
433#[cfg(all(unix, not(target_os = "redox")))]
434pub fn validate_advice(advice: i32) -> bool {
435 match advice {
436 libc::MADV_NORMAL
437 | libc::MADV_RANDOM
438 | libc::MADV_SEQUENTIAL
439 | libc::MADV_WILLNEED
440 | libc::MADV_DONTNEED => true,
441 #[cfg(any(
442 target_os = "linux",
443 target_os = "macos",
444 target_os = "ios",
445 target_os = "freebsd"
446 ))]
447 libc::MADV_FREE => true,
448 #[cfg(target_os = "linux")]
449 libc::MADV_DONTFORK
450 | libc::MADV_DOFORK
451 | libc::MADV_MERGEABLE
452 | libc::MADV_UNMERGEABLE
453 | libc::MADV_HUGEPAGE
454 | libc::MADV_NOHUGEPAGE
455 | libc::MADV_REMOVE
456 | libc::MADV_DONTDUMP
457 | libc::MADV_DODUMP
458 | libc::MADV_HWPOISON => true,
459 #[cfg(target_os = "freebsd")]
460 libc::MADV_NOSYNC
461 | libc::MADV_AUTOSYNC
462 | libc::MADV_NOCORE
463 | libc::MADV_CORE
464 | libc::MADV_PROTECT => true,
465 _ => false,
466 }
467}
468
469#[cfg(windows)]
470pub fn map_handle(
471 handle: Handle,
472 offset: i64,
473 size: usize,
474 access: AccessMode,
475) -> io::Result<MappedFile> {
476 use std::{
477 fs::File,
478 os::windows::io::{FromRawHandle, RawHandle},
479 };
480
481 let file = unsafe { File::from_raw_handle(handle as RawHandle) };
482 let mut mmap_opt = MmapOptions::new();
483 let mmap_opt = mmap_opt.offset(offset as u64).len(size);
484
485 let result = match access {
486 AccessMode::Default | AccessMode::Write => {
487 unsafe { mmap_opt.map_mut(&file) }.map(MappedFile::Write)
488 }
489 AccessMode::Read => unsafe { mmap_opt.map(&file) }.map(MappedFile::Read),
490 AccessMode::Copy => unsafe { mmap_opt.map_copy(&file) }.map(MappedFile::Write),
491 };
492
493 core::mem::forget(file);
494 result
495}