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 = "android",
47 all(target_os = "linux", not(target_env = "musl"))
48))]
49use libc::{mmap64 as mmap, off64_t as off_t};
50
51#[cfg(not(any(
52 target_os = "android",
53 all(target_os = "linux", not(target_env = "musl"))
54)))]
55use libc::{mmap, off_t};
56
57pub struct MmapInner {
58 ptr: *mut libc::c_void,
59 len: usize,
60}
61
62impl MmapInner {
63 fn new(
67 len: usize,
68 prot: libc::c_int,
69 flags: libc::c_int,
70 file: RawFd,
71 offset: u64,
72 ) -> io::Result<MmapInner> {
73 let alignment = offset % page_size() as u64;
74 let aligned_offset = offset - alignment;
75
76 let (map_len, map_offset) = Self::adjust_mmap_params(len, alignment as usize)?;
77
78 unsafe {
79 let ptr = mmap(
80 ptr::null_mut(),
81 map_len as libc::size_t,
82 prot,
83 flags,
84 file,
85 aligned_offset as off_t,
86 );
87
88 if ptr == libc::MAP_FAILED {
89 Err(io::Error::last_os_error())
90 } else {
91 Ok(Self::from_raw_parts(ptr, len, map_offset))
92 }
93 }
94 }
95
96 fn adjust_mmap_params(len: usize, alignment: usize) -> io::Result<(usize, usize)> {
97 if std::mem::size_of::<usize>() < 8 && len > isize::MAX as usize {
107 return Err(io::Error::new(
108 io::ErrorKind::InvalidData,
109 "memory map length overflows isize",
110 ));
111 }
112
113 let map_len = len + alignment;
114 let map_offset = alignment;
115
116 let map_len = map_len.max(1);
123
124 Ok((map_len, map_offset))
148 }
149
150 fn as_mmap_params(&self) -> (*mut libc::c_void, usize, usize) {
155 let offset = self.ptr as usize % page_size();
156 let len = self.len + offset;
157
158 if len == 0 {
192 (self.ptr, 1, 0)
193 } else {
194 (unsafe { self.ptr.offset(-(offset as isize)) }, len, offset)
195 }
196 }
197
198 unsafe fn from_raw_parts(ptr: *mut libc::c_void, len: usize, offset: usize) -> Self {
208 debug_assert_eq!(ptr as usize % page_size(), 0, "ptr not page-aligned");
209 debug_assert!(offset < page_size(), "offset larger than page size");
210
211 Self {
212 ptr: ptr.add(offset),
213 len,
214 }
215 }
216
217 pub fn map(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
218 let populate = if populate { MAP_POPULATE } else { 0 };
219 MmapInner::new(
220 len,
221 libc::PROT_READ,
222 libc::MAP_SHARED | populate,
223 file,
224 offset,
225 )
226 }
227
228 pub fn map_exec(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
229 let populate = if populate { MAP_POPULATE } else { 0 };
230 MmapInner::new(
231 len,
232 libc::PROT_READ | libc::PROT_EXEC,
233 libc::MAP_SHARED | populate,
234 file,
235 offset,
236 )
237 }
238
239 pub fn map_mut(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
240 let populate = if populate { MAP_POPULATE } else { 0 };
241 MmapInner::new(
242 len,
243 libc::PROT_READ | libc::PROT_WRITE,
244 libc::MAP_SHARED | populate,
245 file,
246 offset,
247 )
248 }
249
250 pub fn map_copy(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
251 let populate = if populate { MAP_POPULATE } else { 0 };
252 MmapInner::new(
253 len,
254 libc::PROT_READ | libc::PROT_WRITE,
255 libc::MAP_PRIVATE | populate,
256 file,
257 offset,
258 )
259 }
260
261 pub fn map_copy_read_only(
262 len: usize,
263 file: RawFd,
264 offset: u64,
265 populate: bool,
266 ) -> io::Result<MmapInner> {
267 let populate = if populate { MAP_POPULATE } else { 0 };
268 MmapInner::new(
269 len,
270 libc::PROT_READ,
271 libc::MAP_PRIVATE | populate,
272 file,
273 offset,
274 )
275 }
276
277 pub fn map_anon(
279 len: usize,
280 stack: bool,
281 populate: bool,
282 huge: Option<u8>,
283 ) -> io::Result<MmapInner> {
284 let stack = if stack { MAP_STACK } else { 0 };
285 let populate = if populate { MAP_POPULATE } else { 0 };
286 let hugetlb = if huge.is_some() { MAP_HUGETLB } else { 0 };
287 let hugetlb_size = huge.map_or(0, |mask| {
288 (u64::from(mask) & (MAP_HUGE_MASK as u64)) << MAP_HUGE_SHIFT
289 }) as i32;
290 MmapInner::new(
291 len,
292 libc::PROT_READ | libc::PROT_WRITE,
293 libc::MAP_PRIVATE | libc::MAP_ANON | stack | populate | hugetlb | hugetlb_size,
294 -1,
295 0,
296 )
297 }
298
299 pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
300 let alignment = (self.ptr as usize + offset) % page_size();
301 let offset = offset as isize - alignment as isize;
302 let len = len + alignment;
303 let result =
304 unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) };
305 if result == 0 {
306 Ok(())
307 } else {
308 Err(io::Error::last_os_error())
309 }
310 }
311
312 pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
313 let alignment = (self.ptr as usize + offset) % page_size();
314 let offset = offset as isize - alignment as isize;
315 let len = len + alignment;
316 let result =
317 unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_ASYNC) };
318 if result == 0 {
319 Ok(())
320 } else {
321 Err(io::Error::last_os_error())
322 }
323 }
324
325 fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> {
326 unsafe {
327 let alignment = self.ptr as usize % page_size();
328 let ptr = self.ptr.offset(-(alignment as isize));
329 let len = self.len + alignment;
330 let len = len.max(1);
331 if libc::mprotect(ptr, len, prot) == 0 {
332 Ok(())
333 } else {
334 Err(io::Error::last_os_error())
335 }
336 }
337 }
338
339 pub fn make_read_only(&mut self) -> io::Result<()> {
340 self.mprotect(libc::PROT_READ)
341 }
342
343 pub fn make_exec(&mut self) -> io::Result<()> {
344 self.mprotect(libc::PROT_READ | libc::PROT_EXEC)
345 }
346
347 pub fn make_mut(&mut self) -> io::Result<()> {
348 self.mprotect(libc::PROT_READ | libc::PROT_WRITE)
349 }
350
351 #[inline]
352 pub fn ptr(&self) -> *const u8 {
353 self.ptr as *const u8
354 }
355
356 #[inline]
357 pub fn mut_ptr(&mut self) -> *mut u8 {
358 self.ptr.cast()
359 }
360
361 #[inline]
362 pub fn len(&self) -> usize {
363 self.len
364 }
365
366 pub fn advise(&self, advice: libc::c_int, offset: usize, len: usize) -> io::Result<()> {
367 let alignment = (self.ptr as usize + offset) % page_size();
368 let offset = offset as isize - alignment as isize;
369 let len = len + alignment;
370 unsafe {
371 if libc::madvise(self.ptr.offset(offset), len, advice) != 0 {
372 Err(io::Error::last_os_error())
373 } else {
374 Ok(())
375 }
376 }
377 }
378
379 #[cfg(target_os = "linux")]
380 pub fn remap(&mut self, new_len: usize, options: crate::RemapOptions) -> io::Result<()> {
381 let (old_ptr, old_len, offset) = self.as_mmap_params();
382 let (map_len, offset) = Self::adjust_mmap_params(new_len, offset)?;
383
384 unsafe {
385 let new_ptr = libc::mremap(old_ptr, old_len, map_len, options.into_flags());
386
387 if new_ptr == libc::MAP_FAILED {
388 Err(io::Error::last_os_error())
389 } else {
390 ptr::write(self, Self::from_raw_parts(new_ptr, new_len, offset));
392 Ok(())
393 }
394 }
395 }
396
397 pub fn lock(&self) -> io::Result<()> {
398 unsafe {
399 if libc::mlock(self.ptr, self.len) != 0 {
400 Err(io::Error::last_os_error())
401 } else {
402 Ok(())
403 }
404 }
405 }
406
407 pub fn unlock(&self) -> io::Result<()> {
408 unsafe {
409 if libc::munlock(self.ptr, self.len) != 0 {
410 Err(io::Error::last_os_error())
411 } else {
412 Ok(())
413 }
414 }
415 }
416}
417
418impl Drop for MmapInner {
419 fn drop(&mut self) {
420 let (ptr, len, _) = self.as_mmap_params();
421
422 unsafe { libc::munmap(ptr, len as libc::size_t) };
426 }
427}
428
429unsafe impl Sync for MmapInner {}
430unsafe impl Send for MmapInner {}
431
432fn page_size() -> usize {
433 static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
434
435 match PAGE_SIZE.load(Ordering::Relaxed) {
436 0 => {
437 let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
438
439 PAGE_SIZE.store(page_size, Ordering::Relaxed);
440
441 page_size
442 }
443 page_size => page_size,
444 }
445}
446
447pub fn file_len(file: RawFd) -> io::Result<u64> {
448 unsafe {
451 let file = ManuallyDrop::new(File::from_raw_fd(file));
452 Ok(file.metadata()?.len())
453 }
454}