├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples └── cat.rs └── src ├── advice.rs ├── lib.rs ├── stub.rs ├── unix.rs └── windows.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | 15 | - name: Install toolchain 16 | uses: dtolnay/rust-toolchain@stable 17 | with: 18 | targets: x86_64-pc-windows-gnu 19 | components: clippy, rustfmt 20 | 21 | - name: Run checks 22 | env: 23 | CLIPPY_OPTS: --all-targets 24 | run: | 25 | cargo fmt --check 26 | cargo clippy $CLIPPY_OPTS 27 | cargo clippy --target x86_64-pc-windows-gnu $CLIPPY_OPTS 28 | 29 | test-win: 30 | runs-on: windows-latest 31 | strategy: 32 | matrix: 33 | target: 34 | - i686-pc-windows-gnu 35 | - i686-pc-windows-msvc 36 | - x86_64-pc-windows-gnu 37 | - x86_64-pc-windows-msvc 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v3 41 | 42 | - name: Install toolchain 43 | uses: dtolnay/rust-toolchain@master 44 | with: 45 | toolchain: stable-${{ matrix.target }} 46 | 47 | - name: Run tests 48 | run: cargo test --all-features 49 | 50 | test-macos: 51 | runs-on: macos-latest 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v3 55 | 56 | - name: Install toolchain 57 | uses: dtolnay/rust-toolchain@stable 58 | 59 | - name: Run tests 60 | run: cargo test --all-features 61 | 62 | test-linux: 63 | runs-on: ubuntu-latest 64 | strategy: 65 | matrix: 66 | target: 67 | - i686-unknown-linux-gnu 68 | - i686-unknown-linux-musl 69 | - x86_64-unknown-linux-gnu 70 | - x86_64-unknown-linux-musl 71 | - x86_64-linux-android 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v3 75 | 76 | # We need nightly for -Zminimal-versions 77 | - name: Install nightly toolchain 78 | uses: dtolnay/rust-toolchain@nightly 79 | 80 | - name: Install toolchain 81 | uses: dtolnay/rust-toolchain@stable 82 | with: 83 | target: ${{ matrix.target }} 84 | 85 | - name: Install multilib 86 | if: ${{ contains(matrix.target, 'i686-unknown-linux-') }} 87 | run: | 88 | sudo apt update -yqq 89 | sudo apt install gcc-multilib 90 | 91 | # Make sure we're testing against the minimal libc version. 92 | - name: Generate Cargo.lock 93 | run: cargo +nightly update -Zminimal-versions 94 | 95 | - name: Run tests 96 | if: ${{ !contains(matrix.target, 'android') }} 97 | run: cargo test --locked --all-features --target ${{ matrix.target }} 98 | 99 | # Just make sure it builds. Not tests running. 100 | - name: Run tests 101 | if: ${{ contains(matrix.target, 'android') }} 102 | run: cargo build --locked --all-features --target ${{ matrix.target }} 103 | 104 | check-stub: 105 | runs-on: ubuntu-latest 106 | steps: 107 | - name: Checkout 108 | uses: actions/checkout@v3 109 | 110 | - name: Install toolchain 111 | uses: dtolnay/rust-toolchain@stable 112 | with: 113 | targets: wasm32-unknown-unknown 114 | 115 | - name: Run check 116 | run: cargo check --all-features --target wasm32-unknown-unknown 117 | 118 | test-msrv: 119 | runs-on: ubuntu-latest 120 | steps: 121 | - name: Checkout 122 | uses: actions/checkout@v3 123 | 124 | - name: Install toolchain 125 | uses: dtolnay/rust-toolchain@1.63.0 126 | 127 | # do not test, because dev-dependencies do not follow MSRV 128 | - name: Build 129 | run: cargo build --all-features 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /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 | ## [0.9.6] - 2025-05-14 8 | ### Fixed 9 | - Fix huge page mappings with non-default page-bits. 10 | 11 | ## [0.9.5] - 2024-09-13 12 | ### Added 13 | - `Advise::is_supported` and `UncheckedAdvice::is_supported`. Linux only. 14 | [@xzfc](https://github.com/xzfc) 15 | - Documentation improvements. 16 | [@RalfJung](https://github.com/RalfJung) 17 | [@betelgeuse](https://github.com/betelgeuse) 18 | [@ADSteele916](https://github.com/ADSteele916) 19 | 20 | ## [0.9.4] - 2024-01-25 21 | ### Changed 22 | - The `libc` crate >= 0.2.151 is required now. 23 | 24 | ### Fixed 25 | - Build on Android with an older `libc` crate. 26 | 27 | ## [0.9.3] - 2023-12-19 28 | ### Fixed 29 | - Build on Android. 30 | 31 | ## [0.9.2] - 2023-12-17 32 | ### Fixed 33 | - Build on FreeBSD. 34 | 35 | ## [0.9.1] - 2023-12-16 36 | ### Changed 37 | - Added `MmapOptions::huge` method to support mapping hugetlb. Linux only. 38 | [@ollie-etl](https://github.com/ollie-etl) 39 | [@oliverbunting](https://github.com/oliverbunting) 40 | 41 | ## [0.9.0] - 2023-10-03 42 | ### Changed 43 | - The `Advice` struct was split into two enums: `Advice` and `UncheckedAdvice`.
44 | `Advice` can be passed to safe `advise` and `advise_range` methods. 45 | And `UncheckedAdvice` can be passed to unsafe `unchecked_advise` 46 | and `unchecked_advise_range` methods.
47 | [@adamreichold](https://github.com/adamreichold) 48 | 49 | ## [0.8.0] - 2023-09-25 50 | ### Changed 51 | - The `Advice` type is a struct and not an enum now. 52 | [@adamreichold](https://github.com/adamreichold) 53 | 54 | ### Fixed 55 | - Some of the `Advise` variants were unsound and now require `unsafe` to be constructed. 56 | [@adamreichold](https://github.com/adamreichold) 57 | 58 | ## [0.7.1] - 2023-06-24 59 | ### Fixed 60 | - Mapping beyond 4GB offset on 32 bit glibc. Linux-only. 61 | [@lvella](https://github.com/lvella) 62 | 63 | ## [0.7.0] - 2023-06-08 64 | ### Added 65 | - `Mmap::remap`, `MmapMut::remap` and `MmapRaw::remap`. Linux-only. 66 | [@Phantomical](https://github.com/Phantomical) 67 | - `Advice::PopulateRead` and `Advice::PopulateWrite`. Linux-only. 68 | [@Jesse-Bakker](https://github.com/Jesse-Bakker) 69 | 70 | ### Changed 71 | - libc crate >= 0.2.143 is required now. 72 | 73 | ## [0.6.2] - 2023-05-24 74 | ### Fixed 75 | - Alignment for empty files on Windows. 76 | [@timvisee](https://github.com/timvisee) 77 | 78 | ## [0.6.1] - 2023-05-10 79 | ### Added 80 | - Add `MmapOptions::map_raw_read_only` to avoid intermediate invalid `Mmap` instances. 81 | [@adamreichold](https://github.com/adamreichold) 82 | 83 | ## [0.6.0] - 2023-05-09 84 | ### Changed 85 | - `lock()` and `unlock` methods require `&self` and not `&mut self` now. 86 | [@timvisee](https://github.com/timvisee) 87 | 88 | ## [0.5.10] - 2023-02-22 89 | ### Added 90 | - `MmapOptions::map_anon` accounts for `populate` on Linux now. 91 | [@jsgf](https://github.com/jsgf) 92 | 93 | ## [0.5.9] - 2023-02-17 94 | ### Added 95 | - `From for MmapRaw` and `From for MmapRaw`. 96 | [@swlynch99](https://github.com/swlynch99) 97 | - `Mmap::advise_range`, `MmapMut::advise_range`, `MmapRaw::advise_range`. 98 | [@ho-229](https://github.com/ho-229) 99 | 100 | ## [0.5.8] - 2022-11-09 101 | ### Added 102 | - `MmapRaw::advise`, `MmapRaw::lock` and `MmapRaw::unlock`. 103 | [@diwic](https://github.com/diwic) 104 | - Improve `MmapMut::make_exec` documentation. 105 | 106 | ## [0.5.7] - 2022-08-15 107 | ### Changed 108 | - Simplify file size retrieving code. 109 | [@saethlin](https://github.com/saethlin) 110 | 111 | ## [0.5.6] - 2022-08-11 112 | ### Added 113 | - Memory locking and unlocking. See `Mmap::lock`, `Mmap::unlock`, 114 | `MmapMut::lock` and `MmapMut::unlock`. 115 | [@vmx](https://github.com/vmx) 116 | 117 | ## [0.5.5] - 2022-07-09 118 | ### Fixed 119 | - Limit mapping length to `isize::MAX` to prevent undefined behavior 120 | on calling `std::slice::from_raw_parts`. Technically affects only 32-bit systems. 121 | [@adamreichold](https://github.com/adamreichold) 122 | 123 | ## [0.5.4] - 2022-06-04 124 | ### Added 125 | - Add madvice operations specific to Darwin. [@turbocool3r](https://github.com/turbocool3r) 126 | - Implement common traits for the `Advice` enum. [@nyurik](https://github.com/nyurik) 127 | 128 | ### Changed 129 | - Make stub implementation Infallible. [@coolreader18](https://github.com/coolreader18) 130 | - Use `tempfile` crate instead of `tempdir` in tests. 131 | [@alexanderkjall](https://github.com/alexanderkjall) 132 | 133 | ## [0.5.3] - 2022-02-10 134 | ### Added 135 | - `Mmap::advise` and `MmapMut::advise`. [@nyurik](https://github.com/nyurik) 136 | 137 | ## [0.5.2] - 2022-01-10 138 | ### Added 139 | - `flush`, `flush_async`, `flush_range` and `flush_async_range` to `MmapRaw` matching 140 | the corresponding methods on `MmapMut`. 141 | [@cberner](https://github.com/cberner) 142 | 143 | ## [0.5.1] - 2022-01-09 144 | ### Fixed 145 | - Explicitly call `fstat64` on Linux, emscripten and l4re targets. 146 | [@adamreichold](https://github.com/adamreichold) 147 | 148 | ## [0.5.0] - 2021-09-19 149 | ### Added 150 | - `MmapOptions` accepts any type that supports `RawHandle`/`RawFd` returning now. 151 | This allows using `memmap2` not only with Rust std types, but also with 152 | [async-std](https://github.com/async-rs/async-std) one. 153 | [@adamreichold](https://github.com/adamreichold) 154 | - (unix) Memoize page size to avoid repeatedly calling into sysconf machinery. 155 | [@adamreichold](https://github.com/adamreichold) 156 | 157 | ### Changed 158 | - (win) Use `std::os::windows::io::AsRawHandle` directly, without relying on `std::fs::File`. 159 | [@adamreichold](https://github.com/adamreichold) 160 | - Do not panic when failing to release resources in Drop impls. 161 | [@adamreichold](https://github.com/adamreichold) 162 | 163 | ## [0.4.0] - 2021-09-16 164 | ### Added 165 | - Optional [`StableDeref`](https://github.com/storyyeller/stable_deref_trait) support. 166 | [@SimonSapin](https://github.com/SimonSapin) 167 | 168 | ### Changed 169 | - Mapping of zero-sized files is no longer an error. 170 | [@SimonSapin](https://github.com/SimonSapin) 171 | - MSRV changed from 1.31 to 1.36 172 | 173 | ## [0.3.1] - 2021-08-15 174 | ### Fixed 175 | - Integer overflow during file length calculation on 32bit targets. 176 | - Stub implementation. [@Mrmaxmeier](https://github.com/Mrmaxmeier) 177 | 178 | ## [0.3.0] - 2021-06-10 179 | ### Changed 180 | - `MmapOptions` allows mapping using Unix descriptors and not only `std::fs::File` now. 181 | [@mripard](https://github.com/mripard) 182 | 183 | ## [0.2.3] - 2021-05-24 184 | ### Added 185 | - Allow compilation on unsupported platforms. 186 | The code will panic on access just like in `std`. 187 | [@jcaesar](https://github.com/jcaesar) 188 | 189 | ## [0.2.2] - 2021-04-03 190 | ### Added 191 | - `MmapOptions::populate`. [@adamreichold](https://github.com/adamreichold) 192 | 193 | ### Fixed 194 | - Fix alignment computation for `flush_async` to match `flush`. 195 | [@adamreichold](https://github.com/adamreichold) 196 | 197 | ## [0.2.1] - 2021-02-08 198 | ### Added 199 | - `MmapOptions::map_raw` and `MmapRaw`. [@diwic](https://github.com/diwic) 200 | 201 | ## [0.2.0] - 2020-12-19 202 | ### Changed 203 | - MSRV is 1.31 now (edition 2018). 204 | - Make anonymous memory maps private by default on unix. [@CensoredUsername](https://github.com/CensoredUsername) 205 | - Add `map_copy_read_only`. [@zseri](https://github.com/zseri) 206 | 207 | ## 0.1.0 - 2020-01-18 208 | ### Added 209 | - Fork [memmap-rs](https://github.com/danburkert/memmap-rs). 210 | 211 | ### Changed 212 | - Use `LICENSE-APACHE` instead of `README.md` for some tests since it's immutable. 213 | 214 | ### Removed 215 | - `winapi` dependency. [memmap-rs/pull/89](https://github.com/danburkert/memmap-rs/pull/89) 216 | 217 | [Unreleased]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.5...HEAD 218 | [0.9.5]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.4...v0.9.5 219 | [0.9.4]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.3...v0.9.4 220 | [0.9.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.2...v0.9.3 221 | [0.9.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.1...v0.9.2 222 | [0.9.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.0...v0.9.1 223 | [0.9.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.8.0...v0.9.0 224 | [0.8.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.7.1...v0.8.0 225 | [0.7.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.7.0...v0.7.1 226 | [0.7.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.6.2...v0.7.0 227 | [0.6.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.6.1...v0.6.2 228 | [0.6.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.6.0...v0.6.1 229 | [0.6.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.10...v0.6.0 230 | [0.5.10]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.9...v0.5.10 231 | [0.5.9]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.8...v0.5.9 232 | [0.5.8]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.7...v0.5.8 233 | [0.5.7]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.6...v0.5.7 234 | [0.5.6]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.5...v0.5.6 235 | [0.5.5]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.4...v0.5.5 236 | [0.5.4]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.3...v0.5.4 237 | [0.5.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.2...v0.5.3 238 | [0.5.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.1...v0.5.2 239 | [0.5.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.0...v0.5.1 240 | [0.5.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.4.0...v0.5.0 241 | [0.4.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.1...v0.4.0 242 | [0.3.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.0...v0.3.1 243 | [0.3.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.3...v0.3.0 244 | [0.2.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.2...v0.2.3 245 | [0.2.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.1...v0.2.2 246 | [0.2.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.0...v0.2.1 247 | [0.2.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.1.0...v0.2.0 248 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memmap2" 3 | version = "0.9.6" 4 | authors = [ 5 | "Dan Burkert ", 6 | "Yevhenii Reizner ", 7 | ] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/RazrFalcon/memmap2-rs" 10 | documentation = "https://docs.rs/memmap2" 11 | description = "Cross-platform Rust API for memory-mapped file IO" 12 | keywords = ["mmap", "memory-map", "io", "file"] 13 | edition = "2021" 14 | rust-version = "1.63" 15 | 16 | [dependencies] 17 | stable_deref_trait = { version = "1.0", optional = true } 18 | 19 | [target.'cfg(unix)'.dependencies] 20 | libc = "0.2.151" 21 | 22 | [dev-dependencies] 23 | tempfile = "3" 24 | owning_ref = "0.4.1" 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # memmap2 2 | ![Build Status](https://github.com/RazrFalcon/memmap2-rs/workflows/Rust/badge.svg) 3 | [![Crates.io](https://img.shields.io/crates/v/memmap2.svg)](https://crates.io/crates/memmap2) 4 | [![Documentation](https://docs.rs/memmap2/badge.svg)](https://docs.rs/memmap2) 5 | [![MSRV 1.63.0](https://img.shields.io/badge/msrv-1.63.0-dea584.svg?logo=rust)](https://github.com/rust-lang/rust/releases/tag/1.63.0) 6 | 7 | A Rust library for cross-platform memory mapped IO. 8 | 9 | This is a **fork** of the [memmap-rs](https://github.com/danburkert/memmap-rs) crate. 10 | 11 | ## Features 12 | 13 | - [x] file-backed memory maps 14 | - [x] anonymous memory maps 15 | - [x] synchronous and asynchronous flushing 16 | - [x] copy-on-write memory maps 17 | - [x] read-only memory maps 18 | - [x] stack support (`MAP_STACK` on unix) 19 | - [x] executable memory maps 20 | - [x] huge page support (linux only) 21 | 22 | A list of supported/tested targets can be found in [Actions](https://github.com/RazrFalcon/memmap2-rs/actions). 23 | 24 | ## License 25 | 26 | `memmap2` is primarily distributed under the terms of both the MIT license and the 27 | Apache License (Version 2.0). 28 | 29 | See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details. 30 | 31 | Copyright (c) 2020 Yevhenii Reizner 32 | 33 | Copyright (c) 2015 Dan Burkert 34 | -------------------------------------------------------------------------------- /examples/cat.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::{self, Write}; 4 | 5 | use memmap2::Mmap; 6 | 7 | /// Output a file's contents to stdout. The file path must be provided as the first process 8 | /// argument. 9 | fn main() { 10 | let path = env::args() 11 | .nth(1) 12 | .expect("supply a single path as the program argument"); 13 | 14 | let file = File::open(path).expect("failed to open the file"); 15 | 16 | let mmap = unsafe { Mmap::map(&file).expect("failed to map the file") }; 17 | 18 | io::stdout() 19 | .write_all(&mmap[..]) 20 | .expect("failed to output the file contents"); 21 | } 22 | -------------------------------------------------------------------------------- /src/advice.rs: -------------------------------------------------------------------------------- 1 | /// Values supported by [`Mmap::advise`][crate::Mmap::advise] and [`MmapMut::advise`][crate::MmapMut::advise] functions. 2 | /// 3 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 4 | #[repr(i32)] 5 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 6 | pub enum Advice { 7 | /// **MADV_NORMAL** 8 | /// 9 | /// No special treatment. This is the default. 10 | Normal = libc::MADV_NORMAL, 11 | 12 | /// **MADV_RANDOM** 13 | /// 14 | /// Expect page references in random order. (Hence, read 15 | /// ahead may be less useful than normally.) 16 | Random = libc::MADV_RANDOM, 17 | 18 | /// **MADV_SEQUENTIAL** 19 | /// 20 | /// Expect page references in sequential order. (Hence, pages 21 | /// in the given range can be aggressively read ahead, and may 22 | /// be freed soon after they are accessed.) 23 | Sequential = libc::MADV_SEQUENTIAL, 24 | 25 | /// **MADV_WILLNEED** 26 | /// 27 | /// Expect access in the near future. (Hence, it might be a 28 | /// good idea to read some pages ahead.) 29 | WillNeed = libc::MADV_WILLNEED, 30 | 31 | /// **MADV_DONTFORK** - Linux only (since Linux 2.6.16) 32 | /// 33 | /// Do not make the pages in this range available to the child 34 | /// after a fork(2). This is useful to prevent copy-on-write 35 | /// semantics from changing the physical location of a page if 36 | /// the parent writes to it after a fork(2). (Such page 37 | /// relocations cause problems for hardware that DMAs into the 38 | /// page.) 39 | #[cfg(target_os = "linux")] 40 | DontFork = libc::MADV_DONTFORK, 41 | 42 | /// **MADV_DOFORK** - Linux only (since Linux 2.6.16) 43 | /// 44 | /// Undo the effect of MADV_DONTFORK, restoring the default 45 | /// behavior, whereby a mapping is inherited across fork(2). 46 | #[cfg(target_os = "linux")] 47 | DoFork = libc::MADV_DOFORK, 48 | 49 | /// **MADV_MERGEABLE** - Linux only (since Linux 2.6.32) 50 | /// 51 | /// Enable Kernel Samepage Merging (KSM) for the pages in the 52 | /// range specified by addr and length. The kernel regularly 53 | /// scans those areas of user memory that have been marked as 54 | /// mergeable, looking for pages with identical content. 55 | /// These are replaced by a single write-protected page (which 56 | /// is automatically copied if a process later wants to update 57 | /// the content of the page). KSM merges only private 58 | /// anonymous pages (see mmap(2)). 59 | /// 60 | /// The KSM feature is intended for applications that generate 61 | /// many instances of the same data (e.g., virtualization 62 | /// systems such as KVM). It can consume a lot of processing 63 | /// power; use with care. See the Linux kernel source file 64 | /// Documentation/admin-guide/mm/ksm.rst for more details. 65 | /// 66 | /// The MADV_MERGEABLE and MADV_UNMERGEABLE operations are 67 | /// available only if the kernel was configured with 68 | /// CONFIG_KSM. 69 | #[cfg(target_os = "linux")] 70 | Mergeable = libc::MADV_MERGEABLE, 71 | 72 | /// **MADV_UNMERGEABLE** - Linux only (since Linux 2.6.32) 73 | /// 74 | /// Undo the effect of an earlier MADV_MERGEABLE operation on 75 | /// the specified address range; KSM unmerges whatever pages 76 | /// it had merged in the address range specified by addr and 77 | /// length. 78 | #[cfg(target_os = "linux")] 79 | Unmergeable = libc::MADV_UNMERGEABLE, 80 | 81 | /// **MADV_HUGEPAGE** - Linux only (since Linux 2.6.38) 82 | /// 83 | /// Enable Transparent Huge Pages (THP) for pages in the range 84 | /// specified by addr and length. Currently, Transparent Huge 85 | /// Pages work only with private anonymous pages (see 86 | /// mmap(2)). The kernel will regularly scan the areas marked 87 | /// as huge page candidates to replace them with huge pages. 88 | /// The kernel will also allocate huge pages directly when the 89 | /// region is naturally aligned to the huge page size (see 90 | /// posix_memalign(2)). 91 | /// 92 | /// This feature is primarily aimed at applications that use 93 | /// large mappings of data and access large regions of that 94 | /// memory at a time (e.g., virtualization systems such as 95 | /// QEMU). It can very easily waste memory (e.g., a 2 MB 96 | /// mapping that only ever accesses 1 byte will result in 2 MB 97 | /// of wired memory instead of one 4 KB page). See the Linux 98 | /// kernel source file 99 | /// Documentation/admin-guide/mm/transhuge.rst for more 100 | /// details. 101 | /// 102 | /// Most common kernels configurations provide MADV_HUGEPAGE- 103 | /// style behavior by default, and thus MADV_HUGEPAGE is 104 | /// normally not necessary. It is mostly intended for 105 | /// embedded systems, where MADV_HUGEPAGE-style behavior may 106 | /// not be enabled by default in the kernel. On such systems, 107 | /// this flag can be used in order to selectively enable THP. 108 | /// Whenever MADV_HUGEPAGE is used, it should always be in 109 | /// regions of memory with an access pattern that the 110 | /// developer knows in advance won't risk to increase the 111 | /// memory footprint of the application when transparent 112 | /// hugepages are enabled. 113 | /// 114 | /// The MADV_HUGEPAGE and MADV_NOHUGEPAGE operations are 115 | /// available only if the kernel was configured with 116 | /// CONFIG_TRANSPARENT_HUGEPAGE. 117 | #[cfg(target_os = "linux")] 118 | HugePage = libc::MADV_HUGEPAGE, 119 | 120 | /// **MADV_NOHUGEPAGE** - Linux only (since Linux 2.6.38) 121 | /// 122 | /// Ensures that memory in the address range specified by addr 123 | /// and length will not be backed by transparent hugepages. 124 | #[cfg(target_os = "linux")] 125 | NoHugePage = libc::MADV_NOHUGEPAGE, 126 | 127 | /// **MADV_DONTDUMP** - Linux only (since Linux 3.4) 128 | /// 129 | /// Exclude from a core dump those pages in the range 130 | /// specified by addr and length. This is useful in 131 | /// applications that have large areas of memory that are 132 | /// known not to be useful in a core dump. The effect of 133 | /// **MADV_DONTDUMP** takes precedence over the bit mask that is 134 | /// set via the `/proc/[pid]/coredump_filter` file (see 135 | /// core(5)). 136 | #[cfg(target_os = "linux")] 137 | DontDump = libc::MADV_DONTDUMP, 138 | 139 | /// **MADV_DODUMP** - Linux only (since Linux 3.4) 140 | /// 141 | /// Undo the effect of an earlier MADV_DONTDUMP. 142 | #[cfg(target_os = "linux")] 143 | DoDump = libc::MADV_DODUMP, 144 | 145 | /// **MADV_HWPOISON** - Linux only (since Linux 2.6.32) 146 | /// 147 | /// Poison the pages in the range specified by addr and length 148 | /// and handle subsequent references to those pages like a 149 | /// hardware memory corruption. This operation is available 150 | /// only for privileged (CAP_SYS_ADMIN) processes. This 151 | /// operation may result in the calling process receiving a 152 | /// SIGBUS and the page being unmapped. 153 | /// 154 | /// This feature is intended for testing of memory error- 155 | /// handling code; it is available only if the kernel was 156 | /// configured with CONFIG_MEMORY_FAILURE. 157 | #[cfg(target_os = "linux")] 158 | HwPoison = libc::MADV_HWPOISON, 159 | 160 | /// **MADV_POPULATE_READ** - Linux only (since Linux 5.14) 161 | /// 162 | /// Populate (prefault) page tables readable, faulting in all 163 | /// pages in the range just as if manually reading from each 164 | /// page; however, avoid the actual memory access that would have 165 | /// been performed after handling the fault. 166 | /// 167 | /// In contrast to MAP_POPULATE, MADV_POPULATE_READ does not hide 168 | /// errors, can be applied to (parts of) existing mappings and 169 | /// will always populate (prefault) page tables readable. One 170 | /// example use case is prefaulting a file mapping, reading all 171 | /// file content from disk; however, pages won't be dirtied and 172 | /// consequently won't have to be written back to disk when 173 | /// evicting the pages from memory. 174 | /// 175 | /// Depending on the underlying mapping, map the shared zeropage, 176 | /// preallocate memory or read the underlying file; files with 177 | /// holes might or might not preallocate blocks. If populating 178 | /// fails, a SIGBUS signal is not generated; instead, an error is 179 | /// returned. 180 | /// 181 | /// If MADV_POPULATE_READ succeeds, all page tables have been 182 | /// populated (prefaulted) readable once. If MADV_POPULATE_READ 183 | /// fails, some page tables might have been populated. 184 | /// 185 | /// MADV_POPULATE_READ cannot be applied to mappings without read 186 | /// permissions and special mappings, for example, mappings 187 | /// marked with kernel-internal flags such as VM_PFNMAP or VM_IO, 188 | /// or secret memory regions created using memfd_secret(2). 189 | /// 190 | /// Note that with MADV_POPULATE_READ, the process can be killed 191 | /// at any moment when the system runs out of memory. 192 | #[cfg(target_os = "linux")] 193 | PopulateRead = libc::MADV_POPULATE_READ, 194 | 195 | /// **MADV_POPULATE_WRITE** - Linux only (since Linux 5.14) 196 | /// 197 | /// Populate (prefault) page tables writable, faulting in all 198 | /// pages in the range just as if manually writing to each each 199 | /// page; however, avoid the actual memory access that would have 200 | /// been performed after handling the fault. 201 | /// 202 | /// In contrast to MAP_POPULATE, MADV_POPULATE_WRITE does not 203 | /// hide errors, can be applied to (parts of) existing mappings 204 | /// and will always populate (prefault) page tables writable. 205 | /// One example use case is preallocating memory, breaking any 206 | /// CoW (Copy on Write). 207 | /// 208 | /// Depending on the underlying mapping, preallocate memory or 209 | /// read the underlying file; files with holes will preallocate 210 | /// blocks. If populating fails, a SIGBUS signal is not gener‐ 211 | /// ated; instead, an error is returned. 212 | /// 213 | /// If MADV_POPULATE_WRITE succeeds, all page tables have been 214 | /// populated (prefaulted) writable once. If MADV_POPULATE_WRITE 215 | /// fails, some page tables might have been populated. 216 | /// 217 | /// MADV_POPULATE_WRITE cannot be applied to mappings without 218 | /// write permissions and special mappings, for example, mappings 219 | /// marked with kernel-internal flags such as VM_PFNMAP or VM_IO, 220 | /// or secret memory regions created using memfd_secret(2). 221 | /// 222 | /// Note that with MADV_POPULATE_WRITE, the process can be killed 223 | /// at any moment when the system runs out of memory. 224 | #[cfg(target_os = "linux")] 225 | PopulateWrite = libc::MADV_POPULATE_WRITE, 226 | 227 | /// **MADV_ZERO_WIRED_PAGES** - Darwin only 228 | /// 229 | /// Indicates that the application would like the wired pages in this address range to be 230 | /// zeroed out if the address range is deallocated without first unwiring the pages (i.e. 231 | /// a munmap(2) without a preceding munlock(2) or the application quits). This is used 232 | /// with `madvise()` system call. 233 | #[cfg(any(target_os = "macos", target_os = "ios"))] 234 | ZeroWiredPages = libc::MADV_ZERO_WIRED_PAGES, 235 | } 236 | 237 | /// Values supported by [`Mmap::unsafe_advise`][crate::Mmap::unsafe_advise] and [`MmapMut::unsafe_advise`][crate::MmapMut::unsafe_advise] functions. 238 | /// 239 | /// These flags can be passed to the [madvise (2)][man_page] system call 240 | /// and effects on the mapped pages which are conceptually writes, 241 | /// i.e. the change the observable contents of these pages which 242 | /// implies undefined behaviour if the mapping is still borrowed. 243 | /// 244 | /// Hence, these potentially unsafe flags must be used with the unsafe 245 | /// methods and the programmer has to justify that the code 246 | /// does not keep any borrows of the mapping active while the mapped pages 247 | /// are updated by the kernel's memory management subsystem. 248 | /// 249 | /// [man_page]: https://man7.org/linux/man-pages/man2/madvise.2.html 250 | #[repr(i32)] 251 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 252 | pub enum UncheckedAdvice { 253 | /// **MADV_DONTNEED** 254 | /// 255 | /// Do not expect access in the near future. (For the time 256 | /// being, the application is finished with the given range, 257 | /// so the kernel can free resources associated with it.) 258 | /// 259 | /// After a successful MADV_DONTNEED operation, the semantics 260 | /// of memory access in the specified region are changed: 261 | /// subsequent accesses of pages in the range will succeed, 262 | /// but will result in either repopulating the memory contents 263 | /// from the up-to-date contents of the underlying mapped file 264 | /// (for shared file mappings, shared anonymous mappings, and 265 | /// shmem-based techniques such as System V shared memory 266 | /// segments) or zero-fill-on-demand pages for anonymous 267 | /// private mappings. 268 | /// 269 | /// Note that, when applied to shared mappings, MADV_DONTNEED 270 | /// might not lead to immediate freeing of the pages in the 271 | /// range. The kernel is free to delay freeing the pages 272 | /// until an appropriate moment. The resident set size (RSS) 273 | /// of the calling process will be immediately reduced 274 | /// however. 275 | /// 276 | /// **MADV_DONTNEED** cannot be applied to locked pages, Huge TLB 277 | /// pages, or VM_PFNMAP pages. (Pages marked with the kernel- 278 | /// internal VM_PFNMAP flag are special memory areas that are 279 | /// not managed by the virtual memory subsystem. Such pages 280 | /// are typically created by device drivers that map the pages 281 | /// into user space.) 282 | /// 283 | /// # Safety 284 | /// 285 | /// Using the returned value with conceptually write to the 286 | /// mapped pages, i.e. borrowing the mapping when the pages 287 | /// are freed results in undefined behaviour. 288 | DontNeed = libc::MADV_DONTNEED, 289 | 290 | // 291 | // The rest are Linux-specific 292 | // 293 | /// **MADV_FREE** - Linux (since Linux 4.5) and Darwin 294 | /// 295 | /// The application no longer requires the pages in the range 296 | /// specified by addr and len. The kernel can thus free these 297 | /// pages, but the freeing could be delayed until memory 298 | /// pressure occurs. For each of the pages that has been 299 | /// marked to be freed but has not yet been freed, the free 300 | /// operation will be canceled if the caller writes into the 301 | /// page. After a successful MADV_FREE operation, any stale 302 | /// data (i.e., dirty, unwritten pages) will be lost when the 303 | /// kernel frees the pages. However, subsequent writes to 304 | /// pages in the range will succeed and then kernel cannot 305 | /// free those dirtied pages, so that the caller can always 306 | /// see just written data. If there is no subsequent write, 307 | /// the kernel can free the pages at any time. Once pages in 308 | /// the range have been freed, the caller will see zero-fill- 309 | /// on-demand pages upon subsequent page references. 310 | /// 311 | /// The MADV_FREE operation can be applied only to private 312 | /// anonymous pages (see mmap(2)). In Linux before version 313 | /// 4.12, when freeing pages on a swapless system, the pages 314 | /// in the given range are freed instantly, regardless of 315 | /// memory pressure. 316 | /// 317 | /// # Safety 318 | /// 319 | /// Using the returned value with conceptually write to the 320 | /// mapped pages, i.e. borrowing the mapping while the pages 321 | /// are still being freed results in undefined behaviour. 322 | #[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))] 323 | Free = libc::MADV_FREE, 324 | 325 | /// **MADV_REMOVE** - Linux only (since Linux 2.6.16) 326 | /// 327 | /// Free up a given range of pages and its associated backing 328 | /// store. This is equivalent to punching a hole in the 329 | /// corresponding byte range of the backing store (see 330 | /// fallocate(2)). Subsequent accesses in the specified 331 | /// address range will see bytes containing zero. 332 | /// 333 | /// The specified address range must be mapped shared and 334 | /// writable. This flag cannot be applied to locked pages, 335 | /// Huge TLB pages, or VM_PFNMAP pages. 336 | /// 337 | /// In the initial implementation, only tmpfs(5) was supported 338 | /// **MADV_REMOVE**; but since Linux 3.5, any filesystem which 339 | /// supports the fallocate(2) FALLOC_FL_PUNCH_HOLE mode also 340 | /// supports MADV_REMOVE. Hugetlbfs fails with the error 341 | /// EINVAL and other filesystems fail with the error 342 | /// EOPNOTSUPP. 343 | /// 344 | /// # Safety 345 | /// 346 | /// Using the returned value with conceptually write to the 347 | /// mapped pages, i.e. borrowing the mapping when the pages 348 | /// are freed results in undefined behaviour. 349 | #[cfg(target_os = "linux")] 350 | Remove = libc::MADV_REMOVE, 351 | 352 | /// **MADV_FREE_REUSABLE** - Darwin only 353 | /// 354 | /// Behaves like **MADV_FREE**, but the freed pages are accounted for in the RSS of the process. 355 | /// 356 | /// # Safety 357 | /// 358 | /// Using the returned value with conceptually write to the 359 | /// mapped pages, i.e. borrowing the mapping while the pages 360 | /// are still being freed results in undefined behaviour. 361 | #[cfg(any(target_os = "macos", target_os = "ios"))] 362 | FreeReusable = libc::MADV_FREE_REUSABLE, 363 | 364 | /// **MADV_FREE_REUSE** - Darwin only 365 | /// 366 | /// Marks a memory region previously freed by **MADV_FREE_REUSABLE** as non-reusable, accounts 367 | /// for the pages in the RSS of the process. Pages that have been freed will be replaced by 368 | /// zero-filled pages on demand, other pages will be left as is. 369 | /// 370 | /// # Safety 371 | /// 372 | /// Using the returned value with conceptually write to the 373 | /// mapped pages, i.e. borrowing the mapping while the pages 374 | /// are still being freed results in undefined behaviour. 375 | #[cfg(any(target_os = "macos", target_os = "ios"))] 376 | FreeReuse = libc::MADV_FREE_REUSE, 377 | } 378 | 379 | // Future expansion: 380 | // MADV_SOFT_OFFLINE (since Linux 2.6.33) 381 | // MADV_WIPEONFORK (since Linux 4.14) 382 | // MADV_KEEPONFORK (since Linux 4.14) 383 | // MADV_COLD (since Linux 5.4) 384 | // MADV_PAGEOUT (since Linux 5.4) 385 | 386 | #[cfg(target_os = "linux")] 387 | impl Advice { 388 | /// Performs a runtime check if this advice is supported by the kernel. 389 | /// Only supported on Linux. See the [`madvise(2)`] man page. 390 | /// 391 | /// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html#VERSIONS 392 | pub fn is_supported(self) -> bool { 393 | (unsafe { libc::madvise(std::ptr::null_mut(), 0, self as libc::c_int) }) == 0 394 | } 395 | } 396 | 397 | #[cfg(target_os = "linux")] 398 | impl UncheckedAdvice { 399 | /// Performs a runtime check if this advice is supported by the kernel. 400 | /// Only supported on Linux. See the [`madvise(2)`] man page. 401 | /// 402 | /// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html#VERSIONS 403 | pub fn is_supported(self) -> bool { 404 | (unsafe { libc::madvise(std::ptr::null_mut(), 0, self as libc::c_int) }) == 0 405 | } 406 | } 407 | 408 | #[cfg(test)] 409 | mod tests { 410 | #[cfg(target_os = "linux")] 411 | #[test] 412 | fn test_is_supported() { 413 | use super::*; 414 | 415 | assert!(Advice::Normal.is_supported()); 416 | assert!(Advice::Random.is_supported()); 417 | assert!(Advice::Sequential.is_supported()); 418 | assert!(Advice::WillNeed.is_supported()); 419 | 420 | assert!(UncheckedAdvice::DontNeed.is_supported()); 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(clippy::all, clippy::pedantic)] 2 | #![allow( 3 | // pedantic exceptions 4 | clippy::cast_possible_truncation, 5 | clippy::cast_possible_wrap, 6 | clippy::cast_sign_loss, 7 | clippy::doc_markdown, 8 | clippy::explicit_deref_methods, 9 | clippy::missing_errors_doc, 10 | clippy::module_name_repetitions, 11 | clippy::must_use_candidate, 12 | clippy::needless_pass_by_value, 13 | clippy::return_self_not_must_use, 14 | clippy::unreadable_literal, 15 | clippy::upper_case_acronyms, 16 | )] 17 | 18 | //! A cross-platform Rust API for memory mapped buffers. 19 | //! 20 | //! The core functionality is provided by either [`Mmap`] or [`MmapMut`], 21 | //! which correspond to mapping a [`File`] to a [`&[u8]`](https://doc.rust-lang.org/std/primitive.slice.html) 22 | //! or [`&mut [u8]`](https://doc.rust-lang.org/std/primitive.slice.html) 23 | //! respectively. Both function by dereferencing to a slice, allowing the 24 | //! [`Mmap`]/[`MmapMut`] to be used in the same way you would the equivalent slice 25 | //! types. 26 | //! 27 | //! [`File`]: std::fs::File 28 | //! 29 | //! # Examples 30 | //! 31 | //! For simple cases [`Mmap`] can be used directly: 32 | //! 33 | //! ``` 34 | //! use std::fs::File; 35 | //! use std::io::Read; 36 | //! 37 | //! use memmap2::Mmap; 38 | //! 39 | //! # fn main() -> std::io::Result<()> { 40 | //! let mut file = File::open("LICENSE-APACHE")?; 41 | //! 42 | //! let mut contents = Vec::new(); 43 | //! file.read_to_end(&mut contents)?; 44 | //! 45 | //! let mmap = unsafe { Mmap::map(&file)? }; 46 | //! 47 | //! assert_eq!(&contents[..], &mmap[..]); 48 | //! # Ok(()) 49 | //! # } 50 | //! ``` 51 | //! 52 | //! However for cases which require configuration of the mapping, then 53 | //! you can use [`MmapOptions`] in order to further configure a mapping 54 | //! before you create it. 55 | 56 | #![allow(clippy::len_without_is_empty, clippy::missing_safety_doc)] 57 | 58 | #[cfg_attr(unix, path = "unix.rs")] 59 | #[cfg_attr(windows, path = "windows.rs")] 60 | #[cfg_attr(not(any(unix, windows)), path = "stub.rs")] 61 | mod os; 62 | use crate::os::{file_len, MmapInner}; 63 | 64 | #[cfg(unix)] 65 | mod advice; 66 | #[cfg(unix)] 67 | pub use crate::advice::{Advice, UncheckedAdvice}; 68 | 69 | use std::fmt; 70 | #[cfg(not(any(unix, windows)))] 71 | use std::fs::File; 72 | use std::io::{Error, ErrorKind, Result}; 73 | use std::mem; 74 | use std::ops::{Deref, DerefMut}; 75 | #[cfg(unix)] 76 | use std::os::unix::io::{AsRawFd, RawFd}; 77 | #[cfg(windows)] 78 | use std::os::windows::io::{AsRawHandle, RawHandle}; 79 | use std::slice; 80 | 81 | #[cfg(not(any(unix, windows)))] 82 | pub struct MmapRawDescriptor<'a>(&'a File); 83 | 84 | #[cfg(unix)] 85 | pub struct MmapRawDescriptor(RawFd); 86 | 87 | #[cfg(windows)] 88 | pub struct MmapRawDescriptor(RawHandle); 89 | 90 | pub trait MmapAsRawDesc { 91 | fn as_raw_desc(&self) -> MmapRawDescriptor; 92 | } 93 | 94 | #[cfg(not(any(unix, windows)))] 95 | impl MmapAsRawDesc for &File { 96 | fn as_raw_desc(&self) -> MmapRawDescriptor { 97 | MmapRawDescriptor(self) 98 | } 99 | } 100 | 101 | #[cfg(unix)] 102 | impl MmapAsRawDesc for RawFd { 103 | fn as_raw_desc(&self) -> MmapRawDescriptor { 104 | MmapRawDescriptor(*self) 105 | } 106 | } 107 | 108 | #[cfg(unix)] 109 | impl MmapAsRawDesc for &T 110 | where 111 | T: AsRawFd, 112 | { 113 | fn as_raw_desc(&self) -> MmapRawDescriptor { 114 | MmapRawDescriptor(self.as_raw_fd()) 115 | } 116 | } 117 | 118 | #[cfg(windows)] 119 | impl MmapAsRawDesc for RawHandle { 120 | fn as_raw_desc(&self) -> MmapRawDescriptor { 121 | MmapRawDescriptor(*self) 122 | } 123 | } 124 | 125 | #[cfg(windows)] 126 | impl MmapAsRawDesc for &T 127 | where 128 | T: AsRawHandle, 129 | { 130 | fn as_raw_desc(&self) -> MmapRawDescriptor { 131 | MmapRawDescriptor(self.as_raw_handle()) 132 | } 133 | } 134 | 135 | /// A memory map builder, providing advanced options and flags for specifying memory map behavior. 136 | /// 137 | /// `MmapOptions` can be used to create an anonymous memory map using [`map_anon()`], or a 138 | /// file-backed memory map using one of [`map()`], [`map_mut()`], [`map_exec()`], 139 | /// [`map_copy()`], or [`map_copy_read_only()`]. 140 | /// 141 | /// ## Safety 142 | /// 143 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 144 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 145 | /// out of process. Applications must consider the risk and take appropriate precautions when 146 | /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. 147 | /// unlinked) files exist but are platform specific and limited. 148 | /// 149 | /// [`map_anon()`]: MmapOptions::map_anon() 150 | /// [`map()`]: MmapOptions::map() 151 | /// [`map_mut()`]: MmapOptions::map_mut() 152 | /// [`map_exec()`]: MmapOptions::map_exec() 153 | /// [`map_copy()`]: MmapOptions::map_copy() 154 | /// [`map_copy_read_only()`]: MmapOptions::map_copy_read_only() 155 | #[derive(Clone, Debug, Default)] 156 | pub struct MmapOptions { 157 | offset: u64, 158 | len: Option, 159 | huge: Option, 160 | stack: bool, 161 | populate: bool, 162 | } 163 | 164 | impl MmapOptions { 165 | /// Creates a new set of options for configuring and creating a memory map. 166 | /// 167 | /// # Example 168 | /// 169 | /// ``` 170 | /// use memmap2::{MmapMut, MmapOptions}; 171 | /// # use std::io::Result; 172 | /// 173 | /// # fn main() -> Result<()> { 174 | /// // Create a new memory map builder. 175 | /// let mut mmap_options = MmapOptions::new(); 176 | /// 177 | /// // Configure the memory map builder using option setters, then create 178 | /// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`, 179 | /// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`: 180 | /// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?; 181 | /// 182 | /// // Use the memory map: 183 | /// mmap.copy_from_slice(b"...data to copy to the memory map..."); 184 | /// # Ok(()) 185 | /// # } 186 | /// ``` 187 | pub fn new() -> MmapOptions { 188 | MmapOptions::default() 189 | } 190 | 191 | /// Configures the memory map to start at byte `offset` from the beginning of the file. 192 | /// 193 | /// This option has no effect on anonymous memory maps. 194 | /// 195 | /// By default, the offset is 0. 196 | /// 197 | /// # Example 198 | /// 199 | /// ``` 200 | /// use memmap2::MmapOptions; 201 | /// use std::fs::File; 202 | /// 203 | /// # fn main() -> std::io::Result<()> { 204 | /// let mmap = unsafe { 205 | /// MmapOptions::new() 206 | /// .offset(30) 207 | /// .map(&File::open("LICENSE-APACHE")?)? 208 | /// }; 209 | /// assert_eq!(&b"Apache License"[..], 210 | /// &mmap[..14]); 211 | /// # Ok(()) 212 | /// # } 213 | /// ``` 214 | pub fn offset(&mut self, offset: u64) -> &mut Self { 215 | self.offset = offset; 216 | self 217 | } 218 | 219 | /// Configures the created memory mapped buffer to be `len` bytes long. 220 | /// 221 | /// This option is mandatory for anonymous memory maps. 222 | /// 223 | /// For file-backed memory maps, the length will default to the file length. 224 | /// 225 | /// # Example 226 | /// 227 | /// ``` 228 | /// use memmap2::MmapOptions; 229 | /// use std::fs::File; 230 | /// 231 | /// # fn main() -> std::io::Result<()> { 232 | /// let mmap = unsafe { 233 | /// MmapOptions::new() 234 | /// .len(9) 235 | /// .map(&File::open("README.md")?)? 236 | /// }; 237 | /// assert_eq!(&b"# memmap2"[..], &mmap[..]); 238 | /// # Ok(()) 239 | /// # } 240 | /// ``` 241 | pub fn len(&mut self, len: usize) -> &mut Self { 242 | self.len = Some(len); 243 | self 244 | } 245 | 246 | /// Returns the configured length, or the length of the provided file. 247 | fn get_len(&self, file: &T) -> Result { 248 | self.len.map_or_else( 249 | || { 250 | let desc = file.as_raw_desc(); 251 | let file_len = file_len(desc.0)?; 252 | 253 | if file_len < self.offset { 254 | return Err(Error::new( 255 | ErrorKind::InvalidData, 256 | "memory map offset is larger than length", 257 | )); 258 | } 259 | let len = file_len - self.offset; 260 | 261 | // Rust's slice cannot be larger than isize::MAX. 262 | // See https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html 263 | // 264 | // This is not a problem on 64-bit targets, but on 32-bit one 265 | // having a file or an anonymous mapping larger than 2GB is quite normal 266 | // and we have to prevent it. 267 | // 268 | // The code below is essentially the same as in Rust's std: 269 | // https://github.com/rust-lang/rust/blob/db78ab70a88a0a5e89031d7ee4eccec835dcdbde/library/alloc/src/raw_vec.rs#L495 270 | if mem::size_of::() < 8 && len > isize::MAX as u64 { 271 | return Err(Error::new( 272 | ErrorKind::InvalidData, 273 | "memory map length overflows isize", 274 | )); 275 | } 276 | 277 | Ok(len as usize) 278 | }, 279 | Ok, 280 | ) 281 | } 282 | 283 | /// Configures the anonymous memory map to be suitable for a process or thread stack. 284 | /// 285 | /// This option corresponds to the `MAP_STACK` flag on Linux. It has no effect on Windows. 286 | /// 287 | /// This option has no effect on file-backed memory maps. 288 | /// 289 | /// # Example 290 | /// 291 | /// ``` 292 | /// use memmap2::MmapOptions; 293 | /// 294 | /// # fn main() -> std::io::Result<()> { 295 | /// let stack = MmapOptions::new().stack().len(4096).map_anon(); 296 | /// # Ok(()) 297 | /// # } 298 | /// ``` 299 | pub fn stack(&mut self) -> &mut Self { 300 | self.stack = true; 301 | self 302 | } 303 | 304 | /// Configures the anonymous memory map to be allocated using huge pages. 305 | /// 306 | /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. 307 | /// 308 | /// The size of the requested page can be specified in page bits. If not provided, the system 309 | /// default is requested. The requested length should be a multiple of this, or the mapping 310 | /// will fail. 311 | /// 312 | /// This option has no effect on file-backed memory maps. 313 | /// 314 | /// # Example 315 | /// 316 | /// ``` 317 | /// use memmap2::MmapOptions; 318 | /// 319 | /// # fn main() -> std::io::Result<()> { 320 | /// let stack = MmapOptions::new().huge(Some(21)).len(2*1024*1024).map_anon(); 321 | /// # Ok(()) 322 | /// # } 323 | /// ``` 324 | /// 325 | /// The number 21 corresponds to `MAP_HUGE_2MB`. See mmap(2) for more details. 326 | pub fn huge(&mut self, page_bits: Option) -> &mut Self { 327 | self.huge = Some(page_bits.unwrap_or(0)); 328 | self 329 | } 330 | /// Populate (prefault) page tables for a mapping. 331 | /// 332 | /// For a file mapping, this causes read-ahead on the file. This will help to reduce blocking on page faults later. 333 | /// 334 | /// This option corresponds to the `MAP_POPULATE` flag on Linux. It has no effect on Windows. 335 | /// 336 | /// # Example 337 | /// 338 | /// ``` 339 | /// use memmap2::MmapOptions; 340 | /// use std::fs::File; 341 | /// 342 | /// # fn main() -> std::io::Result<()> { 343 | /// let file = File::open("LICENSE-MIT")?; 344 | /// 345 | /// let mmap = unsafe { 346 | /// MmapOptions::new().populate().map(&file)? 347 | /// }; 348 | /// 349 | /// assert_eq!(&b"Copyright"[..], &mmap[..9]); 350 | /// # Ok(()) 351 | /// # } 352 | /// ``` 353 | pub fn populate(&mut self) -> &mut Self { 354 | self.populate = true; 355 | self 356 | } 357 | 358 | /// Creates a read-only memory map backed by a file. 359 | /// 360 | /// # Safety 361 | /// 362 | /// See the [type-level][MmapOptions] docs for why this function is unsafe. 363 | /// 364 | /// # Errors 365 | /// 366 | /// This method returns an error when the underlying system call fails, which can happen for a 367 | /// variety of reasons, such as when the file is not open with read permissions. 368 | /// 369 | /// # Example 370 | /// 371 | /// ``` 372 | /// use memmap2::MmapOptions; 373 | /// use std::fs::File; 374 | /// use std::io::Read; 375 | /// 376 | /// # fn main() -> std::io::Result<()> { 377 | /// let mut file = File::open("LICENSE-APACHE")?; 378 | /// 379 | /// let mut contents = Vec::new(); 380 | /// file.read_to_end(&mut contents)?; 381 | /// 382 | /// let mmap = unsafe { 383 | /// MmapOptions::new().map(&file)? 384 | /// }; 385 | /// 386 | /// assert_eq!(&contents[..], &mmap[..]); 387 | /// # Ok(()) 388 | /// # } 389 | /// ``` 390 | pub unsafe fn map(&self, file: T) -> Result { 391 | let desc = file.as_raw_desc(); 392 | 393 | MmapInner::map(self.get_len(&file)?, desc.0, self.offset, self.populate) 394 | .map(|inner| Mmap { inner }) 395 | } 396 | 397 | /// Creates a readable and executable memory map backed by a file. 398 | /// 399 | /// # Safety 400 | /// 401 | /// See the [type-level][MmapOptions] docs for why this function is unsafe. 402 | /// 403 | /// # Errors 404 | /// 405 | /// This method returns an error when the underlying system call fails, which can happen for a 406 | /// variety of reasons, such as when the file is not open with read permissions. 407 | pub unsafe fn map_exec(&self, file: T) -> Result { 408 | let desc = file.as_raw_desc(); 409 | 410 | MmapInner::map_exec(self.get_len(&file)?, desc.0, self.offset, self.populate) 411 | .map(|inner| Mmap { inner }) 412 | } 413 | 414 | /// Creates a writeable memory map backed by a file. 415 | /// 416 | /// # Safety 417 | /// 418 | /// See the [type-level][MmapOptions] docs for why this function is unsafe. 419 | /// 420 | /// # Errors 421 | /// 422 | /// This method returns an error when the underlying system call fails, which can happen for a 423 | /// variety of reasons, such as when the file is not open with read and write permissions. 424 | /// 425 | /// # Example 426 | /// 427 | /// ``` 428 | /// use std::fs::OpenOptions; 429 | /// use std::path::PathBuf; 430 | /// 431 | /// use memmap2::MmapOptions; 432 | /// # 433 | /// # fn main() -> std::io::Result<()> { 434 | /// # let tempdir = tempfile::tempdir()?; 435 | /// let path: PathBuf = /* path to file */ 436 | /// # tempdir.path().join("map_mut"); 437 | /// let file = OpenOptions::new().read(true).write(true).create(true).truncate(true).open(&path)?; 438 | /// file.set_len(13)?; 439 | /// 440 | /// let mut mmap = unsafe { 441 | /// MmapOptions::new().map_mut(&file)? 442 | /// }; 443 | /// 444 | /// mmap.copy_from_slice(b"Hello, world!"); 445 | /// # Ok(()) 446 | /// # } 447 | /// ``` 448 | pub unsafe fn map_mut(&self, file: T) -> Result { 449 | let desc = file.as_raw_desc(); 450 | 451 | MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate) 452 | .map(|inner| MmapMut { inner }) 453 | } 454 | 455 | /// Creates a copy-on-write memory map backed by a file. 456 | /// 457 | /// Data written to the memory map will not be visible by other processes, 458 | /// and will not be carried through to the underlying file. 459 | /// 460 | /// # Safety 461 | /// 462 | /// See the [type-level][MmapOptions] docs for why this function is unsafe. 463 | /// 464 | /// # Errors 465 | /// 466 | /// This method returns an error when the underlying system call fails, which can happen for a 467 | /// variety of reasons, such as when the file is not open with writable permissions. 468 | /// 469 | /// # Example 470 | /// 471 | /// ``` 472 | /// use memmap2::MmapOptions; 473 | /// use std::fs::File; 474 | /// use std::io::Write; 475 | /// 476 | /// # fn main() -> std::io::Result<()> { 477 | /// let file = File::open("LICENSE-APACHE")?; 478 | /// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? }; 479 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; 480 | /// # Ok(()) 481 | /// # } 482 | /// ``` 483 | pub unsafe fn map_copy(&self, file: T) -> Result { 484 | let desc = file.as_raw_desc(); 485 | 486 | MmapInner::map_copy(self.get_len(&file)?, desc.0, self.offset, self.populate) 487 | .map(|inner| MmapMut { inner }) 488 | } 489 | 490 | /// Creates a copy-on-write read-only memory map backed by a file. 491 | /// 492 | /// # Safety 493 | /// 494 | /// See the [type-level][MmapOptions] docs for why this function is unsafe. 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 permissions. 500 | /// 501 | /// # Example 502 | /// 503 | /// ``` 504 | /// use memmap2::MmapOptions; 505 | /// use std::fs::File; 506 | /// use std::io::Read; 507 | /// 508 | /// # fn main() -> std::io::Result<()> { 509 | /// let mut file = File::open("README.md")?; 510 | /// 511 | /// let mut contents = Vec::new(); 512 | /// file.read_to_end(&mut contents)?; 513 | /// 514 | /// let mmap = unsafe { 515 | /// MmapOptions::new().map_copy_read_only(&file)? 516 | /// }; 517 | /// 518 | /// assert_eq!(&contents[..], &mmap[..]); 519 | /// # Ok(()) 520 | /// # } 521 | /// ``` 522 | pub unsafe fn map_copy_read_only(&self, file: T) -> Result { 523 | let desc = file.as_raw_desc(); 524 | 525 | MmapInner::map_copy_read_only(self.get_len(&file)?, desc.0, self.offset, self.populate) 526 | .map(|inner| Mmap { inner }) 527 | } 528 | 529 | /// Creates an anonymous memory map. 530 | /// 531 | /// The memory map length should be configured using [`MmapOptions::len()`] 532 | /// before creating an anonymous memory map, otherwise a zero-length mapping 533 | /// will be crated. 534 | /// 535 | /// # Errors 536 | /// 537 | /// This method returns an error when the underlying system call fails or 538 | /// when `len > isize::MAX`. 539 | pub fn map_anon(&self) -> Result { 540 | let len = self.len.unwrap_or(0); 541 | 542 | // See get_len() for details. 543 | if mem::size_of::() < 8 && len > isize::MAX as usize { 544 | return Err(Error::new( 545 | ErrorKind::InvalidData, 546 | "memory map length overflows isize", 547 | )); 548 | } 549 | 550 | MmapInner::map_anon(len, self.stack, self.populate, self.huge) 551 | .map(|inner| MmapMut { inner }) 552 | } 553 | 554 | /// Creates a raw memory map. 555 | /// 556 | /// # Errors 557 | /// 558 | /// This method returns an error when the underlying system call fails, which can happen for a 559 | /// variety of reasons, such as when the file is not open with read and write permissions. 560 | pub fn map_raw(&self, file: T) -> Result { 561 | let desc = file.as_raw_desc(); 562 | 563 | MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate) 564 | .map(|inner| MmapRaw { inner }) 565 | } 566 | 567 | /// Creates a read-only raw memory map 568 | /// 569 | /// This is primarily useful to avoid intermediate `Mmap` instances when 570 | /// read-only access to files modified elsewhere are required. 571 | /// 572 | /// # Errors 573 | /// 574 | /// This method returns an error when the underlying system call fails 575 | pub fn map_raw_read_only(&self, file: T) -> Result { 576 | let desc = file.as_raw_desc(); 577 | 578 | MmapInner::map(self.get_len(&file)?, desc.0, self.offset, self.populate) 579 | .map(|inner| MmapRaw { inner }) 580 | } 581 | } 582 | 583 | /// A handle to an immutable memory mapped buffer. 584 | /// 585 | /// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. Use 586 | /// [`MmapOptions`] or [`map()`] to create a file-backed memory map. To create an immutable 587 | /// anonymous memory map, first create a mutable anonymous memory map, and then make it immutable 588 | /// with [`MmapMut::make_read_only()`]. 589 | /// 590 | /// A file backed `Mmap` is created by `&File` reference, and will remain valid even after the 591 | /// `File` is dropped. In other words, the `Mmap` handle is completely independent of the `File` 592 | /// used to create it. For consistency, on some platforms this is achieved by duplicating the 593 | /// underlying file handle. The memory will be unmapped when the `Mmap` handle is dropped. 594 | /// 595 | /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping 596 | /// the mapped pages into physical memory) though the details of this are platform specific. 597 | /// 598 | /// `Mmap` is [`Sync`] and [`Send`]. 599 | /// 600 | /// See [`MmapMut`] for the mutable version. 601 | /// 602 | /// ## Safety 603 | /// 604 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 605 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 606 | /// out of process. Applications must consider the risk and take appropriate precautions when using 607 | /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) 608 | /// files exist but are platform specific and limited. 609 | /// 610 | /// ## Example 611 | /// 612 | /// ``` 613 | /// use memmap2::MmapOptions; 614 | /// use std::io::Write; 615 | /// use std::fs::File; 616 | /// 617 | /// # fn main() -> std::io::Result<()> { 618 | /// let file = File::open("README.md")?; 619 | /// let mmap = unsafe { MmapOptions::new().map(&file)? }; 620 | /// assert_eq!(b"# memmap2", &mmap[0..9]); 621 | /// # Ok(()) 622 | /// # } 623 | /// ``` 624 | /// 625 | /// [`map()`]: Mmap::map() 626 | pub struct Mmap { 627 | inner: MmapInner, 628 | } 629 | 630 | impl Mmap { 631 | /// Creates a read-only memory map backed by a file. 632 | /// 633 | /// This is equivalent to calling `MmapOptions::new().map(file)`. 634 | /// 635 | /// # Safety 636 | /// 637 | /// See the [type-level][Mmap] docs for why this function is unsafe. 638 | /// 639 | /// # Errors 640 | /// 641 | /// This method returns an error when the underlying system call fails, which can happen for a 642 | /// variety of reasons, such as when the file is not open with read permissions. 643 | /// 644 | /// # Example 645 | /// 646 | /// ``` 647 | /// use std::fs::File; 648 | /// use std::io::Read; 649 | /// 650 | /// use memmap2::Mmap; 651 | /// 652 | /// # fn main() -> std::io::Result<()> { 653 | /// let mut file = File::open("LICENSE-APACHE")?; 654 | /// 655 | /// let mut contents = Vec::new(); 656 | /// file.read_to_end(&mut contents)?; 657 | /// 658 | /// let mmap = unsafe { Mmap::map(&file)? }; 659 | /// 660 | /// assert_eq!(&contents[..], &mmap[..]); 661 | /// # Ok(()) 662 | /// # } 663 | /// ``` 664 | pub unsafe fn map(file: T) -> Result { 665 | MmapOptions::new().map(file) 666 | } 667 | 668 | /// Transition the memory map to be writable. 669 | /// 670 | /// If the memory map is file-backed, the file must have been opened with write permissions. 671 | /// 672 | /// # Errors 673 | /// 674 | /// This method returns an error when the underlying system call fails, which can happen for a 675 | /// variety of reasons, such as when the file is not open with writable permissions. 676 | /// 677 | /// # Example 678 | /// 679 | /// ``` 680 | /// use memmap2::Mmap; 681 | /// use std::ops::DerefMut; 682 | /// use std::io::Write; 683 | /// # use std::fs::OpenOptions; 684 | /// 685 | /// # fn main() -> std::io::Result<()> { 686 | /// # let tempdir = tempfile::tempdir()?; 687 | /// let file = /* file opened with write permissions */ 688 | /// # OpenOptions::new() 689 | /// # .read(true) 690 | /// # .write(true) 691 | /// # .create(true) 692 | /// # .truncate(true) 693 | /// # .open(tempdir.path() 694 | /// # .join("make_mut"))?; 695 | /// # file.set_len(128)?; 696 | /// let mmap = unsafe { Mmap::map(&file)? }; 697 | /// // ... use the read-only memory map ... 698 | /// let mut mut_mmap = mmap.make_mut()?; 699 | /// mut_mmap.deref_mut().write_all(b"hello, world!")?; 700 | /// # Ok(()) 701 | /// # } 702 | /// ``` 703 | pub fn make_mut(mut self) -> Result { 704 | self.inner.make_mut()?; 705 | Ok(MmapMut { inner: self.inner }) 706 | } 707 | 708 | /// Advise OS how this memory map will be accessed. 709 | /// 710 | /// Only supported on Unix. 711 | /// 712 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 713 | #[cfg(unix)] 714 | pub fn advise(&self, advice: Advice) -> Result<()> { 715 | self.inner 716 | .advise(advice as libc::c_int, 0, self.inner.len()) 717 | } 718 | 719 | /// Advise OS how this memory map will be accessed. 720 | /// 721 | /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. 722 | /// 723 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 724 | #[cfg(unix)] 725 | pub unsafe fn unchecked_advise(&self, advice: UncheckedAdvice) -> Result<()> { 726 | self.inner 727 | .advise(advice as libc::c_int, 0, self.inner.len()) 728 | } 729 | 730 | /// Advise OS how this range of memory map will be accessed. 731 | /// 732 | /// Only supported on Unix. 733 | /// 734 | /// The offset and length must be in the bounds of the memory map. 735 | /// 736 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 737 | #[cfg(unix)] 738 | pub fn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { 739 | self.inner.advise(advice as libc::c_int, offset, len) 740 | } 741 | 742 | /// Advise OS how this range of memory map will be accessed. 743 | /// 744 | /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. 745 | /// 746 | /// The offset and length must be in the bounds of the memory map. 747 | /// 748 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 749 | #[cfg(unix)] 750 | pub unsafe fn unchecked_advise_range( 751 | &self, 752 | advice: UncheckedAdvice, 753 | offset: usize, 754 | len: usize, 755 | ) -> Result<()> { 756 | self.inner.advise(advice as libc::c_int, offset, len) 757 | } 758 | 759 | /// Lock the whole memory map into RAM. Only supported on Unix. 760 | /// 761 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. 762 | #[cfg(unix)] 763 | pub fn lock(&self) -> Result<()> { 764 | self.inner.lock() 765 | } 766 | 767 | /// Unlock the whole memory map. Only supported on Unix. 768 | /// 769 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. 770 | #[cfg(unix)] 771 | pub fn unlock(&self) -> Result<()> { 772 | self.inner.unlock() 773 | } 774 | 775 | /// Adjust the size of the memory mapping. 776 | /// 777 | /// This will try to resize the memory mapping in place. If 778 | /// [`RemapOptions::may_move`] is specified it will move the mapping if it 779 | /// could not resize in place, otherwise it will error. 780 | /// 781 | /// Only supported on Linux. 782 | /// 783 | /// See the [`mremap(2)`] man page. 784 | /// 785 | /// # Safety 786 | /// 787 | /// Resizing the memory mapping beyond the end of the mapped file will 788 | /// result in UB should you happen to access memory beyond the end of the 789 | /// file. 790 | /// 791 | /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html 792 | #[cfg(target_os = "linux")] 793 | pub unsafe fn remap(&mut self, new_len: usize, options: RemapOptions) -> Result<()> { 794 | self.inner.remap(new_len, options) 795 | } 796 | } 797 | 798 | #[cfg(feature = "stable_deref_trait")] 799 | unsafe impl stable_deref_trait::StableDeref for Mmap {} 800 | 801 | impl Deref for Mmap { 802 | type Target = [u8]; 803 | 804 | #[inline] 805 | fn deref(&self) -> &[u8] { 806 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } 807 | } 808 | } 809 | 810 | impl AsRef<[u8]> for Mmap { 811 | #[inline] 812 | fn as_ref(&self) -> &[u8] { 813 | self.deref() 814 | } 815 | } 816 | 817 | impl fmt::Debug for Mmap { 818 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 819 | fmt.debug_struct("Mmap") 820 | .field("ptr", &self.as_ptr()) 821 | .field("len", &self.len()) 822 | .finish() 823 | } 824 | } 825 | 826 | /// A handle to a raw memory mapped buffer. 827 | /// 828 | /// This struct never hands out references to its interior, only raw pointers. 829 | /// This can be helpful when creating shared memory maps between untrusted processes. 830 | /// 831 | /// For the safety concerns that arise when converting these raw pointers to references, 832 | /// see the [`Mmap`] safety documentation. 833 | pub struct MmapRaw { 834 | inner: MmapInner, 835 | } 836 | 837 | impl MmapRaw { 838 | /// Creates a writeable memory map backed by a file. 839 | /// 840 | /// This is equivalent to calling `MmapOptions::new().map_raw(file)`. 841 | /// 842 | /// # Errors 843 | /// 844 | /// This method returns an error when the underlying system call fails, which can happen for a 845 | /// variety of reasons, such as when the file is not open with read and write permissions. 846 | pub fn map_raw(file: T) -> Result { 847 | MmapOptions::new().map_raw(file) 848 | } 849 | 850 | /// Returns a raw pointer to the memory mapped file. 851 | /// 852 | /// Before dereferencing this pointer, you have to make sure that the file has not been 853 | /// truncated since the memory map was created. 854 | /// Avoiding this will not introduce memory safety issues in Rust terms, 855 | /// but will cause SIGBUS (or equivalent) signal. 856 | #[inline] 857 | pub fn as_ptr(&self) -> *const u8 { 858 | self.inner.ptr() 859 | } 860 | 861 | /// Returns an unsafe mutable pointer to the memory mapped file. 862 | /// 863 | /// Before dereferencing this pointer, you have to make sure that the file has not been 864 | /// truncated since the memory map was created. 865 | /// Avoiding this will not introduce memory safety issues in Rust terms, 866 | /// but will cause SIGBUS (or equivalent) signal. 867 | #[inline] 868 | pub fn as_mut_ptr(&self) -> *mut u8 { 869 | self.inner.ptr() as *mut u8 870 | } 871 | 872 | /// Returns the length in bytes of the memory map. 873 | /// 874 | /// Note that truncating the file can cause the length to change (and render this value unusable). 875 | #[inline] 876 | pub fn len(&self) -> usize { 877 | self.inner.len() 878 | } 879 | 880 | /// Flushes outstanding memory map modifications to disk. 881 | /// 882 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 883 | /// memory map are guaranteed to be durably stored. The file's metadata (including last 884 | /// modification timestamp) may not be updated. 885 | /// 886 | /// # Example 887 | /// 888 | /// ``` 889 | /// use std::fs::OpenOptions; 890 | /// use std::io::Write; 891 | /// use std::path::PathBuf; 892 | /// use std::slice; 893 | /// 894 | /// use memmap2::MmapRaw; 895 | /// 896 | /// # fn main() -> std::io::Result<()> { 897 | /// let tempdir = tempfile::tempdir()?; 898 | /// let path: PathBuf = /* path to file */ 899 | /// # tempdir.path().join("flush"); 900 | /// let file = OpenOptions::new().read(true).write(true).create(true).truncate(true).open(&path)?; 901 | /// file.set_len(128)?; 902 | /// 903 | /// let mut mmap = unsafe { MmapRaw::map_raw(&file)? }; 904 | /// 905 | /// let mut memory = unsafe { slice::from_raw_parts_mut(mmap.as_mut_ptr(), 128) }; 906 | /// memory.write_all(b"Hello, world!")?; 907 | /// mmap.flush()?; 908 | /// # Ok(()) 909 | /// # } 910 | /// ``` 911 | pub fn flush(&self) -> Result<()> { 912 | let len = self.len(); 913 | self.inner.flush(0, len) 914 | } 915 | 916 | /// Asynchronously flushes outstanding memory map modifications to disk. 917 | /// 918 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 919 | /// the operation to complete before returning. The file's metadata (including last 920 | /// modification timestamp) may not be updated. 921 | pub fn flush_async(&self) -> Result<()> { 922 | let len = self.len(); 923 | self.inner.flush_async(0, len) 924 | } 925 | 926 | /// Flushes outstanding memory map modifications in the range to disk. 927 | /// 928 | /// The offset and length must be in the bounds of the memory map. 929 | /// 930 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 931 | /// memory in the range are guaranteed to be durable stored. The file's metadata (including 932 | /// last modification timestamp) may not be updated. It is not guaranteed the only the changes 933 | /// in the specified range are flushed; other outstanding changes to the memory map may be 934 | /// flushed as well. 935 | pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> { 936 | self.inner.flush(offset, len) 937 | } 938 | 939 | /// Asynchronously flushes outstanding memory map modifications in the range to disk. 940 | /// 941 | /// The offset and length must be in the bounds of the memory map. 942 | /// 943 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 944 | /// the operation to complete before returning. The file's metadata (including last 945 | /// modification timestamp) may not be updated. It is not guaranteed that the only changes 946 | /// flushed are those in the specified range; other outstanding changes to the memory map may 947 | /// be flushed as well. 948 | pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { 949 | self.inner.flush_async(offset, len) 950 | } 951 | 952 | /// Advise OS how this memory map will be accessed. 953 | /// 954 | /// Only supported on Unix. 955 | /// 956 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 957 | #[cfg(unix)] 958 | pub fn advise(&self, advice: Advice) -> Result<()> { 959 | self.inner 960 | .advise(advice as libc::c_int, 0, self.inner.len()) 961 | } 962 | 963 | /// Advise OS how this memory map will be accessed. 964 | /// 965 | /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. 966 | /// 967 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 968 | #[cfg(unix)] 969 | pub unsafe fn unchecked_advise(&self, advice: UncheckedAdvice) -> Result<()> { 970 | self.inner 971 | .advise(advice as libc::c_int, 0, self.inner.len()) 972 | } 973 | 974 | /// Advise OS how this range of memory map will be accessed. 975 | /// 976 | /// The offset and length must be in the bounds of the memory map. 977 | /// 978 | /// Only supported on Unix. 979 | /// 980 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 981 | #[cfg(unix)] 982 | pub fn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { 983 | self.inner.advise(advice as libc::c_int, offset, len) 984 | } 985 | 986 | /// Advise OS how this range of memory map will be accessed. 987 | /// 988 | /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. 989 | /// 990 | /// The offset and length must be in the bounds of the memory map. 991 | /// 992 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 993 | #[cfg(unix)] 994 | pub unsafe fn unchecked_advise_range( 995 | &self, 996 | advice: UncheckedAdvice, 997 | offset: usize, 998 | len: usize, 999 | ) -> Result<()> { 1000 | self.inner.advise(advice as libc::c_int, offset, len) 1001 | } 1002 | 1003 | /// Lock the whole memory map into RAM. Only supported on Unix. 1004 | /// 1005 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. 1006 | #[cfg(unix)] 1007 | pub fn lock(&self) -> Result<()> { 1008 | self.inner.lock() 1009 | } 1010 | 1011 | /// Unlock the whole memory map. Only supported on Unix. 1012 | /// 1013 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. 1014 | #[cfg(unix)] 1015 | pub fn unlock(&self) -> Result<()> { 1016 | self.inner.unlock() 1017 | } 1018 | 1019 | /// Adjust the size of the memory mapping. 1020 | /// 1021 | /// This will try to resize the memory mapping in place. If 1022 | /// [`RemapOptions::may_move`] is specified it will move the mapping if it 1023 | /// could not resize in place, otherwise it will error. 1024 | /// 1025 | /// Only supported on Linux. 1026 | /// 1027 | /// See the [`mremap(2)`] man page. 1028 | /// 1029 | /// # Safety 1030 | /// 1031 | /// Resizing the memory mapping beyond the end of the mapped file will 1032 | /// result in UB should you happen to access memory beyond the end of the 1033 | /// file. 1034 | /// 1035 | /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html 1036 | #[cfg(target_os = "linux")] 1037 | pub unsafe fn remap(&mut self, new_len: usize, options: RemapOptions) -> Result<()> { 1038 | self.inner.remap(new_len, options) 1039 | } 1040 | } 1041 | 1042 | impl fmt::Debug for MmapRaw { 1043 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1044 | fmt.debug_struct("MmapRaw") 1045 | .field("ptr", &self.as_ptr()) 1046 | .field("len", &self.len()) 1047 | .finish() 1048 | } 1049 | } 1050 | 1051 | impl From for MmapRaw { 1052 | fn from(value: Mmap) -> Self { 1053 | Self { inner: value.inner } 1054 | } 1055 | } 1056 | 1057 | impl From for MmapRaw { 1058 | fn from(value: MmapMut) -> Self { 1059 | Self { inner: value.inner } 1060 | } 1061 | } 1062 | 1063 | /// A handle to a mutable memory mapped buffer. 1064 | /// 1065 | /// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous 1066 | /// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use 1067 | /// [`MmapMut::map_mut()`] and [`MmapMut::map_anon()`] to create a mutable memory map of the 1068 | /// respective types, or [`MmapOptions::map_mut()`] and [`MmapOptions::map_anon()`] if non-default 1069 | /// options are required. 1070 | /// 1071 | /// A file backed `MmapMut` is created by `&File` reference, and will remain valid even after the 1072 | /// `File` is dropped. In other words, the `MmapMut` handle is completely independent of the `File` 1073 | /// used to create it. For consistency, on some platforms this is achieved by duplicating the 1074 | /// underlying file handle. The memory will be unmapped when the `MmapMut` handle is dropped. 1075 | /// 1076 | /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping 1077 | /// the mapped pages into physical memory) though the details of this are platform specific. 1078 | /// 1079 | /// `MmapMut` is [`Sync`] and [`Send`]. 1080 | /// 1081 | /// See [`Mmap`] for the immutable version. 1082 | /// 1083 | /// ## Safety 1084 | /// 1085 | /// All file-backed memory map constructors are marked `unsafe` because of the potential for 1086 | /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or 1087 | /// out of process. Applications must consider the risk and take appropriate precautions when using 1088 | /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) 1089 | /// files exist but are platform specific and limited. 1090 | pub struct MmapMut { 1091 | inner: MmapInner, 1092 | } 1093 | 1094 | impl MmapMut { 1095 | /// Creates a writeable memory map backed by a file. 1096 | /// 1097 | /// This is equivalent to calling `MmapOptions::new().map_mut(file)`. 1098 | /// 1099 | /// # Safety 1100 | /// 1101 | /// See the [type-level][MmapMut] docs for why this function is unsafe. 1102 | /// 1103 | /// # Errors 1104 | /// 1105 | /// This method returns an error when the underlying system call fails, which can happen for a 1106 | /// variety of reasons, such as when the file is not open with read and write permissions. 1107 | /// 1108 | /// # Example 1109 | /// 1110 | /// ``` 1111 | /// use std::fs::OpenOptions; 1112 | /// use std::path::PathBuf; 1113 | /// 1114 | /// use memmap2::MmapMut; 1115 | /// # 1116 | /// # fn main() -> std::io::Result<()> { 1117 | /// # let tempdir = tempfile::tempdir()?; 1118 | /// let path: PathBuf = /* path to file */ 1119 | /// # tempdir.path().join("map_mut"); 1120 | /// let file = OpenOptions::new() 1121 | /// .read(true) 1122 | /// .write(true) 1123 | /// .create(true) 1124 | /// .truncate(true) 1125 | /// .open(&path)?; 1126 | /// file.set_len(13)?; 1127 | /// 1128 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; 1129 | /// 1130 | /// mmap.copy_from_slice(b"Hello, world!"); 1131 | /// # Ok(()) 1132 | /// # } 1133 | /// ``` 1134 | pub unsafe fn map_mut(file: T) -> Result { 1135 | MmapOptions::new().map_mut(file) 1136 | } 1137 | 1138 | /// Creates an anonymous memory map. 1139 | /// 1140 | /// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`. 1141 | /// 1142 | /// # Errors 1143 | /// 1144 | /// This method returns an error when the underlying system call fails or 1145 | /// when `len > isize::MAX`. 1146 | pub fn map_anon(length: usize) -> Result { 1147 | MmapOptions::new().len(length).map_anon() 1148 | } 1149 | 1150 | /// Flushes outstanding memory map modifications to disk. 1151 | /// 1152 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 1153 | /// memory map are guaranteed to be durably stored. The file's metadata (including last 1154 | /// modification timestamp) may not be updated. 1155 | /// 1156 | /// # Example 1157 | /// 1158 | /// ``` 1159 | /// use std::fs::OpenOptions; 1160 | /// use std::io::Write; 1161 | /// use std::path::PathBuf; 1162 | /// 1163 | /// use memmap2::MmapMut; 1164 | /// 1165 | /// # fn main() -> std::io::Result<()> { 1166 | /// # let tempdir = tempfile::tempdir()?; 1167 | /// let path: PathBuf = /* path to file */ 1168 | /// # tempdir.path().join("flush"); 1169 | /// let file = OpenOptions::new().read(true).write(true).create(true).truncate(true).open(&path)?; 1170 | /// file.set_len(128)?; 1171 | /// 1172 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; 1173 | /// 1174 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; 1175 | /// mmap.flush()?; 1176 | /// # Ok(()) 1177 | /// # } 1178 | /// ``` 1179 | pub fn flush(&self) -> Result<()> { 1180 | let len = self.len(); 1181 | self.inner.flush(0, len) 1182 | } 1183 | 1184 | /// Asynchronously flushes outstanding memory map modifications to disk. 1185 | /// 1186 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 1187 | /// the operation to complete before returning. The file's metadata (including last 1188 | /// modification timestamp) may not be updated. 1189 | pub fn flush_async(&self) -> Result<()> { 1190 | let len = self.len(); 1191 | self.inner.flush_async(0, len) 1192 | } 1193 | 1194 | /// Flushes outstanding memory map modifications in the range to disk. 1195 | /// 1196 | /// The offset and length must be in the bounds of the memory map. 1197 | /// 1198 | /// When this method returns with a non-error result, all outstanding changes to a file-backed 1199 | /// memory in the range are guaranteed to be durable stored. The file's metadata (including 1200 | /// last modification timestamp) may not be updated. It is not guaranteed the only the changes 1201 | /// in the specified range are flushed; other outstanding changes to the memory map may be 1202 | /// flushed as well. 1203 | pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> { 1204 | self.inner.flush(offset, len) 1205 | } 1206 | 1207 | /// Asynchronously flushes outstanding memory map modifications in the range to disk. 1208 | /// 1209 | /// The offset and length must be in the bounds of the memory map. 1210 | /// 1211 | /// This method initiates flushing modified pages to durable storage, but it will not wait for 1212 | /// the operation to complete before returning. The file's metadata (including last 1213 | /// modification timestamp) may not be updated. It is not guaranteed that the only changes 1214 | /// flushed are those in the specified range; other outstanding changes to the memory map may 1215 | /// be flushed as well. 1216 | pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { 1217 | self.inner.flush_async(offset, len) 1218 | } 1219 | 1220 | /// Returns an immutable version of this memory mapped buffer. 1221 | /// 1222 | /// If the memory map is file-backed, the file must have been opened with read permissions. 1223 | /// 1224 | /// # Errors 1225 | /// 1226 | /// This method returns an error when the underlying system call fails, which can happen for a 1227 | /// variety of reasons, such as when the file has not been opened with read permissions. 1228 | /// 1229 | /// # Example 1230 | /// 1231 | /// ``` 1232 | /// use std::io::Write; 1233 | /// use std::path::PathBuf; 1234 | /// 1235 | /// use memmap2::{Mmap, MmapMut}; 1236 | /// 1237 | /// # fn main() -> std::io::Result<()> { 1238 | /// let mut mmap = MmapMut::map_anon(128)?; 1239 | /// 1240 | /// (&mut mmap[..]).write(b"Hello, world!")?; 1241 | /// 1242 | /// let mmap: Mmap = mmap.make_read_only()?; 1243 | /// # Ok(()) 1244 | /// # } 1245 | /// ``` 1246 | pub fn make_read_only(mut self) -> Result { 1247 | self.inner.make_read_only()?; 1248 | Ok(Mmap { inner: self.inner }) 1249 | } 1250 | 1251 | /// Transition the memory map to be readable and executable. 1252 | /// 1253 | /// If the memory map is file-backed, the file must have been opened with execute permissions. 1254 | /// 1255 | /// On systems with separate instructions and data caches (a category that includes many ARM 1256 | /// chips), a platform-specific call may be needed to ensure that the changes are visible to the 1257 | /// execution unit (e.g. when using this function to implement a JIT compiler). For more 1258 | /// details, see [this ARM write-up](https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/caches-and-self-modifying-code) 1259 | /// 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). 1260 | /// 1261 | /// # Errors 1262 | /// 1263 | /// This method returns an error when the underlying system call fails, which can happen for a 1264 | /// variety of reasons, such as when the file has not been opened with execute permissions. 1265 | pub fn make_exec(mut self) -> Result { 1266 | self.inner.make_exec()?; 1267 | Ok(Mmap { inner: self.inner }) 1268 | } 1269 | 1270 | /// Advise OS how this memory map will be accessed. 1271 | /// 1272 | /// Only supported on Unix. 1273 | /// 1274 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 1275 | #[cfg(unix)] 1276 | pub fn advise(&self, advice: Advice) -> Result<()> { 1277 | self.inner 1278 | .advise(advice as libc::c_int, 0, self.inner.len()) 1279 | } 1280 | 1281 | /// Advise OS how this memory map will be accessed. 1282 | /// 1283 | /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. 1284 | /// 1285 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 1286 | #[cfg(unix)] 1287 | pub unsafe fn unchecked_advise(&self, advice: UncheckedAdvice) -> Result<()> { 1288 | self.inner 1289 | .advise(advice as libc::c_int, 0, self.inner.len()) 1290 | } 1291 | 1292 | /// Advise OS how this range of memory map will be accessed. 1293 | /// 1294 | /// Only supported on Unix. 1295 | /// 1296 | /// The offset and length must be in the bounds of the memory map. 1297 | /// 1298 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 1299 | #[cfg(unix)] 1300 | pub fn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { 1301 | self.inner.advise(advice as libc::c_int, offset, len) 1302 | } 1303 | 1304 | /// Advise OS how this range of memory map will be accessed. 1305 | /// 1306 | /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. 1307 | /// 1308 | /// The offset and length must be in the bounds of the memory map. 1309 | /// 1310 | /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. 1311 | #[cfg(unix)] 1312 | pub unsafe fn unchecked_advise_range( 1313 | &self, 1314 | advice: UncheckedAdvice, 1315 | offset: usize, 1316 | len: usize, 1317 | ) -> Result<()> { 1318 | self.inner.advise(advice as libc::c_int, offset, len) 1319 | } 1320 | 1321 | /// Lock the whole memory map into RAM. Only supported on Unix. 1322 | /// 1323 | /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. 1324 | #[cfg(unix)] 1325 | pub fn lock(&self) -> Result<()> { 1326 | self.inner.lock() 1327 | } 1328 | 1329 | /// Unlock the whole memory map. Only supported on Unix. 1330 | /// 1331 | /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. 1332 | #[cfg(unix)] 1333 | pub fn unlock(&self) -> Result<()> { 1334 | self.inner.unlock() 1335 | } 1336 | 1337 | /// Adjust the size of the memory mapping. 1338 | /// 1339 | /// This will try to resize the memory mapping in place. If 1340 | /// [`RemapOptions::may_move`] is specified it will move the mapping if it 1341 | /// could not resize in place, otherwise it will error. 1342 | /// 1343 | /// Only supported on Linux. 1344 | /// 1345 | /// See the [`mremap(2)`] man page. 1346 | /// 1347 | /// # Safety 1348 | /// 1349 | /// Resizing the memory mapping beyond the end of the mapped file will 1350 | /// result in UB should you happen to access memory beyond the end of the 1351 | /// file. 1352 | /// 1353 | /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html 1354 | #[cfg(target_os = "linux")] 1355 | pub unsafe fn remap(&mut self, new_len: usize, options: RemapOptions) -> Result<()> { 1356 | self.inner.remap(new_len, options) 1357 | } 1358 | } 1359 | 1360 | #[cfg(feature = "stable_deref_trait")] 1361 | unsafe impl stable_deref_trait::StableDeref for MmapMut {} 1362 | 1363 | impl Deref for MmapMut { 1364 | type Target = [u8]; 1365 | 1366 | #[inline] 1367 | fn deref(&self) -> &[u8] { 1368 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } 1369 | } 1370 | } 1371 | 1372 | impl DerefMut for MmapMut { 1373 | #[inline] 1374 | fn deref_mut(&mut self) -> &mut [u8] { 1375 | unsafe { slice::from_raw_parts_mut(self.inner.mut_ptr(), self.inner.len()) } 1376 | } 1377 | } 1378 | 1379 | impl AsRef<[u8]> for MmapMut { 1380 | #[inline] 1381 | fn as_ref(&self) -> &[u8] { 1382 | self.deref() 1383 | } 1384 | } 1385 | 1386 | impl AsMut<[u8]> for MmapMut { 1387 | #[inline] 1388 | fn as_mut(&mut self) -> &mut [u8] { 1389 | self.deref_mut() 1390 | } 1391 | } 1392 | 1393 | impl fmt::Debug for MmapMut { 1394 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 1395 | fmt.debug_struct("MmapMut") 1396 | .field("ptr", &self.as_ptr()) 1397 | .field("len", &self.len()) 1398 | .finish() 1399 | } 1400 | } 1401 | 1402 | /// Options for [`Mmap::remap`] and [`MmapMut::remap`]. 1403 | #[derive(Copy, Clone, Default, Debug)] 1404 | #[cfg(target_os = "linux")] 1405 | pub struct RemapOptions { 1406 | may_move: bool, 1407 | } 1408 | 1409 | #[cfg(target_os = "linux")] 1410 | impl RemapOptions { 1411 | /// Creates a mew set of options for resizing a memory map. 1412 | pub fn new() -> Self { 1413 | Self::default() 1414 | } 1415 | 1416 | /// Controls whether the memory map can be moved if it is not possible to 1417 | /// resize it in place. 1418 | /// 1419 | /// If false then the memory map is guaranteed to remain at the same 1420 | /// address when being resized but attempting to resize will return an 1421 | /// error if the new memory map would overlap with something else in the 1422 | /// current process' memory. 1423 | /// 1424 | /// By default this is false. 1425 | /// 1426 | /// # `may_move` and `StableDeref` 1427 | /// If the `stable_deref_trait` feature is enabled then [`Mmap`] and 1428 | /// [`MmapMut`] implement `StableDeref`. `StableDeref` promises that the 1429 | /// memory map dereferences to a fixed address, however, calling `remap` 1430 | /// with `may_move` set may result in the backing memory of the mapping 1431 | /// being moved to a new address. This may cause UB in other code 1432 | /// depending on the `StableDeref` guarantees. 1433 | pub fn may_move(mut self, may_move: bool) -> Self { 1434 | self.may_move = may_move; 1435 | self 1436 | } 1437 | 1438 | pub(crate) fn into_flags(self) -> libc::c_int { 1439 | if self.may_move { 1440 | libc::MREMAP_MAYMOVE 1441 | } else { 1442 | 0 1443 | } 1444 | } 1445 | } 1446 | 1447 | #[cfg(test)] 1448 | mod test { 1449 | #[cfg(unix)] 1450 | use crate::advice::Advice; 1451 | use std::fs::{File, OpenOptions}; 1452 | use std::io::{Read, Write}; 1453 | use std::mem; 1454 | #[cfg(unix)] 1455 | use std::os::unix::io::AsRawFd; 1456 | #[cfg(windows)] 1457 | use std::os::windows::fs::OpenOptionsExt; 1458 | 1459 | #[cfg(windows)] 1460 | const GENERIC_ALL: u32 = 0x10000000; 1461 | 1462 | use super::{Mmap, MmapMut, MmapOptions}; 1463 | 1464 | #[test] 1465 | fn map_file() { 1466 | let expected_len = 128; 1467 | let tempdir = tempfile::tempdir().unwrap(); 1468 | let path = tempdir.path().join("mmap"); 1469 | 1470 | let file = OpenOptions::new() 1471 | .read(true) 1472 | .write(true) 1473 | .create(true) 1474 | .truncate(true) 1475 | .open(path) 1476 | .unwrap(); 1477 | 1478 | file.set_len(expected_len as u64).unwrap(); 1479 | 1480 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1481 | let len = mmap.len(); 1482 | assert_eq!(expected_len, len); 1483 | 1484 | let zeros = vec![0; len]; 1485 | let incr: Vec = (0..len as u8).collect(); 1486 | 1487 | // check that the mmap is empty 1488 | assert_eq!(&zeros[..], &mmap[..]); 1489 | 1490 | // write values into the mmap 1491 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1492 | 1493 | // read values back 1494 | assert_eq!(&incr[..], &mmap[..]); 1495 | } 1496 | 1497 | #[test] 1498 | #[cfg(unix)] 1499 | fn map_fd() { 1500 | let expected_len = 128; 1501 | let tempdir = tempfile::tempdir().unwrap(); 1502 | let path = tempdir.path().join("mmap"); 1503 | 1504 | let file = OpenOptions::new() 1505 | .read(true) 1506 | .write(true) 1507 | .create(true) 1508 | .truncate(true) 1509 | .open(path) 1510 | .unwrap(); 1511 | 1512 | file.set_len(expected_len as u64).unwrap(); 1513 | 1514 | let mut mmap = unsafe { MmapMut::map_mut(file.as_raw_fd()).unwrap() }; 1515 | let len = mmap.len(); 1516 | assert_eq!(expected_len, len); 1517 | 1518 | let zeros = vec![0; len]; 1519 | let incr: Vec = (0..len as u8).collect(); 1520 | 1521 | // check that the mmap is empty 1522 | assert_eq!(&zeros[..], &mmap[..]); 1523 | 1524 | // write values into the mmap 1525 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1526 | 1527 | // read values back 1528 | assert_eq!(&incr[..], &mmap[..]); 1529 | } 1530 | 1531 | /// Checks that "mapping" a 0-length file derefs to an empty slice. 1532 | #[test] 1533 | fn map_empty_file() { 1534 | let tempdir = tempfile::tempdir().unwrap(); 1535 | let path = tempdir.path().join("mmap"); 1536 | 1537 | let file = OpenOptions::new() 1538 | .read(true) 1539 | .write(true) 1540 | .create(true) 1541 | .truncate(true) 1542 | .open(path) 1543 | .unwrap(); 1544 | let mmap = unsafe { Mmap::map(&file).unwrap() }; 1545 | assert!(mmap.is_empty()); 1546 | assert_eq!(mmap.as_ptr().align_offset(mem::size_of::()), 0); 1547 | let mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1548 | assert!(mmap.is_empty()); 1549 | assert_eq!(mmap.as_ptr().align_offset(mem::size_of::()), 0); 1550 | } 1551 | 1552 | #[test] 1553 | fn map_anon() { 1554 | let expected_len = 128; 1555 | let mut mmap = MmapMut::map_anon(expected_len).unwrap(); 1556 | let len = mmap.len(); 1557 | assert_eq!(expected_len, len); 1558 | 1559 | let zeros = vec![0; len]; 1560 | let incr: Vec = (0..len as u8).collect(); 1561 | 1562 | // check that the mmap is empty 1563 | assert_eq!(&zeros[..], &mmap[..]); 1564 | 1565 | // write values into the mmap 1566 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1567 | 1568 | // read values back 1569 | assert_eq!(&incr[..], &mmap[..]); 1570 | } 1571 | 1572 | #[test] 1573 | fn map_anon_zero_len() { 1574 | assert!(MmapOptions::new().map_anon().unwrap().is_empty()); 1575 | } 1576 | 1577 | #[test] 1578 | #[cfg(target_pointer_width = "32")] 1579 | fn map_anon_len_overflow() { 1580 | let res = MmapMut::map_anon(0x80000000); 1581 | 1582 | assert_eq!( 1583 | res.unwrap_err().to_string(), 1584 | "memory map length overflows isize" 1585 | ); 1586 | } 1587 | 1588 | #[test] 1589 | fn file_write() { 1590 | let tempdir = tempfile::tempdir().unwrap(); 1591 | let path = tempdir.path().join("mmap"); 1592 | 1593 | let mut file = OpenOptions::new() 1594 | .read(true) 1595 | .write(true) 1596 | .create(true) 1597 | .truncate(true) 1598 | .open(path) 1599 | .unwrap(); 1600 | file.set_len(128).unwrap(); 1601 | 1602 | let write = b"abc123"; 1603 | let mut read = [0u8; 6]; 1604 | 1605 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1606 | (&mut mmap[..]).write_all(write).unwrap(); 1607 | mmap.flush().unwrap(); 1608 | 1609 | file.read_exact(&mut read).unwrap(); 1610 | assert_eq!(write, &read); 1611 | } 1612 | 1613 | #[test] 1614 | fn flush_range() { 1615 | let tempdir = tempfile::tempdir().unwrap(); 1616 | let path = tempdir.path().join("mmap"); 1617 | 1618 | let file = OpenOptions::new() 1619 | .read(true) 1620 | .write(true) 1621 | .create(true) 1622 | .truncate(true) 1623 | .open(path) 1624 | .unwrap(); 1625 | file.set_len(128).unwrap(); 1626 | let write = b"abc123"; 1627 | 1628 | let mut mmap = unsafe { 1629 | MmapOptions::new() 1630 | .offset(2) 1631 | .len(write.len()) 1632 | .map_mut(&file) 1633 | .unwrap() 1634 | }; 1635 | (&mut mmap[..]).write_all(write).unwrap(); 1636 | mmap.flush_async_range(0, write.len()).unwrap(); 1637 | mmap.flush_range(0, write.len()).unwrap(); 1638 | } 1639 | 1640 | #[test] 1641 | fn map_copy() { 1642 | let tempdir = tempfile::tempdir().unwrap(); 1643 | let path = tempdir.path().join("mmap"); 1644 | 1645 | let mut file = OpenOptions::new() 1646 | .read(true) 1647 | .write(true) 1648 | .create(true) 1649 | .truncate(true) 1650 | .open(path) 1651 | .unwrap(); 1652 | file.set_len(128).unwrap(); 1653 | 1654 | let nulls = b"\0\0\0\0\0\0"; 1655 | let write = b"abc123"; 1656 | let mut read = [0u8; 6]; 1657 | 1658 | let mut mmap = unsafe { MmapOptions::new().map_copy(&file).unwrap() }; 1659 | 1660 | (&mut mmap[..]).write_all(write).unwrap(); 1661 | mmap.flush().unwrap(); 1662 | 1663 | // The mmap contains the write 1664 | (&mmap[..]).read_exact(&mut read).unwrap(); 1665 | assert_eq!(write, &read); 1666 | 1667 | // The file does not contain the write 1668 | file.read_exact(&mut read).unwrap(); 1669 | assert_eq!(nulls, &read); 1670 | 1671 | // another mmap does not contain the write 1672 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1673 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1674 | assert_eq!(nulls, &read); 1675 | } 1676 | 1677 | #[test] 1678 | fn map_copy_read_only() { 1679 | let tempdir = tempfile::tempdir().unwrap(); 1680 | let path = tempdir.path().join("mmap"); 1681 | 1682 | let file = OpenOptions::new() 1683 | .read(true) 1684 | .write(true) 1685 | .create(true) 1686 | .truncate(true) 1687 | .open(path) 1688 | .unwrap(); 1689 | file.set_len(128).unwrap(); 1690 | 1691 | let nulls = b"\0\0\0\0\0\0"; 1692 | let mut read = [0u8; 6]; 1693 | 1694 | let mmap = unsafe { MmapOptions::new().map_copy_read_only(&file).unwrap() }; 1695 | (&mmap[..]).read_exact(&mut read).unwrap(); 1696 | assert_eq!(nulls, &read); 1697 | 1698 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1699 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1700 | assert_eq!(nulls, &read); 1701 | } 1702 | 1703 | #[test] 1704 | fn map_offset() { 1705 | let tempdir = tempfile::tempdir().unwrap(); 1706 | let path = tempdir.path().join("mmap"); 1707 | 1708 | let file = OpenOptions::new() 1709 | .read(true) 1710 | .write(true) 1711 | .create(true) 1712 | .truncate(true) 1713 | .open(path) 1714 | .unwrap(); 1715 | 1716 | let offset = u64::from(u32::MAX) + 2; 1717 | let len = 5432; 1718 | file.set_len(offset + len as u64).unwrap(); 1719 | 1720 | // Check inferred length mmap. 1721 | let mmap = unsafe { MmapOptions::new().offset(offset).map_mut(&file).unwrap() }; 1722 | assert_eq!(len, mmap.len()); 1723 | 1724 | // Check explicit length mmap. 1725 | let mut mmap = unsafe { 1726 | MmapOptions::new() 1727 | .offset(offset) 1728 | .len(len) 1729 | .map_mut(&file) 1730 | .unwrap() 1731 | }; 1732 | assert_eq!(len, mmap.len()); 1733 | 1734 | let zeros = vec![0; len]; 1735 | let incr: Vec<_> = (0..len).map(|i| i as u8).collect(); 1736 | 1737 | // check that the mmap is empty 1738 | assert_eq!(&zeros[..], &mmap[..]); 1739 | 1740 | // write values into the mmap 1741 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 1742 | 1743 | // read values back 1744 | assert_eq!(&incr[..], &mmap[..]); 1745 | } 1746 | 1747 | #[test] 1748 | fn index() { 1749 | let mut mmap = MmapMut::map_anon(128).unwrap(); 1750 | mmap[0] = 42; 1751 | assert_eq!(42, mmap[0]); 1752 | } 1753 | 1754 | #[test] 1755 | fn sync_send() { 1756 | fn is_sync_send(_val: T) 1757 | where 1758 | T: Sync + Send, 1759 | { 1760 | } 1761 | 1762 | let mmap = MmapMut::map_anon(129).unwrap(); 1763 | is_sync_send(mmap); 1764 | } 1765 | 1766 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 1767 | fn jit_x86(mut mmap: MmapMut) { 1768 | mmap[0] = 0xB8; // mov eax, 0xAB 1769 | mmap[1] = 0xAB; 1770 | mmap[2] = 0x00; 1771 | mmap[3] = 0x00; 1772 | mmap[4] = 0x00; 1773 | mmap[5] = 0xC3; // ret 1774 | 1775 | let mmap = mmap.make_exec().expect("make_exec"); 1776 | 1777 | let jitfn: extern "C" fn() -> u8 = unsafe { mem::transmute(mmap.as_ptr()) }; 1778 | assert_eq!(jitfn(), 0xab); 1779 | } 1780 | 1781 | #[test] 1782 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 1783 | fn jit_x86_anon() { 1784 | jit_x86(MmapMut::map_anon(4096).unwrap()); 1785 | } 1786 | 1787 | #[test] 1788 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 1789 | fn jit_x86_file() { 1790 | let tempdir = tempfile::tempdir().unwrap(); 1791 | let mut options = OpenOptions::new(); 1792 | #[cfg(windows)] 1793 | options.access_mode(GENERIC_ALL); 1794 | 1795 | let file = options 1796 | .read(true) 1797 | .write(true) 1798 | .create(true) 1799 | .truncate(true) 1800 | .open(tempdir.path().join("jit_x86")) 1801 | .expect("open"); 1802 | 1803 | file.set_len(4096).expect("set_len"); 1804 | jit_x86(unsafe { MmapMut::map_mut(&file).expect("map_mut") }); 1805 | } 1806 | 1807 | #[test] 1808 | fn mprotect_file() { 1809 | let tempdir = tempfile::tempdir().unwrap(); 1810 | let path = tempdir.path().join("mmap"); 1811 | 1812 | let mut options = OpenOptions::new(); 1813 | #[cfg(windows)] 1814 | options.access_mode(GENERIC_ALL); 1815 | 1816 | let mut file = options 1817 | .read(true) 1818 | .write(true) 1819 | .create(true) 1820 | .truncate(true) 1821 | .open(path) 1822 | .expect("open"); 1823 | file.set_len(256_u64).expect("set_len"); 1824 | 1825 | let mmap = unsafe { MmapMut::map_mut(&file).expect("map_mut") }; 1826 | 1827 | let mmap = mmap.make_read_only().expect("make_read_only"); 1828 | let mut mmap = mmap.make_mut().expect("make_mut"); 1829 | 1830 | let write = b"abc123"; 1831 | let mut read = [0u8; 6]; 1832 | 1833 | (&mut mmap[..]).write_all(write).unwrap(); 1834 | mmap.flush().unwrap(); 1835 | 1836 | // The mmap contains the write 1837 | (&mmap[..]).read_exact(&mut read).unwrap(); 1838 | assert_eq!(write, &read); 1839 | 1840 | // The file should contain the write 1841 | file.read_exact(&mut read).unwrap(); 1842 | assert_eq!(write, &read); 1843 | 1844 | // another mmap should contain the write 1845 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1846 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1847 | assert_eq!(write, &read); 1848 | 1849 | let mmap = mmap.make_exec().expect("make_exec"); 1850 | 1851 | drop(mmap); 1852 | } 1853 | 1854 | #[test] 1855 | fn mprotect_copy() { 1856 | let tempdir = tempfile::tempdir().unwrap(); 1857 | let path = tempdir.path().join("mmap"); 1858 | 1859 | let mut options = OpenOptions::new(); 1860 | #[cfg(windows)] 1861 | options.access_mode(GENERIC_ALL); 1862 | 1863 | let mut file = options 1864 | .read(true) 1865 | .write(true) 1866 | .create(true) 1867 | .truncate(true) 1868 | .open(path) 1869 | .expect("open"); 1870 | file.set_len(256_u64).expect("set_len"); 1871 | 1872 | let mmap = unsafe { MmapOptions::new().map_copy(&file).expect("map_mut") }; 1873 | 1874 | let mmap = mmap.make_read_only().expect("make_read_only"); 1875 | let mut mmap = mmap.make_mut().expect("make_mut"); 1876 | 1877 | let nulls = b"\0\0\0\0\0\0"; 1878 | let write = b"abc123"; 1879 | let mut read = [0u8; 6]; 1880 | 1881 | (&mut mmap[..]).write_all(write).unwrap(); 1882 | mmap.flush().unwrap(); 1883 | 1884 | // The mmap contains the write 1885 | (&mmap[..]).read_exact(&mut read).unwrap(); 1886 | assert_eq!(write, &read); 1887 | 1888 | // The file does not contain the write 1889 | file.read_exact(&mut read).unwrap(); 1890 | assert_eq!(nulls, &read); 1891 | 1892 | // another mmap does not contain the write 1893 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; 1894 | (&mmap2[..]).read_exact(&mut read).unwrap(); 1895 | assert_eq!(nulls, &read); 1896 | 1897 | let mmap = mmap.make_exec().expect("make_exec"); 1898 | 1899 | drop(mmap); 1900 | } 1901 | 1902 | #[test] 1903 | fn mprotect_anon() { 1904 | let mmap = MmapMut::map_anon(256).expect("map_mut"); 1905 | 1906 | let mmap = mmap.make_read_only().expect("make_read_only"); 1907 | let mmap = mmap.make_mut().expect("make_mut"); 1908 | let mmap = mmap.make_exec().expect("make_exec"); 1909 | drop(mmap); 1910 | } 1911 | 1912 | #[test] 1913 | fn raw() { 1914 | let tempdir = tempfile::tempdir().unwrap(); 1915 | let path = tempdir.path().join("mmapraw"); 1916 | 1917 | let mut options = OpenOptions::new(); 1918 | let mut file = options 1919 | .read(true) 1920 | .write(true) 1921 | .create(true) 1922 | .truncate(true) 1923 | .open(path) 1924 | .expect("open"); 1925 | file.write_all(b"abc123").unwrap(); 1926 | let mmap = MmapOptions::new().map_raw(&file).unwrap(); 1927 | assert_eq!(mmap.len(), 6); 1928 | assert!(!mmap.as_ptr().is_null()); 1929 | assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a'); 1930 | } 1931 | 1932 | #[test] 1933 | fn raw_read_only() { 1934 | let tempdir = tempfile::tempdir().unwrap(); 1935 | let path = tempdir.path().join("mmaprawro"); 1936 | 1937 | File::create(&path).unwrap().write_all(b"abc123").unwrap(); 1938 | 1939 | let mmap = MmapOptions::new() 1940 | .map_raw_read_only(&File::open(&path).unwrap()) 1941 | .unwrap(); 1942 | 1943 | assert_eq!(mmap.len(), 6); 1944 | assert!(!mmap.as_ptr().is_null()); 1945 | assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a'); 1946 | } 1947 | 1948 | /// Something that relies on StableDeref 1949 | #[test] 1950 | #[cfg(feature = "stable_deref_trait")] 1951 | fn owning_ref() { 1952 | let mut map = MmapMut::map_anon(128).unwrap(); 1953 | map[10] = 42; 1954 | let owning = owning_ref::OwningRef::new(map); 1955 | let sliced = owning.map(|map| &map[10..20]); 1956 | assert_eq!(42, sliced[0]); 1957 | 1958 | let map = sliced.into_owner().make_read_only().unwrap(); 1959 | let owning = owning_ref::OwningRef::new(map); 1960 | let sliced = owning.map(|map| &map[10..20]); 1961 | assert_eq!(42, sliced[0]); 1962 | } 1963 | 1964 | #[test] 1965 | #[cfg(unix)] 1966 | fn advise() { 1967 | let expected_len = 128; 1968 | let tempdir = tempfile::tempdir().unwrap(); 1969 | let path = tempdir.path().join("mmap_advise"); 1970 | 1971 | let file = OpenOptions::new() 1972 | .read(true) 1973 | .write(true) 1974 | .create(true) 1975 | .truncate(true) 1976 | .open(path) 1977 | .unwrap(); 1978 | 1979 | file.set_len(expected_len as u64).unwrap(); 1980 | 1981 | // Test MmapMut::advise 1982 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 1983 | mmap.advise(Advice::Random) 1984 | .expect("mmap advising should be supported on unix"); 1985 | 1986 | let len = mmap.len(); 1987 | assert_eq!(expected_len, len); 1988 | 1989 | let zeros = vec![0; len]; 1990 | let incr: Vec = (0..len as u8).collect(); 1991 | 1992 | // check that the mmap is empty 1993 | assert_eq!(&zeros[..], &mmap[..]); 1994 | 1995 | mmap.advise_range(Advice::Sequential, 0, mmap.len()) 1996 | .expect("mmap advising should be supported on unix"); 1997 | 1998 | // write values into the mmap 1999 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); 2000 | 2001 | // read values back 2002 | assert_eq!(&incr[..], &mmap[..]); 2003 | 2004 | // Set advice and Read from the read-only map 2005 | let mmap = unsafe { Mmap::map(&file).unwrap() }; 2006 | 2007 | mmap.advise(Advice::Random) 2008 | .expect("mmap advising should be supported on unix"); 2009 | 2010 | // read values back 2011 | assert_eq!(&incr[..], &mmap[..]); 2012 | } 2013 | 2014 | #[test] 2015 | #[cfg(target_os = "linux")] 2016 | fn advise_writes_unsafely() { 2017 | let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; 2018 | 2019 | let mut mmap = MmapMut::map_anon(page_size).unwrap(); 2020 | mmap.as_mut().fill(255); 2021 | let mmap = mmap.make_read_only().unwrap(); 2022 | 2023 | let a = mmap.as_ref()[0]; 2024 | unsafe { 2025 | mmap.unchecked_advise(crate::UncheckedAdvice::DontNeed) 2026 | .unwrap(); 2027 | } 2028 | let b = mmap.as_ref()[0]; 2029 | 2030 | assert_eq!(a, 255); 2031 | assert_eq!(b, 0); 2032 | } 2033 | 2034 | #[test] 2035 | #[cfg(target_os = "linux")] 2036 | fn advise_writes_unsafely_to_part_of_map() { 2037 | let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; 2038 | 2039 | let mut mmap = MmapMut::map_anon(2 * page_size).unwrap(); 2040 | mmap.as_mut().fill(255); 2041 | let mmap = mmap.make_read_only().unwrap(); 2042 | 2043 | let a = mmap.as_ref()[0]; 2044 | let b = mmap.as_ref()[page_size]; 2045 | unsafe { 2046 | mmap.unchecked_advise_range(crate::UncheckedAdvice::DontNeed, page_size, page_size) 2047 | .unwrap(); 2048 | } 2049 | let c = mmap.as_ref()[0]; 2050 | let d = mmap.as_ref()[page_size]; 2051 | 2052 | assert_eq!(a, 255); 2053 | assert_eq!(b, 255); 2054 | assert_eq!(c, 255); 2055 | assert_eq!(d, 0); 2056 | } 2057 | 2058 | /// Returns true if a non-zero amount of memory is locked. 2059 | #[cfg(target_os = "linux")] 2060 | fn is_locked() -> bool { 2061 | let status = &std::fs::read_to_string("/proc/self/status") 2062 | .expect("/proc/self/status should be available"); 2063 | for line in status.lines() { 2064 | if line.starts_with("VmLck:") { 2065 | let numbers = line.replace(|c: char| !c.is_ascii_digit(), ""); 2066 | return numbers != "0"; 2067 | } 2068 | } 2069 | panic!("cannot get VmLck information") 2070 | } 2071 | 2072 | #[test] 2073 | #[cfg(unix)] 2074 | fn lock() { 2075 | let tempdir = tempfile::tempdir().unwrap(); 2076 | let path = tempdir.path().join("mmap_lock"); 2077 | 2078 | let file = OpenOptions::new() 2079 | .read(true) 2080 | .write(true) 2081 | .create(true) 2082 | .truncate(true) 2083 | .open(path) 2084 | .unwrap(); 2085 | file.set_len(128).unwrap(); 2086 | 2087 | let mmap = unsafe { Mmap::map(&file).unwrap() }; 2088 | #[cfg(target_os = "linux")] 2089 | assert!(!is_locked()); 2090 | 2091 | mmap.lock().expect("mmap lock should be supported on unix"); 2092 | #[cfg(target_os = "linux")] 2093 | assert!(is_locked()); 2094 | 2095 | mmap.lock() 2096 | .expect("mmap lock again should not cause problems"); 2097 | #[cfg(target_os = "linux")] 2098 | assert!(is_locked()); 2099 | 2100 | mmap.unlock() 2101 | .expect("mmap unlock should be supported on unix"); 2102 | #[cfg(target_os = "linux")] 2103 | assert!(!is_locked()); 2104 | 2105 | mmap.unlock() 2106 | .expect("mmap unlock again should not cause problems"); 2107 | #[cfg(target_os = "linux")] 2108 | assert!(!is_locked()); 2109 | } 2110 | 2111 | #[test] 2112 | #[cfg(target_os = "linux")] 2113 | fn remap_grow() { 2114 | use crate::RemapOptions; 2115 | 2116 | let initial_len = 128; 2117 | let final_len = 2000; 2118 | 2119 | let zeros = vec![0u8; final_len]; 2120 | let incr: Vec = (0..final_len).map(|v| v as u8).collect(); 2121 | 2122 | let file = tempfile::tempfile().unwrap(); 2123 | file.set_len(final_len as u64).unwrap(); 2124 | 2125 | let mut mmap = unsafe { MmapOptions::new().len(initial_len).map_mut(&file).unwrap() }; 2126 | assert_eq!(mmap.len(), initial_len); 2127 | assert_eq!(&mmap[..], &zeros[..initial_len]); 2128 | 2129 | unsafe { 2130 | mmap.remap(final_len, RemapOptions::new().may_move(true)) 2131 | .unwrap(); 2132 | } 2133 | 2134 | // The size should have been updated 2135 | assert_eq!(mmap.len(), final_len); 2136 | 2137 | // Should still be all zeros 2138 | assert_eq!(&mmap[..], &zeros); 2139 | 2140 | // Write out to the whole expanded slice. 2141 | mmap.copy_from_slice(&incr); 2142 | } 2143 | 2144 | #[test] 2145 | #[cfg(target_os = "linux")] 2146 | fn remap_shrink() { 2147 | use crate::RemapOptions; 2148 | 2149 | let initial_len = 20000; 2150 | let final_len = 400; 2151 | 2152 | let incr: Vec = (0..final_len).map(|v| v as u8).collect(); 2153 | 2154 | let file = tempfile::tempfile().unwrap(); 2155 | file.set_len(initial_len as u64).unwrap(); 2156 | 2157 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; 2158 | assert_eq!(mmap.len(), initial_len); 2159 | 2160 | unsafe { mmap.remap(final_len, RemapOptions::new()).unwrap() }; 2161 | assert_eq!(mmap.len(), final_len); 2162 | 2163 | // Check that the mmap is still writable along the slice length 2164 | mmap.copy_from_slice(&incr); 2165 | } 2166 | 2167 | #[test] 2168 | #[cfg(target_os = "linux")] 2169 | #[cfg(target_pointer_width = "32")] 2170 | fn remap_len_overflow() { 2171 | use crate::RemapOptions; 2172 | 2173 | let file = tempfile::tempfile().unwrap(); 2174 | file.set_len(1024).unwrap(); 2175 | let mut mmap = unsafe { MmapOptions::new().len(1024).map(&file).unwrap() }; 2176 | 2177 | let res = unsafe { mmap.remap(0x80000000, RemapOptions::new().may_move(true)) }; 2178 | assert_eq!( 2179 | res.unwrap_err().to_string(), 2180 | "memory map length overflows isize" 2181 | ); 2182 | 2183 | assert_eq!(mmap.len(), 1024); 2184 | } 2185 | 2186 | #[test] 2187 | #[cfg(target_os = "linux")] 2188 | fn remap_with_offset() { 2189 | use crate::RemapOptions; 2190 | 2191 | let offset = 77; 2192 | let initial_len = 128; 2193 | let final_len = 2000; 2194 | 2195 | let zeros = vec![0u8; final_len]; 2196 | let incr: Vec = (0..final_len).map(|v| v as u8).collect(); 2197 | 2198 | let file = tempfile::tempfile().unwrap(); 2199 | file.set_len(final_len as u64 + offset).unwrap(); 2200 | 2201 | let mut mmap = unsafe { 2202 | MmapOptions::new() 2203 | .len(initial_len) 2204 | .offset(offset) 2205 | .map_mut(&file) 2206 | .unwrap() 2207 | }; 2208 | assert_eq!(mmap.len(), initial_len); 2209 | assert_eq!(&mmap[..], &zeros[..initial_len]); 2210 | 2211 | unsafe { 2212 | mmap.remap(final_len, RemapOptions::new().may_move(true)) 2213 | .unwrap(); 2214 | } 2215 | 2216 | // The size should have been updated 2217 | assert_eq!(mmap.len(), final_len); 2218 | 2219 | // Should still be all zeros 2220 | assert_eq!(&mmap[..], &zeros); 2221 | 2222 | // Write out to the whole expanded slice. 2223 | mmap.copy_from_slice(&incr); 2224 | } 2225 | } 2226 | -------------------------------------------------------------------------------- /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, _: Option) -> 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 | -------------------------------------------------------------------------------- /src/unix.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::mem::ManuallyDrop; 3 | use std::os::unix::io::{FromRawFd, RawFd}; 4 | use std::sync::atomic::{AtomicUsize, Ordering}; 5 | use std::{io, ptr}; 6 | 7 | #[cfg(any( 8 | all(target_os = "linux", not(target_arch = "mips")), 9 | target_os = "freebsd", 10 | target_os = "android" 11 | ))] 12 | const MAP_STACK: libc::c_int = libc::MAP_STACK; 13 | 14 | #[cfg(not(any( 15 | all(target_os = "linux", not(target_arch = "mips")), 16 | target_os = "freebsd", 17 | target_os = "android" 18 | )))] 19 | const MAP_STACK: libc::c_int = 0; 20 | 21 | #[cfg(any(target_os = "linux", target_os = "android"))] 22 | const MAP_POPULATE: libc::c_int = libc::MAP_POPULATE; 23 | 24 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 25 | const MAP_POPULATE: libc::c_int = 0; 26 | 27 | #[cfg(any(target_os = "linux", target_os = "android"))] 28 | const MAP_HUGETLB: libc::c_int = libc::MAP_HUGETLB; 29 | 30 | #[cfg(target_os = "linux")] 31 | const MAP_HUGE_MASK: libc::c_int = libc::MAP_HUGE_MASK; 32 | 33 | #[cfg(any(target_os = "linux", target_os = "android"))] 34 | const MAP_HUGE_SHIFT: libc::c_int = libc::MAP_HUGE_SHIFT; 35 | 36 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 37 | const MAP_HUGETLB: libc::c_int = 0; 38 | 39 | #[cfg(not(target_os = "linux"))] 40 | const MAP_HUGE_MASK: libc::c_int = 0; 41 | 42 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 43 | const MAP_HUGE_SHIFT: libc::c_int = 0; 44 | 45 | #[cfg(any( 46 | target_os = "android", 47 | all(target_os = "linux", not(target_env = "musl")) 48 | ))] 49 | use libc::{mmap64 as mmap, off64_t as off_t}; 50 | 51 | #[cfg(not(any( 52 | target_os = "android", 53 | all(target_os = "linux", not(target_env = "musl")) 54 | )))] 55 | use libc::{mmap, off_t}; 56 | 57 | pub struct MmapInner { 58 | ptr: *mut libc::c_void, 59 | len: usize, 60 | } 61 | 62 | impl MmapInner { 63 | /// Creates a new `MmapInner`. 64 | /// 65 | /// This is a thin wrapper around the `mmap` system call. 66 | fn new( 67 | len: usize, 68 | prot: libc::c_int, 69 | flags: libc::c_int, 70 | file: RawFd, 71 | offset: u64, 72 | ) -> io::Result { 73 | let alignment = offset % page_size() as u64; 74 | let aligned_offset = offset - alignment; 75 | 76 | let (map_len, map_offset) = Self::adjust_mmap_params(len, alignment as usize)?; 77 | 78 | unsafe { 79 | let ptr = mmap( 80 | ptr::null_mut(), 81 | map_len as libc::size_t, 82 | prot, 83 | flags, 84 | file, 85 | aligned_offset as off_t, 86 | ); 87 | 88 | if ptr == libc::MAP_FAILED { 89 | Err(io::Error::last_os_error()) 90 | } else { 91 | Ok(Self::from_raw_parts(ptr, len, map_offset)) 92 | } 93 | } 94 | } 95 | 96 | fn adjust_mmap_params(len: usize, alignment: usize) -> io::Result<(usize, usize)> { 97 | // 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 libc::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 libc::c_void, len: usize, offset: usize) -> Self { 208 | debug_assert_eq!(ptr as usize % page_size(), 0, "ptr not page-aligned"); 209 | debug_assert!(offset < page_size(), "offset larger than page size"); 210 | 211 | Self { 212 | ptr: ptr.add(offset), 213 | len, 214 | } 215 | } 216 | 217 | pub fn map(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 218 | let populate = if populate { MAP_POPULATE } else { 0 }; 219 | MmapInner::new( 220 | len, 221 | libc::PROT_READ, 222 | libc::MAP_SHARED | populate, 223 | file, 224 | offset, 225 | ) 226 | } 227 | 228 | pub fn map_exec(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 229 | let populate = if populate { MAP_POPULATE } else { 0 }; 230 | MmapInner::new( 231 | len, 232 | libc::PROT_READ | libc::PROT_EXEC, 233 | libc::MAP_SHARED | populate, 234 | file, 235 | offset, 236 | ) 237 | } 238 | 239 | pub fn map_mut(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 240 | let populate = if populate { MAP_POPULATE } else { 0 }; 241 | MmapInner::new( 242 | len, 243 | libc::PROT_READ | libc::PROT_WRITE, 244 | libc::MAP_SHARED | populate, 245 | file, 246 | offset, 247 | ) 248 | } 249 | 250 | pub fn map_copy(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result { 251 | let populate = if populate { MAP_POPULATE } else { 0 }; 252 | MmapInner::new( 253 | len, 254 | libc::PROT_READ | libc::PROT_WRITE, 255 | libc::MAP_PRIVATE | populate, 256 | file, 257 | offset, 258 | ) 259 | } 260 | 261 | pub fn map_copy_read_only( 262 | len: usize, 263 | file: RawFd, 264 | offset: u64, 265 | populate: bool, 266 | ) -> io::Result { 267 | let populate = if populate { MAP_POPULATE } else { 0 }; 268 | MmapInner::new( 269 | len, 270 | libc::PROT_READ, 271 | libc::MAP_PRIVATE | populate, 272 | file, 273 | offset, 274 | ) 275 | } 276 | 277 | /// Open an anonymous memory map. 278 | pub fn map_anon( 279 | len: usize, 280 | stack: bool, 281 | populate: bool, 282 | huge: Option, 283 | ) -> io::Result { 284 | let stack = if stack { MAP_STACK } else { 0 }; 285 | let populate = if populate { MAP_POPULATE } else { 0 }; 286 | let hugetlb = if huge.is_some() { MAP_HUGETLB } else { 0 }; 287 | let hugetlb_size = huge.map_or(0, |mask| { 288 | (u64::from(mask) & (MAP_HUGE_MASK as u64)) << MAP_HUGE_SHIFT 289 | }) as i32; 290 | MmapInner::new( 291 | len, 292 | libc::PROT_READ | libc::PROT_WRITE, 293 | libc::MAP_PRIVATE | libc::MAP_ANON | stack | populate | hugetlb | hugetlb_size, 294 | -1, 295 | 0, 296 | ) 297 | } 298 | 299 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { 300 | let alignment = (self.ptr as usize + offset) % page_size(); 301 | let offset = offset as isize - alignment as isize; 302 | let len = len + alignment; 303 | let result = 304 | unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) }; 305 | if result == 0 { 306 | Ok(()) 307 | } else { 308 | Err(io::Error::last_os_error()) 309 | } 310 | } 311 | 312 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { 313 | let alignment = (self.ptr as usize + offset) % page_size(); 314 | let offset = offset as isize - alignment as isize; 315 | let len = len + alignment; 316 | let result = 317 | unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_ASYNC) }; 318 | if result == 0 { 319 | Ok(()) 320 | } else { 321 | Err(io::Error::last_os_error()) 322 | } 323 | } 324 | 325 | fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> { 326 | unsafe { 327 | let alignment = self.ptr as usize % page_size(); 328 | let ptr = self.ptr.offset(-(alignment as isize)); 329 | let len = self.len + alignment; 330 | let len = len.max(1); 331 | if libc::mprotect(ptr, len, prot) == 0 { 332 | Ok(()) 333 | } else { 334 | Err(io::Error::last_os_error()) 335 | } 336 | } 337 | } 338 | 339 | pub fn make_read_only(&mut self) -> io::Result<()> { 340 | self.mprotect(libc::PROT_READ) 341 | } 342 | 343 | pub fn make_exec(&mut self) -> io::Result<()> { 344 | self.mprotect(libc::PROT_READ | libc::PROT_EXEC) 345 | } 346 | 347 | pub fn make_mut(&mut self) -> io::Result<()> { 348 | self.mprotect(libc::PROT_READ | libc::PROT_WRITE) 349 | } 350 | 351 | #[inline] 352 | pub fn ptr(&self) -> *const u8 { 353 | self.ptr as *const u8 354 | } 355 | 356 | #[inline] 357 | pub fn mut_ptr(&mut self) -> *mut u8 { 358 | self.ptr.cast() 359 | } 360 | 361 | #[inline] 362 | pub fn len(&self) -> usize { 363 | self.len 364 | } 365 | 366 | pub fn advise(&self, advice: libc::c_int, offset: usize, len: usize) -> io::Result<()> { 367 | let alignment = (self.ptr as usize + offset) % page_size(); 368 | let offset = offset as isize - alignment as isize; 369 | let len = len + alignment; 370 | unsafe { 371 | if libc::madvise(self.ptr.offset(offset), len, advice) != 0 { 372 | Err(io::Error::last_os_error()) 373 | } else { 374 | Ok(()) 375 | } 376 | } 377 | } 378 | 379 | #[cfg(target_os = "linux")] 380 | pub fn remap(&mut self, new_len: usize, options: crate::RemapOptions) -> io::Result<()> { 381 | let (old_ptr, old_len, offset) = self.as_mmap_params(); 382 | let (map_len, offset) = Self::adjust_mmap_params(new_len, offset)?; 383 | 384 | unsafe { 385 | let new_ptr = libc::mremap(old_ptr, old_len, map_len, options.into_flags()); 386 | 387 | if new_ptr == libc::MAP_FAILED { 388 | Err(io::Error::last_os_error()) 389 | } else { 390 | // We explicitly don't drop self since the pointer within is no longer valid. 391 | ptr::write(self, Self::from_raw_parts(new_ptr, new_len, offset)); 392 | Ok(()) 393 | } 394 | } 395 | } 396 | 397 | pub fn lock(&self) -> io::Result<()> { 398 | unsafe { 399 | if libc::mlock(self.ptr, self.len) != 0 { 400 | Err(io::Error::last_os_error()) 401 | } else { 402 | Ok(()) 403 | } 404 | } 405 | } 406 | 407 | pub fn unlock(&self) -> io::Result<()> { 408 | unsafe { 409 | if libc::munlock(self.ptr, self.len) != 0 { 410 | Err(io::Error::last_os_error()) 411 | } else { 412 | Ok(()) 413 | } 414 | } 415 | } 416 | } 417 | 418 | impl Drop for MmapInner { 419 | fn drop(&mut self) { 420 | let (ptr, len, _) = self.as_mmap_params(); 421 | 422 | // Any errors during unmapping/closing are ignored as the only way 423 | // to report them would be through panicking which is highly discouraged 424 | // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97 425 | unsafe { libc::munmap(ptr, len as libc::size_t) }; 426 | } 427 | } 428 | 429 | unsafe impl Sync for MmapInner {} 430 | unsafe impl Send for MmapInner {} 431 | 432 | fn page_size() -> usize { 433 | static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); 434 | 435 | match PAGE_SIZE.load(Ordering::Relaxed) { 436 | 0 => { 437 | let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; 438 | 439 | PAGE_SIZE.store(page_size, Ordering::Relaxed); 440 | 441 | page_size 442 | } 443 | page_size => page_size, 444 | } 445 | } 446 | 447 | pub fn file_len(file: RawFd) -> io::Result { 448 | // SAFETY: We must not close the passed-in fd by dropping the File we create, 449 | // we ensure this by immediately wrapping it in a ManuallyDrop. 450 | unsafe { 451 | let file = ManuallyDrop::new(File::from_raw_fd(file)); 452 | Ok(file.metadata()?.len()) 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /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 | #[allow(dead_code)] 81 | #[repr(C)] 82 | #[derive(Copy, Clone)] 83 | pub struct FILETIME { 84 | pub dwLowDateTime: DWORD, 85 | pub dwHighDateTime: DWORD, 86 | } 87 | 88 | extern "system" { 89 | fn GetCurrentProcess() -> HANDLE; 90 | 91 | fn CloseHandle(hObject: HANDLE) -> BOOL; 92 | 93 | fn DuplicateHandle( 94 | hSourceProcessHandle: HANDLE, 95 | hSourceHandle: HANDLE, 96 | hTargetProcessHandle: HANDLE, 97 | lpTargetHandle: LPHANDLE, 98 | dwDesiredAccess: DWORD, 99 | bInheritHandle: BOOL, 100 | dwOptions: DWORD, 101 | ) -> BOOL; 102 | 103 | fn CreateFileMappingW( 104 | hFile: HANDLE, 105 | lpFileMappingAttributes: LPSECURITY_ATTRIBUTES, 106 | flProtect: DWORD, 107 | dwMaximumSizeHigh: DWORD, 108 | dwMaximumSizeLow: DWORD, 109 | lpName: LPCWSTR, 110 | ) -> HANDLE; 111 | 112 | fn FlushFileBuffers(hFile: HANDLE) -> BOOL; 113 | 114 | fn FlushViewOfFile(lpBaseAddress: LPCVOID, dwNumberOfBytesToFlush: SIZE_T) -> BOOL; 115 | 116 | fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL; 117 | 118 | fn MapViewOfFile( 119 | hFileMappingObject: HANDLE, 120 | dwDesiredAccess: DWORD, 121 | dwFileOffsetHigh: DWORD, 122 | dwFileOffsetLow: DWORD, 123 | dwNumberOfBytesToMap: SIZE_T, 124 | ) -> LPVOID; 125 | 126 | fn VirtualProtect( 127 | lpAddress: LPVOID, 128 | dwSize: SIZE_T, 129 | flNewProtect: DWORD, 130 | lpflOldProtect: PDWORD, 131 | ) -> BOOL; 132 | 133 | fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO); 134 | } 135 | 136 | /// Returns a fixed aligned pointer that is valid for `slice::from_raw_parts::` with `len == 0`. 137 | /// 138 | /// This aligns the pointer to `allocation_granularity()` or 1 if unknown. 139 | fn empty_slice_ptr() -> *mut c_void { 140 | allocation_granularity().max(1) as *mut c_void 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, 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( 347 | len: usize, 348 | _stack: bool, 349 | _populate: bool, 350 | _huge: Option, 351 | ) -> io::Result { 352 | // Ensure a non-zero length for the underlying mapping 353 | let mapped_len = len.max(1); 354 | unsafe { 355 | // Create a mapping and view with maximum access permissions, then use `VirtualProtect` 356 | // to set the actual `Protection`. This way, we can set more permissive protection later 357 | // on. 358 | // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx 359 | 360 | let mapping = CreateFileMappingW( 361 | INVALID_HANDLE_VALUE, 362 | ptr::null_mut(), 363 | PAGE_EXECUTE_READWRITE, 364 | (mapped_len >> 16 >> 16) as DWORD, 365 | (mapped_len & 0xffffffff) as DWORD, 366 | ptr::null(), 367 | ); 368 | if mapping.is_null() { 369 | return Err(io::Error::last_os_error()); 370 | } 371 | let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE; 372 | let ptr = MapViewOfFile(mapping, access, 0, 0, mapped_len as SIZE_T); 373 | CloseHandle(mapping); 374 | 375 | if ptr.is_null() { 376 | return Err(io::Error::last_os_error()); 377 | } 378 | 379 | let mut old = 0; 380 | let result = VirtualProtect(ptr, mapped_len as SIZE_T, PAGE_READWRITE, &mut old); 381 | if result != 0 { 382 | Ok(MmapInner { 383 | handle: None, 384 | ptr, 385 | len, 386 | copy: false, 387 | }) 388 | } else { 389 | Err(io::Error::last_os_error()) 390 | } 391 | } 392 | } 393 | 394 | pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { 395 | self.flush_async(offset, len)?; 396 | 397 | if let Some(handle) = self.handle { 398 | let ok = unsafe { FlushFileBuffers(handle) }; 399 | if ok == 0 { 400 | return Err(io::Error::last_os_error()); 401 | } 402 | } 403 | 404 | Ok(()) 405 | } 406 | 407 | pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { 408 | if self.ptr == empty_slice_ptr() { 409 | return Ok(()); 410 | } 411 | let result = unsafe { FlushViewOfFile(self.ptr.add(offset), len as SIZE_T) }; 412 | if result != 0 { 413 | Ok(()) 414 | } else { 415 | Err(io::Error::last_os_error()) 416 | } 417 | } 418 | 419 | fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { 420 | if self.ptr == empty_slice_ptr() { 421 | return Ok(()); 422 | } 423 | unsafe { 424 | let alignment = self.ptr as usize % allocation_granularity(); 425 | let ptr = self.ptr.offset(-(alignment as isize)); 426 | let aligned_len = self.len as SIZE_T + alignment as SIZE_T; 427 | 428 | let mut old = 0; 429 | let result = VirtualProtect(ptr, aligned_len, protect, &mut old); 430 | 431 | if result != 0 { 432 | Ok(()) 433 | } else { 434 | Err(io::Error::last_os_error()) 435 | } 436 | } 437 | } 438 | 439 | pub fn make_read_only(&mut self) -> io::Result<()> { 440 | self.virtual_protect(PAGE_READONLY) 441 | } 442 | 443 | pub fn make_exec(&mut self) -> io::Result<()> { 444 | if self.copy { 445 | self.virtual_protect(PAGE_EXECUTE_WRITECOPY) 446 | } else { 447 | self.virtual_protect(PAGE_EXECUTE_READ) 448 | } 449 | } 450 | 451 | pub fn make_mut(&mut self) -> io::Result<()> { 452 | if self.copy { 453 | self.virtual_protect(PAGE_WRITECOPY) 454 | } else { 455 | self.virtual_protect(PAGE_READWRITE) 456 | } 457 | } 458 | 459 | #[inline] 460 | pub fn ptr(&self) -> *const u8 { 461 | self.ptr as *const u8 462 | } 463 | 464 | #[inline] 465 | pub fn mut_ptr(&mut self) -> *mut u8 { 466 | self.ptr.cast() 467 | } 468 | 469 | #[inline] 470 | pub fn len(&self) -> usize { 471 | self.len 472 | } 473 | } 474 | 475 | impl Drop for MmapInner { 476 | fn drop(&mut self) { 477 | if self.ptr == empty_slice_ptr() { 478 | return; 479 | } 480 | let alignment = self.ptr as usize % allocation_granularity(); 481 | // Any errors during unmapping/closing are ignored as the only way 482 | // to report them would be through panicking which is highly discouraged 483 | // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97 484 | unsafe { 485 | let ptr = self.ptr.offset(-(alignment as isize)); 486 | UnmapViewOfFile(ptr); 487 | 488 | if let Some(handle) = self.handle { 489 | CloseHandle(handle); 490 | } 491 | } 492 | } 493 | } 494 | 495 | unsafe impl Sync for MmapInner {} 496 | unsafe impl Send for MmapInner {} 497 | 498 | fn protection_supported(handle: RawHandle, protection: DWORD) -> bool { 499 | unsafe { 500 | let mapping = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null()); 501 | if mapping.is_null() { 502 | return false; 503 | } 504 | CloseHandle(mapping); 505 | true 506 | } 507 | } 508 | 509 | fn allocation_granularity() -> usize { 510 | unsafe { 511 | let mut info = mem::zeroed(); 512 | GetSystemInfo(&mut info); 513 | info.dwAllocationGranularity as usize 514 | } 515 | } 516 | 517 | pub fn file_len(handle: RawHandle) -> io::Result { 518 | // SAFETY: We must not close the passed-in fd by dropping the File we create, 519 | // we ensure this by immediately wrapping it in a ManuallyDrop. 520 | unsafe { 521 | let file = ManuallyDrop::new(File::from_raw_handle(handle)); 522 | Ok(file.metadata()?.len()) 523 | } 524 | } 525 | --------------------------------------------------------------------------------