1use std::fs::File;
2use std::mem::ManuallyDrop;
3use std::os::unix::io::{FromRawFd, RawFd};
4use std::sync::atomic::{AtomicUsize, Ordering};
5use std::{io, ptr};
6
7#[cfg(any(
8 all(target_os = "linux", not(target_arch = "mips")),
9 target_os = "freebsd",
10 target_os = "android"
11))]
12const MAP_STACK: libc::c_int = libc::MAP_STACK;
13
14#[cfg(not(any(
15 all(target_os = "linux", not(target_arch = "mips")),
16 target_os = "freebsd",
17 target_os = "android"
18)))]
19const MAP_STACK: libc::c_int = 0;
20
21#[cfg(any(target_os = "linux", target_os = "android"))]
22const MAP_POPULATE: libc::c_int = libc::MAP_POPULATE;
23
24#[cfg(not(any(target_os = "linux", target_os = "android")))]
25const MAP_POPULATE: libc::c_int = 0;
26
27#[cfg(any(target_os = "linux", target_os = "android"))]
28const MAP_HUGETLB: libc::c_int = libc::MAP_HUGETLB;
29
30#[cfg(target_os = "linux")]
31const MAP_HUGE_MASK: libc::c_int = libc::MAP_HUGE_MASK;
32
33#[cfg(any(target_os = "linux", target_os = "android"))]
34const MAP_HUGE_SHIFT: libc::c_int = libc::MAP_HUGE_SHIFT;
35
36#[cfg(not(any(target_os = "linux", target_os = "android")))]
37const MAP_HUGETLB: libc::c_int = 0;
38
39#[cfg(not(target_os = "linux"))]
40const MAP_HUGE_MASK: libc::c_int = 0;
41
42#[cfg(not(any(target_os = "linux", target_os = "android")))]
43const MAP_HUGE_SHIFT: libc::c_int = 0;
44
45#[cfg(any(
46 target_os = "linux",
47 target_os = "android",
48 target_os = "macos",
49 target_os = "netbsd",
50 target_os = "solaris",
51 target_os = "illumos",
52))]
53const MAP_NORESERVE: libc::c_int = libc::MAP_NORESERVE;
54
55#[cfg(not(any(
56 target_os = "linux",
57 target_os = "android",
58 target_os = "macos",
59 target_os = "netbsd",
60 target_os = "solaris",
61 target_os = "illumos",
62)))]
63const MAP_NORESERVE: libc::c_int = 0;
64
65#[cfg(any(
66 target_os = "android",
67 all(target_os = "linux", not(target_env = "musl"))
68))]
69use libc::{mmap64 as mmap, off64_t as off_t};
70
71#[cfg(not(any(
72 target_os = "android",
73 all(target_os = "linux", not(target_env = "musl"))
74)))]
75use libc::{mmap, off_t};
76
77pub struct MmapInner {
78 ptr: *mut libc::c_void,
79 len: usize,
80}
81
82impl MmapInner {
83 fn new(
87 len: usize,
88 prot: libc::c_int,
89 flags: libc::c_int,
90 file: RawFd,
91 offset: u64,
92 ) -> io::Result<MmapInner> {
93 let alignment = offset % page_size() as u64;
94 let aligned_offset = offset - alignment;
95
96 let (map_len, map_offset) = Self::adjust_mmap_params(len, alignment as usize)?;
97
98 unsafe {
99 let ptr = mmap(
100 ptr::null_mut(),
101 map_len as libc::size_t,
102 prot,
103 flags,
104 file,
105 aligned_offset as off_t,
106 );
107
108 if ptr == libc::MAP_FAILED {
109 Err(io::Error::last_os_error())
110 } else {
111 Ok(Self::from_raw_parts(ptr, len, map_offset))
112 }
113 }
114 }
115
116 fn adjust_mmap_params(len: usize, alignment: usize) -> io::Result<(usize, usize)> {
117 if std::mem::size_of::<usize>() < 8 && len > isize::MAX as usize {
127 return Err(io::Error::new(
128 io::ErrorKind::InvalidData,
129 "memory map length overflows isize",
130 ));
131 }
132
133 let map_len = len + alignment;
134 let map_offset = alignment;
135
136 let map_len = map_len.max(1);
143
144 Ok((map_len, map_offset))
168 }
169
170 fn as_mmap_params(&self) -> (*mut libc::c_void, usize, usize) {
175 let offset = self.ptr as usize % page_size();
176 let len = self.len + offset;
177
178 if len == 0 {
212 (self.ptr, 1, 0)
213 } else {
214 (unsafe { self.ptr.offset(-(offset as isize)) }, len, offset)
215 }
216 }
217
218 unsafe fn from_raw_parts(ptr: *mut libc::c_void, len: usize, offset: usize) -> Self {
228 debug_assert_eq!(ptr as usize % page_size(), 0, "ptr not page-aligned");
229 debug_assert!(offset < page_size(), "offset larger than page size");
230
231 Self {
232 ptr: ptr.add(offset),
233 len,
234 }
235 }
236
237 pub fn map(
238 len: usize,
239 file: RawFd,
240 offset: u64,
241 populate: bool,
242 no_reserve: bool,
243 ) -> io::Result<MmapInner> {
244 let populate = if populate { MAP_POPULATE } else { 0 };
245 let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
246 MmapInner::new(
247 len,
248 libc::PROT_READ,
249 libc::MAP_SHARED | populate | no_reserve,
250 file,
251 offset,
252 )
253 }
254
255 pub fn map_exec(
256 len: usize,
257 file: RawFd,
258 offset: u64,
259 populate: bool,
260 no_reserve: bool,
261 ) -> io::Result<MmapInner> {
262 let populate = if populate { MAP_POPULATE } else { 0 };
263 let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
264 MmapInner::new(
265 len,
266 libc::PROT_READ | libc::PROT_EXEC,
267 libc::MAP_SHARED | populate | no_reserve,
268 file,
269 offset,
270 )
271 }
272
273 pub fn map_mut(
274 len: usize,
275 file: RawFd,
276 offset: u64,
277 populate: bool,
278 no_reserve: bool,
279 ) -> io::Result<MmapInner> {
280 let populate = if populate { MAP_POPULATE } else { 0 };
281 let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
282 MmapInner::new(
283 len,
284 libc::PROT_READ | libc::PROT_WRITE,
285 libc::MAP_SHARED | populate | no_reserve,
286 file,
287 offset,
288 )
289 }
290
291 pub fn map_copy(
292 len: usize,
293 file: RawFd,
294 offset: u64,
295 populate: bool,
296 no_reserve: bool,
297 ) -> io::Result<MmapInner> {
298 let populate = if populate { MAP_POPULATE } else { 0 };
299 let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
300 MmapInner::new(
301 len,
302 libc::PROT_READ | libc::PROT_WRITE,
303 libc::MAP_PRIVATE | populate | no_reserve,
304 file,
305 offset,
306 )
307 }
308
309 pub fn map_copy_read_only(
310 len: usize,
311 file: RawFd,
312 offset: u64,
313 populate: bool,
314 no_reserve: bool,
315 ) -> io::Result<MmapInner> {
316 let populate = if populate { MAP_POPULATE } else { 0 };
317 let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
318 MmapInner::new(
319 len,
320 libc::PROT_READ,
321 libc::MAP_PRIVATE | populate | no_reserve,
322 file,
323 offset,
324 )
325 }
326
327 pub fn map_anon(
329 len: usize,
330 stack: bool,
331 populate: bool,
332 huge: Option<u8>,
333 no_reserve: bool,
334 ) -> io::Result<MmapInner> {
335 let stack = if stack { MAP_STACK } else { 0 };
336 let populate = if populate { MAP_POPULATE } else { 0 };
337 let hugetlb = if huge.is_some() { MAP_HUGETLB } else { 0 };
338 let hugetlb_size = huge.map_or(0, |mask| {
339 (u64::from(mask) & (MAP_HUGE_MASK as u64)) << MAP_HUGE_SHIFT
340 }) as i32;
341 let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
342 MmapInner::new(
343 len,
344 libc::PROT_READ | libc::PROT_WRITE,
345 libc::MAP_PRIVATE
346 | libc::MAP_ANON
347 | stack
348 | populate
349 | hugetlb
350 | hugetlb_size
351 | no_reserve,
352 -1,
353 0,
354 )
355 }
356
357 pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
358 let alignment = (self.ptr as usize + offset) % page_size();
359 let offset = offset as isize - alignment as isize;
360 let len = len + alignment;
361 let result =
362 unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) };
363 if result == 0 {
364 Ok(())
365 } else {
366 Err(io::Error::last_os_error())
367 }
368 }
369
370 pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
371 let alignment = (self.ptr as usize + offset) % page_size();
372 let offset = offset as isize - alignment as isize;
373 let len = len + alignment;
374 let result =
375 unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_ASYNC) };
376 if result == 0 {
377 Ok(())
378 } else {
379 Err(io::Error::last_os_error())
380 }
381 }
382
383 fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> {
384 unsafe {
385 let alignment = self.ptr as usize % page_size();
386 let ptr = self.ptr.offset(-(alignment as isize));
387 let len = self.len + alignment;
388 let len = len.max(1);
389 if libc::mprotect(ptr, len, prot) == 0 {
390 Ok(())
391 } else {
392 Err(io::Error::last_os_error())
393 }
394 }
395 }
396
397 pub fn make_read_only(&mut self) -> io::Result<()> {
398 self.mprotect(libc::PROT_READ)
399 }
400
401 pub fn make_exec(&mut self) -> io::Result<()> {
402 self.mprotect(libc::PROT_READ | libc::PROT_EXEC)
403 }
404
405 pub fn make_mut(&mut self) -> io::Result<()> {
406 self.mprotect(libc::PROT_READ | libc::PROT_WRITE)
407 }
408
409 #[inline]
410 pub fn ptr(&self) -> *const u8 {
411 self.ptr as *const u8
412 }
413
414 #[inline]
415 pub fn mut_ptr(&mut self) -> *mut u8 {
416 self.ptr.cast()
417 }
418
419 #[inline]
420 pub fn len(&self) -> usize {
421 self.len
422 }
423
424 pub fn advise(&self, advice: libc::c_int, offset: usize, len: usize) -> io::Result<()> {
425 let alignment = (self.ptr as usize + offset) % page_size();
426 let offset = offset as isize - alignment as isize;
427 let len = len + alignment;
428 unsafe {
429 if libc::madvise(self.ptr.offset(offset), len, advice) != 0 {
430 Err(io::Error::last_os_error())
431 } else {
432 Ok(())
433 }
434 }
435 }
436
437 #[cfg(target_os = "linux")]
438 pub fn remap(&mut self, new_len: usize, options: crate::RemapOptions) -> io::Result<()> {
439 let (old_ptr, old_len, offset) = self.as_mmap_params();
440 let (map_len, offset) = Self::adjust_mmap_params(new_len, offset)?;
441
442 unsafe {
443 let new_ptr = libc::mremap(old_ptr, old_len, map_len, options.into_flags());
444
445 if new_ptr == libc::MAP_FAILED {
446 Err(io::Error::last_os_error())
447 } else {
448 ptr::write(self, Self::from_raw_parts(new_ptr, new_len, offset));
450 Ok(())
451 }
452 }
453 }
454
455 pub fn lock(&self) -> io::Result<()> {
456 unsafe {
457 if libc::mlock(self.ptr, self.len) != 0 {
458 Err(io::Error::last_os_error())
459 } else {
460 Ok(())
461 }
462 }
463 }
464
465 pub fn unlock(&self) -> io::Result<()> {
466 unsafe {
467 if libc::munlock(self.ptr, self.len) != 0 {
468 Err(io::Error::last_os_error())
469 } else {
470 Ok(())
471 }
472 }
473 }
474}
475
476impl Drop for MmapInner {
477 fn drop(&mut self) {
478 let (ptr, len, _) = self.as_mmap_params();
479
480 unsafe { libc::munmap(ptr, len as libc::size_t) };
484 }
485}
486
487unsafe impl Sync for MmapInner {}
488unsafe impl Send for MmapInner {}
489
490fn page_size() -> usize {
491 static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
492
493 match PAGE_SIZE.load(Ordering::Relaxed) {
494 0 => {
495 let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
496
497 PAGE_SIZE.store(page_size, Ordering::Relaxed);
498
499 page_size
500 }
501 page_size => page_size,
502 }
503}
504
505pub fn file_len(file: RawFd) -> io::Result<u64> {
506 unsafe {
509 let file = ManuallyDrop::new(File::from_raw_fd(file));
510 Ok(file.metadata()?.len())
511 }
512}