Bug 1774512 - Update memmap2 to 0.5. r=emilio,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D149463
This commit is contained in:
Mike Hommey 2022-06-16 20:59:07 +00:00
parent a211cd2aea
commit 504fed29a9
14 changed files with 952 additions and 251 deletions

8
Cargo.lock generated
View File

@ -3213,14 +3213,14 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
name = "memmap2"
version = "0.2.999"
dependencies = [
"memmap2 0.3.1",
"memmap2 0.5.4",
]
[[package]]
name = "memmap2"
version = "0.3.1"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357"
checksum = "d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae"
dependencies = [
"libc",
]
@ -4237,7 +4237,7 @@ dependencies = [
name = "profiler_helper"
version = "0.1.0"
dependencies = [
"memmap2 0.3.1",
"memmap2 0.5.4",
"object",
"rustc-demangle",
"thin-vec",

View File

@ -8,4 +8,4 @@ license = "MPL-2.0"
path = "lib.rs"
[dependencies]
memmap2 = "0.3"
memmap2 = "0.5"

View File

@ -896,7 +896,7 @@ version = "2.5.0"
criteria = "safe-to-deploy"
[[unaudited.memmap2]]
version = "0.3.1"
version = "0.5.4"
criteria = "safe-to-deploy"
[[unaudited.memoffset]]

View File

@ -1 +1 @@
{"files":{"CHANGELOG.md":"1d984b7fd11609a7dafd72c48ae294aa6c0cd202c45797c4e5bbb9e861ef4334","Cargo.lock":"589809de388088d9ef97fad54e41d817013fae9d861029bf564181ca3a4fd973","Cargo.toml":"f9e95c4b10ade059adbdc85235f631cb45fa5e6386cdc928713aa3e9f93319b8","LICENSE-APACHE":"04ea4849dba9dcae07113850c6f1b1a69052c625210639914eee352023f750ad","LICENSE-MIT":"0d25d03b5ab49576178ad0cae7a2648d12c17ad0452fe49c07e55e4b59aa5257","README.md":"a318cac19725dfbf39890da55f6a48fcb11a32790819c66d776eaeb20fb03d50","examples/cat.rs":"ab0b575d19662e2d5b6c7cea2756b57530e495d56acdb4fd2b56c0ba4d768dfd","src/lib.rs":"fd69a723368ccf3656877230328dbbbef5778061f0d73567c434c111e97078bd","src/stub.rs":"6041ed9bbc6186e05a04ed3a8e5dbcb7ad8feb0135e617a3b0f1f09134ebcdf8","src/unix.rs":"6e6871ccfe0b0d56ba23400c0b45878433eb033d1a080dbf9d524923d76e4b9a","src/windows.rs":"1028d14387456fa785d2a31d8198794e2d4fe109f9ce8422610cc1bd26ace57c"},"package":"00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357"}
{"files":{"CHANGELOG.md":"9a280d0719b9d9a1fb12d31a2da544d103cb15f01ee2cf40bdcf28c6484eb1b8","Cargo.lock":"47b6feef3907ecbd586b49aadff92eaf84695d7508214210978fbf58b4f00ab6","Cargo.toml":"b93bec2a5377b79002ab53089a9ea9c0a8107f25c1dfb796ba0c01a50e7a1dec","LICENSE-APACHE":"04ea4849dba9dcae07113850c6f1b1a69052c625210639914eee352023f750ad","LICENSE-MIT":"0d25d03b5ab49576178ad0cae7a2648d12c17ad0452fe49c07e55e4b59aa5257","README.md":"c7b3cd928f0d1a10faa255e2f84a2a06636e55ea3e7edd4f6334dd9215151205","examples/cat.rs":"ab0b575d19662e2d5b6c7cea2756b57530e495d56acdb4fd2b56c0ba4d768dfd","src/advice.rs":"194bfd6a32495f6b0c739d083b06230ae656927767f15c1b49b245b63431cc4d","src/lib.rs":"2fa8c6162297441c82471132b9e26d3effcb9c06ae414a0104f9d871eba0aa00","src/stub.rs":"f276bb5e4bc29c2129ebc660b01a1de173b9575e2e866ea5a34e0ee6318f1177","src/unix.rs":"4fa52c514f15dd5e990e03671599a0c3f15770b8fb5f8fea3ea6333766b1e31c","src/windows.rs":"73838f8b2dc8b0c8d37045db36c3c7eb2dc00e6f5d5a7bc0c693bdb55d3ec721"},"package":"d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae"}

View File

@ -6,6 +6,56 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## [0.5.4] - 2022-06-04
### Added
- Add madvice operations specific to Darwin. [@turbocool3r](https://github.com/turbocool3r)
- Implement common traits for the `Advice` enum. [@nyurik](https://github.com/nyurik)
### Changed
- Make stub implementation Infallible. [@coolreader18](https://github.com/coolreader18)
- Use `tempfile` crate instead of `tempdir` in tests.
[@alexanderkjall](https://github.com/alexanderkjall)
## [0.5.3] - 2022-02-10
### Added
- `Mmap::advise` and `MmapMut::advise`. [@nyurik](https://github.com/nyurik)
## [0.5.2] - 2022-01-10
### Added
- `flush`, `flush_async`, `flush_range` and `flush_async_range` to `MmapRaw` matching
the corresponding methods on `MmapMut`.
[@cberner](https://github.com/cberner)
## [0.5.1] - 2022-01-09
### Fixed
- Explicitly call `fstat64` on Linux, emscripten and l4re targets.
[@adamreichold](https://github.com/adamreichold)
## [0.5.0] - 2021-09-19
### Added
- `MmapOptions` accepts any type that supports `RawHandle`/`RawFd` returning now.
This allows using `memmap2` not only with Rust std types, but also with
[async-std](https://github.com/async-rs/async-std) one.
[@adamreichold](https://github.com/adamreichold)
- (unix) Memoize page size to avoid repeatedly calling into sysconf machinery.
[@adamreichold](https://github.com/adamreichold)
### Changed
- (win) Use `std::os::windows::io::AsRawHandle` directly, without relying on `std::fs::File`.
[@adamreichold](https://github.com/adamreichold)
- Do not panic when failing to release resources in Drop impls.
[@adamreichold](https://github.com/adamreichold)
## [0.4.0] - 2021-09-16
### Added
- Optional [`StableDeref`](https://github.com/storyyeller/stable_deref_trait) support.
[@SimonSapin](https://github.com/SimonSapin)
### Changed
- Mapping of zero-sized files is no longer an error.
[@SimonSapin](https://github.com/SimonSapin)
- MSRV changed from 1.31 to 1.36
## [0.3.1] - 2021-08-15
### Fixed
- Integer overflow during file length calculation on 32bit targets.
@ -38,7 +88,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed
- MSRV is 1.31 now (edition 2018).
- Make anonymous memory maps private by default on unix. [@CensoredUsername](https://github.com/CensoredUsername)
- Add `map_copy_read_only`. [@zserik](https://github.com/zserik)
- Add `map_copy_read_only`. [@zseri](https://github.com/zseri)
## 0.1.0 - 2020-01-18
### Added
@ -50,7 +100,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Removed
- `winapi` dependency. [memmap-rs/pull/89](https://github.com/danburkert/memmap-rs/pull/89)
[Unreleased]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.1...HEAD
[Unreleased]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.4...HEAD
[0.5.4]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.3...v0.5.4
[0.5.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.2...v0.5.3
[0.5.2]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.3...v0.3.0
[0.2.3]: https://github.com/RazrFalcon/memmap2-rs/compare/v0.2.2...v0.2.3

89
third_party/rust/memmap2/Cargo.lock generated vendored
View File

@ -3,10 +3,34 @@
version = 3
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "libc"
@ -16,47 +40,30 @@ checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
[[package]]
name = "memmap2"
version = "0.3.1"
version = "0.5.4"
dependencies = [
"libc",
"tempdir",
"owning_ref",
"stable_deref_trait",
"tempfile",
]
[[package]]
name = "rand"
version = "0.4.6"
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
"stable_deref_trait",
]
[[package]]
name = "rand_core"
version = "0.3.1"
name = "redox_syscall"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
"bitflags",
]
[[package]]
@ -69,13 +76,23 @@ dependencies = [
]
[[package]]
name = "tempdir"
version = "0.3.7"
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"rand",
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]

View File

@ -3,24 +3,40 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "memmap2"
version = "0.3.1"
authors = ["Dan Burkert <dan@danburkert.com>", "Yevhenii Reizner <razrfalcon@gmail.com>"]
version = "0.5.4"
authors = [
"Dan Burkert <dan@danburkert.com>",
"Yevhenii Reizner <razrfalcon@gmail.com>",
]
description = "Cross-platform Rust API for memory-mapped file IO"
documentation = "https://docs.rs/memmap2"
keywords = ["mmap", "memory-map", "io", "file"]
keywords = [
"mmap",
"memory-map",
"io",
"file",
]
license = "MIT/Apache-2.0"
repository = "https://github.com/RazrFalcon/memmap2-rs"
[dev-dependencies.tempdir]
version = "0.3"
[dependencies.stable_deref_trait]
version = "1.0"
optional = true
[dev-dependencies.owning_ref]
version = "0.4.1"
[dev-dependencies.tempfile]
version = "3"
[target."cfg(unix)".dependencies.libc]
version = "0.2"

View File

@ -2,7 +2,7 @@
![Build Status](https://github.com/RazrFalcon/memmap2-rs/workflows/Rust/badge.svg)
[![Crates.io](https://img.shields.io/crates/v/memmap2.svg)](https://crates.io/crates/memmap2)
[![Documentation](https://docs.rs/memmap2/badge.svg)](https://docs.rs/memmap2)
[![Rust 1.31+](https://img.shields.io/badge/rust-1.31+-orange.svg)](https://www.rust-lang.org)
[![Rust 1.36+](https://img.shields.io/badge/rust-1.36+-orange.svg)](https://www.rust-lang.org)
A Rust library for cross-platform memory mapped IO.

273
third_party/rust/memmap2/src/advice.rs vendored Normal file
View File

@ -0,0 +1,273 @@
// The use statement is needed for the `cargo docs`
#[allow(unused_imports)]
use crate::{Mmap, MmapMut};
/// Values supported by [Mmap::advise] and [MmapMut::advise] functions.
/// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page.
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Advice {
/// **MADV_NORMAL**
///
/// No special treatment. This is the default.
Normal = libc::MADV_NORMAL,
/// **MADV_RANDOM**
///
/// Expect page references in random order. (Hence, read
/// ahead may be less useful than normally.)
Random = libc::MADV_RANDOM,
/// **MADV_SEQUENTIAL**
///
/// Expect page references in sequential order. (Hence, pages
/// in the given range can be aggressively read ahead, and may
/// be freed soon after they are accessed.)
Sequential = libc::MADV_SEQUENTIAL,
/// **MADV_WILLNEED**
///
/// Expect access in the near future. (Hence, it might be a
/// good idea to read some pages ahead.)
WillNeed = libc::MADV_WILLNEED,
/// **MADV_DONTNEED**
///
/// Do not expect access in the near future. (For the time
/// being, the application is finished with the given range,
/// so the kernel can free resources associated with it.)
///
/// After a successful MADV_DONTNEED operation, the semantics
/// of memory access in the specified region are changed:
/// subsequent accesses of pages in the range will succeed,
/// but will result in either repopulating the memory contents
/// from the up-to-date contents of the underlying mapped file
/// (for shared file mappings, shared anonymous mappings, and
/// shmem-based techniques such as System V shared memory
/// segments) or zero-fill-on-demand pages for anonymous
/// private mappings.
///
/// Note that, when applied to shared mappings, MADV_DONTNEED
/// might not lead to immediate freeing of the pages in the
/// range. The kernel is free to delay freeing the pages
/// until an appropriate moment. The resident set size (RSS)
/// of the calling process will be immediately reduced
/// however.
///
/// **MADV_DONTNEED** cannot be applied to locked pages, Huge TLB
/// pages, or VM_PFNMAP pages. (Pages marked with the kernel-
/// internal VM_PFNMAP flag are special memory areas that are
/// not managed by the virtual memory subsystem. Such pages
/// are typically created by device drivers that map the pages
/// into user space.)
DontNeed = libc::MADV_DONTNEED,
//
// The rest are Linux-specific
//
/// **MADV_FREE** - Linux (since Linux 4.5) and Darwin
///
/// The application no longer requires the pages in the range
/// specified by addr and len. The kernel can thus free these
/// pages, but the freeing could be delayed until memory
/// pressure occurs. For each of the pages that has been
/// marked to be freed but has not yet been freed, the free
/// operation will be canceled if the caller writes into the
/// page. After a successful MADV_FREE operation, any stale
/// data (i.e., dirty, unwritten pages) will be lost when the
/// kernel frees the pages. However, subsequent writes to
/// pages in the range will succeed and then kernel cannot
/// free those dirtied pages, so that the caller can always
/// see just written data. If there is no subsequent write,
/// the kernel can free the pages at any time. Once pages in
/// the range have been freed, the caller will see zero-fill-
/// on-demand pages upon subsequent page references.
///
/// The MADV_FREE operation can be applied only to private
/// anonymous pages (see mmap(2)). In Linux before version
/// 4.12, when freeing pages on a swapless system, the pages
/// in the given range are freed instantly, regardless of
/// memory pressure.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios"))]
Free = libc::MADV_FREE,
/// **MADV_REMOVE** - Linux only (since Linux 2.6.16)
///
/// Free up a given range of pages and its associated backing
/// store. This is equivalent to punching a hole in the
/// corresponding byte range of the backing store (see
/// fallocate(2)). Subsequent accesses in the specified
/// address range will see bytes containing zero.
///
/// The specified address range must be mapped shared and
/// writable. This flag cannot be applied to locked pages,
/// Huge TLB pages, or VM_PFNMAP pages.
///
/// In the initial implementation, only tmpfs(5) was supported
/// **MADV_REMOVE**; but since Linux 3.5, any filesystem which
/// supports the fallocate(2) FALLOC_FL_PUNCH_HOLE mode also
/// supports MADV_REMOVE. Hugetlbfs fails with the error
/// EINVAL and other filesystems fail with the error
/// EOPNOTSUPP.
#[cfg(target_os = "linux")]
Remove = libc::MADV_REMOVE,
/// **MADV_DONTFORK** - Linux only (since Linux 2.6.16)
///
/// Do not make the pages in this range available to the child
/// after a fork(2). This is useful to prevent copy-on-write
/// semantics from changing the physical location of a page if
/// the parent writes to it after a fork(2). (Such page
/// relocations cause problems for hardware that DMAs into the
/// page.)
#[cfg(target_os = "linux")]
DontFork = libc::MADV_DONTFORK,
/// **MADV_DOFORK** - Linux only (since Linux 2.6.16)
///
/// Undo the effect of MADV_DONTFORK, restoring the default
/// behavior, whereby a mapping is inherited across fork(2).
#[cfg(target_os = "linux")]
DoFork = libc::MADV_DOFORK,
/// **MADV_MERGEABLE** - Linux only (since Linux 2.6.32)
///
/// Enable Kernel Samepage Merging (KSM) for the pages in the
/// range specified by addr and length. The kernel regularly
/// scans those areas of user memory that have been marked as
/// mergeable, looking for pages with identical content.
/// These are replaced by a single write-protected page (which
/// is automatically copied if a process later wants to update
/// the content of the page). KSM merges only private
/// anonymous pages (see mmap(2)).
///
/// The KSM feature is intended for applications that generate
/// many instances of the same data (e.g., virtualization
/// systems such as KVM). It can consume a lot of processing
/// power; use with care. See the Linux kernel source file
/// Documentation/admin-guide/mm/ksm.rst for more details.
///
/// The MADV_MERGEABLE and MADV_UNMERGEABLE operations are
/// available only if the kernel was configured with
/// CONFIG_KSM.
#[cfg(target_os = "linux")]
Mergeable = libc::MADV_MERGEABLE,
/// **MADV_UNMERGEABLE** - Linux only (since Linux 2.6.32)
///
/// Undo the effect of an earlier MADV_MERGEABLE operation on
/// the specified address range; KSM unmerges whatever pages
/// it had merged in the address range specified by addr and
/// length.
#[cfg(target_os = "linux")]
Unmergeable = libc::MADV_UNMERGEABLE,
/// **MADV_HUGEPAGE** - Linux only (since Linux 2.6.38)
///
/// Enable Transparent Huge Pages (THP) for pages in the range
/// specified by addr and length. Currently, Transparent Huge
/// Pages work only with private anonymous pages (see
/// mmap(2)). The kernel will regularly scan the areas marked
/// as huge page candidates to replace them with huge pages.
/// The kernel will also allocate huge pages directly when the
/// region is naturally aligned to the huge page size (see
/// posix_memalign(2)).
///
/// This feature is primarily aimed at applications that use
/// large mappings of data and access large regions of that
/// memory at a time (e.g., virtualization systems such as
/// QEMU). It can very easily waste memory (e.g., a 2 MB
/// mapping that only ever accesses 1 byte will result in 2 MB
/// of wired memory instead of one 4 KB page). See the Linux
/// kernel source file
/// Documentation/admin-guide/mm/transhuge.rst for more
/// details.
///
/// Most common kernels configurations provide MADV_HUGEPAGE-
/// style behavior by default, and thus MADV_HUGEPAGE is
/// normally not necessary. It is mostly intended for
/// embedded systems, where MADV_HUGEPAGE-style behavior may
/// not be enabled by default in the kernel. On such systems,
/// this flag can be used in order to selectively enable THP.
/// Whenever MADV_HUGEPAGE is used, it should always be in
/// regions of memory with an access pattern that the
/// developer knows in advance won't risk to increase the
/// memory footprint of the application when transparent
/// hugepages are enabled.
///
/// The MADV_HUGEPAGE and MADV_NOHUGEPAGE operations are
/// available only if the kernel was configured with
/// CONFIG_TRANSPARENT_HUGEPAGE.
#[cfg(target_os = "linux")]
HugePage = libc::MADV_HUGEPAGE,
/// **MADV_NOHUGEPAGE** - Linux only (since Linux 2.6.38)
///
/// Ensures that memory in the address range specified by addr
/// and length will not be backed by transparent hugepages.
#[cfg(target_os = "linux")]
NoHugePage = libc::MADV_NOHUGEPAGE,
/// **MADV_DONTDUMP** - Linux only (since Linux 3.4)
///
/// Exclude from a core dump those pages in the range
/// specified by addr and length. This is useful in
/// applications that have large areas of memory that are
/// known not to be useful in a core dump. The effect of
/// **MADV_DONTDUMP** takes precedence over the bit mask that is
/// set via the `/proc/[pid]/coredump_filter` file (see
/// core(5)).
#[cfg(target_os = "linux")]
DontDump = libc::MADV_DONTDUMP,
/// **MADV_DODUMP** - Linux only (since Linux 3.4)
///
/// Undo the effect of an earlier MADV_DONTDUMP.
#[cfg(target_os = "linux")]
DoDump = libc::MADV_DODUMP,
/// **MADV_HWPOISON** - Linux only (since Linux 2.6.32)
///
/// Poison the pages in the range specified by addr and length
/// and handle subsequent references to those pages like a
/// hardware memory corruption. This operation is available
/// only for privileged (CAP_SYS_ADMIN) processes. This
/// operation may result in the calling process receiving a
/// SIGBUS and the page being unmapped.
///
/// This feature is intended for testing of memory error-
/// handling code; it is available only if the kernel was
/// configured with CONFIG_MEMORY_FAILURE.
#[cfg(target_os = "linux")]
HwPoison = libc::MADV_HWPOISON,
/// **MADV_ZERO_WIRED_PAGES** - Darwin only
///
/// Indicates that the application would like the wired pages in this address range to be
/// zeroed out if the address range is deallocated without first unwiring the pages (i.e.
/// a munmap(2) without a preceding munlock(2) or the application quits). This is used
/// with madvise() system call.
#[cfg(any(target_os = "macos", target_os = "ios"))]
ZeroWiredPages = libc::MADV_ZERO_WIRED_PAGES,
/// **MADV_FREE_REUSABLE** - Darwin only
///
/// Behaves like **MADV_FREE**, but the freed pages are accounted for in the RSS of the process.
#[cfg(any(target_os = "macos", target_os = "ios"))]
FreeReusable = libc::MADV_FREE_REUSABLE,
/// **MADV_FREE_REUSE** - Darwin only
///
/// Marks a memory region previously freed by **MADV_FREE_REUSABLE** as non-reusable, accounts
/// for the pages in the RSS of the process. Pages that have been freed will be replaced by
/// zero-filled pages on demand, other pages will be left as is.
#[cfg(any(target_os = "macos", target_os = "ios"))]
FreeReuse = libc::MADV_FREE_REUSE,
}
// Future expansion:
// MADV_SOFT_OFFLINE (since Linux 2.6.33)
// MADV_WIPEONFORK (since Linux 4.14)
// MADV_KEEPONFORK (since Linux 4.14)
// MADV_COLD (since Linux 5.4)
// MADV_PAGEOUT (since Linux 5.4)

View File

@ -1,51 +1,78 @@
//! A cross-platform Rust API for memory mapped buffers.
//!
//! The core functionality is provided by either [`Mmap`] or [`MmapMut`],
//! which correspond to mapping a [`File`] to a [`&[u8]`](https://doc.rust-lang.org/std/primitive.slice.html)
//! or [`&mut [u8]`](https://doc.rust-lang.org/std/primitive.slice.html)
//! respectively. Both function by dereferencing to a slice, allowing the
//! [`Mmap`]/[`MmapMut`] to be used in the same way you would the equivelant slice
//! types.
//!
//! [`File`]: std::fs::File
//!
//! # Examples
//!
//! For simple cases [`Mmap`] can be used directly:
//!
//! ```
//! use std::fs::File;
//! use std::io::Read;
//!
//! use memmap2::Mmap;
//!
//! # fn main() -> std::io::Result<()> {
//! let mut file = File::open("LICENSE-APACHE")?;
//!
//! let mut contents = Vec::new();
//! file.read_to_end(&mut contents)?;
//!
//! let mmap = unsafe { Mmap::map(&file)? };
//!
//! assert_eq!(&contents[..], &mmap[..]);
//! # Ok(())
//! # }
//! ```
//!
//! However for cases which require configuration of the mapping, then
//! you can use [`MmapOptions`] in order to further configure a mapping
//! before you create it.
#![doc(html_root_url = "https://docs.rs/memmap2/0.3.1")]
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use crate::windows::file_len;
#[cfg(windows)]
use crate::windows::MmapInner;
#[cfg_attr(unix, path = "unix.rs")]
#[cfg_attr(windows, path = "windows.rs")]
#[cfg_attr(not(any(unix, windows)), path = "stub.rs")]
mod os;
use crate::os::{file_len, MmapInner};
#[cfg(unix)]
mod unix;
mod advice;
#[cfg(unix)]
use crate::unix::file_len;
#[cfg(unix)]
use crate::unix::MmapInner;
#[cfg(not(any(unix, windows)))]
mod stub;
#[cfg(not(any(unix, windows)))]
use crate::stub::file_len;
#[cfg(not(any(unix, windows)))]
use crate::stub::MmapInner;
pub use crate::advice::Advice;
use std::fmt;
#[cfg(not(any(unix, windows)))]
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::ops::{Deref, DerefMut};
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawHandle, RawHandle};
use std::slice;
use std::usize;
#[cfg(windows)]
#[cfg(not(any(unix, windows)))]
pub struct MmapRawDescriptor<'a>(&'a File);
#[cfg(unix)]
pub struct MmapRawDescriptor(std::os::unix::io::RawFd);
pub struct MmapRawDescriptor(RawFd);
#[cfg(not(any(unix, windows)))]
pub struct MmapRawDescriptor<'a>(&'a File);
#[cfg(windows)]
pub struct MmapRawDescriptor(RawHandle);
pub trait MmapAsRawDesc {
fn as_raw_desc(&self) -> MmapRawDescriptor;
}
#[cfg(windows)]
#[cfg(not(any(unix, windows)))]
impl MmapAsRawDesc for &File {
fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(self)
@ -53,23 +80,36 @@ impl MmapAsRawDesc for &File {
}
#[cfg(unix)]
impl MmapAsRawDesc for &File {
fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(self.as_raw_fd())
}
}
#[cfg(unix)]
impl MmapAsRawDesc for std::os::unix::io::RawFd {
impl MmapAsRawDesc for RawFd {
fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(*self)
}
}
#[cfg(not(any(unix, windows)))]
impl MmapAsRawDesc for &File {
#[cfg(unix)]
impl<'a, T> MmapAsRawDesc for &'a T
where
T: AsRawFd,
{
fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(self)
MmapRawDescriptor(self.as_raw_fd())
}
}
#[cfg(windows)]
impl MmapAsRawDesc for RawHandle {
fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(*self)
}
}
#[cfg(windows)]
impl<'a, T> MmapAsRawDesc for &'a T
where
T: AsRawHandle,
{
fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(self.as_raw_handle())
}
}
@ -306,7 +346,7 @@ impl MmapOptions {
let desc = file.as_raw_desc();
MmapInner::map_exec(self.get_len(&file)?, desc.0, self.offset, self.populate)
.map(|inner| Mmap { inner: inner })
.map(|inner| Mmap { inner })
}
/// Creates a writeable memory map backed by a file.
@ -320,7 +360,7 @@ impl MmapOptions {
///
/// ```
/// # extern crate memmap2;
/// # extern crate tempdir;
/// # extern crate tempfile;
/// #
/// use std::fs::OpenOptions;
/// use std::path::PathBuf;
@ -328,7 +368,7 @@ impl MmapOptions {
/// use memmap2::MmapOptions;
/// #
/// # fn main() -> std::io::Result<()> {
/// # let tempdir = tempdir::TempDir::new("mmap")?;
/// # let tempdir = tempfile::tempdir()?;
/// let path: PathBuf = /* path to file */
/// # tempdir.path().join("map_mut");
/// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?;
@ -346,7 +386,7 @@ impl MmapOptions {
let desc = file.as_raw_desc();
MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate)
.map(|inner| MmapMut { inner: inner })
.map(|inner| MmapMut { inner })
}
/// Creates a copy-on-write memory map backed by a file.
@ -377,7 +417,7 @@ impl MmapOptions {
let desc = file.as_raw_desc();
MmapInner::map_copy(self.get_len(&file)?, desc.0, self.offset, self.populate)
.map(|inner| MmapMut { inner: inner })
.map(|inner| MmapMut { inner })
}
/// Creates a copy-on-write read-only memory map backed by a file.
@ -412,7 +452,7 @@ impl MmapOptions {
let desc = file.as_raw_desc();
MmapInner::map_copy_read_only(self.get_len(&file)?, desc.0, self.offset, self.populate)
.map(|inner| Mmap { inner: inner })
.map(|inner| Mmap { inner })
}
/// Creates an anonymous memory map.
@ -437,7 +477,7 @@ impl MmapOptions {
let desc = file.as_raw_desc();
MmapInner::map_mut(self.get_len(&file)?, desc.0, self.offset, self.populate)
.map(|inner| MmapRaw { inner: inner })
.map(|inner| MmapRaw { inner })
}
}
@ -535,7 +575,7 @@ impl Mmap {
///
/// ```
/// # extern crate memmap2;
/// # extern crate tempdir;
/// # extern crate tempfile;
/// #
/// use memmap2::Mmap;
/// use std::ops::DerefMut;
@ -543,7 +583,7 @@ impl Mmap {
/// # use std::fs::OpenOptions;
///
/// # fn main() -> std::io::Result<()> {
/// # let tempdir = tempdir::TempDir::new("mmap")?;
/// # let tempdir = tempfile::tempdir()?;
/// let file = /* file opened with write permissions */
/// # OpenOptions::new()
/// # .read(true)
@ -563,8 +603,19 @@ impl Mmap {
self.inner.make_mut()?;
Ok(MmapMut { inner: self.inner })
}
/// Advise OS how this memory map will be accessed. Only supported on Unix.
///
/// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page.
#[cfg(unix)]
pub fn advise(&self, advice: Advice) -> Result<()> {
self.inner.advise(advice)
}
}
#[cfg(feature = "stable_deref_trait")]
unsafe impl stable_deref_trait::StableDeref for Mmap {}
impl Deref for Mmap {
type Target = [u8];
@ -640,6 +691,81 @@ impl MmapRaw {
pub fn len(&self) -> usize {
self.inner.len()
}
/// Flushes outstanding memory map modifications to disk.
///
/// When this method returns with a non-error result, all outstanding changes to a file-backed
/// memory map are guaranteed to be durably stored. The file's metadata (including last
/// modification timestamp) may not be updated.
///
/// # Example
///
/// ```
/// # extern crate memmap2;
/// # extern crate tempfile;
/// #
/// use std::fs::OpenOptions;
/// use std::io::Write;
/// use std::path::PathBuf;
/// use std::slice;
///
/// use memmap2::MmapRaw;
///
/// # fn main() -> std::io::Result<()> {
/// let tempdir = tempfile::tempdir()?;
/// let path: PathBuf = /* path to file */
/// # tempdir.path().join("flush");
/// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?;
/// file.set_len(128)?;
///
/// let mut mmap = unsafe { MmapRaw::map_raw(&file)? };
///
/// let mut memory = unsafe { slice::from_raw_parts_mut(mmap.as_mut_ptr(), 128) };
/// memory.write_all(b"Hello, world!")?;
/// mmap.flush()?;
/// # Ok(())
/// # }
/// ```
pub fn flush(&self) -> Result<()> {
let len = self.len();
self.inner.flush(0, len)
}
/// Asynchronously flushes outstanding memory map modifications to disk.
///
/// This method initiates flushing modified pages to durable storage, but it will not wait for
/// the operation to complete before returning. The file's metadata (including last
/// modification timestamp) may not be updated.
pub fn flush_async(&self) -> Result<()> {
let len = self.len();
self.inner.flush_async(0, len)
}
/// Flushes outstanding memory map modifications in the range to disk.
///
/// The offset and length must be in the bounds of the memory map.
///
/// When this method returns with a non-error result, all outstanding changes to a file-backed
/// memory in the range are guaranteed to be durable stored. The file's metadata (including
/// last modification timestamp) may not be updated. It is not guaranteed the only the changes
/// in the specified range are flushed; other outstanding changes to the memory map may be
/// flushed as well.
pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> {
self.inner.flush(offset, len)
}
/// Asynchronously flushes outstanding memory map modifications in the range to disk.
///
/// The offset and length must be in the bounds of the memory map.
///
/// This method initiates flushing modified pages to durable storage, but it will not wait for
/// the operation to complete before returning. The file's metadata (including last
/// modification timestamp) may not be updated. It is not guaranteed that the only changes
/// flushed are those in the specified range; other outstanding changes to the memory map may
/// be flushed as well.
pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> {
self.inner.flush_async(offset, len)
}
}
impl fmt::Debug for MmapRaw {
@ -696,7 +822,7 @@ impl MmapMut {
///
/// ```
/// # extern crate memmap2;
/// # extern crate tempdir;
/// # extern crate tempfile;
/// #
/// use std::fs::OpenOptions;
/// use std::path::PathBuf;
@ -704,7 +830,7 @@ impl MmapMut {
/// use memmap2::MmapMut;
/// #
/// # fn main() -> std::io::Result<()> {
/// # let tempdir = tempdir::TempDir::new("mmap")?;
/// # let tempdir = tempfile::tempdir()?;
/// let path: PathBuf = /* path to file */
/// # tempdir.path().join("map_mut");
/// let file = OpenOptions::new()
@ -745,7 +871,7 @@ impl MmapMut {
///
/// ```
/// # extern crate memmap2;
/// # extern crate tempdir;
/// # extern crate tempfile;
/// #
/// use std::fs::OpenOptions;
/// use std::io::Write;
@ -754,7 +880,7 @@ impl MmapMut {
/// use memmap2::MmapMut;
///
/// # fn main() -> std::io::Result<()> {
/// # let tempdir = tempdir::TempDir::new("mmap")?;
/// # let tempdir = tempfile::tempdir()?;
/// let path: PathBuf = /* path to file */
/// # tempdir.path().join("flush");
/// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?;
@ -853,8 +979,19 @@ impl MmapMut {
self.inner.make_exec()?;
Ok(Mmap { inner: self.inner })
}
/// Advise OS how this memory map will be accessed. Only supported on Unix.
///
/// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page.
#[cfg(unix)]
pub fn advise(&self, advice: Advice) -> Result<()> {
self.inner.advise(advice)
}
}
#[cfg(feature = "stable_deref_trait")]
unsafe impl stable_deref_trait::StableDeref for MmapMut {}
impl Deref for MmapMut {
type Target = [u8];
@ -896,8 +1033,10 @@ impl fmt::Debug for MmapMut {
#[cfg(test)]
mod test {
extern crate tempdir;
extern crate tempfile;
#[cfg(unix)]
use crate::advice::Advice;
use std::fs::OpenOptions;
use std::io::{Read, Write};
#[cfg(unix)]
@ -913,7 +1052,7 @@ mod test {
#[test]
fn map_file() {
let expected_len = 128;
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
@ -946,7 +1085,7 @@ mod test {
#[cfg(unix)]
fn map_fd() {
let expected_len = 128;
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
@ -975,10 +1114,10 @@ mod test {
assert_eq!(&incr[..], &mmap[..]);
}
/// Checks that a 0-length file will not be mapped.
/// Checks that "mapping" a 0-length file derefs to an empty slice.
#[test]
fn map_empty_file() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
@ -987,8 +1126,10 @@ mod test {
.create(true)
.open(&path)
.unwrap();
let mmap = unsafe { Mmap::map(&file) };
assert!(mmap.is_err());
let mmap = unsafe { Mmap::map(&file).unwrap() };
assert!(mmap.is_empty());
let mmap = unsafe { MmapMut::map_mut(&file).unwrap() };
assert!(mmap.is_empty());
}
#[test]
@ -1013,12 +1154,12 @@ mod test {
#[test]
fn map_anon_zero_len() {
assert!(MmapOptions::new().map_anon().is_err())
assert!(MmapOptions::new().map_anon().unwrap().is_empty())
}
#[test]
fn file_write() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let mut file = OpenOptions::new()
@ -1042,7 +1183,7 @@ mod test {
#[test]
fn flush_range() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
@ -1068,7 +1209,7 @@ mod test {
#[test]
fn map_copy() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let mut file = OpenOptions::new()
@ -1104,7 +1245,7 @@ mod test {
#[test]
fn map_copy_read_only() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
@ -1127,32 +1268,9 @@ mod test {
assert_eq!(nulls, &read);
}
// 32bit Linux cannot map a file larger than i32, but Windows can.
#[cfg(all(target_os = "linux", target_pointer_width = "32"))]
#[test]
fn map_offset() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
let offset = u32::max_value() as u64 + 2;
let len = 5432;
file.set_len(offset + len as u64).unwrap();
let mmap = unsafe { MmapOptions::new().offset(offset).map_mut(&file) };
assert!(mmap.is_err());
}
#[cfg(not(all(target_os = "linux", target_pointer_width = "32")))]
#[test]
fn map_offset() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
@ -1238,7 +1356,7 @@ mod test {
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn jit_x86_file() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let mut options = OpenOptions::new();
#[cfg(windows)]
options.access_mode(GENERIC_ALL);
@ -1256,7 +1374,7 @@ mod test {
#[test]
fn mprotect_file() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let mut options = OpenOptions::new();
@ -1302,7 +1420,7 @@ mod test {
#[test]
fn mprotect_copy() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap");
let mut options = OpenOptions::new();
@ -1359,7 +1477,7 @@ mod test {
#[test]
fn raw() {
let tempdir = tempdir::TempDir::new("mmap").unwrap();
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmapraw");
let mut options = OpenOptions::new();
@ -1375,4 +1493,68 @@ mod test {
assert!(!mmap.as_ptr().is_null());
assert_eq!(unsafe { std::ptr::read(mmap.as_ptr()) }, b'a');
}
/// Something that relies on StableDeref
#[test]
#[cfg(feature = "stable_deref_trait")]
fn owning_ref() {
extern crate owning_ref;
let mut map = MmapMut::map_anon(128).unwrap();
map[10] = 42;
let owning = owning_ref::OwningRef::new(map);
let sliced = owning.map(|map| &map[10..20]);
assert_eq!(42, sliced[0]);
let map = sliced.into_owner().make_read_only().unwrap();
let owning = owning_ref::OwningRef::new(map);
let sliced = owning.map(|map| &map[10..20]);
assert_eq!(42, sliced[0]);
}
#[test]
#[cfg(unix)]
fn advise() {
let expected_len = 128;
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("mmap_advise");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
file.set_len(expected_len as u64).unwrap();
// Test MmapMut::advise
let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() };
mmap.advise(Advice::Random)
.expect("mmap advising should be supported on unix");
let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = vec![0; len];
let incr: Vec<u8> = (0..len as u8).collect();
// check that the mmap is empty
assert_eq!(&zeros[..], &mmap[..]);
// write values into the mmap
(&mut mmap[..]).write_all(&incr[..]).unwrap();
// read values back
assert_eq!(&incr[..], &mmap[..]);
// Set advice and Read from the read-only map
let mmap = unsafe { Mmap::map(&file).unwrap() };
mmap.advise(Advice::Random)
.expect("mmap advising should be supported on unix");
// read values back
assert_eq!(&incr[..], &mmap[..]);
}
}

View File

@ -1,10 +1,11 @@
use std::fs::File;
use std::io;
// A stable alternative to https://doc.rust-lang.org/stable/std/primitive.never.html
enum Never {}
pub struct MmapInner {
// Private member to prevent external construction
// (might be nice to change the type to ! once that's stable)
__: (),
never: Never,
}
impl MmapInner {
@ -40,38 +41,38 @@ impl MmapInner {
}
pub fn flush(&self, _: usize, _: usize) -> io::Result<()> {
unreachable!("self unconstructable");
match self.never {}
}
pub fn flush_async(&self, _: usize, _: usize) -> io::Result<()> {
unreachable!("self unconstructable");
match self.never {}
}
pub fn make_read_only(&mut self) -> io::Result<()> {
unreachable!("self unconstructable");
match self.never {}
}
pub fn make_exec(&mut self) -> io::Result<()> {
unreachable!("self unconstructable");
match self.never {}
}
pub fn make_mut(&mut self) -> io::Result<()> {
unreachable!("self unconstructable");
match self.never {}
}
#[inline]
pub fn ptr(&self) -> *const u8 {
unreachable!("self unconstructable");
match self.never {}
}
#[inline]
pub fn mut_ptr(&mut self) -> *mut u8 {
unreachable!("self unconstructable");
match self.never {}
}
#[inline]
pub fn len(&self) -> usize {
unreachable!("self unconstructable");
match self.never {}
}
}

View File

@ -1,8 +1,12 @@
extern crate libc;
use std::mem::MaybeUninit;
use std::os::unix::io::RawFd;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{io, ptr};
use crate::advice::Advice;
#[cfg(any(
all(target_os = "linux", not(target_arch = "mips")),
target_os = "freebsd",
@ -42,13 +46,38 @@ impl MmapInner {
let alignment = offset % page_size() as u64;
let aligned_offset = offset - alignment;
let aligned_len = len + alignment as usize;
if aligned_len == 0 {
// Normally the OS would catch this, but it segfaults under QEMU.
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"memory map must have a non-zero length",
));
}
// `libc::mmap` does not support zero-size mappings. POSIX defines:
//
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html
// > If `len` is zero, `mmap()` shall fail and no mapping shall be established.
//
// So if we would create such a mapping, crate a one-byte mapping instead:
let aligned_len = aligned_len.max(1);
// Note that in that case `MmapInner::len` is still set to zero,
// and `Mmap` will still dereferences to an empty slice.
//
// If this mapping is backed by an empty file, we create a mapping larger than the file.
// This is unusual but well-defined. On the same man page, POSIX further defines:
//
// > The `mmap()` function can be used to map a region of memory that is larger
// > than the current size of the object.
//
// (The object here is the file.)
//
// > Memory access within the mapping but beyond the current end of the underlying
// > objects may result in SIGBUS signals being sent to the process. The reason for this
// > is that the size of the object can be manipulated by other processes and can change
// > at any moment. The implementation should tell the application that a memory reference
// > is outside the object where this can be detected; otherwise, written data may be lost
// > and read data may not reflect actual data in the object.
//
// Because `MmapInner::len` is not incremented, this increment of `aligned_len`
// will not allow accesses past the end of the file and will not cause SIGBUS.
//
// (SIGBUS is still possible by mapping a non-empty file and then truncating it
// to a shorter size, but that is unrelated to this handling of empty files.)
unsafe {
let ptr = libc::mmap(
@ -174,6 +203,7 @@ impl MmapInner {
let alignment = self.ptr as usize % page_size();
let ptr = self.ptr.offset(-(alignment as isize));
let len = self.len + alignment;
let len = len.max(1);
if libc::mprotect(ptr, len, prot) == 0 {
Ok(())
} else {
@ -208,20 +238,29 @@ impl MmapInner {
pub fn len(&self) -> usize {
self.len
}
pub fn advise(&self, advice: Advice) -> io::Result<()> {
unsafe {
if libc::madvise(self.ptr, self.len, advice as i32) != 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
}
impl Drop for MmapInner {
fn drop(&mut self) {
let alignment = self.ptr as usize % page_size();
let len = self.len + alignment;
let len = len.max(1);
// Any errors during unmapping/closing are ignored as the only way
// to report them would be through panicking which is highly discouraged
// in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97
unsafe {
assert!(
libc::munmap(
self.ptr.offset(-(alignment as isize)),
(self.len + alignment) as libc::size_t
) == 0,
"unable to unmap mmap: {}",
io::Error::last_os_error()
);
let ptr = self.ptr.offset(-(alignment as isize));
libc::munmap(ptr, len as libc::size_t);
}
}
}
@ -230,16 +269,32 @@ unsafe impl Sync for MmapInner {}
unsafe impl Send for MmapInner {}
fn page_size() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
match PAGE_SIZE.load(Ordering::Relaxed) {
0 => {
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
PAGE_SIZE.store(page_size, Ordering::Relaxed);
page_size
}
page_size => page_size,
}
}
pub fn file_len(file: RawFd) -> io::Result<u64> {
unsafe {
let mut stat: libc::stat = std::mem::zeroed();
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "l4re")))]
use libc::{fstat, stat};
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
use libc::{fstat64 as fstat, stat64 as stat};
let result = libc::fstat(file, &mut stat);
unsafe {
let mut stat = MaybeUninit::<stat>::uninit();
let result = fstat(file, stat.as_mut_ptr());
if result == 0 {
Ok(stat.st_size as u64)
Ok(stat.assume_init().st_size as u64)
} else {
Err(io::Error::last_os_error())
}

View File

@ -1,9 +1,8 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::fs::File;
use std::os::raw::c_void;
use std::os::windows::io::{AsRawHandle, RawHandle};
use std::os::windows::io::RawHandle;
use std::{io, mem, ptr};
type BOOL = i32;
@ -11,6 +10,7 @@ type WORD = u16;
type DWORD = u32;
type WCHAR = u16;
type HANDLE = *mut c_void;
type LPHANDLE = *mut HANDLE;
type LPVOID = *mut c_void;
type LPCVOID = *const c_void;
type ULONG_PTR = usize;
@ -23,6 +23,8 @@ type LPSYSTEM_INFO = *mut SYSTEM_INFO;
const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002;
const STANDARD_RIGHTS_REQUIRED: DWORD = 0x000F0000;
const SECTION_QUERY: DWORD = 0x0001;
@ -52,7 +54,6 @@ const FILE_MAP_EXECUTE: DWORD = SECTION_MAP_EXECUTE_EXPLICIT;
const FILE_MAP_COPY: DWORD = 0x00000001;
#[repr(C)]
#[derive(Clone, Copy)]
struct SECURITY_ATTRIBUTES {
nLength: DWORD,
lpSecurityDescriptor: LPVOID,
@ -60,20 +61,9 @@ struct SECURITY_ATTRIBUTES {
}
#[repr(C)]
#[derive(Clone, Copy)]
struct SYSTEM_INFO_u_s {
struct SYSTEM_INFO {
wProcessorArchitecture: WORD,
wReserved: WORD,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct SYSTEM_INFO_u([u32; 1]);
#[repr(C)]
#[derive(Clone, Copy)]
struct SYSTEM_INFO {
u: SYSTEM_INFO_u,
dwPageSize: DWORD,
lpMinimumApplicationAddress: LPVOID,
lpMaximumApplicationAddress: LPVOID,
@ -85,9 +75,44 @@ struct SYSTEM_INFO {
wProcessorRevision: WORD,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct FILETIME {
pub dwLowDateTime: DWORD,
pub dwHighDateTime: DWORD,
}
#[repr(C)]
struct BY_HANDLE_FILE_INFORMATION {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
dwVolumeSerialNumber: DWORD,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
nNumberOfLinks: DWORD,
nFileIndexHigh: DWORD,
nFileIndexLow: DWORD,
}
type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION;
extern "system" {
fn GetCurrentProcess() -> HANDLE;
fn CloseHandle(hObject: HANDLE) -> BOOL;
fn DuplicateHandle(
hSourceProcessHandle: HANDLE,
hSourceHandle: HANDLE,
hTargetProcessHandle: HANDLE,
lpTargetHandle: LPHANDLE,
dwDesiredAccess: DWORD,
bInheritHandle: BOOL,
dwOptions: DWORD,
) -> BOOL;
fn CreateFileMappingW(
hFile: HANDLE,
lpFileMappingAttributes: LPSECURITY_ATTRIBUTES,
@ -97,6 +122,13 @@ extern "system" {
lpName: LPCWSTR,
) -> HANDLE;
fn FlushFileBuffers(hFile: HANDLE) -> BOOL;
fn GetFileInformationByHandle(
hFile: HANDLE,
lpFileInformation: LPBY_HANDLE_FILE_INFORMATION,
) -> BOOL;
fn FlushViewOfFile(lpBaseAddress: LPCVOID, dwNumberOfBytesToFlush: SIZE_T) -> BOOL;
fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL;
@ -119,8 +151,13 @@ extern "system" {
fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
}
/// Returns a fixed pointer that is valid for `slice::from_raw_parts::<u8>` with `len == 0`.
fn empty_slice_ptr() -> *mut c_void {
std::ptr::NonNull::<u8>::dangling().cast().as_ptr()
}
pub struct MmapInner {
file: Option<File>,
handle: Option<RawHandle>,
ptr: *mut c_void,
len: usize,
copy: bool,
@ -131,7 +168,7 @@ impl MmapInner {
///
/// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls.
pub fn new(
file: &File,
handle: RawHandle,
protect: DWORD,
access: DWORD,
offset: u64,
@ -141,45 +178,74 @@ impl MmapInner {
let alignment = offset % allocation_granularity() as u64;
let aligned_offset = offset - alignment as u64;
let aligned_len = len + alignment as usize;
if aligned_len == 0 {
// `CreateFileMappingW` documents:
//
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw
// > An attempt to map a file with a length of 0 (zero) fails with an error code
// > of ERROR_FILE_INVALID. Applications should test for files with a length of 0
// > (zero) and reject those files.
//
// For such files, dont create a mapping at all and use a marker pointer instead.
return Ok(MmapInner {
handle: None,
ptr: empty_slice_ptr(),
len: 0,
copy,
});
}
unsafe {
let handle = CreateFileMappingW(
file.as_raw_handle(),
ptr::null_mut(),
protect,
0,
0,
ptr::null(),
);
if handle.is_null() {
let mapping = CreateFileMappingW(handle, ptr::null_mut(), protect, 0, 0, ptr::null());
if mapping.is_null() {
return Err(io::Error::last_os_error());
}
let ptr = MapViewOfFile(
handle,
mapping,
access,
(aligned_offset >> 16 >> 16) as DWORD,
(aligned_offset & 0xffffffff) as DWORD,
aligned_len as SIZE_T,
);
CloseHandle(handle);
CloseHandle(mapping);
if ptr.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(MmapInner {
file: Some(file.try_clone()?),
ptr: ptr.offset(alignment as isize),
len: len as usize,
copy,
})
return Err(io::Error::last_os_error());
}
let mut new_handle = 0 as RawHandle;
let cur_proc = GetCurrentProcess();
let ok = DuplicateHandle(
cur_proc,
handle,
cur_proc,
&mut new_handle,
0,
0,
DUPLICATE_SAME_ACCESS,
);
if ok == 0 {
UnmapViewOfFile(ptr);
return Err(io::Error::last_os_error());
}
Ok(MmapInner {
handle: Some(new_handle),
ptr: ptr.offset(alignment as isize),
len: len as usize,
copy,
})
}
}
pub fn map(len: usize, file: &File, offset: u64, _populate: bool) -> io::Result<MmapInner> {
let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE);
let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ);
pub fn map(
len: usize,
handle: RawHandle,
offset: u64,
_populate: bool,
) -> io::Result<MmapInner> {
let write = protection_supported(handle, PAGE_READWRITE);
let exec = protection_supported(handle, PAGE_EXECUTE_READ);
let mut access = FILE_MAP_READ;
let protection = match (write, exec) {
(true, true) => {
@ -197,7 +263,7 @@ impl MmapInner {
(false, false) => PAGE_READONLY,
};
let mut inner = MmapInner::new(file, protection, access, offset, len, false)?;
let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?;
if write || exec {
inner.make_read_only()?;
}
@ -206,11 +272,11 @@ impl MmapInner {
pub fn map_exec(
len: usize,
file: &File,
handle: RawHandle,
offset: u64,
_populate: bool,
) -> io::Result<MmapInner> {
let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE);
let write = protection_supported(handle, PAGE_READWRITE);
let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE;
let protection = if write {
access |= FILE_MAP_WRITE;
@ -219,15 +285,20 @@ impl MmapInner {
PAGE_EXECUTE_READ
};
let mut inner = MmapInner::new(file, protection, access, offset, len, false)?;
let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?;
if write {
inner.make_exec()?;
}
Ok(inner)
}
pub fn map_mut(len: usize, file: &File, offset: u64, _populate: bool) -> io::Result<MmapInner> {
let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ);
pub fn map_mut(
len: usize,
handle: RawHandle,
offset: u64,
_populate: bool,
) -> io::Result<MmapInner> {
let exec = protection_supported(handle, PAGE_EXECUTE_READ);
let mut access = FILE_MAP_READ | FILE_MAP_WRITE;
let protection = if exec {
access |= FILE_MAP_EXECUTE;
@ -236,7 +307,7 @@ impl MmapInner {
PAGE_READWRITE
};
let mut inner = MmapInner::new(file, protection, access, offset, len, false)?;
let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?;
if exec {
inner.make_mut()?;
}
@ -245,11 +316,11 @@ impl MmapInner {
pub fn map_copy(
len: usize,
file: &File,
handle: RawHandle,
offset: u64,
_populate: bool,
) -> io::Result<MmapInner> {
let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READWRITE);
let exec = protection_supported(handle, PAGE_EXECUTE_READWRITE);
let mut access = FILE_MAP_COPY;
let protection = if exec {
access |= FILE_MAP_EXECUTE;
@ -258,7 +329,7 @@ impl MmapInner {
PAGE_WRITECOPY
};
let mut inner = MmapInner::new(file, protection, access, offset, len, true)?;
let mut inner = MmapInner::new(handle, protection, access, offset, len, true)?;
if exec {
inner.make_mut()?;
}
@ -267,12 +338,12 @@ impl MmapInner {
pub fn map_copy_read_only(
len: usize,
file: &File,
handle: RawHandle,
offset: u64,
_populate: bool,
) -> io::Result<MmapInner> {
let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE);
let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ);
let write = protection_supported(handle, PAGE_READWRITE);
let exec = protection_supported(handle, PAGE_EXECUTE_READ);
let mut access = FILE_MAP_COPY;
let protection = if exec {
access |= FILE_MAP_EXECUTE;
@ -281,7 +352,7 @@ impl MmapInner {
PAGE_WRITECOPY
};
let mut inner = MmapInner::new(file, protection, access, offset, len, true)?;
let mut inner = MmapInner::new(handle, protection, access, offset, len, true)?;
if write || exec {
inner.make_read_only()?;
}
@ -289,36 +360,38 @@ impl MmapInner {
}
pub fn map_anon(len: usize, _stack: bool) -> io::Result<MmapInner> {
// Ensure a non-zero length for the underlying mapping
let mapped_len = len.max(1);
unsafe {
// Create a mapping and view with maximum access permissions, then use `VirtualProtect`
// to set the actual `Protection`. This way, we can set more permissive protection later
// on.
// Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx
let handle = CreateFileMappingW(
let mapping = CreateFileMappingW(
INVALID_HANDLE_VALUE,
ptr::null_mut(),
PAGE_EXECUTE_READWRITE,
(len >> 16 >> 16) as DWORD,
(len & 0xffffffff) as DWORD,
(mapped_len >> 16 >> 16) as DWORD,
(mapped_len & 0xffffffff) as DWORD,
ptr::null(),
);
if handle.is_null() {
if mapping.is_null() {
return Err(io::Error::last_os_error());
}
let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
let ptr = MapViewOfFile(handle, access, 0, 0, len as SIZE_T);
CloseHandle(handle);
let ptr = MapViewOfFile(mapping, access, 0, 0, mapped_len as SIZE_T);
CloseHandle(mapping);
if ptr.is_null() {
return Err(io::Error::last_os_error());
}
let mut old = 0;
let result = VirtualProtect(ptr, len as SIZE_T, PAGE_READWRITE, &mut old);
let result = VirtualProtect(ptr, mapped_len as SIZE_T, PAGE_READWRITE, &mut old);
if result != 0 {
Ok(MmapInner {
file: None,
handle: None,
ptr,
len: len as usize,
copy: false,
@ -331,13 +404,21 @@ impl MmapInner {
pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
self.flush_async(offset, len)?;
if let Some(ref file) = self.file {
file.sync_data()?;
if let Some(handle) = self.handle {
let ok = unsafe { FlushFileBuffers(handle) };
if ok == 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
if self.ptr == empty_slice_ptr() {
return Ok(());
}
let result = unsafe { FlushViewOfFile(self.ptr.add(offset), len as SIZE_T) };
if result != 0 {
Ok(())
@ -347,6 +428,9 @@ impl MmapInner {
}
fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> {
if self.ptr == empty_slice_ptr() {
return Ok(());
}
unsafe {
let alignment = self.ptr as usize % allocation_granularity();
let ptr = self.ptr.offset(-(alignment as isize));
@ -401,14 +485,20 @@ impl MmapInner {
impl Drop for MmapInner {
fn drop(&mut self) {
if self.ptr == empty_slice_ptr() {
return;
}
let alignment = self.ptr as usize % allocation_granularity();
// Any errors during unmapping/closing are ignored as the only way
// to report them would be through panicking which is highly discouraged
// in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97
unsafe {
let ptr = self.ptr.offset(-(alignment as isize));
assert!(
UnmapViewOfFile(ptr) != 0,
"unable to unmap mmap: {}",
io::Error::last_os_error()
);
UnmapViewOfFile(ptr);
if let Some(handle) = self.handle {
CloseHandle(handle);
}
}
}
}
@ -418,11 +508,11 @@ unsafe impl Send for MmapInner {}
fn protection_supported(handle: RawHandle, protection: DWORD) -> bool {
unsafe {
let handle = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null());
if handle.is_null() {
let mapping = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null());
if mapping.is_null() {
return false;
}
CloseHandle(handle);
CloseHandle(mapping);
true
}
}
@ -435,6 +525,17 @@ fn allocation_granularity() -> usize {
}
}
pub fn file_len(file: &File) -> io::Result<u64> {
Ok(file.metadata()?.len())
pub fn file_len(handle: RawHandle) -> io::Result<u64> {
let info = unsafe {
let mut info = mem::MaybeUninit::<BY_HANDLE_FILE_INFORMATION>::uninit();
let ok = GetFileInformationByHandle(handle, info.as_mut_ptr());
if ok == 0 {
return Err(io::Error::last_os_error());
}
info.assume_init()
};
Ok((info.nFileSizeHigh as u64) << 32 | info.nFileSizeLow as u64)
}

View File

@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Markus Stange <mstange@themasta.com>"]
[dependencies]
memmap2 = "0.3"
memmap2 = "0.5"
rustc-demangle = "0.1"
uuid = "0.8"