diff --git a/Cargo.lock b/Cargo.lock index a2be9161eea7..d96c10d5d25e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/build/rust/memmap2/Cargo.toml b/build/rust/memmap2/Cargo.toml index bfebf7f532cf..1e4e61d8e209 100644 --- a/build/rust/memmap2/Cargo.toml +++ b/build/rust/memmap2/Cargo.toml @@ -8,4 +8,4 @@ license = "MPL-2.0" path = "lib.rs" [dependencies] -memmap2 = "0.3" +memmap2 = "0.5" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 11dc0e4f4577..8b2115a07016 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -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]] diff --git a/third_party/rust/memmap2/.cargo-checksum.json b/third_party/rust/memmap2/.cargo-checksum.json index 1be0acbff5d5..4cb59ff0381f 100644 --- a/third_party/rust/memmap2/.cargo-checksum.json +++ b/third_party/rust/memmap2/.cargo-checksum.json @@ -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"} \ No newline at end of file +{"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"} \ No newline at end of file diff --git a/third_party/rust/memmap2/CHANGELOG.md b/third_party/rust/memmap2/CHANGELOG.md index 2e56a72518b9..e54a7812237f 100644 --- a/third_party/rust/memmap2/CHANGELOG.md +++ b/third_party/rust/memmap2/CHANGELOG.md @@ -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 diff --git a/third_party/rust/memmap2/Cargo.lock b/third_party/rust/memmap2/Cargo.lock index f924140ebc7e..7f41430a44c2 100644 --- a/third_party/rust/memmap2/Cargo.lock +++ b/third_party/rust/memmap2/Cargo.lock @@ -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]] diff --git a/third_party/rust/memmap2/Cargo.toml b/third_party/rust/memmap2/Cargo.toml index a82faa9b81ea..bdbc259e31d8 100644 --- a/third_party/rust/memmap2/Cargo.toml +++ b/third_party/rust/memmap2/Cargo.toml @@ -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 ", "Yevhenii Reizner "] +version = "0.5.4" +authors = [ + "Dan Burkert ", + "Yevhenii Reizner ", +] 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" diff --git a/third_party/rust/memmap2/README.md b/third_party/rust/memmap2/README.md index 8ca85d74e472..df0252b1ad56 100644 --- a/third_party/rust/memmap2/README.md +++ b/third_party/rust/memmap2/README.md @@ -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. diff --git a/third_party/rust/memmap2/src/advice.rs b/third_party/rust/memmap2/src/advice.rs new file mode 100644 index 000000000000..185743e07780 --- /dev/null +++ b/third_party/rust/memmap2/src/advice.rs @@ -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) diff --git a/third_party/rust/memmap2/src/lib.rs b/third_party/rust/memmap2/src/lib.rs index c13170228694..b90d1bcf6ed9 100644 --- a/third_party/rust/memmap2/src/lib.rs +++ b/third_party/rust/memmap2/src/lib.rs @@ -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 = (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[..]); + } } diff --git a/third_party/rust/memmap2/src/stub.rs b/third_party/rust/memmap2/src/stub.rs index a555a4f353c5..e756da4f68f3 100644 --- a/third_party/rust/memmap2/src/stub.rs +++ b/third_party/rust/memmap2/src/stub.rs @@ -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 {} } } diff --git a/third_party/rust/memmap2/src/unix.rs b/third_party/rust/memmap2/src/unix.rs index 97b318388db3..cd3dcdbce304 100644 --- a/third_party/rust/memmap2/src/unix.rs +++ b/third_party/rust/memmap2/src/unix.rs @@ -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 { - 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::::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()) } diff --git a/third_party/rust/memmap2/src/windows.rs b/third_party/rust/memmap2/src/windows.rs index 7f13f683bd21..b3b8c620aafc 100644 --- a/third_party/rust/memmap2/src/windows.rs +++ b/third_party/rust/memmap2/src/windows.rs @@ -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::` with `len == 0`. +fn empty_slice_ptr() -> *mut c_void { + std::ptr::NonNull::::dangling().cast().as_ptr() +} + pub struct MmapInner { - file: Option, + handle: Option, 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, don’t 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 { - 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 { + 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 { - 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 { - 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 { + 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 { - 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 { - 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 { + // 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 { - Ok(file.metadata()?.len()) +pub fn file_len(handle: RawHandle) -> io::Result { + let info = unsafe { + let mut info = mem::MaybeUninit::::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) } diff --git a/tools/profiler/rust-helper/Cargo.toml b/tools/profiler/rust-helper/Cargo.toml index 97c6af4c9c8f..05dce61ce080 100644 --- a/tools/profiler/rust-helper/Cargo.toml +++ b/tools/profiler/rust-helper/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Markus Stange "] [dependencies] -memmap2 = "0.3" +memmap2 = "0.5" rustc-demangle = "0.1" uuid = "0.8"