├── .gitignore ├── ci ├── script.sh └── install.sh ├── examples └── cat.rs ├── Cargo.toml ├── LICENSE-MIT ├── .appveyor.yml ├── README.md ├── .travis.yml ├── src ├── unix.rs ├── windows.rs └── lib.rs └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | # This script takes care of testing your crate 2 | 3 | set -ex 4 | 5 | main() { 6 | cross build --target $TARGET 7 | cross build --target $TARGET --release 8 | 9 | if [ ! -z $DISABLE_TESTS ]; then 10 | return 11 | fi 12 | 13 | cross test --target $TARGET 14 | cross test --target $TARGET --release 15 | } 16 | 17 | # we don't run the "test phase" when doing deploys 18 | if [ -z $TRAVIS_TAG ]; then 19 | main 20 | fi 21 | -------------------------------------------------------------------------------- /examples/cat.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::{self, Write}; 4 | 5 | use mapr::Mmap; 6 | 7 | /// Output a file's contents to stdout. The file path must be provided as the first process 8 | /// argument. 9 | fn main() { 10 | let path = env::args() 11 | .nth(1) 12 | .expect("supply a single path as the program argument"); 13 | 14 | let file = File::open(path).expect("failed to open the file"); 15 | 16 | let mmap = unsafe { Mmap::map(&file).expect("failed to map the file") }; 17 | 18 | io::stdout() 19 | .write_all(&mmap[..]) 20 | .expect("failed to output the file contents"); 21 | } 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mapr" 3 | # NB: When modifying, also modify html_root_url in lib.rs 4 | version = "0.8.0" 5 | authors = ["dignifiedquire ", "Dan Burkert "] 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/filecoin-project/mapr" 8 | documentation = "https://docs.rs/mapr" 9 | description = "Cross-platform Rust API for memory-mapped file IO" 10 | keywords = ["mmap", "memory-map", "io", "file"] 11 | edition = "2018" 12 | 13 | [target.'cfg(unix)'.dependencies] 14 | libc = "0.2" 15 | 16 | [target.'cfg(windows)'.dependencies] 17 | winapi = { version = "0.3", features = ["basetsd", "handleapi", "memoryapi", "minwindef", "std", "sysinfoapi"] } 18 | 19 | [dev-dependencies] 20 | tempdir = "0.3" 21 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Dan Burkert 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | # Based on the "trust" template v0.1.2 2 | # https://github.com/japaric/trust/tree/v0.1.2 3 | 4 | environment: 5 | matrix: 6 | # MinGW 7 | - TARGET: i686-pc-windows-gnu 8 | - TARGET: x86_64-pc-windows-gnu 9 | 10 | # MSVC 11 | - TARGET: i686-pc-windows-msvc 12 | - TARGET: x86_64-pc-windows-msvc 13 | 14 | install: 15 | - ps: >- 16 | If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { 17 | $Env:PATH += ';C:\msys64\mingw64\bin' 18 | } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { 19 | $Env:PATH += ';C:\msys64\mingw32\bin' 20 | } 21 | - curl -sSf -o rustup-init.exe https://win.rustup.rs/ 22 | - rustup-init.exe -y --default-host %TARGET% --default-toolchain stable 23 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 24 | - rustc -Vv 25 | - cargo -V 26 | 27 | build_script: 28 | - cargo build --target %TARGET% && 29 | cargo build --target %TARGET% --release 30 | 31 | test_script: 32 | - cargo test --target %TARGET% && 33 | cargo test --target %TARGET% --release 34 | 35 | cache: 36 | - C:\Users\appveyor\.cargo\registry 37 | - target 38 | 39 | branches: 40 | only: 41 | - master 42 | 43 | notifications: 44 | - provider: Email 45 | on_build_success: false 46 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | 3 | main() { 4 | local target= 5 | if [ $TRAVIS_OS_NAME = linux ]; then 6 | target=x86_64-unknown-linux-musl 7 | sort=sort 8 | else 9 | target=x86_64-apple-darwin 10 | sort=gsort # for `sort --sort-version`, from brew's coreutils. 11 | fi 12 | 13 | # Builds for iOS are done on OSX, but require the specific target to be 14 | # installed. 15 | case $TARGET in 16 | aarch64-apple-ios) 17 | rustup target install aarch64-apple-ios 18 | ;; 19 | armv7-apple-ios) 20 | rustup target install armv7-apple-ios 21 | ;; 22 | armv7s-apple-ios) 23 | rustup target install armv7s-apple-ios 24 | ;; 25 | i386-apple-ios) 26 | rustup target install i386-apple-ios 27 | ;; 28 | x86_64-apple-ios) 29 | rustup target install x86_64-apple-ios 30 | ;; 31 | esac 32 | 33 | # This fetches latest stable release 34 | local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ 35 | | cut -d/ -f3 \ 36 | | grep -E '^v[0.1.0-9.]+$' \ 37 | | $sort --version-sort \ 38 | | tail -n1) 39 | curl -LSfs https://japaric.github.io/trust/install.sh | \ 40 | sh -s -- \ 41 | --force \ 42 | --git japaric/cross \ 43 | --tag $tag \ 44 | --target $target 45 | } 46 | 47 | main 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mapr 2 | 3 | > Fork of the great [memmap](https://github.com/danburkert/memmap-rs) library. 4 | 5 | A Rust library for cross-platform memory mapped IO. 6 | 7 | [![Documentation](https://docs.rs/mapr/badge.svg)](https://docs.rs/mapr) 8 | [![Crate](https://img.shields.io/crates/v/mapr.svg)](https://crates.io/crates/mapr) 9 | 10 | ## Features 11 | 12 | - [x] file-backed memory maps 13 | - [x] anonymous memory maps 14 | - [x] synchronous and asynchronous flushing 15 | - [x] copy-on-write memory maps 16 | - [x] read-only memory maps 17 | - [x] stack support (`MAP_STACK` on unix) 18 | - [x] executable memory maps 19 | - [ ] huge page support 20 | 21 | ## Platforms 22 | 23 | `mapr` should work on any platform supported by 24 | [`libc`](https://github.com/rust-lang-nursery/libc#platforms-and-documentation). 25 | `mapr` requires Rust stable 1.13 or greater. 26 | 27 | `mapr` is continuously tested on: 28 | * `x86_64-unknown-linux-gnu` (Linux) 29 | * `i686-unknown-linux-gnu` 30 | * `x86_64-unknown-linux-musl` (Linux MUSL) 31 | * `x86_64-apple-darwin` (OSX) 32 | * `i686-apple-darwin` 33 | * `x86_64-pc-windows-msvc` (Windows) 34 | * `i686-pc-windows-msvc` 35 | * `x86_64-pc-windows-gnu` 36 | * `i686-pc-windows-gnu` 37 | 38 | `mapr` is continuously cross-compiled against: 39 | * `arm-linux-androideabi` (Android) 40 | * `aarch64-unknown-linux-gnu` (ARM) 41 | * `arm-unknown-linux-gnueabihf` 42 | * `mips-unknown-linux-gnu` (MIPS) 43 | * `x86_64-apple-ios` (iOS) 44 | * `i686-apple-ios` 45 | 46 | ## License 47 | 48 | `mapr` is primarily distributed under the terms of both the MIT license and the 49 | Apache License (Version 2.0). 50 | 51 | See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details. 52 | 53 | Copyright (c) 2015 Dan Burkert. 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Based on the "trust" template v0.1.2 2 | # https://github.com/japaric/trust/tree/v0.1.2 3 | 4 | dist: trusty 5 | language: rust 6 | services: docker 7 | sudo: required 8 | 9 | env: 10 | global: 11 | - CRATE_NAME=memmap 12 | 13 | matrix: 14 | include: 15 | # Android 16 | - env: TARGET=aarch64-linux-android DISABLE_TESTS=1 17 | - env: TARGET=arm-linux-androideabi DISABLE_TESTS=1 18 | - env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1 19 | - env: TARGET=i686-linux-android DISABLE_TESTS=1 20 | - env: TARGET=x86_64-linux-android DISABLE_TESTS=1 21 | 22 | # iOS 23 | - env: TARGET=aarch64-apple-ios DISABLE_TESTS=1 24 | os: osx 25 | - env: TARGET=armv7-apple-ios DISABLE_TESTS=1 26 | os: osx 27 | - env: TARGET=armv7s-apple-ios DISABLE_TESTS=1 28 | os: osx 29 | - env: TARGET=i386-apple-ios DISABLE_TESTS=1 30 | os: osx 31 | - env: TARGET=x86_64-apple-ios DISABLE_TESTS=1 32 | os: osx 33 | 34 | # Linux 35 | - env: TARGET=aarch64-unknown-linux-gnu 36 | - env: TARGET=arm-unknown-linux-gnueabi 37 | - env: TARGET=armv7-unknown-linux-gnueabihf 38 | - env: TARGET=i686-unknown-linux-gnu 39 | - env: TARGET=i686-unknown-linux-musl 40 | - env: TARGET=mips-unknown-linux-gnu 41 | - env: TARGET=mips64-unknown-linux-gnuabi64 42 | - env: TARGET=mips64el-unknown-linux-gnuabi64 43 | - env: TARGET=mipsel-unknown-linux-gnu 44 | - env: TARGET=powerpc-unknown-linux-gnu 45 | - env: TARGET=powerpc64-unknown-linux-gnu 46 | - env: TARGET=powerpc64le-unknown-linux-gnu 47 | - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 48 | - env: TARGET=x86_64-unknown-linux-gnu 49 | - env: TARGET=x86_64-unknown-linux-musl 50 | 51 | # OSX 52 | - env: TARGET=i686-apple-darwin 53 | os: osx 54 | - env: TARGET=x86_64-apple-darwin 55 | os: osx 56 | 57 | # *BSD 58 | - env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 59 | - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 60 | - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 61 | 62 | # rustc minimum version. 63 | - env: TARGET=x86_64-unknown-linux-gnu DISABLE_TESTS=1 64 | rust: 1.13.0 65 | 66 | before_install: 67 | - set -e 68 | - rustup self update 69 | 70 | install: 71 | - sh ci/install.sh 72 | - source ~/.cargo/env || true 73 | 74 | script: 75 | - bash ci/script.sh 76 | 77 | after_script: set +e 78 | 79 | cache: cargo 80 | before_cache: 81 | # Travis can't cache files that are not readable by "others" 82 | - chmod -R a+r $HOME/.cargo 83 | 84 | branches: 85 | only: 86 | - master 87 | 88 | notifications: 89 | email: 90 | on_success: never 91 | -------------------------------------------------------------------------------- /src/unix.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | use std::fs::File; 4 | use std::os::unix::io::{AsRawFd, RawFd}; 5 | use 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 | ))] 12 | const 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 | )))] 19 | const MAP_STACK: libc::c_int = 0; 20 | 21 | 22 | #[cfg(any( 23 | all(target_os = "linux", not(target_arch = "mips")), 24 | target_os = "freebsd", 25 | target_os = "android" 26 | ))] 27 | const MAP_LOCKED: libc::c_int = libc::MAP_LOCKED; 28 | 29 | #[cfg(not(any( 30 | all(target_os = "linux", not(target_arch = "mips")), 31 | target_os = "freebsd", 32 | target_os = "android" 33 | )))] 34 | const MAP_LOCKED: libc::c_int = 0; 35 | 36 | pub struct MmapInner { 37 | ptr: *mut libc::c_void, 38 | len: usize, 39 | } 40 | 41 | impl MmapInner { 42 | /// Creates a new `MmapInner`. 43 | /// 44 | /// This is a thin wrapper around the `mmap` sytem call. 45 | fn new( 46 | len: usize, 47 | prot: libc::c_int, 48 | flags: libc::c_int, 49 | file: RawFd, 50 | offset: u64, 51 | ) -> io::Result { 52 | let alignment = offset % page_size() as u64; 53 | let aligned_offset = offset - alignment; 54 | let aligned_len = len + alignment as usize; 55 | if aligned_len == 0 { 56 | // Normally the OS would catch this, but it segfaults under QEMU. 57 | return Err(io::Error::new( 58 | io::ErrorKind::InvalidInput, 59 | "memory map must have a non-zero length", 60 | )); 61 | } 62 | 63 | unsafe { 64 | let ptr = libc::mmap( 65 | ptr::null_mut(), 66 | aligned_len as libc::size_t, 67 | prot, 68 | flags, 69 | file, 70 | aligned_offset as libc::off_t, 71 | ); 72 | 73 | if ptr == libc::MAP_FAILED { 74 | Err(io::Error::last_os_error()) 75 | } else { 76 | Ok(MmapInner { 77 | ptr: ptr.offset(alignment as isize), 78 | len: len, 79 | }) 80 | } 81 | } 82 | } 83 | 84 | pub fn map(len: usize, file: &File, offset: u64, locked: bool, private: bool) -> io::Result { 85 | let locked = if locked { MAP_LOCKED } else { 0 }; 86 | let private = if private { libc::MAP_PRIVATE } else { libc::MAP_SHARED }; 87 | MmapInner::new( 88 | len, 89 | libc::PROT_READ, 90 | locked | private, 91 | file.as_raw_fd(), 92 | offset, 93 | ) 94 | } 95 | 96 | pub fn map_exec(len: usize, file: &File, offset: u64, locked: bool, private: bool) -> io::Result { 97 | let locked = if locked { MAP_LOCKED } else { 0 }; 98 | let private = if private { libc::MAP_PRIVATE } else { libc::MAP_SHARED }; 99 | MmapInner::new( 100 | len, 101 | libc::PROT_READ | libc::PROT_EXEC, 102 | locked | private, 103 | file.as_raw_fd(), 104 | offset, 105 | ) 106 | } 107 | 108 | pub fn map_mut(len: usize, file: &File, offset: u64, locked: bool, private: bool) -> io::Result { 109 | let locked = if locked { MAP_LOCKED } else { 0 }; 110 | let private = if private { libc::MAP_PRIVATE } else { libc::MAP_SHARED }; 111 | MmapInner::new( 112 | len, 113 | libc::PROT_READ | libc::PROT_WRITE, 114 | locked | private, 115 | file.as_raw_fd(), 116 | offset, 117 | ) 118 | } 119 | 120 | pub fn map_copy(len: usize, file: &File, offset: u64, locked: bool) -> io::Result { 121 | let locked = if locked { MAP_LOCKED } else { 0 }; 122 | 123 | MmapInner::new( 124 | len, 125 | libc::PROT_READ | libc::PROT_WRITE, 126 | libc::MAP_PRIVATE | locked, 127 | file.as_raw_fd(), 128 | offset, 129 | ) 130 | } 131 | 132 | /// Open an anonymous memory map. 133 | pub fn map_anon(len: usize, stack: bool, locked: bool, private: bool) -> io::Result { 134 | let stack = if stack { MAP_STACK } else { 0 }; 135 | let locked = if locked { MAP_LOCKED } else { 0 }; 136 | let private = if private { libc::MAP_PRIVATE } else { libc::MAP_SHARED }; 137 | MmapInner::new( 138 | len, 139 | libc::PROT_READ | libc::PROT_WRITE, 140 | libc::MAP_ANON | stack | locked | private, 141 | -1, 142 | 0, 143 | ) 144 | } 145 | 146 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { 147 | let alignment = (self.ptr as usize + offset) % page_size(); 148 | let offset = offset as isize - alignment as isize; 149 | let len = len + alignment; 150 | let result = 151 | unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) }; 152 | if result == 0 { 153 | Ok(()) 154 | } else { 155 | Err(io::Error::last_os_error()) 156 | } 157 | } 158 | 159 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { 160 | let alignment = offset % page_size(); 161 | let aligned_offset = offset - alignment; 162 | let aligned_len = len + alignment; 163 | let result = unsafe { 164 | libc::msync( 165 | self.ptr.offset(aligned_offset as isize), 166 | aligned_len as libc::size_t, 167 | libc::MS_ASYNC, 168 | ) 169 | }; 170 | if result == 0 { 171 | Ok(()) 172 | } else { 173 | Err(io::Error::last_os_error()) 174 | } 175 | } 176 | 177 | fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> { 178 | unsafe { 179 | let alignment = self.ptr as usize % page_size(); 180 | let ptr = self.ptr.offset(-(alignment as isize)); 181 | let len = self.len + alignment; 182 | if libc::mprotect(ptr, len, prot) == 0 { 183 | Ok(()) 184 | } else { 185 | Err(io::Error::last_os_error()) 186 | } 187 | } 188 | } 189 | 190 | pub fn make_read_only(&mut self) -> io::Result<()> { 191 | self.mprotect(libc::PROT_READ) 192 | } 193 | 194 | pub fn make_exec(&mut self) -> io::Result<()> { 195 | self.mprotect(libc::PROT_READ | libc::PROT_EXEC) 196 | } 197 | 198 | pub fn make_mut(&mut self) -> io::Result<()> { 199 | self.mprotect(libc::PROT_READ | libc::PROT_WRITE) 200 | } 201 | 202 | #[inline] 203 | pub fn ptr(&self) -> *const u8 { 204 | self.ptr as *const u8 205 | } 206 | 207 | #[inline] 208 | pub fn mut_ptr(&mut self) -> *mut u8 { 209 | self.ptr as *mut u8 210 | } 211 | 212 | #[inline] 213 | pub fn len(&self) -> usize { 214 | self.len 215 | } 216 | 217 | pub fn mlock(&self) -> io::Result<()> { 218 | unsafe { 219 | if libc::mlock(self.ptr, self.len) == 0 { 220 | Ok(()) 221 | } else { 222 | Err(io::Error::last_os_error()) 223 | } 224 | } 225 | } 226 | 227 | pub fn munlock(&self) -> io::Result<()> { 228 | unsafe { 229 | if libc::munlock(self.ptr, self.len) == 0 { 230 | Ok(()) 231 | } else { 232 | Err(io::Error::last_os_error()) 233 | } 234 | } 235 | } 236 | } 237 | 238 | impl Drop for MmapInner { 239 | fn drop(&mut self) { 240 | let alignment = self.ptr as usize % page_size(); 241 | unsafe { 242 | assert!( 243 | libc::munmap( 244 | self.ptr.offset(-(alignment as isize)), 245 | (self.len + alignment) as libc::size_t 246 | ) == 0, 247 | "unable to unmap mmap: {}", 248 | io::Error::last_os_error() 249 | ); 250 | } 251 | } 252 | } 253 | 254 | unsafe impl Sync for MmapInner {} 255 | unsafe impl Send for MmapInner {} 256 | 257 | fn page_size() -> usize { 258 | unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } 259 | } 260 | -------------------------------------------------------------------------------- /src/windows.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::os::raw::c_void; 3 | use std::os::windows::io::{AsRawHandle, RawHandle}; 4 | use std::{io, mem, ptr}; 5 | 6 | use winapi::shared::basetsd::SIZE_T; 7 | use winapi::shared::minwindef::DWORD; 8 | use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; 9 | use winapi::um::memoryapi::{ 10 | CreateFileMappingW, FlushViewOfFile, MapViewOfFile, UnmapViewOfFile, VirtualProtect, 11 | FILE_MAP_ALL_ACCESS, FILE_MAP_COPY, FILE_MAP_EXECUTE, FILE_MAP_READ, FILE_MAP_WRITE, 12 | }; 13 | use winapi::um::sysinfoapi::GetSystemInfo; 14 | use winapi::um::winnt::{ 15 | PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, 16 | PAGE_READWRITE, PAGE_WRITECOPY, 17 | }; 18 | 19 | pub struct MmapInner { 20 | file: Option, 21 | ptr: *mut c_void, 22 | len: usize, 23 | copy: bool, 24 | } 25 | 26 | impl MmapInner { 27 | /// Creates a new `MmapInner`. 28 | /// 29 | /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls. 30 | pub fn new( 31 | file: &File, 32 | protect: DWORD, 33 | access: DWORD, 34 | offset: u64, 35 | len: usize, 36 | copy: bool, 37 | ) -> io::Result { 38 | let alignment = offset % allocation_granularity() as u64; 39 | let aligned_offset = offset - alignment as u64; 40 | let aligned_len = len + alignment as usize; 41 | 42 | unsafe { 43 | let handle = CreateFileMappingW( 44 | file.as_raw_handle(), 45 | ptr::null_mut(), 46 | protect, 47 | 0, 48 | 0, 49 | ptr::null(), 50 | ); 51 | if handle == ptr::null_mut() { 52 | return Err(io::Error::last_os_error()); 53 | } 54 | 55 | let ptr = MapViewOfFile( 56 | handle, 57 | access, 58 | (aligned_offset >> 16 >> 16) as DWORD, 59 | (aligned_offset & 0xffffffff) as DWORD, 60 | aligned_len as SIZE_T, 61 | ); 62 | CloseHandle(handle); 63 | 64 | if ptr == ptr::null_mut() { 65 | Err(io::Error::last_os_error()) 66 | } else { 67 | Ok(MmapInner { 68 | file: Some(file.try_clone()?), 69 | ptr: ptr.offset(alignment as isize), 70 | len: len as usize, 71 | copy: copy, 72 | }) 73 | } 74 | } 75 | } 76 | 77 | pub fn map(len: usize, file: &File, offset: u64, _locked: bool, _private: bool) -> io::Result { 78 | let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); 79 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); 80 | let mut access = FILE_MAP_READ; 81 | let protection = match (write, exec) { 82 | (true, true) => { 83 | access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE; 84 | PAGE_EXECUTE_READWRITE 85 | } 86 | (true, false) => { 87 | access |= FILE_MAP_WRITE; 88 | PAGE_READWRITE 89 | } 90 | (false, true) => { 91 | access |= FILE_MAP_EXECUTE; 92 | PAGE_EXECUTE_READ 93 | } 94 | (false, false) => PAGE_READONLY, 95 | }; 96 | 97 | let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; 98 | if write || exec { 99 | inner.make_read_only()?; 100 | } 101 | Ok(inner) 102 | } 103 | 104 | pub fn map_exec(len: usize, file: &File, offset: u64, _locked: bool, _private: bool) -> io::Result { 105 | let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); 106 | let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE; 107 | let protection = if write { 108 | access |= FILE_MAP_WRITE; 109 | PAGE_EXECUTE_READWRITE 110 | } else { 111 | PAGE_EXECUTE_READ 112 | }; 113 | 114 | let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; 115 | if write { 116 | inner.make_exec()?; 117 | } 118 | Ok(inner) 119 | } 120 | 121 | pub fn map_mut(len: usize, file: &File, offset: u64, _locked: bool, _private: bool) -> io::Result { 122 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); 123 | let mut access = FILE_MAP_READ | FILE_MAP_WRITE; 124 | let protection = if exec { 125 | access |= FILE_MAP_EXECUTE; 126 | PAGE_EXECUTE_READWRITE 127 | } else { 128 | PAGE_READWRITE 129 | }; 130 | 131 | let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; 132 | if exec { 133 | inner.make_mut()?; 134 | } 135 | Ok(inner) 136 | } 137 | 138 | pub fn map_copy(len: usize, file: &File, offset: u64, _locked: bool) -> io::Result { 139 | let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READWRITE); 140 | let mut access = FILE_MAP_COPY; 141 | let protection = if exec { 142 | access |= FILE_MAP_EXECUTE; 143 | PAGE_EXECUTE_WRITECOPY 144 | } else { 145 | PAGE_WRITECOPY 146 | }; 147 | 148 | let mut inner = MmapInner::new(file, protection, access, offset, len, true)?; 149 | if exec { 150 | inner.make_mut()?; 151 | } 152 | Ok(inner) 153 | } 154 | 155 | pub fn map_anon(len: usize, _stack: bool, _locked: bool, _private: bool) -> io::Result { 156 | unsafe { 157 | // Create a mapping and view with maximum access permissions, then use `VirtualProtect` 158 | // to set the actual `Protection`. This way, we can set more permissive protection later 159 | // on. 160 | // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx 161 | 162 | let handle = CreateFileMappingW( 163 | INVALID_HANDLE_VALUE, 164 | ptr::null_mut(), 165 | PAGE_EXECUTE_READWRITE, 166 | (len >> 16 >> 16) as DWORD, 167 | (len & 0xffffffff) as DWORD, 168 | ptr::null(), 169 | ); 170 | if handle == ptr::null_mut() { 171 | return Err(io::Error::last_os_error()); 172 | } 173 | let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE; 174 | let ptr = MapViewOfFile(handle, access, 0, 0, len as SIZE_T); 175 | CloseHandle(handle); 176 | 177 | if ptr == ptr::null_mut() { 178 | return Err(io::Error::last_os_error()); 179 | } 180 | 181 | let mut old = 0; 182 | let result = VirtualProtect(ptr, len as SIZE_T, PAGE_READWRITE, &mut old); 183 | if result != 0 { 184 | Ok(MmapInner { 185 | file: None, 186 | ptr: ptr, 187 | len: len as usize, 188 | copy: false, 189 | }) 190 | } else { 191 | Err(io::Error::last_os_error()) 192 | } 193 | } 194 | } 195 | 196 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { 197 | self.flush_async(offset, len)?; 198 | if let Some(ref file) = self.file { 199 | file.sync_data()?; 200 | } 201 | Ok(()) 202 | } 203 | 204 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { 205 | let result = unsafe { FlushViewOfFile(self.ptr.offset(offset as isize), len as SIZE_T) }; 206 | if result != 0 { 207 | Ok(()) 208 | } else { 209 | Err(io::Error::last_os_error()) 210 | } 211 | } 212 | 213 | fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { 214 | unsafe { 215 | let alignment = self.ptr as usize % allocation_granularity(); 216 | let ptr = self.ptr.offset(-(alignment as isize)); 217 | let aligned_len = self.len as SIZE_T + alignment as SIZE_T; 218 | 219 | let mut old = 0; 220 | let result = VirtualProtect(ptr, aligned_len, protect, &mut old); 221 | 222 | if result != 0 { 223 | Ok(()) 224 | } else { 225 | Err(io::Error::last_os_error()) 226 | } 227 | } 228 | } 229 | 230 | pub fn make_read_only(&mut self) -> io::Result<()> { 231 | self.virtual_protect(PAGE_READONLY) 232 | } 233 | 234 | pub fn make_exec(&mut self) -> io::Result<()> { 235 | if self.copy { 236 | self.virtual_protect(PAGE_EXECUTE_WRITECOPY) 237 | } else { 238 | self.virtual_protect(PAGE_EXECUTE_READ) 239 | } 240 | } 241 | 242 | pub fn make_mut(&mut self) -> io::Result<()> { 243 | if self.copy { 244 | self.virtual_protect(PAGE_WRITECOPY) 245 | } else { 246 | self.virtual_protect(PAGE_READWRITE) 247 | } 248 | } 249 | 250 | #[inline] 251 | pub fn ptr(&self) -> *const u8 { 252 | self.ptr as *const u8 253 | } 254 | 255 | #[inline] 256 | pub fn mut_ptr(&mut self) -> *mut u8 { 257 | self.ptr as *mut u8 258 | } 259 | 260 | #[inline] 261 | pub fn len(&self) -> usize { 262 | self.len 263 | } 264 | } 265 | 266 | impl Drop for MmapInner { 267 | fn drop(&mut self) { 268 | let alignment = self.ptr as usize % allocation_granularity(); 269 | unsafe { 270 | let ptr = self.ptr.offset(-(alignment as isize)); 271 | assert!( 272 | UnmapViewOfFile(ptr) != 0, 273 | "unable to unmap mmap: {}", 274 | io::Error::last_os_error() 275 | ); 276 | } 277 | } 278 | } 279 | 280 | unsafe impl Sync for MmapInner {} 281 | unsafe impl Send for MmapInner {} 282 | 283 | fn protection_supported(handle: RawHandle, protection: DWORD) -> bool { 284 | unsafe { 285 | let handle = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null()); 286 | if handle == ptr::null_mut() { 287 | return false; 288 | } 289 | CloseHandle(handle); 290 | true 291 | } 292 | } 293 | 294 | fn allocation_granularity() -> usize { 295 | unsafe { 296 | let mut info = mem::zeroed(); 297 | GetSystemInfo(&mut info); 298 | return info.dwAllocationGranularity as usize; 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2015] [Dan Burkert] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A cross-platform Rust API for memory mapped buffers. 2 | 3 | #![doc(html_root_url = "https://docs.rs/mapr/0.7.0")] 4 | 5 | #[cfg(windows)] 6 | mod windows; 7 | #[cfg(windows)] 8 | use windows::MmapInner; 9 | 10 | #[cfg(unix)] 11 | mod unix; 12 | #[cfg(unix)] 13 | use unix::MmapInner; 14 | 15 | use std::fmt; 16 | use std::fs::File; 17 | use std::io::{Error, ErrorKind, Result}; 18 | use std::ops::{Deref, DerefMut}; 19 | use std::slice; 20 | use std::usize; 21 | 22 | /// A memory map builder, providing advanced options and flags for specifying memory map behavior. 23 | /// 24 | /// `MmapOptions` can be used to create an anonymous memory map using [`map_anon()`], or a 25 | /// file-backed memory map using one of [`map()`], [`map_mut()`], [`map_exec()`], or 26 | /// [`map_copy()`]. 27 | /// 28 | /// ## Safety 29 | /// 30 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 31 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 32 | /// out of process. Applications must consider the risk and take appropriate precautions when 33 | /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. 34 | /// unlinked) files exist but are platform specific and limited. 35 | /// 36 | /// [`map_anon()`]: MmapOptions::map_anon() 37 | /// [`map()`]: MmapOptions::map() 38 | /// [`map_mut()`]: MmapOptions::map_mut() 39 | /// [`map_exec()`]: MmapOptions::map_exec() 40 | /// [`map_copy()`]: MmapOptions::map_copy() 41 | #[derive(Clone, Debug, Default)] 42 | pub struct MmapOptions { 43 | offset: u64, 44 | len: Option, 45 | stack: bool, 46 | locked: bool, 47 | private: bool, 48 | } 49 | 50 | impl MmapOptions { 51 | /// Creates a new set of options for configuring and creating a memory map. 52 | /// 53 | /// # Example 54 | /// 55 | /// ``` 56 | /// use mapr::{MmapMut, MmapOptions}; 57 | /// # use std::io::Result; 58 | /// 59 | /// # fn main() -> Result<()> { 60 | /// // Create a new memory map builder. 61 | /// let mut mmap_options = MmapOptions::new(); 62 | /// 63 | /// // Configure the memory map builder using option setters, then create 64 | /// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`, 65 | /// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`: 66 | /// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?; 67 | /// 68 | /// // Use the memory map: 69 | /// mmap.copy_from_slice(b"...data to copy to the memory map..."); 70 | /// # Ok(()) 71 | /// # } 72 | /// ``` 73 | pub fn new() -> MmapOptions { 74 | MmapOptions::default() 75 | } 76 | 77 | /// Configures the memory map to start at byte `offset` from the beginning of the file. 78 | /// 79 | /// This option has no effect on anonymous memory maps. 80 | /// 81 | /// By default, the offset is 0. 82 | /// 83 | /// # Example 84 | /// 85 | /// ``` 86 | /// use mapr::MmapOptions; 87 | /// use std::fs::File; 88 | /// 89 | /// # fn main() -> std::io::Result<()> { 90 | /// let mmap = unsafe { 91 | /// MmapOptions::new() 92 | /// .offset(2) 93 | /// .map(&File::open("README.md")?)? 94 | /// }; 95 | /// assert_eq!(&b"mapr"[..], 96 | /// &mmap[..4]); 97 | /// # Ok(()) 98 | /// # } 99 | /// ``` 100 | pub fn offset(&mut self, offset: u64) -> &mut Self { 101 | self.offset = offset; 102 | self 103 | } 104 | 105 | /// Configures the created memory mapped buffer to be `len` bytes long. 106 | /// 107 | /// This option is mandatory for anonymous memory maps. 108 | /// 109 | /// For file-backed memory maps, the length will default to the file length. 110 | /// 111 | /// # Example 112 | /// 113 | /// ``` 114 | /// use mapr::MmapOptions; 115 | /// use std::fs::File; 116 | /// 117 | /// # fn main() -> std::io::Result<()> { 118 | /// let mmap = unsafe { 119 | /// MmapOptions::new() 120 | /// .len(6) 121 | /// .map(&File::open("README.md")?)? 122 | /// }; 123 | /// assert_eq!(&b"# mapr"[..], &mmap[..]); 124 | /// # Ok(()) 125 | /// # } 126 | /// ``` 127 | pub fn len(&mut self, len: usize) -> &mut Self { 128 | self.len = Some(len); 129 | self 130 | } 131 | 132 | /// Returns the configured length, or the length of the provided file. 133 | fn get_len(&self, file: &File) -> Result { 134 | self.len.map(Ok).unwrap_or_else(|| { 135 | let len = file.metadata()?.len() - self.offset; 136 | if len > (usize::MAX as u64) { 137 | return Err(Error::new( 138 | ErrorKind::InvalidData, 139 | "memory map length overflows usize", 140 | )); 141 | } 142 | Ok(len as usize) 143 | }) 144 | } 145 | 146 | /// Configures the anonymous memory map to be suitable for a process or thread stack. 147 | /// 148 | /// This option corresponds to the `MAP_STACK` flag on Linux. 149 | /// 150 | /// This option has no effect on file-backed memory maps. 151 | /// 152 | /// # Example 153 | /// 154 | /// ``` 155 | /// use mapr::MmapOptions; 156 | /// 157 | /// # fn main() -> std::io::Result<()> { 158 | /// let stack = MmapOptions::new().stack().len(4096).map_anon(); 159 | /// # Ok(()) 160 | /// # } 161 | /// ``` 162 | pub fn stack(&mut self) -> &mut Self { 163 | self.stack = true; 164 | self 165 | } 166 | 167 | /// Configures the memory map to be locked using. 168 | /// 169 | /// This option corresponds to the `MAP_LOCKED` flag on Linux, and has no effect on Window and MacOS. 170 | /// 171 | /// Note this requires privileged access. 172 | pub fn lock(&mut self) -> &mut Self { 173 | self.locked = true; 174 | self 175 | } 176 | 177 | /// Configures the memory map to be private. 178 | /// 179 | /// This option corresponds to the `MAP_PRIVATE` flag on Linux. 180 | pub fn private(&mut self) -> &mut Self { 181 | self.private = true; 182 | self 183 | } 184 | 185 | /// Creates a read-only memory map backed by a file. 186 | /// 187 | /// # Errors 188 | /// 189 | /// This method returns an error when the underlying system call fails, which can happen for a 190 | /// variety of reasons, such as when the file is not open with read permissions. 191 | /// 192 | /// # Example 193 | /// 194 | /// ``` 195 | /// use mapr::MmapOptions; 196 | /// use std::fs::File; 197 | /// use std::io::Read; 198 | /// 199 | /// # fn main() -> std::io::Result<()> { 200 | /// let mut file = File::open("README.md")?; 201 | /// 202 | /// let mut contents = Vec::new(); 203 | /// file.read_to_end(&mut contents)?; 204 | /// 205 | /// let mmap = unsafe { 206 | /// MmapOptions::new().map(&file)? 207 | /// }; 208 | /// 209 | /// assert_eq!(&contents[..], &mmap[..]); 210 | /// # Ok(()) 211 | /// # } 212 | /// ``` 213 | pub unsafe fn map(&self, file: &File) -> Result { 214 | MmapInner::map(self.get_len(file)?, file, self.offset, self.locked, self.private).map(|inner| Mmap { inner: inner }) 215 | } 216 | 217 | /// Creates a readable and executable memory map backed by a file. 218 | /// 219 | /// # Errors 220 | /// 221 | /// This method returns an error when the underlying system call fails, which can happen for a 222 | /// variety of reasons, such as when the file is not open with read permissions. 223 | pub unsafe fn map_exec(&self, file: &File) -> Result { 224 | MmapInner::map_exec(self.get_len(file)?, file, self.offset, self.locked, self.private) 225 | .map(|inner| Mmap { inner: inner }) 226 | } 227 | 228 | /// Creates a writeable memory map backed by a file. 229 | /// 230 | /// # Errors 231 | /// 232 | /// This method returns an error when the underlying system call fails, which can happen for a 233 | /// variety of reasons, such as when the file is not open with read and write permissions. 234 | /// 235 | /// # Example 236 | /// 237 | /// ``` 238 | /// use std::fs::OpenOptions; 239 | /// use std::path::PathBuf; 240 | /// 241 | /// use mapr::MmapOptions; 242 | /// # 243 | /// # fn main() -> std::io::Result<()> { 244 | /// # let tempdir = tempdir::TempDir::new("mmap")?; 245 | /// let path: PathBuf = /* path to file */ 246 | /// # tempdir.path().join("map_mut"); 247 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; 248 | /// file.set_len(13)?; 249 | /// 250 | /// let mut mmap = unsafe { 251 | /// MmapOptions::new().map_mut(&file)? 252 | /// }; 253 | /// 254 | /// mmap.copy_from_slice(b"Hello, world!"); 255 | /// # Ok(()) 256 | /// # } 257 | /// ``` 258 | pub unsafe fn map_mut(&self, file: &File) -> Result { 259 | MmapInner::map_mut(self.get_len(file)?, file, self.offset, self.locked, self.private) 260 | .map(|inner| MmapMut { inner: inner }) 261 | } 262 | 263 | /// Creates a copy-on-write memory map backed by a file. 264 | /// 265 | /// Data written to the memory map will not be visible by other processes, 266 | /// and will not be carried through to the underlying file. 267 | /// 268 | /// # Errors 269 | /// 270 | /// This method returns an error when the underlying system call fails, which can happen for a 271 | /// variety of reasons, such as when the file is not open with writable permissions. 272 | /// 273 | /// # Example 274 | /// 275 | /// ``` 276 | /// use mapr::MmapOptions; 277 | /// use std::fs::File; 278 | /// use std::io::Write; 279 | /// 280 | /// # fn main() -> std::io::Result<()> { 281 | /// let file = File::open("README.md")?; 282 | /// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? }; 283 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; 284 | /// # Ok(()) 285 | /// # } 286 | /// ``` 287 | pub unsafe fn map_copy(&self, file: &File) -> Result { 288 | MmapInner::map_copy(self.get_len(file)?, file, self.offset, self.locked) 289 | .map(|inner| MmapMut { inner: inner }) 290 | } 291 | 292 | /// Creates an anonymous memory map. 293 | /// 294 | /// Note: the memory map length must be configured to be greater than 0 before creating an 295 | /// anonymous memory map using `MmapOptions::len()`. 296 | /// 297 | /// # Errors 298 | /// 299 | /// This method returns an error when the underlying system call fails. 300 | pub fn map_anon(&self) -> Result { 301 | MmapInner::map_anon(self.len.unwrap_or(0), self.stack, self.locked, self.private).map(|inner| MmapMut { inner: inner }) 302 | } 303 | } 304 | 305 | /// A handle to an immutable memory mapped buffer. 306 | /// 307 | /// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. Use 308 | /// [`MmapOptions`] or [`map()`] to create a file-backed memory map. To create an immutable 309 | /// anonymous memory map, first create a mutable anonymous memory map, and then make it immutable 310 | /// with [`MmapMut::make_read_only()`]. 311 | /// 312 | /// A file backed `Mmap` is created by `&File` reference, and will remain valid even after the 313 | /// `File` is dropped. In other words, the `Mmap` handle is completely independent of the `File` 314 | /// used to create it. For consistency, on some platforms this is achieved by duplicating the 315 | /// underlying file handle. The memory will be unmapped when the `Mmap` handle is dropped. 316 | /// 317 | /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping 318 | /// the mapped pages into physical memory) though the details of this are platform specific. 319 | /// 320 | /// `Mmap` is [`Sync`](std::marker::Sync) and [`Send`](std::marker::Send). 321 | /// 322 | /// ## Safety 323 | /// 324 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 325 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 326 | /// out of process. Applications must consider the risk and take appropriate precautions when using 327 | /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) 328 | /// files exist but are platform specific and limited. 329 | /// 330 | /// ## Example 331 | /// 332 | /// ``` 333 | /// use mapr::MmapOptions; 334 | /// use std::io::Write; 335 | /// use std::fs::File; 336 | /// 337 | /// # fn main() -> std::io::Result<()> { 338 | /// let file = File::open("README.md")?; 339 | /// let mmap = unsafe { MmapOptions::new().map(&file)? }; 340 | /// assert_eq!(b"# mapr", &mmap[..6]); 341 | /// # Ok(()) 342 | /// # } 343 | /// ``` 344 | /// 345 | /// See [`MmapMut`] for the mutable version. 346 | /// 347 | /// [`map()`]: Mmap::map() 348 | pub struct Mmap { 349 | inner: MmapInner, 350 | } 351 | 352 | impl Mmap { 353 | /// Creates a read-only memory map backed by a file. 354 | /// 355 | /// This is equivalent to calling `MmapOptions::new().map(file)`. 356 | /// 357 | /// # Errors 358 | /// 359 | /// This method returns an error when the underlying system call fails, which can happen for a 360 | /// variety of reasons, such as when the file is not open with read permissions. 361 | /// 362 | /// # Example 363 | /// 364 | /// ``` 365 | /// use std::fs::File; 366 | /// use std::io::Read; 367 | /// 368 | /// use mapr::Mmap; 369 | /// 370 | /// # fn main() -> std::io::Result<()> { 371 | /// let mut file = File::open("README.md")?; 372 | /// 373 | /// let mut contents = Vec::new(); 374 | /// file.read_to_end(&mut contents)?; 375 | /// 376 | /// let mmap = unsafe { Mmap::map(&file)? }; 377 | /// 378 | /// assert_eq!(&contents[..], &mmap[..]); 379 | /// # Ok(()) 380 | /// # } 381 | /// ``` 382 | pub unsafe fn map(file: &File) -> Result { 383 | MmapOptions::new().map(file) 384 | } 385 | 386 | /// Transition the memory map to be writable. 387 | /// 388 | /// If the memory map is file-backed, the file must have been opened with write permissions. 389 | /// 390 | /// # Errors 391 | /// 392 | /// This method returns an error when the underlying system call fails, which can happen for a 393 | /// variety of reasons, such as when the file is not open with writable permissions. 394 | /// 395 | /// # Example 396 | /// 397 | /// ``` 398 | /// use mapr::Mmap; 399 | /// use std::ops::DerefMut; 400 | /// use std::io::Write; 401 | /// # use std::fs::OpenOptions; 402 | /// 403 | /// # fn main() -> std::io::Result<()> { 404 | /// # let tempdir = tempdir::TempDir::new("mmap")?; 405 | /// let file = /* file opened with write permissions */ 406 | /// # OpenOptions::new() 407 | /// # .read(true) 408 | /// # .write(true) 409 | /// # .create(true) 410 | /// # .open(tempdir.path() 411 | /// # .join("make_mut"))?; 412 | /// # file.set_len(128)?; 413 | /// let mmap = unsafe { Mmap::map(&file)? }; 414 | /// // ... use the read-only memory map ... 415 | /// let mut mut_mmap = mmap.make_mut()?; 416 | /// mut_mmap.deref_mut().write_all(b"hello, world!")?; 417 | /// # Ok(()) 418 | /// # } 419 | /// ``` 420 | pub fn make_mut(mut self) -> Result { 421 | self.inner.make_mut()?; 422 | Ok(MmapMut { inner: self.inner }) 423 | } 424 | 425 | /// Uses `mlock` to lock the whole memory map into RAM. 426 | /// 427 | /// Note this requires privileged access. 428 | #[cfg(unix)] 429 | pub fn mlock(&mut self) -> Result<()> { 430 | self.inner.mlock()?; 431 | 432 | Ok(()) 433 | } 434 | 435 | /// Uses `munlock` to unlock the whole memory map. 436 | /// 437 | /// Note this requires privileged access. 438 | #[cfg(unix)] 439 | pub fn munlock(&mut self) -> Result<()> { 440 | self.inner.munlock()?; 441 | 442 | Ok(()) 443 | } 444 | } 445 | 446 | impl Deref for Mmap { 447 | type Target = [u8]; 448 | 449 | #[inline] 450 | fn deref(&self) -> &[u8] { 451 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } 452 | } 453 | } 454 | 455 | impl AsRef<[u8]> for Mmap { 456 | #[inline] 457 | fn as_ref(&self) -> &[u8] { 458 | self.deref() 459 | } 460 | } 461 | 462 | impl fmt::Debug for Mmap { 463 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 464 | fmt.debug_struct("Mmap") 465 | .field("ptr", &self.as_ptr()) 466 | .field("len", &self.len()) 467 | .finish() 468 | } 469 | } 470 | 471 | /// A handle to a mutable memory mapped buffer. 472 | /// 473 | /// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous 474 | /// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use 475 | /// [`MmapMut::map_mut()`] and [`MmapMut::map_anon()`] to create a mutable memory map of the 476 | /// respective types, or [`MmapOptions::map_mut()`] and [`MmapOptions::map_anon()`] if non-default 477 | /// options are required. 478 | /// 479 | /// A file backed `MmapMut` is created by `&File` reference, and will remain valid even after the 480 | /// `File` is dropped. In other words, the `MmapMut` handle is completely independent of the `File` 481 | /// used to create it. For consistency, on some platforms this is achieved by duplicating the 482 | /// underlying file handle. The memory will be unmapped when the `MmapMut` handle is dropped. 483 | /// 484 | /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping 485 | /// the mapped pages into physical memory) though the details of this are platform specific. 486 | /// 487 | /// `Mmap` is [`Sync`](std::marker::Sync) and [`Send`](std::marker::Send). 488 | /// 489 | /// See [`Mmap`] for the immutable version. 490 | /// 491 | /// ## Safety 492 | /// 493 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 494 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 495 | /// out of process. Applications must consider the risk and take appropriate precautions when using 496 | /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) 497 | /// files exist but are platform specific and limited. 498 | pub struct MmapMut { 499 | inner: MmapInner, 500 | } 501 | 502 | impl MmapMut { 503 | /// Creates a writeable memory map backed by a file. 504 | /// 505 | /// This is equivalent to calling `MmapOptions::new().map_mut(file)`. 506 | /// 507 | /// # Errors 508 | /// 509 | /// This method returns an error when the underlying system call fails, which can happen for a 510 | /// variety of reasons, such as when the file is not open with read and write permissions. 511 | /// 512 | /// # Example 513 | /// 514 | /// ``` 515 | /// use std::fs::OpenOptions; 516 | /// use std::path::PathBuf; 517 | /// 518 | /// use mapr::MmapMut; 519 | /// # 520 | /// # fn main() -> std::io::Result<()> { 521 | /// # let tempdir = tempdir::TempDir::new("mmap")?; 522 | /// let path: PathBuf = /* path to file */ 523 | /// # tempdir.path().join("map_mut"); 524 | /// let file = OpenOptions::new() 525 | /// .read(true) 526 | /// .write(true) 527 | /// .create(true) 528 | /// .open(&path)?; 529 | /// file.set_len(13)?; 530 | /// 531 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; 532 | /// 533 | /// mmap.copy_from_slice(b"Hello, world!"); 534 | /// # Ok(()) 535 | /// # } 536 | /// ``` 537 | pub unsafe fn map_mut(file: &File) -> Result { 538 | MmapOptions::new().map_mut(file) 539 | } 540 | 541 | /// Creates an anonymous memory map. 542 | /// 543 | /// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`. 544 | /// 545 | /// # Errors 546 | /// 547 | /// This method returns an error when the underlying system call fails. 548 | pub fn map_anon(length: usize) -> Result { 549 | MmapOptions::new().len(length).map_anon() 550 | } 551 | 552 | /// Flushes outstanding memory map modifications to disk. 553 | /// 554 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 555 | /// memory map are guaranteed to be durably stored. The file's metadata (including last 556 | /// modification timestamp) may not be updated. 557 | /// 558 | /// # Example 559 | /// 560 | /// ``` 561 | /// use std::fs::OpenOptions; 562 | /// use std::io::Write; 563 | /// use std::path::PathBuf; 564 | /// 565 | /// use mapr::MmapMut; 566 | /// 567 | /// # fn main() -> std::io::Result<()> { 568 | /// # let tempdir = tempdir::TempDir::new("mmap")?; 569 | /// let path: PathBuf = /* path to file */ 570 | /// # tempdir.path().join("flush"); 571 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; 572 | /// file.set_len(128)?; 573 | /// 574 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; 575 | /// 576 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; 577 | /// mmap.flush()?; 578 | /// # Ok(()) 579 | /// # } 580 | /// ``` 581 | pub fn flush(&self) -> Result<()> { 582 | let len = self.len(); 583 | self.inner.flush(0, len) 584 | } 585 | 586 | /// Asynchronously flushes outstanding memory map modifications to disk. 587 | /// 588 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 589 | /// the operation to complete before returning. The file's metadata (including last 590 | /// modification timestamp) may not be updated. 591 | pub fn flush_async(&self) -> Result<()> { 592 | let len = self.len(); 593 | self.inner.flush_async(0, len) 594 | } 595 | 596 | /// Flushes outstanding memory map modifications in the range to disk. 597 | /// 598 | /// The offset and length must be in the bounds of the memory map. 599 | /// 600 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 601 | /// memory in the range are guaranteed to be durable stored. The file's metadata (including 602 | /// last modification timestamp) may not be updated. It is not guaranteed the only the changes 603 | /// in the specified range are flushed; other outstanding changes to the memory map may be 604 | /// flushed as well. 605 | pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> { 606 | self.inner.flush(offset, len) 607 | } 608 | 609 | /// Asynchronously flushes outstanding memory map modifications in the range to disk. 610 | /// 611 | /// The offset and length must be in the bounds of the memory map. 612 | /// 613 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 614 | /// the operation to complete before returning. The file's metadata (including last 615 | /// modification timestamp) may not be updated. It is not guaranteed that the only changes 616 | /// flushed are those in the specified range; other outstanding changes to the memory map may 617 | /// be flushed as well. 618 | pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { 619 | self.inner.flush_async(offset, len) 620 | } 621 | 622 | /// Returns an immutable version of this memory mapped buffer. 623 | /// 624 | /// If the memory map is file-backed, the file must have been opened with read permissions. 625 | /// 626 | /// # Errors 627 | /// 628 | /// This method returns an error when the underlying system call fails, which can happen for a 629 | /// variety of reasons, such as when the file has not been opened with read permissions. 630 | /// 631 | /// # Example 632 | /// 633 | /// ``` 634 | /// use std::io::Write; 635 | /// use std::path::PathBuf; 636 | /// 637 | /// use mapr::{Mmap, MmapMut}; 638 | /// 639 | /// # fn main() -> std::io::Result<()> { 640 | /// let mut mmap = MmapMut::map_anon(128)?; 641 | /// 642 | /// (&mut mmap[..]).write(b"Hello, world!")?; 643 | /// 644 | /// let mmap: Mmap = mmap.make_read_only()?; 645 | /// # Ok(()) 646 | /// # } 647 | /// ``` 648 | pub fn make_read_only(mut self) -> Result { 649 | self.inner.make_read_only()?; 650 | Ok(Mmap { inner: self.inner }) 651 | } 652 | 653 | /// Transition the memory map to be readable and executable. 654 | /// 655 | /// If the memory map is file-backed, the file must have been opened with execute permissions. 656 | /// 657 | /// # Errors 658 | /// 659 | /// This method returns an error when the underlying system call fails, which can happen for a 660 | /// variety of reasons, such as when the file has not been opened with execute permissions. 661 | pub fn make_exec(mut self) -> Result { 662 | self.inner.make_exec()?; 663 | Ok(Mmap { inner: self.inner }) 664 | } 665 | 666 | /// Uses `mlock` to lock the whole memory map into RAM. 667 | /// 668 | /// Note this requires privileged access. 669 | #[cfg(unix)] 670 | pub fn mlock(&mut self) -> Result<()> { 671 | self.inner.mlock()?; 672 | 673 | Ok(()) 674 | } 675 | 676 | /// Uses `munlock` to unlock the whole memory map. 677 | /// 678 | /// Note this requires privileged access. 679 | #[cfg(unix)] 680 | pub fn munlock(&mut self) -> Result<()> { 681 | self.inner.munlock()?; 682 | 683 | Ok(()) 684 | } 685 | } 686 | 687 | impl Deref for MmapMut { 688 | type Target = [u8]; 689 | 690 | #[inline] 691 | fn deref(&self) -> &[u8] { 692 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } 693 | } 694 | } 695 | 696 | impl DerefMut for MmapMut { 697 | #[inline] 698 | fn deref_mut(&mut self) -> &mut [u8] { 699 | unsafe { slice::from_raw_parts_mut(self.inner.mut_ptr(), self.inner.len()) } 700 | } 701 | } 702 | 703 | impl AsRef<[u8]> for MmapMut { 704 | #[inline] 705 | fn as_ref(&self) -> &[u8] { 706 | self.deref() 707 | } 708 | } 709 | 710 | impl AsMut<[u8]> for MmapMut { 711 | #[inline] 712 | fn as_mut(&mut self) -> &mut [u8] { 713 | self.deref_mut() 714 | } 715 | } 716 | 717 | impl fmt::Debug for MmapMut { 718 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 719 | fmt.debug_struct("MmapMut") 720 | .field("ptr", &self.as_ptr()) 721 | .field("len", &self.len()) 722 | .finish() 723 | } 724 | } 725 | 726 | #[cfg(test)] 727 | mod test { 728 | use std::fs::OpenOptions; 729 | use std::io::{Read, Write}; 730 | #[cfg(windows)] 731 | use std::os::windows::fs::OpenOptionsExt; 732 | use std::sync::Arc; 733 | use std::thread; 734 | 735 | #[cfg(windows)] 736 | use winapi::um::winnt::GENERIC_ALL; 737 | 738 | use super::{Mmap, MmapMut, MmapOptions}; 739 | 740 | #[test] 741 | fn map_file() { 742 | let expected_len = 128; 743 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 744 | let path = tempdir.path().join("mmap"); 745 | 746 | let file = OpenOptions::new() 747 | .read(true) 748 | .write(true) 749 | .create(true) 750 | .open(&path) 751 | .unwrap(); 752 | 753 | file.set_len(expected_len as u64).unwrap(); 754 | 755 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 756 | let len = mmap.len(); 757 | assert_eq!(expected_len, len); 758 | 759 | let zeros = vec![0; len]; 760 | let incr: Vec = (0..len as u8).collect(); 761 | 762 | // check that the mmap is empty 763 | assert_eq!(&zeros[..], &mmap[..]); 764 | 765 | // write values into the mmap 766 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 767 | 768 | // read values back 769 | assert_eq!(&incr[..], &mmap[..]); 770 | } 771 | 772 | /// Checks that a 0-length file will not be mapped. 773 | #[test] 774 | fn map_empty_file() { 775 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 776 | let path = tempdir.path().join("mmap"); 777 | 778 | let file = OpenOptions::new() 779 | .read(true) 780 | .write(true) 781 | .create(true) 782 | .open(&path) 783 | .unwrap(); 784 | let mmap = unsafe { Mmap::map(&file) }; 785 | assert!(mmap.is_err()); 786 | } 787 | 788 | #[test] 789 | fn map_anon() { 790 | let expected_len = 128; 791 | let mut mmap = MmapMut::map_anon(expected_len).unwrap(); 792 | let len = mmap.len(); 793 | assert_eq!(expected_len, len); 794 | 795 | let zeros = vec![0; len]; 796 | let incr: Vec = (0..len as u8).collect(); 797 | 798 | // check that the mmap is empty 799 | assert_eq!(&zeros[..], &mmap[..]); 800 | 801 | // write values into the mmap 802 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 803 | 804 | // read values back 805 | assert_eq!(&incr[..], &mmap[..]); 806 | } 807 | 808 | #[test] 809 | fn map_anon_zero_len() { 810 | assert!(MmapOptions::new().map_anon().is_err()) 811 | } 812 | 813 | #[test] 814 | fn file_write() { 815 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 816 | let path = tempdir.path().join("mmap"); 817 | 818 | let mut file = OpenOptions::new() 819 | .read(true) 820 | .write(true) 821 | .create(true) 822 | .open(&path) 823 | .unwrap(); 824 | file.set_len(128).unwrap(); 825 | 826 | let write = b"abc123"; 827 | let mut read = [0u8; 6]; 828 | 829 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 830 | (&mut mmap[..]).write_all(write).unwrap(); 831 | mmap.flush().unwrap(); 832 | 833 | file.read(&mut read).unwrap(); 834 | assert_eq!(write, &read); 835 | } 836 | 837 | #[test] 838 | fn flush_range() { 839 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 840 | let path = tempdir.path().join("mmap"); 841 | 842 | let file = OpenOptions::new() 843 | .read(true) 844 | .write(true) 845 | .create(true) 846 | .open(&path) 847 | .unwrap(); 848 | file.set_len(128).unwrap(); 849 | let write = b"abc123"; 850 | 851 | let mut mmap = unsafe { 852 | MmapOptions::new() 853 | .offset(2) 854 | .len(write.len()) 855 | .map_mut(&file) 856 | .unwrap() 857 | }; 858 | (&mut mmap[..]).write_all(write).unwrap(); 859 | mmap.flush_range(0, write.len()).unwrap(); 860 | } 861 | 862 | #[test] 863 | fn map_copy() { 864 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 865 | let path = tempdir.path().join("mmap"); 866 | 867 | let mut file = OpenOptions::new() 868 | .read(true) 869 | .write(true) 870 | .create(true) 871 | .open(&path) 872 | .unwrap(); 873 | file.set_len(128).unwrap(); 874 | 875 | let nulls = b"\0\0\0\0\0\0"; 876 | let write = b"abc123"; 877 | let mut read = [0u8; 6]; 878 | 879 | let mut mmap = unsafe { MmapOptions::new().map_copy(&file).unwrap() }; 880 | 881 | (&mut mmap[..]).write(write).unwrap(); 882 | mmap.flush().unwrap(); 883 | 884 | // The mmap contains the write 885 | (&mmap[..]).read(&mut read).unwrap(); 886 | assert_eq!(write, &read); 887 | 888 | // The file does not contain the write 889 | file.read(&mut read).unwrap(); 890 | assert_eq!(nulls, &read); 891 | 892 | // another mmap does not contain the write 893 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 894 | (&mmap2[..]).read(&mut read).unwrap(); 895 | assert_eq!(nulls, &read); 896 | } 897 | 898 | #[test] 899 | fn map_offset() { 900 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 901 | let path = tempdir.path().join("mmap"); 902 | 903 | let file = OpenOptions::new() 904 | .read(true) 905 | .write(true) 906 | .create(true) 907 | .open(&path) 908 | .unwrap(); 909 | 910 | let offset = u32::max_value() as u64 + 2; 911 | let len = 5432; 912 | file.set_len(offset + len as u64).unwrap(); 913 | 914 | // Check inferred length mmap. 915 | let mmap = unsafe { MmapOptions::new().offset(offset).map_mut(&file).unwrap() }; 916 | assert_eq!(len, mmap.len()); 917 | 918 | // Check explicit length mmap. 919 | let mut mmap = unsafe { 920 | MmapOptions::new() 921 | .offset(offset) 922 | .len(len) 923 | .map_mut(&file) 924 | .unwrap() 925 | }; 926 | assert_eq!(len, mmap.len()); 927 | 928 | let zeros = vec![0; len]; 929 | let incr: Vec<_> = (0..len).map(|i| i as u8).collect(); 930 | 931 | // check that the mmap is empty 932 | assert_eq!(&zeros[..], &mmap[..]); 933 | 934 | // write values into the mmap 935 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 936 | 937 | // read values back 938 | assert_eq!(&incr[..], &mmap[..]); 939 | } 940 | 941 | #[test] 942 | fn index() { 943 | let mut mmap = MmapMut::map_anon(128).unwrap(); 944 | mmap[0] = 42; 945 | assert_eq!(42, mmap[0]); 946 | } 947 | 948 | #[test] 949 | fn sync_send() { 950 | let mmap = Arc::new(MmapMut::map_anon(129).unwrap()); 951 | thread::spawn(move || { 952 | &mmap[..]; 953 | }); 954 | } 955 | 956 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 957 | fn jit_x86(mut mmap: MmapMut) { 958 | use std::mem; 959 | mmap[0] = 0xB8; // mov eax, 0xAB 960 | mmap[1] = 0xAB; 961 | mmap[2] = 0x00; 962 | mmap[3] = 0x00; 963 | mmap[4] = 0x00; 964 | mmap[5] = 0xC3; // ret 965 | 966 | let mmap = mmap.make_exec().expect("make_exec"); 967 | 968 | let jitfn: extern "C" fn() -> u8 = unsafe { mem::transmute(mmap.as_ptr()) }; 969 | assert_eq!(jitfn(), 0xab); 970 | } 971 | 972 | #[test] 973 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 974 | fn jit_x86_anon() { 975 | jit_x86(MmapMut::map_anon(4096).unwrap()); 976 | } 977 | 978 | #[test] 979 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 980 | fn jit_x86_file() { 981 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 982 | let mut options = OpenOptions::new(); 983 | #[cfg(windows)] 984 | options.access_mode(GENERIC_ALL); 985 | 986 | let file = options 987 | .read(true) 988 | .write(true) 989 | .create(true) 990 | .open(&tempdir.path().join("jit_x86")) 991 | .expect("open"); 992 | 993 | file.set_len(4096).expect("set_len"); 994 | jit_x86(unsafe { MmapMut::map_mut(&file).expect("map_mut") }); 995 | } 996 | 997 | #[test] 998 | fn mprotect_file() { 999 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 1000 | let path = tempdir.path().join("mmap"); 1001 | 1002 | let mut options = OpenOptions::new(); 1003 | #[cfg(windows)] 1004 | options.access_mode(GENERIC_ALL); 1005 | 1006 | let mut file = options 1007 | .read(true) 1008 | .write(true) 1009 | .create(true) 1010 | .open(&path) 1011 | .expect("open"); 1012 | file.set_len(256 as u64).expect("set_len"); 1013 | 1014 | let mmap = unsafe { MmapMut::map_mut(&file).expect("map_mut") }; 1015 | 1016 | let mmap = mmap.make_read_only().expect("make_read_only"); 1017 | let mut mmap = mmap.make_mut().expect("make_mut"); 1018 | 1019 | let write = b"abc123"; 1020 | let mut read = [0u8; 6]; 1021 | 1022 | (&mut mmap[..]).write(write).unwrap(); 1023 | mmap.flush().unwrap(); 1024 | 1025 | // The mmap contains the write 1026 | (&mmap[..]).read(&mut read).unwrap(); 1027 | assert_eq!(write, &read); 1028 | 1029 | // The file should contain the write 1030 | file.read(&mut read).unwrap(); 1031 | assert_eq!(write, &read); 1032 | 1033 | // another mmap should contain the write 1034 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1035 | (&mmap2[..]).read(&mut read).unwrap(); 1036 | assert_eq!(write, &read); 1037 | 1038 | let mmap = mmap.make_exec().expect("make_exec"); 1039 | 1040 | drop(mmap); 1041 | } 1042 | 1043 | #[test] 1044 | fn mprotect_copy() { 1045 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); 1046 | let path = tempdir.path().join("mmap"); 1047 | 1048 | let mut options = OpenOptions::new(); 1049 | #[cfg(windows)] 1050 | options.access_mode(GENERIC_ALL); 1051 | 1052 | let mut file = options 1053 | .read(true) 1054 | .write(true) 1055 | .create(true) 1056 | .open(&path) 1057 | .expect("open"); 1058 | file.set_len(256 as u64).expect("set_len"); 1059 | 1060 | let mmap = unsafe { MmapOptions::new().map_copy(&file).expect("map_mut") }; 1061 | 1062 | let mmap = mmap.make_read_only().expect("make_read_only"); 1063 | let mut mmap = mmap.make_mut().expect("make_mut"); 1064 | 1065 | let nulls = b"\0\0\0\0\0\0"; 1066 | let write = b"abc123"; 1067 | let mut read = [0u8; 6]; 1068 | 1069 | (&mut mmap[..]).write(write).unwrap(); 1070 | mmap.flush().unwrap(); 1071 | 1072 | // The mmap contains the write 1073 | (&mmap[..]).read(&mut read).unwrap(); 1074 | assert_eq!(write, &read); 1075 | 1076 | // The file does not contain the write 1077 | file.read(&mut read).unwrap(); 1078 | assert_eq!(nulls, &read); 1079 | 1080 | // another mmap does not contain the write 1081 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1082 | (&mmap2[..]).read(&mut read).unwrap(); 1083 | assert_eq!(nulls, &read); 1084 | 1085 | let mmap = mmap.make_exec().expect("make_exec"); 1086 | 1087 | drop(mmap); 1088 | } 1089 | 1090 | #[test] 1091 | fn mprotect_anon() { 1092 | let mmap = MmapMut::map_anon(256).expect("map_mut"); 1093 | 1094 | let mmap = mmap.make_read_only().expect("make_read_only"); 1095 | let mmap = mmap.make_mut().expect("make_mut"); 1096 | let mmap = mmap.make_exec().expect("make_exec"); 1097 | drop(mmap); 1098 | } 1099 | } 1100 | --------------------------------------------------------------------------------