├── .gitignore ├── tea.yaml ├── examples └── cat.rs ├── Cargo.toml ├── LICENSE-MIT ├── src ├── stub.rs ├── windows.rs ├── unix.rs └── lib.rs ├── .github └── workflows │ └── main.yml ├── README.md ├── CHANGELOG.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x7cc40A714f3b4572D8Ce05c5325aF8e5B6baB3ae' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /examples/cat.rs: -------------------------------------------------------------------------------- 1 | extern crate memmapix; 2 | 3 | use std::env; 4 | use std::fs::File; 5 | use std::io::{self, Write}; 6 | 7 | use memmapix::Mmap; 8 | 9 | /// Output a file's contents to stdout. The file path must be provided as the first process 10 | /// argument. 11 | fn main() { 12 | let path = env::args() 13 | .nth(1) 14 | .expect("supply a single path as the program argument"); 15 | 16 | let file = File::open(path).expect("failed to open the file"); 17 | 18 | let mmap = unsafe { Mmap::map(&file).expect("failed to map the file") }; 19 | 20 | io::stdout() 21 | .write_all(&mmap[..]) 22 | .expect("failed to output the file contents"); 23 | } 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memmapix" 3 | version = "0.7.6" 4 | authors = ["Dan Burkert ", "Yevhenii Reizner ", "Al Liu "] 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/al8n/memmapix-rs" 7 | documentation = "https://docs.rs/memmapix" 8 | description = "No libc, pure Rust cross-platform Rust API for memory-mapped file IO" 9 | keywords = ["mmap", "memory-map", "memmap2", "memmap", "file"] 10 | categories = ["filesystem", "external-ffi-bindings"] 11 | edition = "2018" 12 | 13 | [target.'cfg(unix)'.dependencies] 14 | rustix = { version = "0.38", features = ["fs", "mm", "param"] } 15 | 16 | [dev-dependencies] 17 | tempfile = "3" 18 | owning_ref = "0.4.1" 19 | 20 | [package.metadata.docs.rs] 21 | all-features = true 22 | rustdoc-args = ["--cfg", "docsrs"] 23 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Yevhenii Reizner 2 | Copyright (c) 2015 Dan Burkert 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /src/stub.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io; 3 | 4 | // A stable alternative to https://doc.rust-lang.org/stable/std/primitive.never.html 5 | enum Never {} 6 | 7 | pub struct MmapInner { 8 | never: Never, 9 | } 10 | 11 | impl MmapInner { 12 | fn new() -> io::Result { 13 | Err(io::Error::new( 14 | io::ErrorKind::Other, 15 | "platform not supported", 16 | )) 17 | } 18 | 19 | pub fn map(_: usize, _: &File, _: u64, _: bool) -> io::Result { 20 | MmapInner::new() 21 | } 22 | 23 | pub fn map_exec(_: usize, _: &File, _: u64, _: bool) -> io::Result { 24 | MmapInner::new() 25 | } 26 | 27 | pub fn map_mut(_: usize, _: &File, _: u64, _: bool) -> io::Result { 28 | MmapInner::new() 29 | } 30 | 31 | pub fn map_copy(_: usize, _: &File, _: u64, _: bool) -> io::Result { 32 | MmapInner::new() 33 | } 34 | 35 | pub fn map_copy_read_only(_: usize, _: &File, _: u64, _: bool) -> io::Result { 36 | MmapInner::new() 37 | } 38 | 39 | pub fn map_anon(_: usize, _: bool, _: bool) -> io::Result { 40 | MmapInner::new() 41 | } 42 | 43 | pub fn flush(&self, _: usize, _: usize) -> io::Result<()> { 44 | match self.never {} 45 | } 46 | 47 | pub fn flush_async(&self, _: usize, _: usize) -> io::Result<()> { 48 | match self.never {} 49 | } 50 | 51 | pub fn make_read_only(&mut self) -> io::Result<()> { 52 | match self.never {} 53 | } 54 | 55 | pub fn make_exec(&mut self) -> io::Result<()> { 56 | match self.never {} 57 | } 58 | 59 | pub fn make_mut(&mut self) -> io::Result<()> { 60 | match self.never {} 61 | } 62 | 63 | #[inline] 64 | pub fn ptr(&self) -> *const u8 { 65 | match self.never {} 66 | } 67 | 68 | #[inline] 69 | pub fn mut_ptr(&mut self) -> *mut u8 { 70 | match self.never {} 71 | } 72 | 73 | #[inline] 74 | pub fn len(&self) -> usize { 75 | match self.never {} 76 | } 77 | } 78 | 79 | pub fn file_len(file: &File) -> io::Result { 80 | Ok(file.metadata()?.len()) 81 | } 82 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | nightly: nightly 8 | 9 | jobs: 10 | check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Install toolchain 17 | uses: dtolnay/rust-toolchain@stable 18 | with: 19 | targets: x86_64-pc-windows-gnu 20 | components: clippy, rustfmt 21 | 22 | - name: Run checks 23 | env: 24 | CLIPPY_OPTS: --all-targets 25 | run: | 26 | cargo fmt --check 27 | cargo clippy $CLIPPY_OPTS 28 | cargo clippy --target x86_64-pc-windows-gnu $CLIPPY_OPTS 29 | 30 | test-win: 31 | runs-on: windows-latest 32 | strategy: 33 | matrix: 34 | target: 35 | - i686-pc-windows-gnu 36 | - i686-pc-windows-msvc 37 | - x86_64-pc-windows-gnu 38 | - x86_64-pc-windows-msvc 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@v3 42 | 43 | - name: Install toolchain 44 | uses: dtolnay/rust-toolchain@master 45 | with: 46 | toolchain: stable-${{ matrix.target }} 47 | 48 | - name: Run tests 49 | run: cargo test --all-features 50 | 51 | test-macos: 52 | runs-on: macos-latest 53 | steps: 54 | - name: Checkout 55 | uses: actions/checkout@v3 56 | 57 | - name: Install toolchain 58 | uses: dtolnay/rust-toolchain@stable 59 | 60 | - name: Run tests 61 | run: cargo test --all-features 62 | 63 | test-linux: 64 | runs-on: ubuntu-latest 65 | strategy: 66 | matrix: 67 | target: 68 | - i686-unknown-linux-gnu 69 | - i686-unknown-linux-musl 70 | - x86_64-unknown-linux-gnu 71 | - x86_64-unknown-linux-musl 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v3 75 | 76 | - name: Install toolchain 77 | uses: dtolnay/rust-toolchain@stable 78 | with: 79 | target: ${{ matrix.target }} 80 | 81 | - name: Install multilib 82 | if: ${{ contains(matrix.target, 'i686-unknown-linux-') }} 83 | run: | 84 | sudo apt update -yqq 85 | sudo apt install gcc-multilib 86 | 87 | - name: Run tests 88 | run: cargo test --all-features --target ${{ matrix.target }} 89 | 90 | check-stub: 91 | runs-on: ubuntu-latest 92 | steps: 93 | - name: Checkout 94 | uses: actions/checkout@v3 95 | 96 | - name: Install toolchain 97 | uses: dtolnay/rust-toolchain@stable 98 | with: 99 | targets: wasm32-unknown-unknown 100 | 101 | - name: Run check 102 | run: cargo check --all-features --target wasm32-unknown-unknown 103 | 104 | test-msrv: 105 | runs-on: ubuntu-latest 106 | steps: 107 | - name: Checkout 108 | uses: actions/checkout@v3 109 | 110 | - name: Install toolchain 111 | uses: actions-rs/toolchain@v1 112 | with: 113 | toolchain: 1.72.0 114 | profile: minimal 115 | override: true 116 | 117 | - name: Run tests 118 | run: cargo test --all-features 119 | # do not test, because dev-dependencies do not follow MSRV 120 | - name: Build 121 | run: cargo build --all-features 122 | docs: 123 | name: docs 124 | runs-on: ubuntu-latest 125 | steps: 126 | - uses: actions/checkout@v2 127 | - uses: actions-rs/toolchain@v1 128 | with: 129 | toolchain: ${{ env.nightly }} 130 | override: true 131 | - uses: Swatinem/rust-cache@v1 132 | - name: "doc --lib --all-features" 133 | run: cargo doc --lib --no-deps --all-features --document-private-items 134 | env: 135 | RUSTFLAGS: --cfg docsrs 136 | RUSTDOCFLAGS: --cfg docsrs -Dwarnings 137 | 138 | coverage: 139 | name: cargo tarpaulin 140 | runs-on: ubuntu-latest 141 | needs: 142 | - check 143 | - test-win 144 | - test-linux 145 | - test-macos 146 | - check-stub 147 | - test-msrv 148 | - docs 149 | steps: 150 | - uses: actions/checkout@v3 151 | - name: Install latest nightly 152 | uses: actions-rs/toolchain@v1 153 | with: 154 | toolchain: nightly 155 | override: true 156 | - uses: actions-rs/install@v0.1 157 | with: 158 | crate: cargo-tarpaulin 159 | version: latest 160 | - name: Cache ~/.cargo 161 | uses: actions/cache@v3 162 | with: 163 | path: ~/.cargo 164 | key: ${{ runner.os }}-coverage-dotcargo 165 | - name: Cache cargo build 166 | uses: actions/cache@v3 167 | with: 168 | path: target 169 | key: ${{ runner.os }}-coverage-cargo-build-target 170 | - name: Run tarpaulin 171 | uses: actions-rs/cargo@v1 172 | with: 173 | command: tarpaulin 174 | args: --run-types all-targets --workspace --out xml 175 | - name: Upload to codecov.io 176 | uses: codecov/codecov-action@v3.1.1 177 | with: 178 | token: ${{ secrets.CODECOV_TOKEN }} 179 | fail_ci_if_error: true 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

memmapix

