├── .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 | 
3 | [](https://crates.io/crates/memmap2)
4 | [](https://docs.rs/memmap2)
5 | [](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 |
--------------------------------------------------------------------------------