Skip to main content

memmap2/
unix.rs

1use std::fs::File;
2use std::io;
3use std::mem::ManuallyDrop;
4use std::os::unix::io::{FromRawFd, RawFd};
5use std::sync::atomic::{AtomicUsize, Ordering};
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_vendor = "apple",
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_vendor = "apple",
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(
68        target_os = "linux",
69        not(any(target_env = "musl", target_env = "ohos"))
70    )
71))]
72use libc::{mmap64 as mmap, off64_t as off_t};
73
74#[cfg(not(any(
75    target_os = "android",
76    all(
77        target_os = "linux",
78        not(any(target_env = "musl", target_env = "ohos"))
79    )
80)))]
81use libc::{mmap, off_t};
82
83pub struct MmapInner {
84    ptr: *mut libc::c_void,
85    len: usize,
86}
87
88impl MmapInner {
89    /// Creates a new `MmapInner`.
90    ///
91    /// This is a thin wrapper around the `mmap` system call.
92    fn new(
93        len: usize,
94        prot: libc::c_int,
95        flags: libc::c_int,
96        file: RawFd,
97        offset: u64,
98    ) -> io::Result<MmapInner> {
99        let alignment = offset % page_size() as u64;
100        let aligned_offset = offset - alignment;
101
102        let (map_len, map_offset) = Self::adjust_mmap_params(len, alignment as usize)?;
103
104        // SAFETY: creating a new memory map with a nullptr as address hint is always sound:
105        // it does not modify any existing mapping or memory contents.
106        let ptr = unsafe {
107            mmap(
108                std::ptr::null_mut(),
109                map_len as libc::size_t,
110                prot,
111                flags,
112                file,
113                aligned_offset as off_t,
114            )
115        };
116
117        if ptr == libc::MAP_FAILED {
118            Err(io::Error::last_os_error())
119        } else {
120            // SAFETY: The ptr and len have been checked,
121            // and the offset has been calculated as required.
122            Ok(unsafe { Self::from_raw_parts(ptr, len, map_offset) })
123        }
124    }
125
126    fn adjust_mmap_params(len: usize, alignment: usize) -> io::Result<(usize, usize)> {
127        // Rust's slice cannot be larger than isize::MAX.
128        // See https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html
129        //
130        // This is not a problem on 64-bit targets, but on 32-bit one
131        // having a file or an anonymous mapping larger than 2GB is quite normal
132        // and we have to prevent it.
133        //
134        // The code below is essentially the same as in Rust's std:
135        // https://github.com/rust-lang/rust/blob/db78ab70a88a0a5e89031d7ee4eccec835dcdbde/library/alloc/src/raw_vec.rs#L495
136        if std::mem::size_of::<usize>() < 8 && len > isize::MAX as usize {
137            return Err(io::Error::new(
138                io::ErrorKind::InvalidData,
139                "memory map length overflows isize",
140            ));
141        }
142
143        let map_len = len + alignment;
144        let map_offset = alignment;
145
146        // `libc::mmap` does not support zero-size mappings. POSIX defines:
147        //
148        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html
149        // > If `len` is zero, `mmap()` shall fail and no mapping shall be established.
150        //
151        // So if we would create such a mapping, crate a one-byte mapping instead:
152        let map_len = map_len.max(1);
153
154        // Note that in that case `MmapInner::len` is still set to zero,
155        // and `Mmap` will still dereferences to an empty slice.
156        //
157        // If this mapping is backed by an empty file, we create a mapping larger than the file.
158        // This is unusual but well-defined. On the same man page, POSIX further defines:
159        //
160        // > The `mmap()` function can be used to map a region of memory that is larger
161        // > than the current size of the object.
162        //
163        // (The object here is the file.)
164        //
165        // > Memory access within the mapping but beyond the current end of the underlying
166        // > objects may result in SIGBUS signals being sent to the process. The reason for this
167        // > is that the size of the object can be manipulated by other processes and can change
168        // > at any moment. The implementation should tell the application that a memory reference
169        // > is outside the object where this can be detected; otherwise, written data may be lost
170        // > and read data may not reflect actual data in the object.
171        //
172        // Because `MmapInner::len` is not incremented, this increment of `aligned_len`
173        // will not allow accesses past the end of the file and will not cause SIGBUS.
174        //
175        // (SIGBUS is still possible by mapping a non-empty file and then truncating it
176        // to a shorter size, but that is unrelated to this handling of empty files.)
177        Ok((map_len, map_offset))
178    }
179
180    /// Get the current memory mapping as a `(ptr, map_len, offset)` tuple.
181    ///
182    /// Note that `map_len` is the length of the memory mapping itself and
183    /// _not_ the one that would be passed to `from_raw_parts`.
184    fn as_mmap_params(&self) -> (*mut libc::c_void, usize, usize) {
185        let offset = self.ptr as usize % page_size();
186        let len = self.len + offset;
187
188        // There are two possible memory layouts we could have, depending on
189        // the length and offset passed when constructing this instance:
190        //
191        // 1. The "normal" memory layout looks like this:
192        //
193        //         |<------------------>|<---------------------->|
194        //     mmap ptr    offset      ptr     public slice
195        //
196        //    That is, we have
197        //    - The start of the page-aligned memory mapping returned by mmap,
198        //      followed by,
199        //    - Some number of bytes that are memory mapped but ignored since
200        //      they are before the byte offset requested by the user, followed
201        //      by,
202        //    - The actual memory mapped slice requested by the user.
203        //
204        //    This maps cleanly to a (ptr, len, offset) tuple.
205        //
206        // 2. Then, we have the case where the user requested a zero-length
207        //    memory mapping. mmap(2) does not support zero-length mappings so
208        //    this crate works around that by actually making a mapping of
209        //    length one. This means that we have
210        //    - A length zero slice, followed by,
211        //    - A single memory mapped byte
212        //
213        //    Note that this only happens if the offset within the page is also
214        //    zero. Otherwise, we have a memory map of offset bytes and not a
215        //    zero-length memory map.
216        //
217        //    This doesn't fit cleanly into a (ptr, len, offset) tuple. Instead,
218        //    we fudge it slightly: a zero-length memory map turns into a
219        //    mapping of length one and can't be told apart outside of this
220        //    method without knowing the original length.
221        if len == 0 {
222            (self.ptr, 1, 0)
223        } else {
224            let offset = self.ptr as usize % page_size();
225            // SAFETY: MmapInner guarantees that rounding `self.ptr` down to a page boundary gives the real address of the memory map.
226            // This means that it points into the same allocation as `self.ptr`.
227            let ptr = unsafe { self.ptr.sub(offset) };
228            (ptr, len, offset)
229        }
230    }
231
232    /// Construct this `MmapInner` from its raw components
233    ///
234    /// # Safety
235    ///
236    /// - `ptr` must point to the start of memory mapping that can be freed
237    ///   using `munmap(2)` (i.e. returned by `mmap(2)` or `mremap(2)`)
238    /// - The memory mapping at `ptr` must have a length of `len + offset`.
239    /// - If `len + offset == 0` then the memory mapping must be of length 1.
240    /// - `offset` must be less than the current page size.
241    unsafe fn from_raw_parts(ptr: *mut libc::c_void, len: usize, offset: usize) -> Self {
242        debug_assert_eq!(ptr as usize % page_size(), 0, "ptr not page-aligned");
243        debug_assert!(offset < page_size(), "offset larger than page size");
244
245        Self {
246            ptr: unsafe { ptr.add(offset) },
247            len,
248        }
249    }
250
251    pub fn map(
252        len: usize,
253        file: RawFd,
254        offset: u64,
255        populate: bool,
256        no_reserve: bool,
257        _probe_handle: bool,
258    ) -> io::Result<MmapInner> {
259        let populate = if populate { MAP_POPULATE } else { 0 };
260        let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
261        MmapInner::new(
262            len,
263            libc::PROT_READ,
264            libc::MAP_SHARED | populate | no_reserve,
265            file,
266            offset,
267        )
268    }
269
270    pub fn map_exec(
271        len: usize,
272        file: RawFd,
273        offset: u64,
274        populate: bool,
275        no_reserve: bool,
276        _probe_handle: bool,
277    ) -> io::Result<MmapInner> {
278        let populate = if populate { MAP_POPULATE } else { 0 };
279        let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
280        MmapInner::new(
281            len,
282            libc::PROT_READ | libc::PROT_EXEC,
283            libc::MAP_SHARED | populate | no_reserve,
284            file,
285            offset,
286        )
287    }
288
289    pub fn map_mut(
290        len: usize,
291        file: RawFd,
292        offset: u64,
293        populate: bool,
294        no_reserve: bool,
295        _probe_handle: bool,
296    ) -> io::Result<MmapInner> {
297        let populate = if populate { MAP_POPULATE } else { 0 };
298        let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
299        MmapInner::new(
300            len,
301            libc::PROT_READ | libc::PROT_WRITE,
302            libc::MAP_SHARED | populate | no_reserve,
303            file,
304            offset,
305        )
306    }
307
308    pub fn map_copy(
309        len: usize,
310        file: RawFd,
311        offset: u64,
312        populate: bool,
313        no_reserve: bool,
314        _probe_handle: 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 | libc::PROT_WRITE,
321            libc::MAP_PRIVATE | populate | no_reserve,
322            file,
323            offset,
324        )
325    }
326
327    pub fn map_copy_read_only(
328        len: usize,
329        file: RawFd,
330        offset: u64,
331        populate: bool,
332        no_reserve: bool,
333        _probe_handle: bool,
334    ) -> io::Result<MmapInner> {
335        let populate = if populate { MAP_POPULATE } else { 0 };
336        let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
337        MmapInner::new(
338            len,
339            libc::PROT_READ,
340            libc::MAP_PRIVATE | populate | no_reserve,
341            file,
342            offset,
343        )
344    }
345
346    /// Open an anonymous memory map.
347    pub fn map_anon(
348        len: usize,
349        stack: bool,
350        populate: bool,
351        huge: Option<u8>,
352        no_reserve: bool,
353    ) -> io::Result<MmapInner> {
354        let stack = if stack { MAP_STACK } else { 0 };
355        let populate = if populate { MAP_POPULATE } else { 0 };
356        let hugetlb = if huge.is_some() { MAP_HUGETLB } else { 0 };
357        let hugetlb_size = huge.map_or(0, |mask| {
358            (u64::from(mask) & (MAP_HUGE_MASK as u64)) << MAP_HUGE_SHIFT
359        }) as i32;
360        let no_reserve = if no_reserve { MAP_NORESERVE } else { 0 };
361        MmapInner::new(
362            len,
363            libc::PROT_READ | libc::PROT_WRITE,
364            libc::MAP_PRIVATE
365                | libc::MAP_ANON
366                | stack
367                | populate
368                | hugetlb
369                | hugetlb_size
370                | no_reserve,
371            -1,
372            0,
373        )
374    }
375
376    pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
377        if offset > self.len || len > self.len - offset {
378            return Err(io::ErrorKind::InvalidInput.into());
379        }
380        let alignment = (self.ptr as usize + offset) % page_size();
381        let offset = offset as isize - alignment as isize;
382        let len = len + alignment;
383        let result =
384            // SAFETY: We've checked that offset and len fall within the mapped region.
385            unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) };
386        if result == 0 {
387            Ok(())
388        } else {
389            Err(io::Error::last_os_error())
390        }
391    }
392
393    pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
394        if offset > self.len || len > self.len - offset {
395            return Err(io::ErrorKind::InvalidInput.into());
396        }
397        let alignment = (self.ptr as usize + offset) % page_size();
398        let offset = offset as isize - alignment as isize;
399        let len = len + alignment;
400        let result =
401            // SAFETY: We've checked that offset and len fall within the mapped region.
402            unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_ASYNC) };
403        if result == 0 {
404            Ok(())
405        } else {
406            Err(io::Error::last_os_error())
407        }
408    }
409
410    fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> {
411        let alignment = self.ptr as usize % page_size();
412        // SAFETY: rounding self.ptr down to the previous page boundary gives the pointer of the actual memory map.
413        let ptr = unsafe { self.ptr.sub(alignment) };
414        let len = self.len + alignment;
415        let len = len.max(1);
416
417        // SAFETY: the contract of MmapInner guarantees ptr and len are valid.
418        if unsafe { libc::mprotect(ptr, len, prot) } == 0 {
419            Ok(())
420        } else {
421            Err(io::Error::last_os_error())
422        }
423    }
424
425    pub fn make_read_only(&mut self) -> io::Result<()> {
426        self.mprotect(libc::PROT_READ)
427    }
428
429    pub fn make_exec(&mut self) -> io::Result<()> {
430        self.mprotect(libc::PROT_READ | libc::PROT_EXEC)
431    }
432
433    pub fn make_mut(&mut self) -> io::Result<()> {
434        self.mprotect(libc::PROT_READ | libc::PROT_WRITE)
435    }
436
437    #[inline]
438    pub fn ptr(&self) -> *const u8 {
439        self.ptr as *const u8
440    }
441
442    #[inline]
443    pub fn mut_ptr(&mut self) -> *mut u8 {
444        self.ptr.cast()
445    }
446
447    #[inline]
448    pub fn len(&self) -> usize {
449        self.len
450    }
451
452    /// Perform an `madvise()`.
453    ///
454    /// # Safety
455    ///
456    /// Some `advise` values can be unsound depending on the situation.
457    /// It is up to the caller to only perform sound madvise() calls on the memory range.
458    pub unsafe fn advise(&self, advice: libc::c_int, offset: usize, len: usize) -> io::Result<()> {
459        if offset > self.len || len > self.len {
460            return Err(std::io::ErrorKind::InvalidInput.into());
461        }
462        let alignment = (self.ptr as usize + offset) % page_size();
463        let offset = offset as isize - alignment as isize;
464        let len = len + alignment;
465
466        // SAFETY: We've checked that offset is within the mapped region.
467        let ptr = unsafe { self.ptr.offset(offset) };
468
469        // The AIX signature of 'madvise()' differs from the POSIX
470        // specification, which expects 'void *' as the type of the
471        // 'addr' argument, whereas AIX uses 'caddr_t' (i.e., 'char *').
472        #[cfg(target_os = "aix")]
473        let ptr = self.ptr.offset(offset).cast();
474
475        // SAFETY: ptr and len are valid. The burden of giving a safe `advice` value is on the caller.
476        if unsafe { libc::madvise(ptr, len, advice) } != 0 {
477            Err(io::Error::last_os_error())
478        } else {
479            Ok(())
480        }
481    }
482
483    #[cfg(target_os = "linux")]
484    pub fn remap(&mut self, new_len: usize, options: crate::RemapOptions) -> io::Result<()> {
485        let (old_ptr, old_len, offset) = self.as_mmap_params();
486        let (map_len, offset) = Self::adjust_mmap_params(new_len, offset)?;
487
488        // SAFETY: we hold a mutable reference to self, so we can adjust the location and size of the mapping.
489        let new_ptr = unsafe { libc::mremap(old_ptr, old_len, map_len, options.into_flags()) };
490
491        if new_ptr == libc::MAP_FAILED {
492            Err(io::Error::last_os_error())
493        } else {
494            // SAFETY: The pointer and length passed to `from_raw_parts` have just been obtained from a real map, so they must be valid.
495            let new_map = unsafe { Self::from_raw_parts(new_ptr, new_len, offset) };
496            // We explicitly don't drop self since the pointer within is no longer valid.
497            // Instead, swap the new map into `self` and forget the old one.
498            let old_map = std::mem::replace(self, new_map);
499            std::mem::forget(old_map);
500            Ok(())
501        }
502    }
503
504    pub fn lock(&self) -> io::Result<()> {
505        unsafe {
506            if libc::mlock(self.ptr, self.len) != 0 {
507                Err(io::Error::last_os_error())
508            } else {
509                Ok(())
510            }
511        }
512    }
513
514    pub fn unlock(&self) -> io::Result<()> {
515        unsafe {
516            if libc::munlock(self.ptr, self.len) != 0 {
517                Err(io::Error::last_os_error())
518            } else {
519                Ok(())
520            }
521        }
522    }
523
524    #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "freebsd")))]
525    #[allow(clippy::unnecessary_wraps)]
526    pub fn check_safe_to_map(
527        _fd: RawFd,
528        _offset: u64,
529        _len: usize,
530    ) -> Result<(), crate::MapIfSafeError> {
531        Err(crate::MapIfSafeError::new_not_supported())
532    }
533
534    /// Check if a requisted region of a file is safe to map.
535    #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
536    pub fn check_safe_to_map(
537        fd: RawFd,
538        offset: u64,
539        len: usize,
540    ) -> Result<(), crate::MapIfSafeError> {
541        // First check if the FD itself is safe.
542        Self::check_fd_safe_to_map(fd)?;
543
544        // Now check if the mapping request including offset and length is safe.
545        let file_size = file_len(fd).map_err(|e| {
546            crate::MapIfSafeError::new_not_safe(io::Error::new(
547                io::ErrorKind::InvalidInput,
548                format!("fd is not safe to map: failed to determine file size: {e}"),
549            ))
550        })?;
551
552        let end = offset.checked_add(len as u64).ok_or_else(|| {
553            crate::MapIfSafeError::new_map_failed(io::Error::new(
554                io::ErrorKind::InvalidInput,
555                "mapping region overflows u64",
556            ))
557        })?;
558        if end > file_size {
559            return Err(crate::MapIfSafeError::new_not_safe(io::Error::new(
560                io::ErrorKind::InvalidInput,
561                format!("map request is not safe: offset + length ({end}) exceeds the size of the file ({file_size})"),
562            )));
563        }
564
565        Ok(())
566    }
567
568    /// Check if a file descriptor is safe to map.
569    ///
570    /// This only checks the file, not the whole map request.
571    /// A request to map more than the file contents must still be rejected.
572    #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
573    fn check_fd_safe_to_map(fd: RawFd) -> Result<(), crate::MapIfSafeError> {
574        // Check for properly sealed memfd on Linux, Android and FreeBSD.
575        if let Some(seals) = Self::get_memfd_seals(fd) {
576            return Self::check_memfd_seals(seals);
577        }
578
579        // On FreeBSD we have not more checks.
580        #[cfg(target_os = "freebsd")]
581        return Err(crate::MapIfSafeError::new_not_safe(io::Error::new(
582            io::ErrorKind::InvalidInput,
583            "fd is not safe to map: it is not a memfd",
584        )));
585
586        // Also check for fs-verity on Linux and Android.
587        #[cfg(any(target_os = "linux", target_os = "android"))]
588        if Self::has_fs_verity(fd) {
589            Ok(())
590        } else {
591            Err(crate::MapIfSafeError::new_not_safe(io::Error::new(
592                io::ErrorKind::InvalidInput,
593                "fd is not safe to map: it is not a memfd and it has no fs-verity",
594            )))
595        }
596    }
597
598    /// Get the seals of a memfd.
599    #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
600    fn get_memfd_seals(fd: RawFd) -> Option<libc::c_int> {
601        // SAFETY: F_GET_SEALS has no side effects, and just returns an error on invalid FDs.
602        let seals = unsafe { libc::fcntl(fd, libc::F_GET_SEALS) };
603        if seals >= 0 {
604            Some(seals)
605        } else {
606            None
607        }
608    }
609
610    /// Check if the seals of a memfd to see if the FD is immutable (and safe to map).
611    #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
612    fn check_memfd_seals(seals: libc::c_int) -> Result<(), crate::MapIfSafeError> {
613        let required = libc::F_SEAL_SHRINK | libc::F_SEAL_WRITE;
614        let overlap = seals & required;
615        if overlap == required {
616            Ok(())
617        } else {
618            let error = match overlap {
619                libc::F_SEAL_SHRINK => "fd is not safe to map: missing seals F_SEAL_WRITE",
620                libc::F_SEAL_WRITE => "fd is not safe to map: missing seals F_SEAL_SHRINK",
621                _ => "fd is not safe to map: missing seals F_SEAL_SHRINK and F_SEAL_WRITE",
622            };
623            Err(crate::MapIfSafeError::new_not_safe(io::Error::new(
624                io::ErrorKind::InvalidInput,
625                error,
626            )))
627        }
628    }
629
630    #[cfg(any(target_os = "linux", target_os = "android"))]
631    /// Check if a file has vs-verity enabled.
632    fn has_fs_verity(fd: RawFd) -> bool {
633        #[repr(C)]
634        struct FsVerityDigest {
635            digest_algorithm: u16,
636            digest_size: u16,
637        }
638        const FS_IOC_MEASURE_VERITY: libc::c_ulong = 0xC004_6686;
639
640        let mut digest = std::mem::MaybeUninit::<FsVerityDigest>::zeroed();
641        #[allow(clippy::cast_possible_wrap)]
642        let ret = unsafe { libc::ioctl(fd, FS_IOC_MEASURE_VERITY as _, digest.as_mut_ptr()) };
643        ret == 0 || io::Error::last_os_error().raw_os_error() == Some(libc::EOVERFLOW)
644    }
645}
646
647impl Drop for MmapInner {
648    fn drop(&mut self) {
649        let (ptr, len, _) = self.as_mmap_params();
650
651        // Any errors during unmapping/closing are ignored as the only way
652        // to report them would be through panicking which is highly discouraged
653        // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97
654        unsafe { libc::munmap(ptr, len as libc::size_t) };
655    }
656}
657
658unsafe impl Sync for MmapInner {}
659unsafe impl Send for MmapInner {}
660
661fn page_size() -> usize {
662    static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
663
664    match PAGE_SIZE.load(Ordering::Relaxed) {
665        0 => {
666            let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
667
668            PAGE_SIZE.store(page_size, Ordering::Relaxed);
669
670            page_size
671        }
672        page_size => page_size,
673    }
674}
675
676pub fn file_len(file: RawFd) -> io::Result<u64> {
677    // SAFETY: We must not close the passed-in fd by dropping the File we create,
678    // we ensure this by immediately wrapping it in a ManuallyDrop.
679    unsafe {
680        let file = ManuallyDrop::new(File::from_raw_fd(file));
681        Ok(file.metadata()?.len())
682    }
683}