3 |
4 |
5 | 6 | [github][Github-url] 7 | [Build][CI-url] 8 | [codecov][codecov-url] 9 | 10 | [docs.rs][doc-url] 11 | [crates.io][crates-url] 12 | license 13 | 14 | A pure Rust library for cross-platform memory mapped IO, which replace `libc` with [`rustix`](https://github.com/bytecodealliance/rustix). 15 | 16 | The project is modified based on the [memmap2-rs](https://github.com/RazrFalcon/memmap2-rs). 17 | 18 |
19 | 20 | ## Features 21 | 22 | - [x] file-backed memory maps 23 | - [x] anonymous memory maps 24 | - [x] synchronous and asynchronous flushing 25 | - [x] copy-on-write memory maps 26 | - [x] read-only memory maps 27 | - [x] stack support (`MAP_STACK` on unix) 28 | - [x] executable memory maps 29 | - [ ] huge page support 30 | 31 | A list of supported/tested targets can be found in [Actions](https://github.com/RazrFalcon/memmap2-rs/actions). 32 | 33 | ## License 34 | 35 | `memmapix` is primarily distributed under the terms of both the MIT license and the 36 | Apache License (Version 2.0). 37 | 38 | See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details. 39 | 40 | Copyright (c) 2022 Al Liu 41 | 42 | Copyright (c) 2020 Yevhenii Reizner 43 | 44 | Copyright (c) 2015 Dan Burkert 45 | 46 | [Github-url]: https://github.com/al8n/memmapix-rs/ 47 | [CI-url]: https://github.com/al8n/memmapix-rs/actions/workflows/main.yml 48 | [doc-url]: https://docs.rs/memmapix 49 | [crates-url]: https://crates.io/crates/memmapix 50 | [codecov-url]: https://app.codecov.io/gh/al8n/memmapix-rs/ 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.7.1] - 2023-06-24 10 | ### Fixed 11 | - Mapping beyond 4GB offset on 32 bit glibc. Linux-only. 12 | [@lvella](https://github.com/lvella) 13 | 14 | ## [0.7.0] - 2023-06-08 15 | ### Added 16 | - `Mmap::remap`, `MmapMut::remap` and `MmapRaw::remap`. Linux-only. 17 | [@Phantomical](https://github.com/Phantomical) 18 | - `Advice::PopulateRead` and `Advice::PopulateWrite`. Linux-only. 19 | [@Jesse-Bakker](https://github.com/Jesse-Bakker) 20 | 21 | ### Changed 22 | - libc crate >= 0.2.143 is required now. 23 | 24 | ## [0.6.2] - 2023-05-24 25 | ### Fixed 26 | - Alignment for empty files on Windows. 27 | [@timvisee](https://github.com/timvisee) 28 | 29 | ## [0.6.1] - 2023-05-10 30 | ### Added 31 | - Add `MmapOptions::map_raw_read_only` to avoid intermediate invalid `Mmap` instances. 32 | [@adamreichold](https://github.com/adamreichold) 33 | 34 | ## [0.6.0] - 2023-05-09 35 | ### Changed 36 | - `lock()` and `unlock` methods require `&self` and not `&mut self` now. 37 | [@timvisee](https://github.com/timvisee) 38 | 39 | ## [0.5.10] - 2023-02-22 40 | ### Added 41 | - `MmapOptions::map_anon` accounts for `populate` on Linux now. 42 | [@jsgf](https://github.com/jsgf) 43 | 44 | ## [0.5.9] - 2023-02-17 45 | ### Added 46 | - `From for MmapRaw` and `From for MmapRaw`. 47 | [@swlynch99](https://github.com/swlynch99) 48 | - `Mmap::advise_range`, `MmapMut::advise_range`, `MmapRaw::advise_range`. 49 | [@ho-229](https://github.com/ho-229) 50 | 51 | ## [0.5.8] - 2022-11-09 52 | ### Added 53 | - `MmapRaw::advise`, `MmapRaw::lock` and `MmapRaw::unlock`. 54 | [@diwic](https://github.com/diwic) 55 | - Improve `MmapMut::make_exec` documentation. 56 | 57 | ## [0.5.7] - 2022-08-15 58 | ### Changed 59 | - Simplify file size retrieving code. 60 | [@saethlin](https://github.com/saethlin) 61 | 62 | ## [0.5.6] - 2022-08-11 63 | ### Added 64 | - Memory locking and unlocking. See `Mmap::lock`, `Mmap::unlock`, 65 | `MmapMut::lock` and `MmapMut::unlock`. 66 | [@vmx](https://github.com/vmx) 67 | 68 | ## [0.5.5] - 2022-07-09 69 | ### Fixed 70 | - Limit mapping length to `isize::MAX` to prevent undefined behavior 71 | on calling `std::slice::from_raw_parts`. Technically affects only 32-bit systems. 72 | [@adamreichold](https://github.com/adamreichold) 73 | 74 | ## [0.5.4] - 2022-06-04 75 | ### Added 76 | - Add madvice operations specific to Darwin. [@turbocool3r](https://github.com/turbocool3r) 77 | - Implement common traits for the `Advice` enum. [@nyurik](https://github.com/nyurik) 78 | 79 | ### Changed 80 | - Make stub implementation Infallible. [@coolreader18](https://github.com/coolreader18) 81 | - Use `tempfile` crate instead of `tempdir` in tests. 82 | [@alexanderkjall](https://github.com/alexanderkjall) 83 | 84 | ## [0.5.3] - 2022-02-10 85 | ### Added 86 | - `Mmap::advise` and `MmapMut::advise`. [@nyurik](https://github.com/nyurik) 87 | 88 | ## [0.5.2] - 2022-01-10 89 | ### Added 90 | - `flush`, `flush_async`, `flush_range` and `flush_async_range` to `MmapRaw` matching 91 | the corresponding methods on `MmapMut`. 92 | [@cberner](https://github.com/cberner) 93 | 94 | ## [0.5.1] - 2022-01-09 95 | ### Fixed 96 | - Explicitly call `fstat64` on Linux, emscripten and l4re targets. 97 | [@adamreichold](https://github.com/adamreichold) 98 | 99 | ## [0.5.0] - 2021-09-19 100 | ### Added 101 | - `MmapOptions` accepts any type that supports `RawHandle`/`RawFd` returning now. 102 | This allows using `memmap2` not only with Rust std types, but also with 103 | [async-std](https://github.com/async-rs/async-std) one. 104 | [@adamreichold](https://github.com/adamreichold) 105 | - (unix) Memoize page size to avoid repeatedly calling into sysconf machinery. 106 | [@adamreichold](https://github.com/adamreichold) 107 | 108 | ### Changed 109 | - (win) Use `std::os::windows::io::AsRawHandle` directly, without relying on `std::fs::File`. 110 | [@adamreichold](https://github.com/adamreichold) 111 | - Do not panic when failing to release resources in Drop impls. 112 | [@adamreichold](https://github.com/adamreichold) 113 | 114 | ## [0.4.0] - 2021-09-16 115 | ### Added 116 | - Optional [`StableDeref`](https://github.com/storyyeller/stable_deref_trait) support. 117 | [@SimonSapin](https://github.com/SimonSapin) 118 | 119 | ### Changed 120 | - Mapping of zero-sized files is no longer an error. 121 | [@SimonSapin](https://github.com/SimonSapin) 122 | - MSRV changed from 1.31 to 1.36 123 | 124 | ## [0.3.1] - 2021-08-15 125 | ### Fixed 126 | - Integer overflow during file length calculation on 32bit targets. 127 | - Stub implementation. [@Mrmaxmeier](https://github.com/Mrmaxmeier) 128 | 129 | ## [0.3.0] - 2021-06-10 130 | ### Changed 131 | - `MmapOptions` allows mapping using Unix descriptors and not only `std::fs::File` now. 132 | [@mripard](https://github.com/mripard) 133 | 134 | ## [0.2.3] - 2021-05-24 135 | ### Added 136 | - Allow compilation on unsupported platforms. 137 | The code will panic on access just like in `std`. 138 | [@jcaesar](https://github.com/jcaesar) 139 | 140 | ## [0.2.2] - 2021-04-03 141 | ### Added 142 | - `MmapOptions::populate`. [@adamreichold](https://github.com/adamreichold) 143 | 144 | ### Fixed 145 | - Fix alignment computation for `flush_async` to match `flush`. 146 | [@adamreichold](https://github.com/adamreichold) 147 | 148 | ## [0.2.1] - 2021-02-08 149 | ### Added 150 | - `MmapOptions::map_raw` and `MmapRaw`. [@diwic](https://github.com/diwic) 151 | 152 | ## [0.2.0] - 2020-12-19 153 | ### Changed 154 | - MSRV is 1.31 now (edition 2018). 155 | - Make anonymous memory maps private by default on unix. [@CensoredUsername](https://github.com/CensoredUsername) 156 | - Add `map_copy_read_only`. [@zseri](https://github.com/zseri) 157 | 158 | ## 0.1.0 - 2020-01-18 159 | ### Added 160 | - Fork [memmap-rs](https://github.com/danburkert/memmap-rs). 161 | 162 | ### Changed 163 | - Use `LICENSE-APACHE` instead of `README.md` for some tests since it's immutable. 164 | 165 | ### Removed 166 | - `winapi` dependency. [memmap-rs/pull/89](https://github.com/danburkert/memmap-rs/pull/89) 167 | 168 | [Unreleased]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.7.1...HEAD 169 | [0.7.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.7.0...v0.7.1 170 | [0.7.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.6.2...v0.7.0 171 | [0.6.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.6.1...v0.6.2 172 | [0.6.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.6.0...v0.6.1 173 | [0.6.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.10...v0.6.0 174 | [0.5.10]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.9...v0.5.10 175 | [0.5.9]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.8...v0.5.9 176 | [0.5.8]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.7...v0.5.8 177 | [0.5.7]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.6...v0.5.7 178 | [0.5.6]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.5...v0.5.6 179 | [0.5.5]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.4...v0.5.5 180 | [0.5.4]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.3...v0.5.4 181 | [0.5.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.2...v0.5.3 182 | [0.5.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.1...v0.5.2 183 | [0.5.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.0...v0.5.1 184 | [0.5.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.4.0...v0.5.0 185 | [0.4.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.1...v0.4.0 186 | [0.3.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.0...v0.3.1 187 | [0.3.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.3...v0.3.0 188 | [0.2.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.2...v0.2.3 189 | [0.2.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.1...v0.2.2 190 | [0.2.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.0...v0.2.1 191 | [0.2.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.1.0...v0.2.0 192 | -------------------------------------------------------------------------------- /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/windows.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(non_snake_case)] 3 | 4 | use std::fs::File; 5 | use std::mem::ManuallyDrop; 6 | use std::os::raw::c_void; 7 | use std::os::windows::io::{FromRawHandle, RawHandle}; 8 | use std::{io, mem, ptr}; 9 | 10 | type BOOL = i32; 11 | type WORD = u16; 12 | type DWORD = u32; 13 | type WCHAR = u16; 14 | type HANDLE = *mut c_void; 15 | type LPHANDLE = *mut HANDLE; 16 | type LPVOID = *mut c_void; 17 | type LPCVOID = *const c_void; 18 | type ULONG_PTR = usize; 19 | type SIZE_T = ULONG_PTR; 20 | type LPCWSTR = *const WCHAR; 21 | type PDWORD = *mut DWORD; 22 | type DWORD_PTR = ULONG_PTR; 23 | type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; 24 | type LPSYSTEM_INFO = *mut SYSTEM_INFO; 25 | 26 | const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE; 27 | 28 | const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002; 29 | 30 | const STANDARD_RIGHTS_REQUIRED: DWORD = 0x000F0000; 31 | 32 | const SECTION_QUERY: DWORD = 0x0001; 33 | const SECTION_MAP_WRITE: DWORD = 0x0002; 34 | const SECTION_MAP_READ: DWORD = 0x0004; 35 | const SECTION_MAP_EXECUTE: DWORD = 0x0008; 36 | const SECTION_EXTEND_SIZE: DWORD = 0x0010; 37 | const SECTION_MAP_EXECUTE_EXPLICIT: DWORD = 0x0020; 38 | const SECTION_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED 39 | | SECTION_QUERY 40 | | SECTION_MAP_WRITE 41 | | SECTION_MAP_READ 42 | | SECTION_MAP_EXECUTE 43 | | SECTION_EXTEND_SIZE; 44 | 45 | const PAGE_READONLY: DWORD = 0x02; 46 | const PAGE_READWRITE: DWORD = 0x04; 47 | const PAGE_WRITECOPY: DWORD = 0x08; 48 | const PAGE_EXECUTE_READ: DWORD = 0x20; 49 | const PAGE_EXECUTE_READWRITE: DWORD = 0x40; 50 | const PAGE_EXECUTE_WRITECOPY: DWORD = 0x80; 51 | 52 | const FILE_MAP_WRITE: DWORD = SECTION_MAP_WRITE; 53 | const FILE_MAP_READ: DWORD = SECTION_MAP_READ; 54 | const FILE_MAP_ALL_ACCESS: DWORD = SECTION_ALL_ACCESS; 55 | const FILE_MAP_EXECUTE: DWORD = SECTION_MAP_EXECUTE_EXPLICIT; 56 | const FILE_MAP_COPY: DWORD = 0x00000001; 57 | 58 | #[repr(C)] 59 | struct SECURITY_ATTRIBUTES { 60 | nLength: DWORD, 61 | lpSecurityDescriptor: LPVOID, 62 | bInheritHandle: BOOL, 63 | } 64 | 65 | #[repr(C)] 66 | struct SYSTEM_INFO { 67 | wProcessorArchitecture: WORD, 68 | wReserved: WORD, 69 | dwPageSize: DWORD, 70 | lpMinimumApplicationAddress: LPVOID, 71 | lpMaximumApplicationAddress: LPVOID, 72 | dwActiveProcessorMask: DWORD_PTR, 73 | dwNumberOfProcessors: DWORD, 74 | dwProcessorType: DWORD, 75 | dwAllocationGranularity: DWORD, 76 | wProcessorLevel: WORD, 77 | wProcessorRevision: WORD, 78 | } 79 | 80 | #[repr(C)] 81 | #[derive(Copy, Clone)] 82 | pub struct FILETIME { 83 | pub dwLowDateTime: DWORD, 84 | pub dwHighDateTime: DWORD, 85 | } 86 | 87 | extern "system" { 88 | fn GetCurrentProcess() -> HANDLE; 89 | 90 | fn CloseHandle(hObject: HANDLE) -> BOOL; 91 | 92 | fn DuplicateHandle( 93 | hSourceProcessHandle: HANDLE, 94 | hSourceHandle: HANDLE, 95 | hTargetProcessHandle: HANDLE, 96 | lpTargetHandle: LPHANDLE, 97 | dwDesiredAccess: DWORD, 98 | bInheritHandle: BOOL, 99 | dwOptions: DWORD, 100 | ) -> BOOL; 101 | 102 | fn CreateFileMappingW( 103 | hFile: HANDLE, 104 | lpFileMappingAttributes: LPSECURITY_ATTRIBUTES, 105 | flProtect: DWORD, 106 | dwMaximumSizeHigh: DWORD, 107 | dwMaximumSizeLow: DWORD, 108 | lpName: LPCWSTR, 109 | ) -> HANDLE; 110 | 111 | fn FlushFileBuffers(hFile: HANDLE) -> BOOL; 112 | 113 | fn FlushViewOfFile(lpBaseAddress: LPCVOID, dwNumberOfBytesToFlush: SIZE_T) -> BOOL; 114 | 115 | fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL; 116 | 117 | fn MapViewOfFile( 118 | hFileMappingObject: HANDLE, 119 | dwDesiredAccess: DWORD, 120 | dwFileOffsetHigh: DWORD, 121 | dwFileOffsetLow: DWORD, 122 | dwNumberOfBytesToMap: SIZE_T, 123 | ) -> LPVOID; 124 | 125 | fn VirtualProtect( 126 | lpAddress: LPVOID, 127 | dwSize: SIZE_T, 128 | flNewProtect: DWORD, 129 | lpflOldProtect: PDWORD, 130 | ) -> BOOL; 131 | 132 | fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO); 133 | } 134 | 135 | /// Returns a fixed aligned pointer that is valid for `slice::from_raw_parts::` with `len == 0`. 136 | /// 137 | /// This aligns the pointer to `allocation_granularity()` or 1 if unknown. 138 | fn empty_slice_ptr() -> *mut c_void { 139 | let align = allocation_granularity().max(1); 140 | unsafe { mem::transmute(align) } 141 | } 142 | 143 | pub struct MmapInner { 144 | handle: Option, 145 | ptr: *mut c_void, 146 | len: usize, 147 | copy: bool, 148 | } 149 | 150 | impl MmapInner { 151 | /// Creates a new `MmapInner`. 152 | /// 153 | /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls. 154 | pub fn new( 155 | handle: RawHandle, 156 | protect: DWORD, 157 | access: DWORD, 158 | offset: u64, 159 | len: usize, 160 | copy: bool, 161 | ) -> io::Result { 162 | let alignment = offset % allocation_granularity() as u64; 163 | let aligned_offset = offset - alignment as u64; 164 | let aligned_len = len + alignment as usize; 165 | if aligned_len == 0 { 166 | // `CreateFileMappingW` documents: 167 | // 168 | // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw 169 | // > An attempt to map a file with a length of 0 (zero) fails with an error code 170 | // > of ERROR_FILE_INVALID. Applications should test for files with a length of 0 171 | // > (zero) and reject those files. 172 | // 173 | // For such files, don’t create a mapping at all and use a marker pointer instead. 174 | return Ok(MmapInner { 175 | handle: None, 176 | ptr: empty_slice_ptr(), 177 | len: 0, 178 | copy, 179 | }); 180 | } 181 | 182 | unsafe { 183 | let mapping = CreateFileMappingW(handle, ptr::null_mut(), protect, 0, 0, ptr::null()); 184 | if mapping.is_null() { 185 | return Err(io::Error::last_os_error()); 186 | } 187 | 188 | let ptr = MapViewOfFile( 189 | mapping, 190 | access, 191 | (aligned_offset >> 16 >> 16) as DWORD, 192 | (aligned_offset & 0xffffffff) as DWORD, 193 | aligned_len as SIZE_T, 194 | ); 195 | CloseHandle(mapping); 196 | if ptr.is_null() { 197 | return Err(io::Error::last_os_error()); 198 | } 199 | 200 | let mut new_handle = 0 as RawHandle; 201 | let cur_proc = GetCurrentProcess(); 202 | let ok = DuplicateHandle( 203 | cur_proc, 204 | handle, 205 | cur_proc, 206 | &mut new_handle, 207 | 0, 208 | 0, 209 | DUPLICATE_SAME_ACCESS, 210 | ); 211 | if ok == 0 { 212 | UnmapViewOfFile(ptr); 213 | return Err(io::Error::last_os_error()); 214 | } 215 | 216 | Ok(MmapInner { 217 | handle: Some(new_handle), 218 | ptr: ptr.offset(alignment as isize), 219 | len: len as usize, 220 | copy, 221 | }) 222 | } 223 | } 224 | 225 | pub fn map( 226 | len: usize, 227 | handle: RawHandle, 228 | offset: u64, 229 | _populate: bool, 230 | ) -> io::Result { 231 | let write = protection_supported(handle, PAGE_READWRITE); 232 | let exec = protection_supported(handle, PAGE_EXECUTE_READ); 233 | let mut access = FILE_MAP_READ; 234 | let protection = match (write, exec) { 235 | (true, true) => { 236 | access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE; 237 | PAGE_EXECUTE_READWRITE 238 | } 239 | (true, false) => { 240 | access |= FILE_MAP_WRITE; 241 | PAGE_READWRITE 242 | } 243 | (false, true) => { 244 | access |= FILE_MAP_EXECUTE; 245 | PAGE_EXECUTE_READ 246 | } 247 | (false, false) => PAGE_READONLY, 248 | }; 249 | 250 | let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?; 251 | if write || exec { 252 | inner.make_read_only()?; 253 | } 254 | Ok(inner) 255 | } 256 | 257 | pub fn map_exec( 258 | len: usize, 259 | handle: RawHandle, 260 | offset: u64, 261 | _populate: bool, 262 | ) -> io::Result { 263 | let write = protection_supported(handle, PAGE_READWRITE); 264 | let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE; 265 | let protection = if write { 266 | access |= FILE_MAP_WRITE; 267 | PAGE_EXECUTE_READWRITE 268 | } else { 269 | PAGE_EXECUTE_READ 270 | }; 271 | 272 | let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?; 273 | if write { 274 | inner.make_exec()?; 275 | } 276 | Ok(inner) 277 | } 278 | 279 | pub fn map_mut( 280 | len: usize, 281 | handle: RawHandle, 282 | offset: u64, 283 | _populate: bool, 284 | ) -> io::Result { 285 | let exec = protection_supported(handle, PAGE_EXECUTE_READ); 286 | let mut access = FILE_MAP_READ | FILE_MAP_WRITE; 287 | let protection = if exec { 288 | access |= FILE_MAP_EXECUTE; 289 | PAGE_EXECUTE_READWRITE 290 | } else { 291 | PAGE_READWRITE 292 | }; 293 | 294 | let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?; 295 | if exec { 296 | inner.make_mut()?; 297 | } 298 | Ok(inner) 299 | } 300 | 301 | pub fn map_copy( 302 | len: usize, 303 | handle: RawHandle, 304 | offset: u64, 305 | _populate: bool, 306 | ) -> io::Result { 307 | let exec = protection_supported(handle, PAGE_EXECUTE_READWRITE); 308 | let mut access = FILE_MAP_COPY; 309 | let protection = if exec { 310 | access |= FILE_MAP_EXECUTE; 311 | PAGE_EXECUTE_WRITECOPY 312 | } else { 313 | PAGE_WRITECOPY 314 | }; 315 | 316 | let mut inner = MmapInner::new(handle, protection, access, offset, len, true)?; 317 | if exec { 318 | inner.make_mut()?; 319 | } 320 | Ok(inner) 321 | } 322 | 323 | pub fn map_copy_read_only( 324 | len: usize, 325 | handle: RawHandle, 326 | offset: u64, 327 | _populate: bool, 328 | ) -> io::Result { 329 | let write = protection_supported(handle, PAGE_READWRITE); 330 | let exec = protection_supported(handle, PAGE_EXECUTE_READ); 331 | let mut access = FILE_MAP_COPY; 332 | let protection = if exec { 333 | access |= FILE_MAP_EXECUTE; 334 | PAGE_EXECUTE_WRITECOPY 335 | } else { 336 | PAGE_WRITECOPY 337 | }; 338 | 339 | let mut inner = MmapInner::new(handle, protection, access, offset, len, true)?; 340 | if write || exec { 341 | inner.make_read_only()?; 342 | } 343 | Ok(inner) 344 | } 345 | 346 | pub fn map_anon(len: usize, _stack: bool, _populate: bool) -> io::Result { 347 | // Ensure a non-zero length for the underlying mapping 348 | let mapped_len = len.max(1); 349 | unsafe { 350 | // Create a mapping and view with maximum access permissions, then use `VirtualProtect` 351 | // to set the actual `Protection`. This way, we can set more permissive protection later 352 | // on. 353 | // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx 354 | 355 | let mapping = CreateFileMappingW( 356 | INVALID_HANDLE_VALUE, 357 | ptr::null_mut(), 358 | PAGE_EXECUTE_READWRITE, 359 | (mapped_len >> 16 >> 16) as DWORD, 360 | (mapped_len & 0xffffffff) as DWORD, 361 | ptr::null(), 362 | ); 363 | if mapping.is_null() { 364 | return Err(io::Error::last_os_error()); 365 | } 366 | let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE; 367 | let ptr = MapViewOfFile(mapping, access, 0, 0, mapped_len as SIZE_T); 368 | CloseHandle(mapping); 369 | 370 | if ptr.is_null() { 371 | return Err(io::Error::last_os_error()); 372 | } 373 | 374 | let mut old = 0; 375 | let result = VirtualProtect(ptr, mapped_len as SIZE_T, PAGE_READWRITE, &mut old); 376 | if result != 0 { 377 | Ok(MmapInner { 378 | handle: None, 379 | ptr, 380 | len: len as usize, 381 | copy: false, 382 | }) 383 | } else { 384 | Err(io::Error::last_os_error()) 385 | } 386 | } 387 | } 388 | 389 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { 390 | self.flush_async(offset, len)?; 391 | 392 | if let Some(handle) = self.handle { 393 | let ok = unsafe { FlushFileBuffers(handle) }; 394 | if ok == 0 { 395 | return Err(io::Error::last_os_error()); 396 | } 397 | } 398 | 399 | Ok(()) 400 | } 401 | 402 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { 403 | if self.ptr == empty_slice_ptr() { 404 | return Ok(()); 405 | } 406 | let result = unsafe { FlushViewOfFile(self.ptr.add(offset), len as SIZE_T) }; 407 | if result != 0 { 408 | Ok(()) 409 | } else { 410 | Err(io::Error::last_os_error()) 411 | } 412 | } 413 | 414 | fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { 415 | if self.ptr == empty_slice_ptr() { 416 | return Ok(()); 417 | } 418 | unsafe { 419 | let alignment = self.ptr as usize % allocation_granularity(); 420 | let ptr = self.ptr.offset(-(alignment as isize)); 421 | let aligned_len = self.len as SIZE_T + alignment as SIZE_T; 422 | 423 | let mut old = 0; 424 | let result = VirtualProtect(ptr, aligned_len, protect, &mut old); 425 | 426 | if result != 0 { 427 | Ok(()) 428 | } else { 429 | Err(io::Error::last_os_error()) 430 | } 431 | } 432 | } 433 | 434 | pub fn make_read_only(&mut self) -> io::Result<()> { 435 | self.virtual_protect(PAGE_READONLY) 436 | } 437 | 438 | pub fn make_exec(&mut self) -> io::Result<()> { 439 | if self.copy { 440 | self.virtual_protect(PAGE_EXECUTE_WRITECOPY) 441 | } else { 442 | self.virtual_protect(PAGE_EXECUTE_READ) 443 | } 444 | } 445 | 446 | pub fn make_mut(&mut self) -> io::Result<()> { 447 | if self.copy { 448 | self.virtual_protect(PAGE_WRITECOPY) 449 | } else { 450 | self.virtual_protect(PAGE_READWRITE) 451 | } 452 | } 453 | 454 | #[inline] 455 | pub fn ptr(&self) -> *const u8 { 456 | self.ptr as *const u8 457 | } 458 | 459 | #[inline] 460 | pub fn mut_ptr(&mut self) -> *mut u8 { 461 | self.ptr as *mut u8 462 | } 463 | 464 | #[inline] 465 | pub fn len(&self) -> usize { 466 | self.len 467 | } 468 | } 469 | 470 | impl Drop for MmapInner { 471 | fn drop(&mut self) { 472 | if self.ptr == empty_slice_ptr() { 473 | return; 474 | } 475 | let alignment = self.ptr as usize % allocation_granularity(); 476 | // Any errors during unmapping/closing are ignored as the only way 477 | // to report them would be through panicking which is highly discouraged 478 | // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97 479 | unsafe { 480 | let ptr = self.ptr.offset(-(alignment as isize)); 481 | UnmapViewOfFile(ptr); 482 | 483 | if let Some(handle) = self.handle { 484 | CloseHandle(handle); 485 | } 486 | } 487 | } 488 | } 489 | 490 | unsafe impl Sync for MmapInner {} 491 | unsafe impl Send for MmapInner {} 492 | 493 | fn protection_supported(handle: RawHandle, protection: DWORD) -> bool { 494 | unsafe { 495 | let mapping = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null()); 496 | if mapping.is_null() { 497 | return false; 498 | } 499 | CloseHandle(mapping); 500 | true 501 | } 502 | } 503 | 504 | fn allocation_granularity() -> usize { 505 | unsafe { 506 | let mut info = mem::zeroed(); 507 | GetSystemInfo(&mut info); 508 | info.dwAllocationGranularity as usize 509 | } 510 | } 511 | 512 | pub fn file_len(handle: RawHandle) -> io::Result { 513 | // SAFETY: We must not close the passed-in fd by dropping the File we create, 514 | // we ensure this by immediately wrapping it in a ManuallyDrop. 515 | unsafe { 516 | let file = ManuallyDrop::new(File::from_raw_handle(handle)); 517 | Ok(file.metadata()?.len()) 518 | } 519 | } 520 | -------------------------------------------------------------------------------- /src/unix.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::io::RawFd; 2 | use std::sync::atomic::{AtomicUsize, Ordering}; 3 | use std::{io, ptr}; 4 | 5 | pub use rustix::mm::Advice; 6 | 7 | #[cfg(target_os = "linux")] 8 | pub use rustix::mm::MremapFlags; 9 | 10 | const MAP_ZERO: rustix::mm::MapFlags = rustix::mm::MapFlags::empty(); 11 | 12 | #[cfg(not(any( 13 | target_os = "dragonfly", 14 | target_os = "illumos", 15 | target_os = "ios", 16 | target_os = "macos", 17 | target_os = "netbsd", 18 | target_os = "redox", 19 | )))] 20 | const MAP_STACK: rustix::mm::MapFlags = rustix::mm::MapFlags::STACK; 21 | 22 | #[cfg(any( 23 | target_os = "dragonfly", 24 | target_os = "illumos", 25 | target_os = "ios", 26 | target_os = "macos", 27 | target_os = "netbsd", 28 | target_os = "redox", 29 | ))] 30 | const MAP_STACK: rustix::mm::MapFlags = MAP_ZERO; 31 | 32 | #[cfg(not(any( 33 | target_os = "dragonfly", 34 | target_os = "freebsd", 35 | target_os = "illumos", 36 | target_os = "ios", 37 | target_os = "macos", 38 | target_os = "netbsd", 39 | target_os = "openbsd", 40 | target_os = "redox", 41 | )))] 42 | const MAP_POPULATE: rustix::mm::MapFlags = rustix::mm::MapFlags::POPULATE; 43 | 44 | #[cfg(any( 45 | target_os = "dragonfly", 46 | target_os = "freebsd", 47 | target_os = "illumos", 48 | target_os = "ios", 49 | target_os = "macos", 50 | target_os = "netbsd", 51 | target_os = "openbsd", 52 | target_os = "redox", 53 | ))] 54 | const MAP_POPULATE: rustix::mm::MapFlags = MAP_ZERO; 55 | 56 | pub struct MmapInner { 57 | ptr: *mut core::ffi::c_void, 58 | len: usize, 59 | } 60 | 61 | impl MmapInner { 62 | /// Creates a new `MmapInner`. 63 | /// 64 | /// This is a thin wrapper around the `mmap` system call. 65 | fn new( 66 | len: usize, 67 | prot: rustix::mm::ProtFlags, 68 | flags: rustix::mm::MapFlags, 69 | file: RawFd, 70 | offset: u64, 71 | ) -> io::Result { 72 | let alignment = offset % page_size() as u64; 73 | let aligned_offset = offset - alignment; 74 | 75 | let (map_len, map_offset) = Self::adjust_mmap_params(len, alignment as usize)?; 76 | 77 | unsafe { 78 | let ptr = rustix::mm::mmap( 79 | ptr::null_mut(), 80 | map_len, 81 | prot, 82 | flags, 83 | rustix::fd::BorrowedFd::borrow_raw(file), 84 | aligned_offset, 85 | ); 86 | 87 | match ptr { 88 | Ok(ptr) => Ok(Self::from_raw_parts(ptr, len, map_offset)), 89 | Err(e) => Err(io::Error::from_raw_os_error(e.raw_os_error())), 90 | } 91 | } 92 | } 93 | 94 | fn adjust_mmap_params(len: usize, alignment: usize) -> io::Result<(usize, usize)> { 95 | use std::isize; 96 | 97 | // Rust's slice cannot be larger than isize::MAX. 98 | // See https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html 99 | // 100 | // This is not a problem on 64-bit targets, but on 32-bit one 101 | // having a file or an anonymous mapping larger than 2GB is quite normal 102 | // and we have to prevent it. 103 | // 104 | // The code below is essentially the same as in Rust's std: 105 | // https://github.com/rust-lang/rust/blob/db78ab70a88a0a5e89031d7ee4eccec835dcdbde/library/alloc/src/raw_vec.rs#L495 106 | if std::mem::size_of::() < 8 && len > isize::MAX as usize { 107 | return Err(io::Error::new( 108 | io::ErrorKind::InvalidData, 109 | "memory map length overflows isize", 110 | )); 111 | } 112 | 113 | let map_len = len + alignment; 114 | let map_offset = alignment; 115 | 116 | // `libc::mmap` does not support zero-size mappings. POSIX defines: 117 | // 118 | // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html 119 | // > If `len` is zero, `mmap()` shall fail and no mapping shall be established. 120 | // 121 | // So if we would create such a mapping, crate a one-byte mapping instead: 122 | let map_len = map_len.max(1); 123 | 124 | // Note that in that case `MmapInner::len` is still set to zero, 125 | // and `Mmap` will still dereferences to an empty slice. 126 | // 127 | // If this mapping is backed by an empty file, we create a mapping larger than the file. 128 | // This is unusual but well-defined. On the same man page, POSIX further defines: 129 | // 130 | // > The `mmap()` function can be used to map a region of memory that is larger 131 | // > than the current size of the object. 132 | // 133 | // (The object here is the file.) 134 | // 135 | // > Memory access within the mapping but beyond the current end of the underlying 136 | // > objects may result in SIGBUS signals being sent to the process. The reason for this 137 | // > is that the size of the object can be manipulated by other processes and can change 138 | // > at any moment. The implementation should tell the application that a memory reference 139 | // > is outside the object where this can be detected; otherwise, written data may be lost 140 | // > and read data may not reflect actual data in the object. 141 | // 142 | // Because `MmapInner::len` is not incremented, this increment of `aligned_len` 143 | // will not allow accesses past the end of the file and will not cause SIGBUS. 144 | // 145 | // (SIGBUS is still possible by mapping a non-empty file and then truncating it 146 | // to a shorter size, but that is unrelated to this handling of empty files.) 147 | Ok((map_len, map_offset)) 148 | } 149 | 150 | /// Get the current memory mapping as a `(ptr, map_len, offset)` tuple. 151 | /// 152 | /// Note that `map_len` is the length of the memory mapping itself and 153 | /// _not_ the one that would be passed to `from_raw_parts`. 154 | fn as_mmap_params(&self) -> (*mut core::ffi::c_void, usize, usize) { 155 | let offset = self.ptr as usize % page_size(); 156 | let len = self.len + offset; 157 | 158 | // There are two possible memory layouts we could have, depending on 159 | // the length and offset passed when constructing this instance: 160 | // 161 | // 1. The "normal" memory layout looks like this: 162 | // 163 | // |<------------------>|<---------------------->| 164 | // mmap ptr offset ptr public slice 165 | // 166 | // That is, we have 167 | // - The start of the page-aligned memory mapping returned by mmap, 168 | // followed by, 169 | // - Some number of bytes that are memory mapped but ignored since 170 | // they are before the byte offset requested by the user, followed 171 | // by, 172 | // - The actual memory mapped slice requested by the user. 173 | // 174 | // This maps cleanly to a (ptr, len, offset) tuple. 175 | // 176 | // 2. Then, we have the case where the user requested a zero-length 177 | // memory mapping. mmap(2) does not support zero-length mappings so 178 | // this crate works around that by actually making a mapping of 179 | // length one. This means that we have 180 | // - A length zero slice, followed by, 181 | // - A single memory mapped byte 182 | // 183 | // Note that this only happens if the offset within the page is also 184 | // zero. Otherwise, we have a memory map of offset bytes and not a 185 | // zero-length memory map. 186 | // 187 | // This doesn't fit cleanly into a (ptr, len, offset) tuple. Instead, 188 | // we fudge it slightly: a zero-length memory map turns into a 189 | // mapping of length one and can't be told apart outside of this 190 | // method without knowing the original length. 191 | if len == 0 { 192 | (self.ptr, 1, 0) 193 | } else { 194 | (unsafe { self.ptr.offset(-(offset as isize)) }, len, offset) 195 | } 196 | } 197 | 198 | /// Construct this `MmapInner` from its raw components 199 | /// 200 | /// # Safety 201 | /// 202 | /// - `ptr` must point to the start of memory mapping that can be freed 203 | /// using `munmap(2)` (i.e. returned by `mmap(2)` or `mremap(2)`) 204 | /// - The memory mapping at `ptr` must have a length of `len + offset`. 205 | /// - If `len + offset == 0` then the memory mapping must be of length 1. 206 | /// - `offset` must be less than the current page size. 207 | unsafe fn from_raw_parts(ptr: *mut core::ffi::c_void, len: usize, offset: usize) -> Self { 208 | debug_assert_eq!(ptr as usize % page_size(), 0, "ptr not page-aligned"); 209 | debug_assert!(offset < page_size(), "offset larger than page size"); 210 | 211 | Self { 212 | ptr: ptr.add(offset), 213 | len, 214 | } 215 | } 216 | 217 | fn new_anon(len: usize, stack: bool, populate: bool) -> io::Result { 218 | let stack = if stack { MAP_STACK } else { MAP_ZERO }; 219 | let populate = if populate { MAP_POPULATE } else { MAP_ZERO }; 220 | let ptr = unsafe { 221 | rustix::mm::mmap_anonymous( 222 | ptr::null_mut(), 223 | len.max(1), 224 | rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE, 225 | rustix::mm::MapFlags::PRIVATE | stack | populate, 226 | ) 227 | }; 228 | 229 | match ptr { 230 | Ok(ptr) => Ok(MmapInner { ptr, len }), 231 | Err(e) => Err(io::Error::from_raw_os_error(e.raw_os_error())), 232 | } 233 | } 234 | 235 | #[inline] 236 | pub fn lock(&self) -> io::Result<()> { 237 | unsafe { 238 | rustix::mm::mlock(self.ptr, self.len) 239 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 240 | } 241 | } 242 | 243 | #[inline] 244 | pub fn unlock(&self) -> io::Result<()> { 245 | unsafe { 246 | rustix::mm::munlock(self.ptr, self.len) 247 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 248 | } 249 | } 250 | 251 | #[inline] 252 | pub fn lock_segment(&self, data_size: usize, offset: usize) -> io::Result<()> { 253 | let alignment = (self.ptr as usize + offset) % page_size(); 254 | let offset = offset as isize - alignment as isize; 255 | let len = self.len.min(data_size) + alignment; 256 | 257 | unsafe { rustix::mm::mlock(self.ptr.offset(offset), len.min(data_size)) } 258 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 259 | } 260 | 261 | #[inline] 262 | pub fn unlock_segment(&self, data_size: usize, offset: usize) -> io::Result<()> { 263 | let alignment = (self.ptr as usize + offset) % page_size(); 264 | let offset = offset as isize - alignment as isize; 265 | let len = self.len.min(data_size) + alignment; 266 | 267 | unsafe { rustix::mm::munlock(self.ptr.offset(offset), len) } 268 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 269 | } 270 | 271 | pub fn map(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 272 | let populate = if populate { MAP_POPULATE } else { MAP_ZERO }; 273 | MmapInner::new( 274 | len, 275 | rustix::mm::ProtFlags::READ, 276 | rustix::mm::MapFlags::SHARED | populate, 277 | file, 278 | offset, 279 | ) 280 | } 281 | 282 | pub fn map_exec(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 283 | let populate = if populate { MAP_POPULATE } else { MAP_ZERO }; 284 | MmapInner::new( 285 | len, 286 | rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::EXEC, 287 | rustix::mm::MapFlags::SHARED | populate, 288 | file, 289 | offset, 290 | ) 291 | } 292 | 293 | pub fn map_mut(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 294 | let populate = if populate { MAP_POPULATE } else { MAP_ZERO }; 295 | MmapInner::new( 296 | len, 297 | rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE, 298 | rustix::mm::MapFlags::SHARED | populate, 299 | file, 300 | offset, 301 | ) 302 | } 303 | 304 | pub fn map_copy(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 305 | let populate = if populate { MAP_POPULATE } else { MAP_ZERO }; 306 | MmapInner::new( 307 | len, 308 | rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE, 309 | rustix::mm::MapFlags::PRIVATE | populate, 310 | file, 311 | offset, 312 | ) 313 | } 314 | 315 | pub fn map_copy_read_only( 316 | len: usize, 317 | file: RawFd, 318 | offset: u64, 319 | populate: bool, 320 | ) -> io::Result { 321 | let populate = if populate { MAP_POPULATE } else { MAP_ZERO }; 322 | MmapInner::new( 323 | len, 324 | rustix::mm::ProtFlags::READ, 325 | rustix::mm::MapFlags::PRIVATE | populate, 326 | file, 327 | offset, 328 | ) 329 | } 330 | 331 | /// Open an anonymous memory map. 332 | #[inline] 333 | pub fn map_anon(len: usize, stack: bool, populate: bool) -> io::Result { 334 | MmapInner::new_anon(len, stack, populate) 335 | } 336 | 337 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { 338 | let alignment = (self.ptr as usize + offset) % page_size(); 339 | let offset = offset as isize - alignment as isize; 340 | let len = len + alignment; 341 | unsafe { rustix::mm::msync(self.ptr.offset(offset), len, rustix::mm::MsyncFlags::SYNC) } 342 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 343 | } 344 | 345 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { 346 | let alignment = (self.ptr as usize + offset) % page_size(); 347 | let offset = offset as isize - alignment as isize; 348 | let len = len + alignment; 349 | unsafe { rustix::mm::msync(self.ptr.offset(offset), len, rustix::mm::MsyncFlags::ASYNC) } 350 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 351 | } 352 | 353 | fn mprotect(&mut self, prot: rustix::mm::MprotectFlags) -> io::Result<()> { 354 | unsafe { 355 | let alignment = self.ptr as usize % page_size(); 356 | let ptr = self.ptr.offset(-(alignment as isize)); 357 | let len = self.len + alignment; 358 | let len = len.max(1); 359 | rustix::mm::mprotect(ptr, len, prot) 360 | .map_err(|e| io::Error::from_raw_os_error(e.raw_os_error())) 361 | } 362 | } 363 | 364 | pub fn make_read_only(&mut self) -> io::Result<()> { 365 | self.mprotect(rustix::mm::MprotectFlags::READ) 366 | } 367 | 368 | pub fn make_exec(&mut self) -> io::Result<()> { 369 | self.mprotect(rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::EXEC) 370 | } 371 | 372 | pub fn make_mut(&mut self) -> io::Result<()> { 373 | self.mprotect(rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE) 374 | } 375 | 376 | #[inline] 377 | pub fn ptr(&self) -> *const u8 { 378 | self.ptr as *const u8 379 | } 380 | 381 | #[inline] 382 | pub fn mut_ptr(&mut self) -> *mut u8 { 383 | self.ptr as *mut u8 384 | } 385 | 386 | #[inline] 387 | pub fn len(&self) -> usize { 388 | self.len 389 | } 390 | 391 | pub fn advise(&self, advice: Advice, offset: usize, len: usize) -> io::Result<()> { 392 | let alignment = (self.ptr as usize + offset) % page_size(); 393 | let offset = offset as isize - alignment as isize; 394 | let len = len + alignment; 395 | 396 | unsafe { 397 | match rustix::mm::madvise(self.ptr.offset(offset), len, advice) { 398 | Ok(_) => Ok(()), 399 | Err(e) => Err(io::Error::from_raw_os_error(e.raw_os_error())), 400 | } 401 | } 402 | } 403 | 404 | #[cfg(target_os = "linux")] 405 | pub fn remap(&mut self, new_len: usize, options: MremapFlags) -> io::Result<()> { 406 | let (old_ptr, old_len, offset) = self.as_mmap_params(); 407 | let (map_len, offset) = Self::adjust_mmap_params(new_len, offset)?; 408 | 409 | unsafe { 410 | match rustix::mm::mremap(old_ptr, old_len, map_len, options) { 411 | Ok(new_ptr) => { 412 | // We explicitly don't drop self since the pointer within is no longer valid. 413 | ptr::write(self, Self::from_raw_parts(new_ptr, new_len, offset)); 414 | Ok(()) 415 | } 416 | Err(e) => { 417 | // We don't drop self since the pointer within is still valid. 418 | Err(io::Error::from_raw_os_error(e.raw_os_error())) 419 | } 420 | } 421 | } 422 | } 423 | } 424 | 425 | impl Drop for MmapInner { 426 | fn drop(&mut self) { 427 | let (ptr, len, _) = self.as_mmap_params(); 428 | 429 | // Any errors during unmapping/closing are ignored as the only way 430 | // to report them would be through panicking which is highly discouraged 431 | // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97 432 | unsafe { 433 | let _ = rustix::mm::munmap(ptr, len); 434 | } 435 | } 436 | } 437 | 438 | unsafe impl Sync for MmapInner {} 439 | unsafe impl Send for MmapInner {} 440 | 441 | fn page_size() -> usize { 442 | static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); 443 | 444 | match PAGE_SIZE.load(Ordering::Relaxed) { 445 | 0 => { 446 | let page_size = rustix::param::page_size(); 447 | 448 | PAGE_SIZE.store(page_size, Ordering::Relaxed); 449 | 450 | page_size 451 | } 452 | page_size => page_size, 453 | } 454 | } 455 | 456 | pub fn file_len(file: RawFd) -> io::Result { 457 | use rustix::fs::fstat; 458 | 459 | let borrowed_fd: rustix::fd::BorrowedFd<'_> = 460 | unsafe { rustix::fd::BorrowedFd::borrow_raw(file) }; 461 | match fstat(borrowed_fd) { 462 | Ok(stat) => Ok(stat.st_size as u64), 463 | Err(e) => Err(io::Error::from_raw_os_error(e.raw_os_error())), 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A pure Rust cross-platform Rust API for memory mapped buffers. 2 | //! 3 | //! The core functionality is provided by either [`Mmap`] or [`MmapMut`], 4 | //! which correspond to mapping a [`File`] to a [`&[u8]`](https://doc.rust-lang.org/std/primitive.slice.html) 5 | //! or [`&mut [u8]`](https://doc.rust-lang.org/std/primitive.slice.html) 6 | //! respectively. Both function by dereferencing to a slice, allowing the 7 | //! [`Mmap`]/[`MmapMut`] to be used in the same way you would the equivalent slice 8 | //! types. 9 | //! 10 | //! [`File`]: std::fs::File 11 | //! 12 | //! # Examples 13 | //! 14 | //! For simple cases [`Mmap`] can be used directly: 15 | //! 16 | //! ``` 17 | //! use std::fs::File; 18 | //! use std::io::Read; 19 | //! 20 | //! use memmapix::Mmap; 21 | //! 22 | //! # fn main() -> std::io::Result<()> { 23 | //! let mut file = File::open("LICENSE-APACHE")?; 24 | //! 25 | //! let mut contents = Vec::new(); 26 | //! file.read_to_end(&mut contents)?; 27 | //! 28 | //! let mmap = unsafe { Mmap::map(&file)? }; 29 | //! 30 | //! assert_eq!(&contents[..], &mmap[..]); 31 | //! # Ok(()) 32 | //! # } 33 | //! ``` 34 | //! 35 | //! However for cases which require configuration of the mapping, then 36 | //! you can use [`MmapOptions`] in order to further configure a mapping 37 | //! before you create it. 38 | 39 | #![allow(clippy::len_without_is_empty, clippy::missing_safety_doc)] 40 | #![cfg_attr(docsrs, feature(doc_cfg))] 41 | #![cfg_attr(docsrs, allow(unused_attributes))] 42 | 43 | #[cfg_attr(unix, path = "unix.rs")] 44 | #[cfg_attr(windows, path = "windows.rs")] 45 | #[cfg_attr(not(any(unix, windows)), path = "stub.rs")] 46 | mod os; 47 | use crate::os::{file_len, MmapInner}; 48 | 49 | #[cfg(unix)] 50 | #[cfg_attr(docsrs, doc(cfg(unix)))] 51 | pub use crate::os::Advice; 52 | #[cfg(target_os = "linux")] 53 | #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))] 54 | pub use crate::os::MremapFlags; 55 | 56 | use std::fmt; 57 | #[cfg(not(any(unix, windows)))] 58 | use std::fs::File; 59 | use std::io::{Error, ErrorKind, Result}; 60 | use std::isize; 61 | use std::mem; 62 | use std::ops::{Deref, DerefMut}; 63 | #[cfg(unix)] 64 | use std::os::unix::io::{AsRawFd, RawFd}; 65 | #[cfg(windows)] 66 | use std::os::windows::io::{AsRawHandle, RawHandle}; 67 | use std::slice; 68 | 69 | #[cfg(not(any(unix, windows)))] 70 | pub struct MmapRawDescriptor<'a>(&'a File); 71 | 72 | #[cfg(unix)] 73 | pub struct MmapRawDescriptor(RawFd); 74 | 75 | #[cfg(windows)] 76 | pub struct MmapRawDescriptor(RawHandle); 77 | 78 | pub trait MmapAsRawDesc { 79 | fn as_raw_desc(&self) -> MmapRawDescriptor; 80 | } 81 | 82 | #[cfg(not(any(unix, windows)))] 83 | impl MmapAsRawDesc for &File { 84 | fn as_raw_desc(&self) -> MmapRawDescriptor { 85 | MmapRawDescriptor(self) 86 | } 87 | } 88 | 89 | #[cfg(unix)] 90 | impl MmapAsRawDesc for RawFd { 91 | fn as_raw_desc(&self) -> MmapRawDescriptor { 92 | MmapRawDescriptor(*self) 93 | } 94 | } 95 | 96 | #[cfg(unix)] 97 | impl<'a, T> MmapAsRawDesc for &'a T 98 | where 99 | T: AsRawFd, 100 | { 101 | fn as_raw_desc(&self) -> MmapRawDescriptor { 102 | MmapRawDescriptor(self.as_raw_fd()) 103 | } 104 | } 105 | 106 | #[cfg(windows)] 107 | impl MmapAsRawDesc for RawHandle { 108 | fn as_raw_desc(&self) -> MmapRawDescriptor { 109 | MmapRawDescriptor(*self) 110 | } 111 | } 112 | 113 | #[cfg(windows)] 114 | impl<'a, T> MmapAsRawDesc for &'a T 115 | where 116 | T: AsRawHandle, 117 | { 118 | fn as_raw_desc(&self) -> MmapRawDescriptor { 119 | MmapRawDescriptor(self.as_raw_handle()) 120 | } 121 | } 122 | 123 | /// A memory map builder, providing advanced options and flags for specifying memory map behavior. 124 | /// 125 | /// `MmapOptions` can be used to create an anonymous memory map using [`map_anon()`], or a 126 | /// file-backed memory map using one of [`map()`], [`map_mut()`], [`map_exec()`], 127 | /// [`map_copy()`], or [`map_copy_read_only()`]. 128 | /// 129 | /// ## Safety 130 | /// 131 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 132 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 133 | /// out of process. Applications must consider the risk and take appropriate precautions when 134 | /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. 135 | /// unlinked) files exist but are platform specific and limited. 136 | /// 137 | /// [`map_anon()`]: MmapOptions::map_anon() 138 | /// [`map()`]: MmapOptions::map() 139 | /// [`map_mut()`]: MmapOptions::map_mut() 140 | /// [`map_exec()`]: MmapOptions::map_exec() 141 | /// [`map_copy()`]: MmapOptions::map_copy() 142 | /// [`map_copy_read_only()`]: MmapOptions::map_copy_read_only() 143 | #[derive(Clone, Debug, Default)] 144 | pub struct MmapOptions { 145 | offset: u64, 146 | len: Option, 147 | stack: bool, 148 | populate: bool, 149 | } 150 | 151 | impl MmapOptions { 152 | /// Creates a new set of options for configuring and creating a memory map. 153 | /// 154 | /// # Example 155 | /// 156 | /// ``` 157 | /// use memmapix::{MmapMut, MmapOptions}; 158 | /// # use std::io::Result; 159 | /// 160 | /// # fn main() -> Result<()> { 161 | /// // Create a new memory map builder. 162 | /// let mut mmap_options = MmapOptions::new(); 163 | /// 164 | /// // Configure the memory map builder using option setters, then create 165 | /// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`, 166 | /// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`: 167 | /// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?; 168 | /// 169 | /// // Use the memory map: 170 | /// mmap.copy_from_slice(b"...data to copy to the memory map..."); 171 | /// # Ok(()) 172 | /// # } 173 | /// ``` 174 | pub fn new() -> MmapOptions { 175 | MmapOptions::default() 176 | } 177 | 178 | /// Configures the memory map to start at byte `offset` from the beginning of the file. 179 | /// 180 | /// This option has no effect on anonymous memory maps. 181 | /// 182 | /// By default, the offset is 0. 183 | /// 184 | /// # Example 185 | /// 186 | /// ``` 187 | /// use memmapix::MmapOptions; 188 | /// use std::fs::File; 189 | /// 190 | /// # fn main() -> std::io::Result<()> { 191 | /// let mmap = unsafe { 192 | /// MmapOptions::new() 193 | /// .offset(30) 194 | /// .map(&File::open("LICENSE-APACHE")?)? 195 | /// }; 196 | /// assert_eq!(&b"Apache License"[..], 197 | /// &mmap[..14]); 198 | /// # Ok(()) 199 | /// # } 200 | /// ``` 201 | pub fn offset(&mut self, offset: u64) -> &mut Self { 202 | self.offset = offset; 203 | self 204 | } 205 | 206 | /// Configures the created memory mapped buffer to be `len` bytes long. 207 | /// 208 | /// This option is mandatory for anonymous memory maps. 209 | /// 210 | /// For file-backed memory maps, the length will default to the file length. 211 | /// 212 | /// # Example 213 | /// 214 | /// ``` 215 | /// use memmapix::MmapOptions; 216 | /// use std::fs::File; 217 | /// 218 | /// # fn main() -> std::io::Result<()> { 219 | /// let mmap = unsafe { 220 | /// MmapOptions::new() 221 | /// .len(20) 222 | /// .map(&File::open("README.md")?)? 223 | /// }; 224 | /// assert_eq!(&b"
"[..], &mmap[..]); 225 | /// # Ok(()) 226 | /// # } 227 | /// ``` 228 | pub fn len(&mut self, len: usize) -> &mut Self { 229 | self.len = Some(len); 230 | self 231 | } 232 | 233 | /// Returns the configured length, or the length of the provided file. 234 | fn get_len(&self, file: &T) -> Result { 235 | self.len.map(Ok).unwrap_or_else(|| { 236 | let desc = file.as_raw_desc(); 237 | let file_len = file_len(desc.0)?; 238 | 239 | if file_len < self.offset { 240 | return Err(Error::new( 241 | ErrorKind::InvalidData, 242 | "memory map offset is larger than length", 243 | )); 244 | } 245 | let len = file_len - self.offset; 246 | 247 | // Rust's slice cannot be larger than isize::MAX. 248 | // See https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html 249 | // 250 | // This is not a problem on 64-bit targets, but on 32-bit one 251 | // having a file or an anonymous mapping larger than 2GB is quite normal 252 | // and we have to prevent it. 253 | // 254 | // The code below is essentially the same as in Rust's std: 255 | // https://github.com/rust-lang/rust/blob/db78ab70a88a0a5e89031d7ee4eccec835dcdbde/library/alloc/src/raw_vec.rs#L495 256 | if mem::size_of::() < 8 && len > isize::MAX as u64 { 257 | return Err(Error::new( 258 | ErrorKind::InvalidData, 259 | "memory map length overflows isize", 260 | )); 261 | } 262 | 263 | Ok(len as usize) 264 | }) 265 | } 266 | 267 | /// Configures the anonymous memory map to be suitable for a process or thread stack. 268 | /// 269 | /// This option corresponds to the `MAP_STACK` flag on Linux. It has no effect on Windows. 270 | /// 271 | /// This option has no effect on file-backed memory maps. 272 | /// 273 | /// # Example 274 | /// 275 | /// ``` 276 | /// use memmapix::MmapOptions; 277 | /// 278 | /// # fn main() -> std::io::Result<()> { 279 | /// let stack = MmapOptions::new().stack().len(4096).map_anon(); 280 | /// # Ok(()) 281 | /// # } 282 | /// ``` 283 | pub fn stack(&mut self) -> &mut Self { 284 | self.stack = true; 285 | self 286 | } 287 | 288 | /// Populate (prefault) page tables for a mapping. 289 | /// 290 | /// For a file mapping, this causes read-ahead on the file. This will help to reduce blocking on page faults later. 291 | /// 292 | /// This option corresponds to the `MAP_POPULATE` flag on Linux. It has no effect on Windows. 293 | /// 294 | /// # Example 295 | /// 296 | /// ``` 297 | /// use memmapix::MmapOptions; 298 | /// use std::fs::File; 299 | /// 300 | /// # fn main() -> std::io::Result<()> { 301 | /// let file = File::open("LICENSE-MIT")?; 302 | /// 303 | /// let mmap = unsafe { 304 | /// MmapOptions::new().populate().map(&file)? 305 | /// }; 306 | /// 307 | /// assert_eq!(&b"Copyright"[..], &mmap[..9]); 308 | /// # Ok(()) 309 | /// # } 310 | /// ``` 311 | pub fn populate(&mut self) -> &mut Self { 312 | self.populate = true; 313 | self 314 | } 315 | 316 | /// Creates a read-only memory map backed by a file. 317 | /// 318 | /// # Errors 319 | /// 320 | /// This method returns an error when the underlying system call fails, which can happen for a 321 | /// variety of reasons, such as when the file is not open with read permissions. 322 | /// 323 | /// # Example 324 | /// 325 | /// ``` 326 | /// use memmapix::MmapOptions; 327 | /// use std::fs::File; 328 | /// use std::io::Read; 329 | /// 330 | /// # fn main() -> std::io::Result<()> { 331 | /// let mut file = File::open("LICENSE-APACHE")?; 332 | /// 333 | /// let mut contents = Vec::new(); 334 | /// file.read_to_end(&mut contents)?; 335 | /// 336 | /// let mmap = unsafe { 337 | /// MmapOptions::new().map(&file)? 338 | /// }; 339 | /// 340 | /// assert_eq!(&contents[..], &mmap[..]); 341 | /// # Ok(()) 342 | /// # } 343 | /// ``` 344 | pub unsafe fn map(&self, file: T) -> Result { 345 | let desc = file.as_raw_desc(); 346 | 347 | MmapInner::map(self.get_len(&file)?, desc.0, self.offset, self.populate) 348 | .map(|inner| Mmap { inner }) 349 | } 350 | 351 | /// Creates a readable and executable memory map backed by a file. 352 | /// 353 | /// # Errors 354 | /// 355 | /// This method returns an error when the underlying system call fails, which can happen for a 356 | /// variety of reasons, such as when the file is not open with read permissions. 357 | pub unsafe fn map_exec(&self, file: T) -> Result { 358 | let desc = file.as_raw_desc(); 359 | 360 | MmapInner::map_exec(self.get_len(&file)?, desc.0, self.offset, self.populate) 361 | .map(|inner| Mmap { inner }) 362 | } 363 | 364 | /// Creates a writeable memory map backed by a file. 365 | /// 366 | /// # Errors 367 | /// 368 | /// This method returns an error when the underlying system call fails, which can happen for a 369 | /// variety of reasons, such as when the file is not open with read and write permissions. 370 | /// 371 | /// # Example 372 | /// 373 | /// ``` 374 | /// # extern crate memmapix; 375 | /// # extern crate tempfile; 376 | /// # 377 | /// use std::fs::OpenOptions; 378 | /// use std::path::PathBuf; 379 | /// 380 | /// use memmapix::MmapOptions; 381 | /// # 382 | /// # fn main() -> std::io::Result<()> { 383 | /// # let tempdir = tempfile::tempdir()?; 384 | /// let path: PathBuf = /* path to file */ 385 | /// # tempdir.path().join("map_mut"); 386 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; 387 | /// file.set_len(13)?; 388 | /// 389 | /// let mut mmap = unsafe { 390 | /// MmapOptions::new().map_mut(&file)? 391 | /// }; 392 | /// 393 | /// mmap.copy_from_slice(b"Hello, world!"); 394 | /// # Ok(()) 395 | /// # } 396 | /// ``` 397 | pub unsafe fn map_mut(&self, file: T) -> Result { 398 | let desc = file.as_raw_desc(); 399 | 400 | MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate) 401 | .map(|inner| MmapMut { inner }) 402 | } 403 | 404 | /// Creates a copy-on-write memory map backed by a file. 405 | /// 406 | /// Data written to the memory map will not be visible by other processes, 407 | /// and will not be carried through to the underlying file. 408 | /// 409 | /// # Errors 410 | /// 411 | /// This method returns an error when the underlying system call fails, which can happen for a 412 | /// variety of reasons, such as when the file is not open with writable permissions. 413 | /// 414 | /// # Example 415 | /// 416 | /// ``` 417 | /// use memmapix::MmapOptions; 418 | /// use std::fs::File; 419 | /// use std::io::Write; 420 | /// 421 | /// # fn main() -> std::io::Result<()> { 422 | /// let file = File::open("LICENSE-APACHE")?; 423 | /// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? }; 424 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; 425 | /// # Ok(()) 426 | /// # } 427 | /// ``` 428 | pub unsafe fn map_copy(&self, file: T) -> Result { 429 | let desc = file.as_raw_desc(); 430 | 431 | MmapInner::map_copy(self.get_len(&file)?, desc.0, self.offset, self.populate) 432 | .map(|inner| MmapMut { inner }) 433 | } 434 | 435 | /// Creates a copy-on-write read-only memory map backed by a file. 436 | /// 437 | /// # Errors 438 | /// 439 | /// This method returns an error when the underlying system call fails, which can happen for a 440 | /// variety of reasons, such as when the file is not open with read permissions. 441 | /// 442 | /// # Example 443 | /// 444 | /// ``` 445 | /// use memmapix::MmapOptions; 446 | /// use std::fs::File; 447 | /// use std::io::Read; 448 | /// 449 | /// # fn main() -> std::io::Result<()> { 450 | /// let mut file = File::open("README.md")?; 451 | /// 452 | /// let mut contents = Vec::new(); 453 | /// file.read_to_end(&mut contents)?; 454 | /// 455 | /// let mmap = unsafe { 456 | /// MmapOptions::new().map_copy_read_only(&file)? 457 | /// }; 458 | /// 459 | /// assert_eq!(&contents[..], &mmap[..]); 460 | /// # Ok(()) 461 | /// # } 462 | /// ``` 463 | pub unsafe fn map_copy_read_only(&self, file: T) -> Result { 464 | let desc = file.as_raw_desc(); 465 | 466 | MmapInner::map_copy_read_only(self.get_len(&file)?, desc.0, self.offset, self.populate) 467 | .map(|inner| Mmap { inner }) 468 | } 469 | 470 | /// Creates an anonymous memory map. 471 | /// 472 | /// The memory map length should be configured using [`MmapOptions::len()`] 473 | /// before creating an anonymous memory map, otherwise a zero-length mapping 474 | /// will be crated. 475 | /// 476 | /// # Errors 477 | /// 478 | /// This method returns an error when the underlying system call fails or 479 | /// when `len > isize::MAX`. 480 | pub fn map_anon(&self) -> Result { 481 | let len = self.len.unwrap_or(0); 482 | 483 | // See get_len() for details. 484 | if mem::size_of::() < 8 && len > isize::MAX as usize { 485 | return Err(Error::new( 486 | ErrorKind::InvalidData, 487 | "memory map length overflows isize", 488 | )); 489 | } 490 | 491 | MmapInner::map_anon(len, self.stack, self.populate).map(|inner| MmapMut { inner }) 492 | } 493 | 494 | /// Creates a raw memory map. 495 | /// 496 | /// # Errors 497 | /// 498 | /// This method returns an error when the underlying system call fails, which can happen for a 499 | /// variety of reasons, such as when the file is not open with read and write permissions. 500 | pub fn map_raw(&self, file: T) -> Result { 501 | let desc = file.as_raw_desc(); 502 | 503 | MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate) 504 | .map(|inner| MmapRaw { inner }) 505 | } 506 | 507 | /// Creates a read-only raw memory map 508 | /// 509 | /// This is primarily useful to avoid intermediate `Mmap` instances when 510 | /// read-only access to files modified elsewhere are required. 511 | /// 512 | /// # Errors 513 | /// 514 | /// This method returns an error when the underlying system call fails 515 | pub fn map_raw_read_only(&self, file: T) -> Result { 516 | let desc = file.as_raw_desc(); 517 | 518 | MmapInner::map(self.get_len(&file)?, desc.0, self.offset, self.populate) 519 | .map(|inner| MmapRaw { inner }) 520 | } 521 | } 522 | 523 | /// A handle to an immutable memory mapped buffer. 524 | /// 525 | /// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. Use 526 | /// [`MmapOptions`] or [`map()`] to create a file-backed memory map. To create an immutable 527 | /// anonymous memory map, first create a mutable anonymous memory map, and then make it immutable 528 | /// with [`MmapMut::make_read_only()`]. 529 | /// 530 | /// A file backed `Mmap` is created by `&File` reference, and will remain valid even after the 531 | /// `File` is dropped. In other words, the `Mmap` handle is completely independent of the `File` 532 | /// used to create it. For consistency, on some platforms this is achieved by duplicating the 533 | /// underlying file handle. The memory will be unmapped when the `Mmap` handle is dropped. 534 | /// 535 | /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping 536 | /// the mapped pages into physical memory) though the details of this are platform specific. 537 | /// 538 | /// `Mmap` is `Sync` and `Send`. 539 | /// 540 | /// ## Safety 541 | /// 542 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 543 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 544 | /// out of process. Applications must consider the risk and take appropriate precautions when using 545 | /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) 546 | /// files exist but are platform specific and limited. 547 | /// 548 | /// ## Example 549 | /// 550 | /// ``` 551 | /// use memmapix::MmapOptions; 552 | /// use std::io::Write; 553 | /// use std::fs::File; 554 | /// 555 | /// # fn main() -> std::io::Result<()> { 556 | /// let file = File::open("README.md")?; 557 | /// let mmap = unsafe { MmapOptions::new().map(&file)? }; 558 | /// assert_eq!(b"
", &mmap[0..20]); 559 | /// # Ok(()) 560 | /// # } 561 | /// ``` 562 | /// 563 | /// See [`MmapMut`] for the mutable version. 564 | /// 565 | /// [`map()`]: Mmap::map() 566 | pub struct Mmap { 567 | inner: MmapInner, 568 | } 569 | 570 | impl Mmap { 571 | /// Creates a read-only memory map backed by a file. 572 | /// 573 | /// This is equivalent to calling `MmapOptions::new().map(file)`. 574 | /// 575 | /// # Errors 576 | /// 577 | /// This method returns an error when the underlying system call fails, which can happen for a 578 | /// variety of reasons, such as when the file is not open with read permissions. 579 | /// 580 | /// # Example 581 | /// 582 | /// ``` 583 | /// use std::fs::File; 584 | /// use std::io::Read; 585 | /// 586 | /// use memmapix::Mmap; 587 | /// 588 | /// # fn main() -> std::io::Result<()> { 589 | /// let mut file = File::open("LICENSE-APACHE")?; 590 | /// 591 | /// let mut contents = Vec::new(); 592 | /// file.read_to_end(&mut contents)?; 593 | /// 594 | /// let mmap = unsafe { Mmap::map(&file)? }; 595 | /// 596 | /// assert_eq!(&contents[..], &mmap[..]); 597 | /// # Ok(()) 598 | /// # } 599 | /// ``` 600 | pub unsafe fn map(file: T) -> Result { 601 | MmapOptions::new().map(file) 602 | } 603 | 604 | /// Transition the memory map to be writable. 605 | /// 606 | /// If the memory map is file-backed, the file must have been opened with write permissions. 607 | /// 608 | /// # Errors 609 | /// 610 | /// This method returns an error when the underlying system call fails, which can happen for a 611 | /// variety of reasons, such as when the file is not open with writable permissions. 612 | /// 613 | /// # Example 614 | /// 615 | /// ``` 616 | /// # extern crate memmapix; 617 | /// # extern crate tempfile; 618 | /// # 619 | /// use memmapix::Mmap; 620 | /// use std::ops::DerefMut; 621 | /// use std::io::Write; 622 | /// # use std::fs::OpenOptions; 623 | /// 624 | /// # fn main() -> std::io::Result<()> { 625 | /// # let tempdir = tempfile::tempdir()?; 626 | /// let file = /* file opened with write permissions */ 627 | /// # OpenOptions::new() 628 | /// # .read(true) 629 | /// # .write(true) 630 | /// # .create(true) 631 | /// # .open(tempdir.path() 632 | /// # .join("make_mut"))?; 633 | /// # file.set_len(128)?; 634 | /// let mmap = unsafe { Mmap::map(&file)? }; 635 | /// // ... use the read-only memory map ... 636 | /// let mut mut_mmap = mmap.make_mut()?; 637 | /// mut_mmap.deref_mut().write_all(b"hello, world!")?; 638 | /// # Ok(()) 639 | /// # } 640 | /// ``` 641 | pub fn make_mut(mut self) -> Result { 642 | self.inner.make_mut()?; 643 | Ok(MmapMut { inner: self.inner }) 644 | } 645 | 646 | /// Advise OS how this memory map will be accessed. Only supported on Unix. 647 | /// 648 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) man page. 649 | #[cfg(unix)] 650 | #[cfg_attr(docsrs, doc(cfg(unix)))] 651 | pub fn advise(&self, advice: rustix::mm::Advice) -> Result<()> { 652 | self.inner.advise(advice, 0, self.inner.len()) 653 | } 654 | 655 | /// Advise OS how this range of memory map will be accessed. 656 | /// 657 | /// The offset and length must be in the bounds of the memory map. 658 | /// 659 | /// Only supported on Unix. 660 | /// 661 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) man page. 662 | #[cfg(unix)] 663 | #[cfg_attr(docsrs, doc(cfg(unix)))] 664 | pub fn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { 665 | self.inner.advise(advice, offset, len) 666 | } 667 | 668 | /// Lock the memory map in `[offset..offset + data_size]` into RAM. Only supported on Unix. 669 | /// 670 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) man page. 671 | #[inline] 672 | #[cfg(unix)] 673 | #[cfg_attr(docsrs, doc(cfg(unix)))] 674 | pub fn lock_segment(&self, data_size: usize, offset: usize) -> Result<()> { 675 | self.inner.lock_segment(data_size, offset) 676 | } 677 | 678 | /// Unlock the memory map in `[offset..offset + data_size]`. Only supported on Unix. 679 | /// 680 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) man page. 681 | #[inline] 682 | #[cfg(unix)] 683 | #[cfg_attr(docsrs, doc(cfg(unix)))] 684 | pub fn unlock_segment(&self, data_size: usize, offset: usize) -> Result<()> { 685 | self.inner.unlock_segment(data_size, offset) 686 | } 687 | 688 | /// Lock the whole memory map into RAM. Only supported on Unix. 689 | /// 690 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) man page. 691 | #[cfg(unix)] 692 | #[cfg_attr(docsrs, doc(cfg(unix)))] 693 | pub fn lock(&self) -> Result<()> { 694 | self.inner.lock() 695 | } 696 | 697 | /// Unlock the whole memory map. Only supported on Unix. 698 | /// 699 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) man page. 700 | #[cfg(unix)] 701 | #[cfg_attr(docsrs, doc(cfg(unix)))] 702 | pub fn unlock(&self) -> Result<()> { 703 | self.inner.unlock() 704 | } 705 | 706 | /// Adjust the size of the memory mapping. 707 | /// 708 | /// This will try to resize the memory mapping in place. If 709 | /// [`MremapFlags::MAYMOVE`] is specified it will move the mapping if it 710 | /// could not resize in place, otherwise it will error. 711 | /// 712 | /// Only supported on Linux. 713 | /// 714 | /// See the [`mremap(2)`] man page. 715 | /// 716 | /// # Safety 717 | /// 718 | /// Resizing the memory mapping beyond the end of the mapped file will 719 | /// result in UB should you happen to access memory beyond the end of the 720 | /// file. 721 | /// 722 | /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html 723 | #[cfg(target_os = "linux")] 724 | #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))] 725 | pub unsafe fn remap(&mut self, new_len: usize, options: MremapFlags) -> Result<()> { 726 | self.inner.remap(new_len, options) 727 | } 728 | } 729 | 730 | #[cfg(feature = "stable_deref_trait")] 731 | unsafe impl stable_deref_trait::StableDeref for Mmap {} 732 | 733 | impl Deref for Mmap { 734 | type Target = [u8]; 735 | 736 | #[inline] 737 | fn deref(&self) -> &[u8] { 738 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } 739 | } 740 | } 741 | 742 | impl AsRef<[u8]> for Mmap { 743 | #[inline] 744 | fn as_ref(&self) -> &[u8] { 745 | self.deref() 746 | } 747 | } 748 | 749 | impl fmt::Debug for Mmap { 750 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 751 | fmt.debug_struct("Mmap") 752 | .field("ptr", &self.as_ptr()) 753 | .field("len", &self.len()) 754 | .finish() 755 | } 756 | } 757 | 758 | /// A handle to a raw memory mapped buffer. 759 | /// 760 | /// This struct never hands out references to its interior, only raw pointers. 761 | /// This can be helpful when creating shared memory maps between untrusted processes. 762 | pub struct MmapRaw { 763 | inner: MmapInner, 764 | } 765 | 766 | impl MmapRaw { 767 | /// Creates a writeable memory map backed by a file. 768 | /// 769 | /// This is equivalent to calling `MmapOptions::new().map_raw(file)`. 770 | /// 771 | /// # Errors 772 | /// 773 | /// This method returns an error when the underlying system call fails, which can happen for a 774 | /// variety of reasons, such as when the file is not open with read and write permissions. 775 | pub fn map_raw(file: T) -> Result { 776 | MmapOptions::new().map_raw(file) 777 | } 778 | 779 | /// Returns a raw pointer to the memory mapped file. 780 | /// 781 | /// Before dereferencing this pointer, you have to make sure that the file has not been 782 | /// truncated since the memory map was created. 783 | /// Avoiding this will not introduce memory safety issues in Rust terms, 784 | /// but will cause SIGBUS (or equivalent) signal. 785 | #[inline] 786 | pub fn as_ptr(&self) -> *const u8 { 787 | self.inner.ptr() 788 | } 789 | 790 | /// Returns an unsafe mutable pointer to the memory mapped file. 791 | /// 792 | /// Before dereferencing this pointer, you have to make sure that the file has not been 793 | /// truncated since the memory map was created. 794 | /// Avoiding this will not introduce memory safety issues in Rust terms, 795 | /// but will cause SIGBUS (or equivalent) signal. 796 | #[inline] 797 | pub fn as_mut_ptr(&self) -> *mut u8 { 798 | self.inner.ptr() as _ 799 | } 800 | 801 | /// Returns the length in bytes of the memory map. 802 | /// 803 | /// Note that truncating the file can cause the length to change (and render this value unusable). 804 | #[inline] 805 | pub fn len(&self) -> usize { 806 | self.inner.len() 807 | } 808 | 809 | /// Returns if the memory map is empty. 810 | #[inline] 811 | pub fn is_empty(&self) -> bool { 812 | self.inner.len() == 0 813 | } 814 | 815 | /// Flushes outstanding memory map modifications to disk. 816 | /// 817 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 818 | /// memory map are guaranteed to be durably stored. The file's metadata (including last 819 | /// modification timestamp) may not be updated. 820 | /// 821 | /// # Example 822 | /// 823 | /// ``` 824 | /// # extern crate memmapix; 825 | /// # extern crate tempfile; 826 | /// # 827 | /// use std::fs::OpenOptions; 828 | /// use std::io::Write; 829 | /// use std::path::PathBuf; 830 | /// use std::slice; 831 | /// 832 | /// use memmapix::MmapRaw; 833 | /// 834 | /// # fn main() -> std::io::Result<()> { 835 | /// let tempdir = tempfile::tempdir()?; 836 | /// let path: PathBuf = /* path to file */ 837 | /// # tempdir.path().join("flush"); 838 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; 839 | /// file.set_len(128)?; 840 | /// 841 | /// let mut mmap = unsafe { MmapRaw::map_raw(&file)? }; 842 | /// 843 | /// let mut memory = unsafe { slice::from_raw_parts_mut(mmap.as_mut_ptr(), 128) }; 844 | /// memory.write_all(b"Hello, world!")?; 845 | /// mmap.flush()?; 846 | /// # Ok(()) 847 | /// # } 848 | /// ``` 849 | pub fn flush(&self) -> Result<()> { 850 | let len = self.len(); 851 | self.inner.flush(0, len) 852 | } 853 | 854 | /// Asynchronously flushes outstanding memory map modifications to disk. 855 | /// 856 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 857 | /// the operation to complete before returning. The file's metadata (including last 858 | /// modification timestamp) may not be updated. 859 | pub fn flush_async(&self) -> Result<()> { 860 | let len = self.len(); 861 | self.inner.flush_async(0, len) 862 | } 863 | 864 | /// Flushes outstanding memory map modifications in the range to disk. 865 | /// 866 | /// The offset and length must be in the bounds of the memory map. 867 | /// 868 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 869 | /// memory in the range are guaranteed to be durable stored. The file's metadata (including 870 | /// last modification timestamp) may not be updated. It is not guaranteed the only the changes 871 | /// in the specified range are flushed; other outstanding changes to the memory map may be 872 | /// flushed as well. 873 | pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> { 874 | self.inner.flush(offset, len) 875 | } 876 | 877 | /// Asynchronously flushes outstanding memory map modifications in the range to disk. 878 | /// 879 | /// The offset and length must be in the bounds of the memory map. 880 | /// 881 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 882 | /// the operation to complete before returning. The file's metadata (including last 883 | /// modification timestamp) may not be updated. It is not guaranteed that the only changes 884 | /// flushed are those in the specified range; other outstanding changes to the memory map may 885 | /// be flushed as well. 886 | pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { 887 | self.inner.flush_async(offset, len) 888 | } 889 | 890 | /// Advise OS how this memory map will be accessed. Only supported on Unix. 891 | /// 892 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) man page. 893 | #[cfg(unix)] 894 | #[cfg_attr(docsrs, doc(cfg(unix)))] 895 | pub fn advise(&self, advice: Advice) -> Result<()> { 896 | self.inner.advise(advice, 0, self.inner.len()) 897 | } 898 | 899 | /// Advise OS how this range of memory map will be accessed. 900 | /// 901 | /// The offset and length must be in the bounds of the memory map. 902 | /// 903 | /// Only supported on Unix. 904 | /// 905 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) man page. 906 | #[cfg(unix)] 907 | #[cfg_attr(docsrs, doc(cfg(unix)))] 908 | pub fn advise_range( 909 | &self, 910 | advice: rustix::mm::Advice, 911 | offset: usize, 912 | len: usize, 913 | ) -> Result<()> { 914 | self.inner.advise(advice, offset, len) 915 | } 916 | 917 | /// Lock the whole memory map into RAM. Only supported on Unix. 918 | /// 919 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) man page. 920 | #[cfg(unix)] 921 | #[cfg_attr(docsrs, doc(cfg(unix)))] 922 | pub fn lock(&self) -> Result<()> { 923 | self.inner.lock() 924 | } 925 | 926 | /// Unlock the whole memory map. Only supported on Unix. 927 | /// 928 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) man page. 929 | #[cfg(unix)] 930 | #[cfg_attr(docsrs, doc(cfg(unix)))] 931 | pub fn unlock(&self) -> Result<()> { 932 | self.inner.unlock() 933 | } 934 | 935 | /// Lock the memory map in `[offset..offset + data_size]` into RAM. Only supported on Unix. 936 | /// 937 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) man page. 938 | #[inline] 939 | #[cfg(unix)] 940 | #[cfg_attr(docsrs, doc(cfg(unix)))] 941 | pub fn lock_segment(&self, data_size: usize, offset: usize) -> Result<()> { 942 | self.inner.lock_segment(data_size, offset) 943 | } 944 | 945 | /// Unlock the memory map in `[offset..offset + data_size]`. Only supported on Unix. 946 | /// 947 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) man page. 948 | #[inline] 949 | #[cfg(unix)] 950 | #[cfg_attr(docsrs, doc(cfg(unix)))] 951 | pub fn unlock_segment(&self, data_size: usize, offset: usize) -> Result<()> { 952 | self.inner.unlock_segment(data_size, offset) 953 | } 954 | 955 | /// Adjust the size of the memory mapping. 956 | /// 957 | /// This will try to resize the memory mapping in place. If 958 | /// [`MremapFlags::MAYMOVE`] is specified it will move the mapping if it 959 | /// could not resize in place, otherwise it will error. 960 | /// 961 | /// Only supported on Linux. 962 | /// 963 | /// See the [`mremap(2)`] man page. 964 | /// 965 | /// # Safety 966 | /// 967 | /// Resizing the memory mapping beyond the end of the mapped file will 968 | /// result in UB should you happen to access memory beyond the end of the 969 | /// file. 970 | /// 971 | /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html 972 | #[cfg(target_os = "linux")] 973 | #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))] 974 | pub unsafe fn remap(&mut self, new_len: usize, options: MremapFlags) -> Result<()> { 975 | self.inner.remap(new_len, options) 976 | } 977 | } 978 | 979 | impl fmt::Debug for MmapRaw { 980 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 981 | fmt.debug_struct("MmapRaw") 982 | .field("ptr", &self.as_ptr()) 983 | .field("len", &self.len()) 984 | .finish() 985 | } 986 | } 987 | 988 | impl From for MmapRaw { 989 | fn from(value: Mmap) -> Self { 990 | Self { inner: value.inner } 991 | } 992 | } 993 | 994 | impl From for MmapRaw { 995 | fn from(value: MmapMut) -> Self { 996 | Self { inner: value.inner } 997 | } 998 | } 999 | 1000 | /// A handle to a mutable memory mapped buffer. 1001 | /// 1002 | /// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous 1003 | /// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use 1004 | /// [`MmapMut::map_mut()`] and [`MmapMut::map_anon()`] to create a mutable memory map of the 1005 | /// respective types, or [`MmapOptions::map_mut()`] and [`MmapOptions::map_anon()`] if non-default 1006 | /// options are required. 1007 | /// 1008 | /// A file backed `MmapMut` is created by `&File` reference, and will remain valid even after the 1009 | /// `File` is dropped. In other words, the `MmapMut` handle is completely independent of the `File` 1010 | /// used to create it. For consistency, on some platforms this is achieved by duplicating the 1011 | /// underlying file handle. The memory will be unmapped when the `MmapMut` handle is dropped. 1012 | /// 1013 | /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping 1014 | /// the mapped pages into physical memory) though the details of this are platform specific. 1015 | /// 1016 | /// `Mmap` is `Sync` and `Send` 1017 | /// 1018 | /// See [`Mmap`] for the immutable version. 1019 | /// 1020 | /// ## Safety 1021 | /// 1022 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 1023 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 1024 | /// out of process. Applications must consider the risk and take appropriate precautions when using 1025 | /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) 1026 | /// files exist but are platform specific and limited. 1027 | pub struct MmapMut { 1028 | inner: MmapInner, 1029 | } 1030 | 1031 | impl MmapMut { 1032 | /// Creates a writeable memory map backed by a file. 1033 | /// 1034 | /// This is equivalent to calling `MmapOptions::new().map_mut(file)`. 1035 | /// 1036 | /// # Errors 1037 | /// 1038 | /// This method returns an error when the underlying system call fails, which can happen for a 1039 | /// variety of reasons, such as when the file is not open with read and write permissions. 1040 | /// 1041 | /// # Example 1042 | /// 1043 | /// ``` 1044 | /// # extern crate memmapix; 1045 | /// # extern crate tempfile; 1046 | /// # 1047 | /// use std::fs::OpenOptions; 1048 | /// use std::path::PathBuf; 1049 | /// 1050 | /// use memmapix::MmapMut; 1051 | /// # 1052 | /// # fn main() -> std::io::Result<()> { 1053 | /// # let tempdir = tempfile::tempdir()?; 1054 | /// let path: PathBuf = /* path to file */ 1055 | /// # tempdir.path().join("map_mut"); 1056 | /// let file = OpenOptions::new() 1057 | /// .read(true) 1058 | /// .write(true) 1059 | /// .create(true) 1060 | /// .open(&path)?; 1061 | /// file.set_len(13)?; 1062 | /// 1063 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; 1064 | /// 1065 | /// mmap.copy_from_slice(b"Hello, world!"); 1066 | /// # Ok(()) 1067 | /// # } 1068 | /// ``` 1069 | pub unsafe fn map_mut(file: T) -> Result { 1070 | MmapOptions::new().map_mut(file) 1071 | } 1072 | 1073 | /// Creates an anonymous memory map. 1074 | /// 1075 | /// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`. 1076 | /// 1077 | /// # Errors 1078 | /// 1079 | /// This method returns an error when the underlying system call fails or 1080 | /// when `len > isize::MAX`. 1081 | pub fn map_anon(length: usize) -> Result { 1082 | MmapOptions::new().len(length).map_anon() 1083 | } 1084 | 1085 | /// Lock the whole memory map into RAM. Only supported on Unix. 1086 | /// 1087 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) man page. 1088 | #[cfg(unix)] 1089 | pub fn lock(&self) -> Result<()> { 1090 | self.inner.lock() 1091 | } 1092 | 1093 | /// Unlock the whole memory map. Only supported on Unix. 1094 | /// 1095 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) man page. 1096 | #[cfg(unix)] 1097 | pub fn unlock(&self) -> Result<()> { 1098 | self.inner.unlock() 1099 | } 1100 | 1101 | /// Lock the memory map segment in `[offset..offset + data_size]` into RAM. Only supported on Unix. 1102 | /// 1103 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) man page. 1104 | #[inline] 1105 | #[cfg(unix)] 1106 | pub fn lock_segment(&self, data_size: usize, offset: usize) -> Result<()> { 1107 | self.inner.lock_segment(data_size, offset) 1108 | } 1109 | 1110 | /// Unlock the memory map segment in `[offset..offset + data_size]`. Only supported on Unix. 1111 | /// 1112 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) man page. 1113 | #[inline] 1114 | #[cfg(unix)] 1115 | pub fn unlock_segment(&self, data_size: usize, offset: usize) -> Result<()> { 1116 | self.inner.unlock_segment(data_size, offset) 1117 | } 1118 | 1119 | /// Flushes outstanding memory map modifications to disk. 1120 | /// 1121 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 1122 | /// memory map are guaranteed to be durably stored. The file's metadata (including last 1123 | /// modification timestamp) may not be updated. 1124 | /// 1125 | /// # Example 1126 | /// 1127 | /// ``` 1128 | /// # extern crate memmapix; 1129 | /// # extern crate tempfile; 1130 | /// # 1131 | /// use std::fs::OpenOptions; 1132 | /// use std::io::Write; 1133 | /// use std::path::PathBuf; 1134 | /// 1135 | /// use memmapix::MmapMut; 1136 | /// 1137 | /// # fn main() -> std::io::Result<()> { 1138 | /// # let tempdir = tempfile::tempdir()?; 1139 | /// let path: PathBuf = /* path to file */ 1140 | /// # tempdir.path().join("flush"); 1141 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; 1142 | /// file.set_len(128)?; 1143 | /// 1144 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; 1145 | /// 1146 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; 1147 | /// mmap.flush()?; 1148 | /// # Ok(()) 1149 | /// # } 1150 | /// ``` 1151 | pub fn flush(&self) -> Result<()> { 1152 | let len = self.len(); 1153 | self.inner.flush(0, len) 1154 | } 1155 | 1156 | /// Asynchronously flushes outstanding memory map modifications to disk. 1157 | /// 1158 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 1159 | /// the operation to complete before returning. The file's metadata (including last 1160 | /// modification timestamp) may not be updated. 1161 | pub fn flush_async(&self) -> Result<()> { 1162 | let len = self.len(); 1163 | self.inner.flush_async(0, len) 1164 | } 1165 | 1166 | /// Flushes outstanding memory map modifications in the range to disk. 1167 | /// 1168 | /// The offset and length must be in the bounds of the memory map. 1169 | /// 1170 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 1171 | /// memory in the range are guaranteed to be durable stored. The file's metadata (including 1172 | /// last modification timestamp) may not be updated. It is not guaranteed the only the changes 1173 | /// in the specified range are flushed; other outstanding changes to the memory map may be 1174 | /// flushed as well. 1175 | pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> { 1176 | self.inner.flush(offset, len) 1177 | } 1178 | 1179 | /// Asynchronously flushes outstanding memory map modifications in the range to disk. 1180 | /// 1181 | /// The offset and length must be in the bounds of the memory map. 1182 | /// 1183 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 1184 | /// the operation to complete before returning. The file's metadata (including last 1185 | /// modification timestamp) may not be updated. It is not guaranteed that the only changes 1186 | /// flushed are those in the specified range; other outstanding changes to the memory map may 1187 | /// be flushed as well. 1188 | pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { 1189 | self.inner.flush_async(offset, len) 1190 | } 1191 | 1192 | /// Returns an immutable version of this memory mapped buffer. 1193 | /// 1194 | /// If the memory map is file-backed, the file must have been opened with read permissions. 1195 | /// 1196 | /// # Errors 1197 | /// 1198 | /// This method returns an error when the underlying system call fails, which can happen for a 1199 | /// variety of reasons, such as when the file has not been opened with read permissions. 1200 | /// 1201 | /// # Example 1202 | /// 1203 | /// ``` 1204 | /// # extern crate memmapix; 1205 | /// # 1206 | /// use std::io::Write; 1207 | /// use std::path::PathBuf; 1208 | /// 1209 | /// use memmapix::{Mmap, MmapMut}; 1210 | /// 1211 | /// # fn main() -> std::io::Result<()> { 1212 | /// let mut mmap = MmapMut::map_anon(128)?; 1213 | /// 1214 | /// (&mut mmap[..]).write(b"Hello, world!")?; 1215 | /// 1216 | /// let mmap: Mmap = mmap.make_read_only()?; 1217 | /// # Ok(()) 1218 | /// # } 1219 | /// ``` 1220 | pub fn make_read_only(mut self) -> Result { 1221 | self.inner.make_read_only()?; 1222 | Ok(Mmap { inner: self.inner }) 1223 | } 1224 | 1225 | /// Transition the memory map to be readable and executable. 1226 | /// 1227 | /// If the memory map is file-backed, the file must have been opened with execute permissions. 1228 | /// 1229 | /// On systems with separate instructions and data caches (a category that includes many ARM 1230 | /// chips), a platform-specific call may be needed to ensure that the changes are visible to the 1231 | /// execution unit (e.g. when using this function to implement a JIT compiler). For more 1232 | /// details, see [this ARM write-up](https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/caches-and-self-modifying-code) 1233 | /// or the `man` page for [`sys_icache_invalidate`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html). 1234 | /// 1235 | /// # Errors 1236 | /// 1237 | /// This method returns an error when the underlying system call fails, which can happen for a 1238 | /// variety of reasons, such as when the file has not been opened with execute permissions. 1239 | pub fn make_exec(mut self) -> Result { 1240 | self.inner.make_exec()?; 1241 | Ok(Mmap { inner: self.inner }) 1242 | } 1243 | 1244 | /// Advise OS how this memory map will be accessed. Only supported on Unix. 1245 | /// 1246 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) man page. 1247 | #[cfg(unix)] 1248 | pub fn advise(&self, advice: Advice) -> Result<()> { 1249 | self.inner.advise(advice, 0, self.inner.len()) 1250 | } 1251 | 1252 | /// Advise OS how this range of memory map will be accessed. 1253 | /// 1254 | /// The offset and length must be in the bounds of the memory map. 1255 | /// 1256 | /// Only supported on Unix. 1257 | /// 1258 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) man page. 1259 | #[cfg(unix)] 1260 | pub fn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { 1261 | self.inner.advise(advice, offset, len) 1262 | } 1263 | 1264 | /// Adjust the size of the memory mapping. 1265 | /// 1266 | /// This will try to resize the memory mapping in place. If 1267 | /// [`MremapFlags::MAYMOVE`] is specified it will move the mapping if it 1268 | /// could not resize in place, otherwise it will error. 1269 | /// 1270 | /// Only supported on Linux. 1271 | /// 1272 | /// See the [`mremap(2)`] man page. 1273 | /// 1274 | /// # Safety 1275 | /// 1276 | /// Resizing the memory mapping beyond the end of the mapped file will 1277 | /// result in UB should you happen to access memory beyond the end of the 1278 | /// file. 1279 | /// 1280 | /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html 1281 | #[cfg(target_os = "linux")] 1282 | #[cfg_attr(docsrs, doc(cfg(target_os = "linux")))] 1283 | pub unsafe fn remap(&mut self, new_len: usize, options: MremapFlags) -> Result<()> { 1284 | self.inner.remap(new_len, options) 1285 | } 1286 | } 1287 | 1288 | #[cfg(feature = "stable_deref_trait")] 1289 | unsafe impl stable_deref_trait::StableDeref for MmapMut {} 1290 | 1291 | impl Deref for MmapMut { 1292 | type Target = [u8]; 1293 | 1294 | #[inline] 1295 | fn deref(&self) -> &[u8] { 1296 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } 1297 | } 1298 | } 1299 | 1300 | impl DerefMut for MmapMut { 1301 | #[inline] 1302 | fn deref_mut(&mut self) -> &mut [u8] { 1303 | unsafe { slice::from_raw_parts_mut(self.inner.mut_ptr(), self.inner.len()) } 1304 | } 1305 | } 1306 | 1307 | impl AsRef<[u8]> for MmapMut { 1308 | #[inline] 1309 | fn as_ref(&self) -> &[u8] { 1310 | self.deref() 1311 | } 1312 | } 1313 | 1314 | impl AsMut<[u8]> for MmapMut { 1315 | #[inline] 1316 | fn as_mut(&mut self) -> &mut [u8] { 1317 | self.deref_mut() 1318 | } 1319 | } 1320 | 1321 | impl fmt::Debug for MmapMut { 1322 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1323 | fmt.debug_struct("MmapMut") 1324 | .field("ptr", &self.as_ptr()) 1325 | .field("len", &self.len()) 1326 | .finish() 1327 | } 1328 | } 1329 | 1330 | #[cfg(test)] 1331 | mod test { 1332 | extern crate tempfile; 1333 | 1334 | #[cfg(unix)] 1335 | use rustix::mm::Advice; 1336 | use std::fs::{File, OpenOptions}; 1337 | use std::io::{Read, Write}; 1338 | use std::mem; 1339 | #[cfg(unix)] 1340 | use std::os::unix::io::AsRawFd; 1341 | #[cfg(windows)] 1342 | use std::os::windows::fs::OpenOptionsExt; 1343 | 1344 | #[cfg(windows)] 1345 | const GENERIC_ALL: u32 = 0x10000000; 1346 | 1347 | use super::{Mmap, MmapMut, MmapOptions}; 1348 | 1349 | #[test] 1350 | fn map_file() { 1351 | let expected_len = 128; 1352 | let tempdir = tempfile::tempdir().unwrap(); 1353 | let path = tempdir.path().join("mmap"); 1354 | 1355 | let file = OpenOptions::new() 1356 | .read(true) 1357 | .write(true) 1358 | .create(true) 1359 | .open(path) 1360 | .unwrap(); 1361 | 1362 | file.set_len(expected_len as u64).unwrap(); 1363 | 1364 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1365 | let len = mmap.len(); 1366 | assert_eq!(expected_len, len); 1367 | 1368 | let zeros = vec![0; len]; 1369 | let incr: Vec = (0..len as u8).collect(); 1370 | 1371 | // check that the mmap is empty 1372 | assert_eq!(&zeros[..], &mmap[..]); 1373 | 1374 | // write values into the mmap 1375 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1376 | 1377 | // read values back 1378 | assert_eq!(&incr[..], &mmap[..]); 1379 | } 1380 | 1381 | #[test] 1382 | #[cfg(unix)] 1383 | fn map_fd() { 1384 | let expected_len = 128; 1385 | let tempdir = tempfile::tempdir().unwrap(); 1386 | let path = tempdir.path().join("mmap"); 1387 | 1388 | let file = OpenOptions::new() 1389 | .read(true) 1390 | .write(true) 1391 | .create(true) 1392 | .open(path) 1393 | .unwrap(); 1394 | 1395 | file.set_len(expected_len as u64).unwrap(); 1396 | 1397 | let mut mmap = unsafe { MmapMut::map_mut(file.as_raw_fd()).unwrap() }; 1398 | let len = mmap.len(); 1399 | assert_eq!(expected_len, len); 1400 | 1401 | let zeros = vec![0; len]; 1402 | let incr: Vec = (0..len as u8).collect(); 1403 | 1404 | // check that the mmap is empty 1405 | assert_eq!(&zeros[..], &mmap[..]); 1406 | 1407 | // write values into the mmap 1408 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1409 | 1410 | // read values back 1411 | assert_eq!(&incr[..], &mmap[..]); 1412 | } 1413 | 1414 | /// Checks that "mapping" a 0-length file derefs to an empty slice. 1415 | #[test] 1416 | fn map_empty_file() { 1417 | let tempdir = tempfile::tempdir().unwrap(); 1418 | let path = tempdir.path().join("mmap"); 1419 | 1420 | let file = OpenOptions::new() 1421 | .read(true) 1422 | .write(true) 1423 | .create(true) 1424 | .open(path) 1425 | .unwrap(); 1426 | let mmap = unsafe { Mmap::map(&file).unwrap() }; 1427 | assert!(mmap.is_empty()); 1428 | assert_eq!(mmap.as_ptr().align_offset(mem::size_of::()), 0); 1429 | let mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1430 | assert!(mmap.is_empty()); 1431 | assert_eq!(mmap.as_ptr().align_offset(mem::size_of::()), 0); 1432 | } 1433 | 1434 | #[test] 1435 | fn map_anon() { 1436 | let expected_len = 128; 1437 | let mut mmap = MmapMut::map_anon(expected_len).unwrap(); 1438 | let len = mmap.len(); 1439 | assert_eq!(expected_len, len); 1440 | 1441 | let zeros = vec![0; len]; 1442 | let incr: Vec = (0..len as u8).collect(); 1443 | 1444 | // check that the mmap is empty 1445 | assert_eq!(&zeros[..], &mmap[..]); 1446 | 1447 | // write values into the mmap 1448 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1449 | 1450 | // read values back 1451 | assert_eq!(&incr[..], &mmap[..]); 1452 | } 1453 | 1454 | #[test] 1455 | fn map_anon_zero_len() { 1456 | assert!(MmapOptions::new().map_anon().unwrap().is_empty()) 1457 | } 1458 | 1459 | #[test] 1460 | #[cfg(target_pointer_width = "32")] 1461 | fn map_anon_len_overflow() { 1462 | let res = MmapMut::map_anon(0x80000000); 1463 | 1464 | assert_eq!( 1465 | res.unwrap_err().to_string(), 1466 | "memory map length overflows isize" 1467 | ); 1468 | } 1469 | 1470 | #[test] 1471 | fn file_write() { 1472 | let tempdir = tempfile::tempdir().unwrap(); 1473 | let path = tempdir.path().join("mmap"); 1474 | 1475 | let mut file = OpenOptions::new() 1476 | .read(true) 1477 | .write(true) 1478 | .create(true) 1479 | .open(path) 1480 | .unwrap(); 1481 | file.set_len(128).unwrap(); 1482 | 1483 | let write = b"abc123"; 1484 | let mut read = [0u8; 6]; 1485 | 1486 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1487 | (&mut mmap[..]).write_all(write).unwrap(); 1488 | mmap.flush().unwrap(); 1489 | 1490 | file.read_exact(&mut read).unwrap(); 1491 | assert_eq!(write, &read); 1492 | } 1493 | 1494 | #[test] 1495 | fn flush_range() { 1496 | let tempdir = tempfile::tempdir().unwrap(); 1497 | let path = tempdir.path().join("mmap"); 1498 | 1499 | let file = OpenOptions::new() 1500 | .read(true) 1501 | .write(true) 1502 | .create(true) 1503 | .open(path) 1504 | .unwrap(); 1505 | file.set_len(128).unwrap(); 1506 | let write = b"abc123"; 1507 | 1508 | let mut mmap = unsafe { 1509 | MmapOptions::new() 1510 | .offset(2) 1511 | .len(write.len()) 1512 | .map_mut(&file) 1513 | .unwrap() 1514 | }; 1515 | (&mut mmap[..]).write_all(write).unwrap(); 1516 | mmap.flush_async_range(0, write.len()).unwrap(); 1517 | mmap.flush_range(0, write.len()).unwrap(); 1518 | } 1519 | 1520 | #[test] 1521 | fn map_copy() { 1522 | let tempdir = tempfile::tempdir().unwrap(); 1523 | let path = tempdir.path().join("mmap"); 1524 | 1525 | let mut file = OpenOptions::new() 1526 | .read(true) 1527 | .write(true) 1528 | .create(true) 1529 | .open(path) 1530 | .unwrap(); 1531 | file.set_len(128).unwrap(); 1532 | 1533 | let nulls = b"\0\0\0\0\0\0"; 1534 | let write = b"abc123"; 1535 | let mut read = [0u8; 6]; 1536 | 1537 | let mut mmap = unsafe { MmapOptions::new().map_copy(&file).unwrap() }; 1538 | 1539 | (&mut mmap[..]).write_all(write).unwrap(); 1540 | mmap.flush().unwrap(); 1541 | 1542 | // The mmap contains the write 1543 | (&mmap[..]).read_exact(&mut read).unwrap(); 1544 | assert_eq!(write, &read); 1545 | 1546 | // The file does not contain the write 1547 | file.read_exact(&mut read).unwrap(); 1548 | assert_eq!(nulls, &read); 1549 | 1550 | // another mmap does not contain the write 1551 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1552 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1553 | assert_eq!(nulls, &read); 1554 | } 1555 | 1556 | #[test] 1557 | fn map_copy_read_only() { 1558 | let tempdir = tempfile::tempdir().unwrap(); 1559 | let path = tempdir.path().join("mmap"); 1560 | 1561 | let file = OpenOptions::new() 1562 | .read(true) 1563 | .write(true) 1564 | .create(true) 1565 | .open(path) 1566 | .unwrap(); 1567 | file.set_len(128).unwrap(); 1568 | 1569 | let nulls = b"\0\0\0\0\0\0"; 1570 | let mut read = [0u8; 6]; 1571 | 1572 | let mmap = unsafe { MmapOptions::new().map_copy_read_only(&file).unwrap() }; 1573 | (&mmap[..]).read_exact(&mut read).unwrap(); 1574 | assert_eq!(nulls, &read); 1575 | 1576 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1577 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1578 | assert_eq!(nulls, &read); 1579 | } 1580 | 1581 | #[test] 1582 | fn map_offset() { 1583 | let tempdir = tempfile::tempdir().unwrap(); 1584 | let path = tempdir.path().join("mmap"); 1585 | 1586 | let file = OpenOptions::new() 1587 | .read(true) 1588 | .write(true) 1589 | .create(true) 1590 | .open(path) 1591 | .unwrap(); 1592 | 1593 | let offset = u32::MAX as u64 + 2; 1594 | let len = 5432; 1595 | file.set_len(offset + len as u64).unwrap(); 1596 | 1597 | // Check inferred length mmap. 1598 | let mmap = unsafe { MmapOptions::new().offset(offset).map_mut(&file).unwrap() }; 1599 | assert_eq!(len, mmap.len()); 1600 | 1601 | // Check explicit length mmap. 1602 | let mut mmap = unsafe { 1603 | MmapOptions::new() 1604 | .offset(offset) 1605 | .len(len) 1606 | .map_mut(&file) 1607 | .unwrap() 1608 | }; 1609 | assert_eq!(len, mmap.len()); 1610 | 1611 | let zeros = vec![0; len]; 1612 | let incr: Vec<_> = (0..len).map(|i| i as u8).collect(); 1613 | 1614 | // check that the mmap is empty 1615 | assert_eq!(&zeros[..], &mmap[..]); 1616 | 1617 | // write values into the mmap 1618 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1619 | 1620 | // read values back 1621 | assert_eq!(&incr[..], &mmap[..]); 1622 | } 1623 | 1624 | #[test] 1625 | fn index() { 1626 | let mut mmap = MmapMut::map_anon(128).unwrap(); 1627 | mmap[0] = 42; 1628 | assert_eq!(42, mmap[0]); 1629 | } 1630 | 1631 | #[test] 1632 | fn sync_send() { 1633 | let mmap = MmapMut::map_anon(129).unwrap(); 1634 | 1635 | fn is_sync_send(_val: T) 1636 | where 1637 | T: Sync + Send, 1638 | { 1639 | } 1640 | 1641 | is_sync_send(mmap); 1642 | } 1643 | 1644 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 1645 | fn jit_x86(mut mmap: MmapMut) { 1646 | mmap[0] = 0xB8; // mov eax, 0xAB 1647 | mmap[1] = 0xAB; 1648 | mmap[2] = 0x00; 1649 | mmap[3] = 0x00; 1650 | mmap[4] = 0x00; 1651 | mmap[5] = 0xC3; // ret 1652 | 1653 | let mmap = mmap.make_exec().expect("make_exec"); 1654 | 1655 | let jitfn: extern "C" fn() -> u8 = unsafe { mem::transmute(mmap.as_ptr()) }; 1656 | assert_eq!(jitfn(), 0xab); 1657 | } 1658 | 1659 | #[test] 1660 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 1661 | fn jit_x86_anon() { 1662 | jit_x86(MmapMut::map_anon(4096).unwrap()); 1663 | } 1664 | 1665 | #[test] 1666 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 1667 | fn jit_x86_file() { 1668 | let tempdir = tempfile::tempdir().unwrap(); 1669 | let mut options = OpenOptions::new(); 1670 | #[cfg(windows)] 1671 | options.access_mode(GENERIC_ALL); 1672 | 1673 | let file = options 1674 | .read(true) 1675 | .write(true) 1676 | .create(true) 1677 | .open(&tempdir.path().join("jit_x86")) 1678 | .expect("open"); 1679 | 1680 | file.set_len(4096).expect("set_len"); 1681 | jit_x86(unsafe { MmapMut::map_mut(&file).expect("map_mut") }); 1682 | } 1683 | 1684 | #[test] 1685 | fn mprotect_file() { 1686 | let tempdir = tempfile::tempdir().unwrap(); 1687 | let path = tempdir.path().join("mmap"); 1688 | 1689 | let mut options = OpenOptions::new(); 1690 | #[cfg(windows)] 1691 | options.access_mode(GENERIC_ALL); 1692 | 1693 | let mut file = options 1694 | .read(true) 1695 | .write(true) 1696 | .create(true) 1697 | .open(path) 1698 | .expect("open"); 1699 | file.set_len(256_u64).expect("set_len"); 1700 | 1701 | let mmap = unsafe { MmapMut::map_mut(&file).expect("map_mut") }; 1702 | 1703 | let mmap = mmap.make_read_only().expect("make_read_only"); 1704 | let mut mmap = mmap.make_mut().expect("make_mut"); 1705 | 1706 | let write = b"abc123"; 1707 | let mut read = [0u8; 6]; 1708 | 1709 | (&mut mmap[..]).write_all(write).unwrap(); 1710 | mmap.flush().unwrap(); 1711 | 1712 | // The mmap contains the write 1713 | (&mmap[..]).read_exact(&mut read).unwrap(); 1714 | assert_eq!(write, &read); 1715 | 1716 | // The file should contain the write 1717 | file.read_exact(&mut read).unwrap(); 1718 | assert_eq!(write, &read); 1719 | 1720 | // another mmap should contain the write 1721 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1722 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1723 | assert_eq!(write, &read); 1724 | 1725 | let mmap = mmap.make_exec().expect("make_exec"); 1726 | 1727 | drop(mmap); 1728 | } 1729 | 1730 | #[test] 1731 | fn mprotect_copy() { 1732 | let tempdir = tempfile::tempdir().unwrap(); 1733 | let path = tempdir.path().join("mmap"); 1734 | 1735 | let mut options = OpenOptions::new(); 1736 | #[cfg(windows)] 1737 | options.access_mode(GENERIC_ALL); 1738 | 1739 | let mut file = options 1740 | .read(true) 1741 | .write(true) 1742 | .create(true) 1743 | .open(path) 1744 | .expect("open"); 1745 | file.set_len(256_u64).expect("set_len"); 1746 | 1747 | let mmap = unsafe { MmapOptions::new().map_copy(&file).expect("map_mut") }; 1748 | 1749 | let mmap = mmap.make_read_only().expect("make_read_only"); 1750 | let mut mmap = mmap.make_mut().expect("make_mut"); 1751 | 1752 | let nulls = b"\0\0\0\0\0\0"; 1753 | let write = b"abc123"; 1754 | let mut read = [0u8; 6]; 1755 | 1756 | (&mut mmap[..]).write_all(write).unwrap(); 1757 | mmap.flush().unwrap(); 1758 | 1759 | // The mmap contains the write 1760 | (&mmap[..]).read_exact(&mut read).unwrap(); 1761 | assert_eq!(write, &read); 1762 | 1763 | // The file does not contain the write 1764 | file.read_exact(&mut read).unwrap(); 1765 | assert_eq!(nulls, &read); 1766 | 1767 | // another mmap does not contain the write 1768 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1769 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1770 | assert_eq!(nulls, &read); 1771 | 1772 | let mmap = mmap.make_exec().expect("make_exec"); 1773 | 1774 | drop(mmap); 1775 | } 1776 | 1777 | #[test] 1778 | fn mprotect_anon() { 1779 | let mmap = MmapMut::map_anon(256).expect("map_mut"); 1780 | 1781 | let mmap = mmap.make_read_only().expect("make_read_only"); 1782 | let mmap = mmap.make_mut().expect("make_mut"); 1783 | let mmap = mmap.make_exec().expect("make_exec"); 1784 | drop(mmap); 1785 | } 1786 | 1787 | #[test] 1788 | fn raw() { 1789 | let tempdir = tempfile::tempdir().unwrap(); 1790 | let path = tempdir.path().join("mmapraw"); 1791 | 1792 | let mut options = OpenOptions::new(); 1793 | let mut file = options 1794 | .read(true) 1795 | .write(true) 1796 | .create(true) 1797 | .open(path) 1798 | .expect("open"); 1799 | file.write_all(b"abc123").unwrap(); 1800 | let mmap = MmapOptions::new().map_raw(&file).unwrap(); 1801 | assert_eq!(mmap.len(), 6); 1802 | assert!(!mmap.as_ptr().is_null()); 1803 | assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a'); 1804 | } 1805 | 1806 | #[test] 1807 | fn raw_read_only() { 1808 | let tempdir = tempfile::tempdir().unwrap(); 1809 | let path = tempdir.path().join("mmaprawro"); 1810 | 1811 | File::create(&path).unwrap().write_all(b"abc123").unwrap(); 1812 | 1813 | let mmap = MmapOptions::new() 1814 | .map_raw_read_only(&File::open(&path).unwrap()) 1815 | .unwrap(); 1816 | 1817 | assert_eq!(mmap.len(), 6); 1818 | assert!(!mmap.as_ptr().is_null()); 1819 | assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a'); 1820 | } 1821 | 1822 | /// Something that relies on StableDeref 1823 | #[test] 1824 | #[cfg(feature = "stable_deref_trait")] 1825 | fn owning_ref() { 1826 | extern crate owning_ref; 1827 | 1828 | let mut map = MmapMut::map_anon(128).unwrap(); 1829 | map[10] = 42; 1830 | let owning = owning_ref::OwningRef::new(map); 1831 | let sliced = owning.map(|map| &map[10..20]); 1832 | assert_eq!(42, sliced[0]); 1833 | 1834 | let map = sliced.into_owner().make_read_only().unwrap(); 1835 | let owning = owning_ref::OwningRef::new(map); 1836 | let sliced = owning.map(|map| &map[10..20]); 1837 | assert_eq!(42, sliced[0]); 1838 | } 1839 | 1840 | #[test] 1841 | #[cfg(unix)] 1842 | fn advise() { 1843 | let expected_len = 128; 1844 | let tempdir = tempfile::tempdir().unwrap(); 1845 | let path = tempdir.path().join("mmap_advise"); 1846 | 1847 | let file = OpenOptions::new() 1848 | .read(true) 1849 | .write(true) 1850 | .create(true) 1851 | .open(path) 1852 | .unwrap(); 1853 | 1854 | file.set_len(expected_len as u64).unwrap(); 1855 | 1856 | // Test MmapMut::advise 1857 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1858 | mmap.advise(Advice::Random) 1859 | .expect("mmap advising should be supported on unix"); 1860 | 1861 | let len = mmap.len(); 1862 | assert_eq!(expected_len, len); 1863 | 1864 | let zeros = vec![0; len]; 1865 | let incr: Vec = (0..len as u8).collect(); 1866 | 1867 | // check that the mmap is empty 1868 | assert_eq!(&zeros[..], &mmap[..]); 1869 | 1870 | mmap.advise_range(Advice::Sequential, 0, mmap.len()) 1871 | .expect("mmap advising should be supported on unix"); 1872 | 1873 | // write values into the mmap 1874 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1875 | 1876 | // read values back 1877 | assert_eq!(&incr[..], &mmap[..]); 1878 | 1879 | // Set advice and Read from the read-only map 1880 | let mmap = unsafe { Mmap::map(&file).unwrap() }; 1881 | 1882 | mmap.advise(Advice::Random) 1883 | .expect("mmap advising should be supported on unix"); 1884 | 1885 | // read values back 1886 | assert_eq!(&incr[..], &mmap[..]); 1887 | } 1888 | 1889 | /// Returns true if a non-zero amount of memory is locked. 1890 | #[cfg(target_os = "linux")] 1891 | fn is_locked() -> bool { 1892 | let status = &std::fs::read_to_string("/proc/self/status") 1893 | .expect("/proc/self/status should be available"); 1894 | for line in status.lines() { 1895 | if line.starts_with("VmLck:") { 1896 | let numbers = line.replace(|c: char| !c.is_ascii_digit(), ""); 1897 | return numbers != "0"; 1898 | } 1899 | } 1900 | panic!("cannot get VmLck information") 1901 | } 1902 | 1903 | #[test] 1904 | #[cfg(unix)] 1905 | fn lock() { 1906 | let tempdir = tempfile::tempdir().unwrap(); 1907 | let path = tempdir.path().join("mmap_lock"); 1908 | 1909 | let file = OpenOptions::new() 1910 | .read(true) 1911 | .write(true) 1912 | .create(true) 1913 | .open(path) 1914 | .unwrap(); 1915 | file.set_len(128).unwrap(); 1916 | 1917 | let mmap = unsafe { Mmap::map(&file).unwrap() }; 1918 | #[cfg(target_os = "linux")] 1919 | assert!(!is_locked()); 1920 | 1921 | mmap.lock().expect("mmap lock should be supported on unix"); 1922 | #[cfg(target_os = "linux")] 1923 | assert!(is_locked()); 1924 | 1925 | mmap.lock() 1926 | .expect("mmap lock again should not cause problems"); 1927 | #[cfg(target_os = "linux")] 1928 | assert!(is_locked()); 1929 | 1930 | mmap.unlock() 1931 | .expect("mmap unlock should be supported on unix"); 1932 | #[cfg(target_os = "linux")] 1933 | assert!(!is_locked()); 1934 | 1935 | mmap.unlock() 1936 | .expect("mmap unlock again should not cause problems"); 1937 | #[cfg(target_os = "linux")] 1938 | assert!(!is_locked()); 1939 | } 1940 | 1941 | #[test] 1942 | #[cfg(target_os = "linux")] 1943 | fn remap_grow() { 1944 | use rustix::mm::MremapFlags; 1945 | 1946 | let initial_len = 128; 1947 | let final_len = 2000; 1948 | 1949 | let zeros = vec![0u8; final_len]; 1950 | let incr: Vec = (0..final_len).map(|v| v as u8).collect(); 1951 | 1952 | let file = tempfile::tempfile().unwrap(); 1953 | file.set_len(final_len as u64).unwrap(); 1954 | 1955 | let mut mmap = unsafe { MmapOptions::new().len(initial_len).map_mut(&file).unwrap() }; 1956 | assert_eq!(mmap.len(), initial_len); 1957 | assert_eq!(&mmap[..], &zeros[..initial_len]); 1958 | 1959 | unsafe { mmap.remap(final_len, MremapFlags::MAYMOVE).unwrap() }; 1960 | 1961 | // The size should have been updated 1962 | assert_eq!(mmap.len(), final_len); 1963 | 1964 | // Should still be all zeros 1965 | assert_eq!(&mmap[..], &zeros); 1966 | 1967 | // Write out to the whole expanded slice. 1968 | mmap.copy_from_slice(&incr); 1969 | } 1970 | 1971 | #[test] 1972 | #[cfg(target_os = "linux")] 1973 | fn remap_shrink() { 1974 | use rustix::mm::MremapFlags; 1975 | 1976 | let initial_len = 20000; 1977 | let final_len = 400; 1978 | 1979 | let incr: Vec = (0..final_len).map(|v| v as u8).collect(); 1980 | 1981 | let file = tempfile::tempfile().unwrap(); 1982 | file.set_len(initial_len as u64).unwrap(); 1983 | 1984 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1985 | assert_eq!(mmap.len(), initial_len); 1986 | 1987 | unsafe { mmap.remap(final_len, MremapFlags::empty()).unwrap() }; 1988 | assert_eq!(mmap.len(), final_len); 1989 | 1990 | // Check that the mmap is still writable along the slice length 1991 | mmap.copy_from_slice(&incr); 1992 | } 1993 | 1994 | #[test] 1995 | #[cfg(target_os = "linux")] 1996 | #[cfg(target_pointer_width = "32")] 1997 | fn remap_len_overflow() { 1998 | use rustix::mm::MremapFlags; 1999 | 2000 | let file = tempfile::tempfile().unwrap(); 2001 | file.set_len(1024).unwrap(); 2002 | let mut mmap = unsafe { MmapOptions::new().len(1024).map(&file).unwrap() }; 2003 | 2004 | let res = unsafe { mmap.remap(0x80000000, MremapFlags::MAYMOVE) }; 2005 | assert_eq!( 2006 | res.unwrap_err().to_string(), 2007 | "memory map length overflows isize" 2008 | ); 2009 | 2010 | assert_eq!(mmap.len(), 1024); 2011 | } 2012 | 2013 | #[test] 2014 | #[cfg(target_os = "linux")] 2015 | fn remap_with_offset() { 2016 | use rustix::mm::MremapFlags; 2017 | 2018 | let offset = 77; 2019 | let initial_len = 128; 2020 | let final_len = 2000; 2021 | 2022 | let zeros = vec![0u8; final_len]; 2023 | let incr: Vec = (0..final_len).map(|v| v as u8).collect(); 2024 | 2025 | let file = tempfile::tempfile().unwrap(); 2026 | file.set_len(final_len as u64 + offset).unwrap(); 2027 | 2028 | let mut mmap = unsafe { 2029 | MmapOptions::new() 2030 | .len(initial_len) 2031 | .offset(offset) 2032 | .map_mut(&file) 2033 | .unwrap() 2034 | }; 2035 | assert_eq!(mmap.len(), initial_len); 2036 | assert_eq!(&mmap[..], &zeros[..initial_len]); 2037 | 2038 | unsafe { mmap.remap(final_len, MremapFlags::MAYMOVE).unwrap() }; 2039 | 2040 | // The size should have been updated 2041 | assert_eq!(mmap.len(), final_len); 2042 | 2043 | // Should still be all zeros 2044 | assert_eq!(&mmap[..], &zeros); 2045 | 2046 | // Write out to the whole expanded slice. 2047 | mmap.copy_from_slice(&incr); 2048 | } 2049 | } 2050 | --------------------------------------------------------------------------------