diff options
Diffstat (limited to 'vendor/memmap2/src/lib.rs')
-rw-r--r-- | vendor/memmap2/src/lib.rs | 280 |
1 files changed, 270 insertions, 10 deletions
diff --git a/vendor/memmap2/src/lib.rs b/vendor/memmap2/src/lib.rs index 2d730ae90..dd99ba12e 100644 --- a/vendor/memmap2/src/lib.rs +++ b/vendor/memmap2/src/lib.rs @@ -4,7 +4,7 @@ //! which correspond to mapping a [`File`] to a [`&[u8]`](https://doc.rust-lang.org/std/primitive.slice.html) //! or [`&mut [u8]`](https://doc.rust-lang.org/std/primitive.slice.html) //! respectively. Both function by dereferencing to a slice, allowing the -//! [`Mmap`]/[`MmapMut`] to be used in the same way you would the equivelant slice +//! [`Mmap`]/[`MmapMut`] to be used in the same way you would the equivalent slice //! types. //! //! [`File`]: std::fs::File @@ -497,6 +497,21 @@ impl MmapOptions { MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate) .map(|inner| MmapRaw { inner }) } + + /// Creates a read-only raw memory map + /// + /// This is primarily useful to avoid intermediate `Mmap` instances when + /// read-only access to files modified elsewhere are required. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails + pub fn map_raw_read_only<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapRaw> { + let desc = file.as_raw_desc(); + + MmapInner::map(self.get_len(&file)?, desc.0, self.offset, self.populate) + .map(|inner| MmapRaw { inner }) + } } /// A handle to an immutable memory mapped buffer. @@ -646,7 +661,7 @@ impl Mmap { /// /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. #[cfg(unix)] - pub fn lock(&mut self) -> Result<()> { + pub fn lock(&self) -> Result<()> { self.inner.lock() } @@ -654,9 +669,31 @@ impl Mmap { /// /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. #[cfg(unix)] - pub fn unlock(&mut self) -> Result<()> { + pub fn unlock(&self) -> Result<()> { self.inner.unlock() } + + /// Adjust the size of the memory mapping. + /// + /// This will try to resize the memory mapping in place. If + /// [`RemapOptions::may_move`] is specified it will move the mapping if it + /// could not resize in place, otherwise it will error. + /// + /// Only supported on Linux. + /// + /// See the [`mremap(2)`] man page. + /// + /// # Safety + /// + /// Resizing the memory mapping beyond the end of the mapped file will + /// result in UB should you happen to access memory beyond the end of the + /// file. + /// + /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html + #[cfg(target_os = "linux")] + pub unsafe fn remap(&mut self, new_len: usize, options: RemapOptions) -> Result<()> { + self.inner.remap(new_len, options) + } } #[cfg(feature = "stable_deref_trait")] @@ -837,7 +874,7 @@ impl MmapRaw { /// /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. #[cfg(unix)] - pub fn lock(&mut self) -> Result<()> { + pub fn lock(&self) -> Result<()> { self.inner.lock() } @@ -845,9 +882,31 @@ impl MmapRaw { /// /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. #[cfg(unix)] - pub fn unlock(&mut self) -> Result<()> { + pub fn unlock(&self) -> Result<()> { self.inner.unlock() } + + /// Adjust the size of the memory mapping. + /// + /// This will try to resize the memory mapping in place. If + /// [`RemapOptions::may_move`] is specified it will move the mapping if it + /// could not resize in place, otherwise it will error. + /// + /// Only supported on Linux. + /// + /// See the [`mremap(2)`] man page. + /// + /// # Safety + /// + /// Resizing the memory mapping beyond the end of the mapped file will + /// result in UB should you happen to access memory beyond the end of the + /// file. + /// + /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html + #[cfg(target_os = "linux")] + pub unsafe fn remap(&mut self, new_len: usize, options: RemapOptions) -> Result<()> { + self.inner.remap(new_len, options) + } } impl fmt::Debug for MmapRaw { @@ -1105,7 +1164,7 @@ impl MmapMut { /// /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. #[cfg(unix)] - pub fn lock(&mut self) -> Result<()> { + pub fn lock(&self) -> Result<()> { self.inner.lock() } @@ -1113,9 +1172,31 @@ impl MmapMut { /// /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. #[cfg(unix)] - pub fn unlock(&mut self) -> Result<()> { + pub fn unlock(&self) -> Result<()> { self.inner.unlock() } + + /// Adjust the size of the memory mapping. + /// + /// This will try to resize the memory mapping in place. If + /// [`RemapOptions::may_move`] is specified it will move the mapping if it + /// could not resize in place, otherwise it will error. + /// + /// Only supported on Linux. + /// + /// See the [`mremap(2)`] man page. + /// + /// # Safety + /// + /// Resizing the memory mapping beyond the end of the mapped file will + /// result in UB should you happen to access memory beyond the end of the + /// file. + /// + /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html + #[cfg(target_os = "linux")] + pub unsafe fn remap(&mut self, new_len: usize, options: RemapOptions) -> Result<()> { + self.inner.remap(new_len, options) + } } #[cfg(feature = "stable_deref_trait")] @@ -1160,14 +1241,60 @@ impl fmt::Debug for MmapMut { } } +/// Options for [`Mmap::remap`] and [`MmapMut::remap`]. +#[derive(Copy, Clone, Default, Debug)] +#[cfg(target_os = "linux")] +pub struct RemapOptions { + may_move: bool, +} + +#[cfg(target_os = "linux")] +impl RemapOptions { + /// Creates a mew set of options for resizing a memory map. + pub fn new() -> Self { + Self::default() + } + + /// Controls whether the memory map can be moved if it is not possible to + /// resize it in place. + /// + /// If false then the memory map is guaranteed to remain at the same + /// address when being resized but attempting to resize will return an + /// error if the new memory map would overlap with something else in the + /// current process' memory. + /// + /// By default this is false. + /// + /// # `may_move` and `StableDeref` + /// If the `stable_deref_trait` feature is enabled then [`Mmap`] and + /// [`MmapMut`] implement `StableDeref`. `StableDeref` promises that the + /// memory map dereferences to a fixed address, however, calling `remap` + /// with `may_move` set may result in the backing memory of the mapping + /// being moved to a new address. This may cause UB in other code + /// depending on the `StableDeref` guarantees. + pub fn may_move(mut self, may_move: bool) -> Self { + self.may_move = may_move; + self + } + + pub(crate) fn into_flags(self) -> libc::c_int { + if self.may_move { + libc::MREMAP_MAYMOVE + } else { + 0 + } + } +} + #[cfg(test)] mod test { extern crate tempfile; #[cfg(unix)] use crate::advice::Advice; - use std::fs::OpenOptions; + use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; + use std::mem; #[cfg(unix)] use std::os::unix::io::AsRawFd; #[cfg(windows)] @@ -1257,8 +1384,10 @@ mod test { .unwrap(); let mmap = unsafe { Mmap::map(&file).unwrap() }; assert!(mmap.is_empty()); + assert_eq!(mmap.as_ptr().align_offset(mem::size_of::<usize>()), 0); let mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; assert!(mmap.is_empty()); + assert_eq!(mmap.as_ptr().align_offset(mem::size_of::<usize>()), 0); } #[test] @@ -1482,7 +1611,7 @@ mod test { let mmap = mmap.make_exec().expect("make_exec"); - let jitfn: extern "C" fn() -> u8 = unsafe { std::mem::transmute(mmap.as_ptr()) }; + let jitfn: extern "C" fn() -> u8 = unsafe { mem::transmute(mmap.as_ptr()) }; assert_eq!(jitfn(), 0xab); } @@ -1633,6 +1762,22 @@ mod test { assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a'); } + #[test] + fn raw_read_only() { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().join("mmaprawro"); + + File::create(&path).unwrap().write_all(b"abc123").unwrap(); + + let mmap = MmapOptions::new() + .map_raw_read_only(&File::open(&path).unwrap()) + .unwrap(); + + assert_eq!(mmap.len(), 6); + assert!(!mmap.as_ptr().is_null()); + assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a'); + } + /// Something that relies on StableDeref #[test] #[cfg(feature = "stable_deref_trait")] @@ -1728,7 +1873,7 @@ mod test { .unwrap(); file.set_len(128).unwrap(); - let mut mmap = unsafe { Mmap::map(&file).unwrap() }; + let mmap = unsafe { Mmap::map(&file).unwrap() }; #[cfg(target_os = "linux")] assert!(!is_locked()); @@ -1751,4 +1896,119 @@ mod test { #[cfg(target_os = "linux")] assert!(!is_locked()); } + + #[test] + #[cfg(target_os = "linux")] + fn remap_grow() { + use crate::RemapOptions; + + let initial_len = 128; + let final_len = 2000; + + let zeros = vec![0u8; final_len]; + let incr: Vec<u8> = (0..final_len).map(|v| v as u8).collect(); + + let file = tempfile::tempfile().unwrap(); + file.set_len(final_len as u64).unwrap(); + + let mut mmap = unsafe { MmapOptions::new().len(initial_len).map_mut(&file).unwrap() }; + assert_eq!(mmap.len(), initial_len); + assert_eq!(&mmap[..], &zeros[..initial_len]); + + unsafe { + mmap.remap(final_len, RemapOptions::new().may_move(true)) + .unwrap() + }; + + // The size should have been updated + assert_eq!(mmap.len(), final_len); + + // Should still be all zeros + assert_eq!(&mmap[..], &zeros); + + // Write out to the whole expanded slice. + mmap.copy_from_slice(&incr); + } + + #[test] + #[cfg(target_os = "linux")] + fn remap_shrink() { + use crate::RemapOptions; + + let initial_len = 20000; + let final_len = 400; + + let incr: Vec<u8> = (0..final_len).map(|v| v as u8).collect(); + + let file = tempfile::tempfile().unwrap(); + file.set_len(initial_len as u64).unwrap(); + + let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; + assert_eq!(mmap.len(), initial_len); + + unsafe { mmap.remap(final_len, RemapOptions::new()).unwrap() }; + assert_eq!(mmap.len(), final_len); + + // Check that the mmap is still writable along the slice length + mmap.copy_from_slice(&incr); + } + + #[test] + #[cfg(target_os = "linux")] + #[cfg(target_pointer_width = "32")] + fn remap_len_overflow() { + use crate::RemapOptions; + + let file = tempfile::tempfile().unwrap(); + file.set_len(1024).unwrap(); + let mut mmap = unsafe { MmapOptions::new().len(1024).map(&file).unwrap() }; + + let res = unsafe { mmap.remap(0x80000000, RemapOptions::new().may_move(true)) }; + assert_eq!( + res.unwrap_err().to_string(), + "memory map length overflows isize" + ); + + assert_eq!(mmap.len(), 1024); + } + + #[test] + #[cfg(target_os = "linux")] + fn remap_with_offset() { + use crate::RemapOptions; + + let offset = 77; + let initial_len = 128; + let final_len = 2000; + + let zeros = vec![0u8; final_len]; + let incr: Vec<u8> = (0..final_len).map(|v| v as u8).collect(); + + let file = tempfile::tempfile().unwrap(); + file.set_len(final_len as u64 + offset).unwrap(); + + let mut mmap = unsafe { + MmapOptions::new() + .len(initial_len) + .offset(offset) + .map_mut(&file) + .unwrap() + }; + assert_eq!(mmap.len(), initial_len); + assert_eq!(&mmap[..], &zeros[..initial_len]); + + unsafe { + mmap.remap(final_len, RemapOptions::new().may_move(true)) + .unwrap() + }; + + // The size should have been updated + assert_eq!(mmap.len(), final_len); + + // Should still be all zeros + assert_eq!(&mmap[..], &zeros); + + // Write out to the whole expanded slice. + mmap.copy_from_slice(&incr); + } } |