| // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. |
| // |
| // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| // |
| // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE-BSD-3-Clause file. |
| // |
| // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause |
| |
| //! Helper structure for working with mmaped memory regions in Unix. |
| |
| use std::io; |
| use std::os::unix::io::AsRawFd; |
| use std::ptr::null_mut; |
| use std::result; |
| |
| use crate::bitmap::{Bitmap, BS}; |
| use crate::guest_memory::FileOffset; |
| use crate::mmap::{check_file_offset, NewBitmap}; |
| use crate::volatile_memory::{self, VolatileMemory, VolatileSlice}; |
| |
| /// Error conditions that may arise when creating a new `MmapRegion` object. |
| #[derive(Debug, thiserror::Error)] |
| pub enum Error { |
| /// The specified file offset and length cause overflow when added. |
| #[error("The specified file offset and length cause overflow when added")] |
| InvalidOffsetLength, |
| /// The specified pointer to the mapping is not page-aligned. |
| #[error("The specified pointer to the mapping is not page-aligned")] |
| InvalidPointer, |
| /// The forbidden `MAP_FIXED` flag was specified. |
| #[error("The forbidden `MAP_FIXED` flag was specified")] |
| MapFixed, |
| /// Mappings using the same fd overlap in terms of file offset and length. |
| #[error("Mappings using the same fd overlap in terms of file offset and length")] |
| MappingOverlap, |
| /// A mapping with offset + length > EOF was attempted. |
| #[error("The specified file offset and length is greater then file length")] |
| MappingPastEof, |
| /// The `mmap` call returned an error. |
| #[error("{0}")] |
| Mmap(io::Error), |
| /// Seeking the end of the file returned an error. |
| #[error("Error seeking the end of the file: {0}")] |
| SeekEnd(io::Error), |
| /// Seeking the start of the file returned an error. |
| #[error("Error seeking the start of the file: {0}")] |
| SeekStart(io::Error), |
| } |
| |
| pub type Result<T> = result::Result<T, Error>; |
| |
| /// A factory struct to build `MmapRegion` objects. |
| pub struct MmapRegionBuilder<B = ()> { |
| size: usize, |
| prot: i32, |
| flags: i32, |
| file_offset: Option<FileOffset>, |
| raw_ptr: Option<*mut u8>, |
| hugetlbfs: Option<bool>, |
| bitmap: B, |
| } |
| |
| impl<B: Bitmap + Default> MmapRegionBuilder<B> { |
| /// Create a new `MmapRegionBuilder` using the default value for |
| /// the inner `Bitmap` object. |
| pub fn new(size: usize) -> Self { |
| Self::new_with_bitmap(size, B::default()) |
| } |
| } |
| |
| impl<B: Bitmap> MmapRegionBuilder<B> { |
| /// Create a new `MmapRegionBuilder` using the provided `Bitmap` object. |
| /// |
| /// When instantiating the builder for a region that does not require dirty bitmap |
| /// bitmap tracking functionality, we can specify a trivial `Bitmap` implementation |
| /// such as `()`. |
| pub fn new_with_bitmap(size: usize, bitmap: B) -> Self { |
| MmapRegionBuilder { |
| size, |
| prot: 0, |
| flags: libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, |
| file_offset: None, |
| raw_ptr: None, |
| hugetlbfs: None, |
| bitmap, |
| } |
| } |
| |
| /// Create the `MmapRegion` object with the specified mmap memory protection flag `prot`. |
| pub fn with_mmap_prot(mut self, prot: i32) -> Self { |
| self.prot = prot; |
| self |
| } |
| |
| /// Create the `MmapRegion` object with the specified mmap `flags`. |
| pub fn with_mmap_flags(mut self, flags: i32) -> Self { |
| self.flags = flags; |
| self |
| } |
| |
| /// Create the `MmapRegion` object with the specified `file_offset`. |
| pub fn with_file_offset(mut self, file_offset: FileOffset) -> Self { |
| self.file_offset = Some(file_offset); |
| self |
| } |
| |
| /// Create the `MmapRegion` object with the specified `hugetlbfs` flag. |
| pub fn with_hugetlbfs(mut self, hugetlbfs: bool) -> Self { |
| self.hugetlbfs = Some(hugetlbfs); |
| self |
| } |
| |
| /// Create the `MmapRegion` object with pre-mmapped raw pointer. |
| /// |
| /// # Safety |
| /// |
| /// To use this safely, the caller must guarantee that `raw_addr` and `self.size` define a |
| /// region within a valid mapping that is already present in the process. |
| pub unsafe fn with_raw_mmap_pointer(mut self, raw_ptr: *mut u8) -> Self { |
| self.raw_ptr = Some(raw_ptr); |
| self |
| } |
| |
| /// Build the `MmapRegion` object. |
| pub fn build(self) -> Result<MmapRegion<B>> { |
| if self.raw_ptr.is_some() { |
| return self.build_raw(); |
| } |
| |
| // Forbid MAP_FIXED, as it doesn't make sense in this context, and is pretty dangerous |
| // in general. |
| if self.flags & libc::MAP_FIXED != 0 { |
| return Err(Error::MapFixed); |
| } |
| |
| let (fd, offset) = if let Some(ref f_off) = self.file_offset { |
| check_file_offset(f_off, self.size)?; |
| (f_off.file().as_raw_fd(), f_off.start()) |
| } else { |
| (-1, 0) |
| }; |
| |
| #[cfg(not(miri))] |
| // SAFETY: This is safe because we're not allowing MAP_FIXED, and invalid parameters |
| // cannot break Rust safety guarantees (things may change if we're mapping /dev/mem or |
| // some wacky file). |
| let addr = unsafe { |
| libc::mmap( |
| null_mut(), |
| self.size, |
| self.prot, |
| self.flags, |
| fd, |
| offset as libc::off_t, |
| ) |
| }; |
| |
| #[cfg(not(miri))] |
| if addr == libc::MAP_FAILED { |
| return Err(Error::Mmap(io::Error::last_os_error())); |
| } |
| |
| #[cfg(miri)] |
| if self.size == 0 { |
| return Err(Error::Mmap(io::Error::from_raw_os_error(libc::EINVAL))); |
| } |
| |
| // Miri does not support the mmap syscall, so we use rust's allocator for miri tests |
| #[cfg(miri)] |
| let addr = unsafe { |
| std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(self.size, 8).unwrap()) |
| }; |
| |
| Ok(MmapRegion { |
| addr: addr as *mut u8, |
| size: self.size, |
| bitmap: self.bitmap, |
| file_offset: self.file_offset, |
| prot: self.prot, |
| flags: self.flags, |
| owned: true, |
| hugetlbfs: self.hugetlbfs, |
| }) |
| } |
| |
| fn build_raw(self) -> Result<MmapRegion<B>> { |
| // SAFETY: Safe because this call just returns the page size and doesn't have any side |
| // effects. |
| let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; |
| let addr = self.raw_ptr.unwrap(); |
| |
| // Check that the pointer to the mapping is page-aligned. |
| if (addr as usize) & (page_size - 1) != 0 { |
| return Err(Error::InvalidPointer); |
| } |
| |
| Ok(MmapRegion { |
| addr, |
| size: self.size, |
| bitmap: self.bitmap, |
| file_offset: self.file_offset, |
| prot: self.prot, |
| flags: self.flags, |
| owned: false, |
| hugetlbfs: self.hugetlbfs, |
| }) |
| } |
| } |
| |
| /// Helper structure for working with mmaped memory regions in Unix. |
| /// |
| /// The structure is used for accessing the guest's physical memory by mmapping it into |
| /// the current process. |
| /// |
| /// # Limitations |
| /// When running a 64-bit virtual machine on a 32-bit hypervisor, only part of the guest's |
| /// physical memory may be mapped into the current process due to the limited virtual address |
| /// space size of the process. |
| #[derive(Debug)] |
| pub struct MmapRegion<B = ()> { |
| addr: *mut u8, |
| size: usize, |
| bitmap: B, |
| file_offset: Option<FileOffset>, |
| prot: i32, |
| flags: i32, |
| owned: bool, |
| hugetlbfs: Option<bool>, |
| } |
| |
| // SAFETY: Send and Sync aren't automatically inherited for the raw address pointer. |
| // Accessing that pointer is only done through the stateless interface which |
| // allows the object to be shared by multiple threads without a decrease in |
| // safety. |
| unsafe impl<B: Send> Send for MmapRegion<B> {} |
| // SAFETY: See comment above. |
| unsafe impl<B: Sync> Sync for MmapRegion<B> {} |
| |
| impl<B: NewBitmap> MmapRegion<B> { |
| /// Creates a shared anonymous mapping of `size` bytes. |
| /// |
| /// # Arguments |
| /// * `size` - The size of the memory region in bytes. |
| pub fn new(size: usize) -> Result<Self> { |
| MmapRegionBuilder::new_with_bitmap(size, B::with_len(size)) |
| .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE) |
| .with_mmap_flags(libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE) |
| .build() |
| } |
| |
| /// Creates a shared file mapping of `size` bytes. |
| /// |
| /// # Arguments |
| /// * `file_offset` - The mapping will be created at offset `file_offset.start` in the file |
| /// referred to by `file_offset.file`. |
| /// * `size` - The size of the memory region in bytes. |
| pub fn from_file(file_offset: FileOffset, size: usize) -> Result<Self> { |
| MmapRegionBuilder::new_with_bitmap(size, B::with_len(size)) |
| .with_file_offset(file_offset) |
| .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE) |
| .with_mmap_flags(libc::MAP_NORESERVE | libc::MAP_SHARED) |
| .build() |
| } |
| |
| /// Creates a mapping based on the provided arguments. |
| /// |
| /// # Arguments |
| /// * `file_offset` - if provided, the method will create a file mapping at offset |
| /// `file_offset.start` in the file referred to by `file_offset.file`. |
| /// * `size` - The size of the memory region in bytes. |
| /// * `prot` - The desired memory protection of the mapping. |
| /// * `flags` - This argument determines whether updates to the mapping are visible to other |
| /// processes mapping the same region, and whether updates are carried through to |
| /// the underlying file. |
| pub fn build( |
| file_offset: Option<FileOffset>, |
| size: usize, |
| prot: i32, |
| flags: i32, |
| ) -> Result<Self> { |
| let mut builder = MmapRegionBuilder::new_with_bitmap(size, B::with_len(size)) |
| .with_mmap_prot(prot) |
| .with_mmap_flags(flags); |
| if let Some(v) = file_offset { |
| builder = builder.with_file_offset(v); |
| } |
| builder.build() |
| } |
| |
| /// Creates a `MmapRegion` instance for an externally managed mapping. |
| /// |
| /// This method is intended to be used exclusively in situations in which the mapping backing |
| /// the region is provided by an entity outside the control of the caller (e.g. the dynamic |
| /// linker). |
| /// |
| /// # Arguments |
| /// * `addr` - Pointer to the start of the mapping. Must be page-aligned. |
| /// * `size` - The size of the memory region in bytes. |
| /// * `prot` - Must correspond to the memory protection attributes of the existing mapping. |
| /// * `flags` - Must correspond to the flags that were passed to `mmap` for the creation of |
| /// the existing mapping. |
| /// |
| /// # Safety |
| /// |
| /// To use this safely, the caller must guarantee that `addr` and `size` define a region within |
| /// a valid mapping that is already present in the process. |
| pub unsafe fn build_raw(addr: *mut u8, size: usize, prot: i32, flags: i32) -> Result<Self> { |
| MmapRegionBuilder::new_with_bitmap(size, B::with_len(size)) |
| .with_raw_mmap_pointer(addr) |
| .with_mmap_prot(prot) |
| .with_mmap_flags(flags) |
| .build() |
| } |
| } |
| |
| impl<B: Bitmap> MmapRegion<B> { |
| /// Returns a pointer to the beginning of the memory region. Mutable accesses performed |
| /// using the resulting pointer are not automatically accounted for by the dirty bitmap |
| /// tracking functionality. |
| /// |
| /// Should only be used for passing this region to ioctls for setting guest memory. |
| pub fn as_ptr(&self) -> *mut u8 { |
| self.addr |
| } |
| |
| /// Returns the size of this region. |
| pub fn size(&self) -> usize { |
| self.size |
| } |
| |
| /// Returns information regarding the offset into the file backing this region (if any). |
| pub fn file_offset(&self) -> Option<&FileOffset> { |
| self.file_offset.as_ref() |
| } |
| |
| /// Returns the value of the `prot` parameter passed to `mmap` when mapping this region. |
| pub fn prot(&self) -> i32 { |
| self.prot |
| } |
| |
| /// Returns the value of the `flags` parameter passed to `mmap` when mapping this region. |
| pub fn flags(&self) -> i32 { |
| self.flags |
| } |
| |
| /// Returns `true` if the mapping is owned by this `MmapRegion` instance. |
| pub fn owned(&self) -> bool { |
| self.owned |
| } |
| |
| /// Checks whether this region and `other` are backed by overlapping |
| /// [`FileOffset`](struct.FileOffset.html) objects. |
| /// |
| /// This is mostly a sanity check available for convenience, as different file descriptors |
| /// can alias the same file. |
| pub fn fds_overlap<T: Bitmap>(&self, other: &MmapRegion<T>) -> bool { |
| if let Some(f_off1) = self.file_offset() { |
| if let Some(f_off2) = other.file_offset() { |
| if f_off1.file().as_raw_fd() == f_off2.file().as_raw_fd() { |
| let s1 = f_off1.start(); |
| let s2 = f_off2.start(); |
| let l1 = self.len() as u64; |
| let l2 = other.len() as u64; |
| |
| if s1 < s2 { |
| return s1 + l1 > s2; |
| } else { |
| return s2 + l2 > s1; |
| } |
| } |
| } |
| } |
| false |
| } |
| |
| /// Set the hugetlbfs of the region |
| pub fn set_hugetlbfs(&mut self, hugetlbfs: bool) { |
| self.hugetlbfs = Some(hugetlbfs) |
| } |
| |
| /// Returns `true` if the region is hugetlbfs |
| pub fn is_hugetlbfs(&self) -> Option<bool> { |
| self.hugetlbfs |
| } |
| |
| /// Returns a reference to the inner bitmap object. |
| pub fn bitmap(&self) -> &B { |
| &self.bitmap |
| } |
| } |
| |
| impl<B: Bitmap> VolatileMemory for MmapRegion<B> { |
| type B = B; |
| |
| fn len(&self) -> usize { |
| self.size |
| } |
| |
| fn get_slice( |
| &self, |
| offset: usize, |
| count: usize, |
| ) -> volatile_memory::Result<VolatileSlice<BS<B>>> { |
| let _ = self.compute_end_offset(offset, count)?; |
| |
| Ok( |
| // SAFETY: Safe because we checked that offset + count was within our range and we only |
| // ever hand out volatile accessors. |
| unsafe { |
| VolatileSlice::with_bitmap( |
| self.addr.add(offset), |
| count, |
| self.bitmap.slice_at(offset), |
| None, |
| ) |
| }, |
| ) |
| } |
| } |
| |
| impl<B> Drop for MmapRegion<B> { |
| fn drop(&mut self) { |
| if self.owned { |
| // SAFETY: This is safe because we mmap the area at addr ourselves, and nobody |
| // else is holding a reference to it. |
| unsafe { |
| #[cfg(not(miri))] |
| libc::munmap(self.addr as *mut libc::c_void, self.size); |
| |
| #[cfg(miri)] |
| std::alloc::dealloc( |
| self.addr, |
| std::alloc::Layout::from_size_align(self.size, 8).unwrap(), |
| ); |
| } |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| #![allow(clippy::undocumented_unsafe_blocks)] |
| use super::*; |
| |
| use std::io::Write; |
| use std::slice; |
| use std::sync::Arc; |
| use vmm_sys_util::tempfile::TempFile; |
| |
| use crate::bitmap::AtomicBitmap; |
| |
| type MmapRegion = super::MmapRegion<()>; |
| |
| // Adding a helper method to extract the errno within an Error::Mmap(e), or return a |
| // distinctive value when the error is represented by another variant. |
| impl Error { |
| pub fn raw_os_error(&self) -> i32 { |
| match self { |
| Error::Mmap(e) => e.raw_os_error().unwrap(), |
| _ => std::i32::MIN, |
| } |
| } |
| } |
| |
| #[test] |
| fn test_mmap_region_new() { |
| assert!(MmapRegion::new(0).is_err()); |
| |
| let size = 4096; |
| |
| let r = MmapRegion::new(4096).unwrap(); |
| assert_eq!(r.size(), size); |
| assert!(r.file_offset().is_none()); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!( |
| r.flags(), |
| libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE |
| ); |
| } |
| |
| #[test] |
| fn test_mmap_region_set_hugetlbfs() { |
| assert!(MmapRegion::new(0).is_err()); |
| |
| let size = 4096; |
| |
| let r = MmapRegion::new(size).unwrap(); |
| assert_eq!(r.size(), size); |
| assert!(r.file_offset().is_none()); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!( |
| r.flags(), |
| libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE |
| ); |
| assert_eq!(r.is_hugetlbfs(), None); |
| |
| let mut r = MmapRegion::new(size).unwrap(); |
| r.set_hugetlbfs(false); |
| assert_eq!(r.size(), size); |
| assert!(r.file_offset().is_none()); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!( |
| r.flags(), |
| libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE |
| ); |
| assert_eq!(r.is_hugetlbfs(), Some(false)); |
| |
| let mut r = MmapRegion::new(size).unwrap(); |
| r.set_hugetlbfs(true); |
| assert_eq!(r.size(), size); |
| assert!(r.file_offset().is_none()); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!( |
| r.flags(), |
| libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE |
| ); |
| assert_eq!(r.is_hugetlbfs(), Some(true)); |
| } |
| |
| #[test] |
| #[cfg(not(miri))] // Miri cannot mmap files |
| fn test_mmap_region_from_file() { |
| let mut f = TempFile::new().unwrap().into_file(); |
| let offset: usize = 0; |
| let buf1 = [1u8, 2, 3, 4, 5]; |
| |
| f.write_all(buf1.as_ref()).unwrap(); |
| let r = MmapRegion::from_file(FileOffset::new(f, offset as u64), buf1.len()).unwrap(); |
| |
| assert_eq!(r.size(), buf1.len() - offset); |
| assert_eq!(r.file_offset().unwrap().start(), offset as u64); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_SHARED); |
| |
| let buf2 = unsafe { slice::from_raw_parts(r.as_ptr(), buf1.len() - offset) }; |
| assert_eq!(&buf1[offset..], buf2); |
| } |
| |
| #[test] |
| #[cfg(not(miri))] // Miri cannot mmap files |
| fn test_mmap_region_build() { |
| let a = Arc::new(TempFile::new().unwrap().into_file()); |
| |
| let prot = libc::PROT_READ | libc::PROT_WRITE; |
| let flags = libc::MAP_NORESERVE | libc::MAP_PRIVATE; |
| let offset = 4096; |
| let size = 1000; |
| |
| // Offset + size will overflow. |
| let r = MmapRegion::build( |
| Some(FileOffset::from_arc(a.clone(), std::u64::MAX)), |
| size, |
| prot, |
| flags, |
| ); |
| assert_eq!(format!("{:?}", r.unwrap_err()), "InvalidOffsetLength"); |
| |
| // Offset + size is greater than the size of the file (which is 0 at this point). |
| let r = MmapRegion::build( |
| Some(FileOffset::from_arc(a.clone(), offset)), |
| size, |
| prot, |
| flags, |
| ); |
| assert_eq!(format!("{:?}", r.unwrap_err()), "MappingPastEof"); |
| |
| // MAP_FIXED was specified among the flags. |
| let r = MmapRegion::build( |
| Some(FileOffset::from_arc(a.clone(), offset)), |
| size, |
| prot, |
| flags | libc::MAP_FIXED, |
| ); |
| assert_eq!(format!("{:?}", r.unwrap_err()), "MapFixed"); |
| |
| // Let's resize the file. |
| assert_eq!(unsafe { libc::ftruncate(a.as_raw_fd(), 1024 * 10) }, 0); |
| |
| // The offset is not properly aligned. |
| let r = MmapRegion::build( |
| Some(FileOffset::from_arc(a.clone(), offset - 1)), |
| size, |
| prot, |
| flags, |
| ); |
| assert_eq!(r.unwrap_err().raw_os_error(), libc::EINVAL); |
| |
| // The build should be successful now. |
| let r = |
| MmapRegion::build(Some(FileOffset::from_arc(a, offset)), size, prot, flags).unwrap(); |
| |
| assert_eq!(r.size(), size); |
| assert_eq!(r.file_offset().unwrap().start(), offset); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE); |
| assert!(r.owned()); |
| |
| let region_size = 0x10_0000; |
| let bitmap = AtomicBitmap::new(region_size, 0x1000); |
| let builder = MmapRegionBuilder::new_with_bitmap(region_size, bitmap) |
| .with_hugetlbfs(true) |
| .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!(builder.size, region_size); |
| assert_eq!(builder.hugetlbfs, Some(true)); |
| assert_eq!(builder.prot, libc::PROT_READ | libc::PROT_WRITE); |
| |
| crate::bitmap::tests::test_volatile_memory(&(builder.build().unwrap())); |
| } |
| |
| #[test] |
| #[cfg(not(miri))] // Causes warnings due to the pointer casts |
| fn test_mmap_region_build_raw() { |
| let addr = 0; |
| let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; |
| let prot = libc::PROT_READ | libc::PROT_WRITE; |
| let flags = libc::MAP_NORESERVE | libc::MAP_PRIVATE; |
| |
| let r = unsafe { MmapRegion::build_raw((addr + 1) as *mut u8, size, prot, flags) }; |
| assert_eq!(format!("{:?}", r.unwrap_err()), "InvalidPointer"); |
| |
| let r = unsafe { MmapRegion::build_raw(addr as *mut u8, size, prot, flags).unwrap() }; |
| |
| assert_eq!(r.size(), size); |
| assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE); |
| assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE); |
| assert!(!r.owned()); |
| } |
| |
| #[test] |
| #[cfg(not(miri))] // Miri cannot mmap files |
| fn test_mmap_region_fds_overlap() { |
| let a = Arc::new(TempFile::new().unwrap().into_file()); |
| assert_eq!(unsafe { libc::ftruncate(a.as_raw_fd(), 1024 * 10) }, 0); |
| |
| let r1 = MmapRegion::from_file(FileOffset::from_arc(a.clone(), 0), 4096).unwrap(); |
| let r2 = MmapRegion::from_file(FileOffset::from_arc(a.clone(), 4096), 4096).unwrap(); |
| assert!(!r1.fds_overlap(&r2)); |
| |
| let r1 = MmapRegion::from_file(FileOffset::from_arc(a.clone(), 0), 5000).unwrap(); |
| assert!(r1.fds_overlap(&r2)); |
| |
| let r2 = MmapRegion::from_file(FileOffset::from_arc(a, 0), 1000).unwrap(); |
| assert!(r1.fds_overlap(&r2)); |
| |
| // Different files, so there's not overlap. |
| let new_file = TempFile::new().unwrap().into_file(); |
| // Resize before mapping. |
| assert_eq!( |
| unsafe { libc::ftruncate(new_file.as_raw_fd(), 1024 * 10) }, |
| 0 |
| ); |
| let r2 = MmapRegion::from_file(FileOffset::new(new_file, 0), 5000).unwrap(); |
| assert!(!r1.fds_overlap(&r2)); |
| |
| // R2 is not file backed, so no overlap. |
| let r2 = MmapRegion::new(5000).unwrap(); |
| assert!(!r1.fds_overlap(&r2)); |
| } |
| |
| #[test] |
| fn test_dirty_tracking() { |
| // Using the `crate` prefix because we aliased `MmapRegion` to `MmapRegion<()>` for |
| // the rest of the unit tests above. |
| let m = crate::MmapRegion::<AtomicBitmap>::new(0x1_0000).unwrap(); |
| crate::bitmap::tests::test_volatile_memory(&m); |
| } |
| } |