diff options
Diffstat (limited to 'third_party/rust/memmap')
-rw-r--r-- | third_party/rust/memmap/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/memmap/Cargo.toml | 33 | ||||
-rw-r--r-- | third_party/rust/memmap/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | third_party/rust/memmap/LICENSE-MIT | 25 | ||||
-rw-r--r-- | third_party/rust/memmap/README.md | 53 | ||||
-rw-r--r-- | third_party/rust/memmap/ci/install.sh | 47 | ||||
-rw-r--r-- | third_party/rust/memmap/ci/script.sh | 20 | ||||
-rw-r--r-- | third_party/rust/memmap/examples/cat.rs | 23 | ||||
-rw-r--r-- | third_party/rust/memmap/src/lib.rs | 1008 | ||||
-rw-r--r-- | third_party/rust/memmap/src/unix.rs | 214 | ||||
-rw-r--r-- | third_party/rust/memmap/src/windows.rs | 300 |
11 files changed, 1925 insertions, 0 deletions
diff --git a/third_party/rust/memmap/.cargo-checksum.json b/third_party/rust/memmap/.cargo-checksum.json new file mode 100644 index 0000000000..6fda693661 --- /dev/null +++ b/third_party/rust/memmap/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"4f6e813f7480758c57f9d180ade1276761a499245846fadb7a96a234342506ca","LICENSE-APACHE":"04ea4849dba9dcae07113850c6f1b1a69052c625210639914eee352023f750ad","LICENSE-MIT":"bd1d8f06a6ce1f7645991e347b95864b970eed43624305eb4bb78c09aef8692d","README.md":"193c46fdc56e4ca953ec7179bf4fd5f302b46557d86181b56b7d6294f25eaa96","ci/install.sh":"8841985e8645d391829ba52cd669bc5c5d98b1fdd8979c7cfd3cd3573c4a0a4b","ci/script.sh":"04127e49ac7346941f0a21d2e1a9177719e62f1e92755579c3601fc98e33006c","examples/cat.rs":"86cfee28482f92ea4c4ae4ebdff97d70fc095208cc5de09db39994118468d1ff","src/lib.rs":"82e116f2b1fbfc7e987a0dfde7e73e51b231f38f33b76369c6de0d46b63c60e1","src/unix.rs":"93fb4fe71dd04e2d1c5e1b6a285bce8b729456bdddbd758aa6768e8ab30ed143","src/windows.rs":"21566160bc1b2f2f82988b9e1274b3b700034cad0e5ca723695863a432da4896"},"package":"6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"}
\ No newline at end of file diff --git a/third_party/rust/memmap/Cargo.toml b/third_party/rust/memmap/Cargo.toml new file mode 100644 index 0000000000..fef331e356 --- /dev/null +++ b/third_party/rust/memmap/Cargo.toml @@ -0,0 +1,33 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# 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 +# +# 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) + +[package] +name = "memmap" +version = "0.7.0" +authors = ["Dan Burkert <dan@danburkert.com>"] +description = "Cross-platform Rust API for memory-mapped file IO" +documentation = "https://docs.rs/memmap" +keywords = ["mmap", "memory-map", "io", "file"] +license = "MIT/Apache-2.0" +repository = "https://github.com/danburkert/memmap-rs" +[dev-dependencies.tempdir] +version = "0.3" +[target."cfg(unix)".dependencies.libc] +version = "0.2" +[target."cfg(windows)".dependencies.winapi] +version = "0.3" +features = ["basetsd", "handleapi", "memoryapi", "minwindef", "std", "sysinfoapi"] +[badges.appveyor] +repository = "danburkert/mmap" + +[badges.travis-ci] +repository = "danburkert/memmap-rs" diff --git a/third_party/rust/memmap/LICENSE-APACHE b/third_party/rust/memmap/LICENSE-APACHE new file mode 100644 index 0000000000..7be3d81ac7 --- /dev/null +++ b/third_party/rust/memmap/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2015] [Dan Burkert] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/memmap/LICENSE-MIT b/third_party/rust/memmap/LICENSE-MIT new file mode 100644 index 0000000000..ebb42e1f3e --- /dev/null +++ b/third_party/rust/memmap/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 Dan Burkert + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/memmap/README.md b/third_party/rust/memmap/README.md new file mode 100644 index 0000000000..c230b32062 --- /dev/null +++ b/third_party/rust/memmap/README.md @@ -0,0 +1,53 @@ +# memmap + +A Rust library for cross-platform memory mapped IO. + +[![Build Status](https://travis-ci.org/danburkert/memmap-rs.svg?branch=master)](https://travis-ci.org/danburkert/memmap-rs) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/ubka00959pstatkg/branch/master?svg=true)](https://ci.appveyor.com/project/danburkert/mmap) +[![Documentation](https://docs.rs/memmap/badge.svg)](https://docs.rs/memmap) +[![Crate](https://img.shields.io/crates/v/memmap.svg)](https://crates.io/crates/memmap) + +## Features + +- [x] file-backed memory maps +- [x] anonymous memory maps +- [x] synchronous and asynchronous flushing +- [x] copy-on-write memory maps +- [x] read-only memory maps +- [x] stack support (`MAP_STACK` on unix) +- [x] executable memory maps +- [ ] huge page support + +## Platforms + +`memmap` should work on any platform supported by +[`libc`](https://github.com/rust-lang-nursery/libc#platforms-and-documentation). +`memmap` requires Rust stable 1.13 or greater. + +`memmap` is continuously tested on: + * `x86_64-unknown-linux-gnu` (Linux) + * `i686-unknown-linux-gnu` + * `x86_64-unknown-linux-musl` (Linux MUSL) + * `x86_64-apple-darwin` (OSX) + * `i686-apple-darwin` + * `x86_64-pc-windows-msvc` (Windows) + * `i686-pc-windows-msvc` + * `x86_64-pc-windows-gnu` + * `i686-pc-windows-gnu` + +`memmap` is continuously cross-compiled against: + * `arm-linux-androideabi` (Android) + * `aarch64-unknown-linux-gnu` (ARM) + * `arm-unknown-linux-gnueabihf` + * `mips-unknown-linux-gnu` (MIPS) + * `x86_64-apple-ios` (iOS) + * `i686-apple-ios` + +## License + +`memmap` is primarily distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details. + +Copyright (c) 2015 Dan Burkert. diff --git a/third_party/rust/memmap/ci/install.sh b/third_party/rust/memmap/ci/install.sh new file mode 100644 index 0000000000..80e18e4720 --- /dev/null +++ b/third_party/rust/memmap/ci/install.sh @@ -0,0 +1,47 @@ +set -ex + +main() { + local target= + if [ $TRAVIS_OS_NAME = linux ]; then + target=x86_64-unknown-linux-musl + sort=sort + else + target=x86_64-apple-darwin + sort=gsort # for `sort --sort-version`, from brew's coreutils. + fi + + # Builds for iOS are done on OSX, but require the specific target to be + # installed. + case $TARGET in + aarch64-apple-ios) + rustup target install aarch64-apple-ios + ;; + armv7-apple-ios) + rustup target install armv7-apple-ios + ;; + armv7s-apple-ios) + rustup target install armv7s-apple-ios + ;; + i386-apple-ios) + rustup target install i386-apple-ios + ;; + x86_64-apple-ios) + rustup target install x86_64-apple-ios + ;; + esac + + # This fetches latest stable release + local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ + | cut -d/ -f3 \ + | grep -E '^v[0.1.0-9.]+$' \ + | $sort --version-sort \ + | tail -n1) + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- \ + --force \ + --git japaric/cross \ + --tag $tag \ + --target $target +} + +main diff --git a/third_party/rust/memmap/ci/script.sh b/third_party/rust/memmap/ci/script.sh new file mode 100644 index 0000000000..3ccf556f61 --- /dev/null +++ b/third_party/rust/memmap/ci/script.sh @@ -0,0 +1,20 @@ +# This script takes care of testing your crate + +set -ex + +main() { + cross build --target $TARGET + cross build --target $TARGET --release + + if [ ! -z $DISABLE_TESTS ]; then + return + fi + + cross test --target $TARGET + cross test --target $TARGET --release +} + +# we don't run the "test phase" when doing deploys +if [ -z $TRAVIS_TAG ]; then + main +fi diff --git a/third_party/rust/memmap/examples/cat.rs b/third_party/rust/memmap/examples/cat.rs new file mode 100644 index 0000000000..4745972ddb --- /dev/null +++ b/third_party/rust/memmap/examples/cat.rs @@ -0,0 +1,23 @@ +extern crate memmap; + +use std::env; +use std::fs::File; +use std::io::{self, Write}; + +use memmap::Mmap; + +/// Output a file's contents to stdout. The file path must be provided as the first process +/// argument. +fn main() { + let path = env::args() + .nth(1) + .expect("supply a single path as the program argument"); + + let file = File::open(path).expect("failed to open the file"); + + let mmap = unsafe { Mmap::map(&file).expect("failed to map the file") }; + + io::stdout() + .write_all(&mmap[..]) + .expect("failed to output the file contents"); +} diff --git a/third_party/rust/memmap/src/lib.rs b/third_party/rust/memmap/src/lib.rs new file mode 100644 index 0000000000..5010c00581 --- /dev/null +++ b/third_party/rust/memmap/src/lib.rs @@ -0,0 +1,1008 @@ +//! A cross-platform Rust API for memory mapped buffers. + +#![doc(html_root_url = "https://docs.rs/memmap/0.7.0")] + +#[cfg(windows)] +extern crate winapi; +#[cfg(windows)] +mod windows; +#[cfg(windows)] +use windows::MmapInner; + +#[cfg(unix)] +mod unix; +#[cfg(unix)] +use unix::MmapInner; + +use std::fmt; +use std::fs::File; +use std::io::{Error, ErrorKind, Result}; +use std::ops::{Deref, DerefMut}; +use std::slice; +use std::usize; + +/// A memory map builder, providing advanced options and flags for specifying memory map behavior. +/// +/// `MmapOptions` can be used to create an anonymous memory map using `MmapOptions::map_anon`, or a +/// file-backed memory map using one of `MmapOptions::map`, `MmapOptions::map_mut`, +/// `MmapOptions::map_exec`, or `MmapOptions::map_copy`. +#[derive(Clone, Debug, Default)] +pub struct MmapOptions { + offset: u64, + len: Option<usize>, + stack: bool, +} + +impl MmapOptions { + /// Creates a new set of options for configuring and creating a memory map. + /// + /// # Example + /// + /// ``` + /// use memmap::{MmapMut, MmapOptions}; + /// # use std::io::Result; + /// + /// # fn main() -> Result<()> { + /// // Create a new memory map builder. + /// let mut mmap_options = MmapOptions::new(); + /// + /// // Configure the memory map builder using option setters, then create + /// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`, + /// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`: + /// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?; + /// + /// // Use the memory map: + /// mmap.copy_from_slice(b"...data to copy to the memory map..."); + /// # let _ = mmap_options; + /// # Ok(()) + /// # } + /// ``` + pub fn new() -> MmapOptions { + MmapOptions::default() + } + + /// Configures the memory map to start at byte `offset` from the beginning of the file. + /// + /// This option has no effect on anonymous memory maps. + /// + /// By default, the offset is 0. + /// + /// # Example + /// + /// ``` + /// use memmap::MmapOptions; + /// use std::fs::File; + /// + /// # fn main() -> std::io::Result<()> { + /// let mmap = unsafe { + /// MmapOptions::new() + /// .offset(10) + /// .map(&File::open("README.md")?)? + /// }; + /// assert_eq!(&b"A Rust library for cross-platform memory mapped IO."[..], + /// &mmap[..51]); + /// # Ok(()) + /// # } + /// ``` + pub fn offset(&mut self, offset: u64) -> &mut Self { + self.offset = offset; + self + } + + /// Configures the created memory mapped buffer to be `len` bytes long. + /// + /// This option is mandatory for anonymous memory maps. + /// + /// For file-backed memory maps, the length will default to the file length. + /// + /// # Example + /// + /// ``` + /// use memmap::MmapOptions; + /// use std::fs::File; + /// + /// # fn main() -> std::io::Result<()> { + /// let mmap = unsafe { + /// MmapOptions::new() + /// .len(8) + /// .map(&File::open("README.md")?)? + /// }; + /// assert_eq!(&b"# memmap"[..], &mmap[..]); + /// # Ok(()) + /// # } + /// ``` + pub fn len(&mut self, len: usize) -> &mut Self { + self.len = Some(len); + self + } + + /// Returns the configured length, or the length of the provided file. + fn get_len(&self, file: &File) -> Result<usize> { + self.len.map(Ok).unwrap_or_else(|| { + let len = file.metadata()?.len() - self.offset; + if len > (usize::MAX as u64) { + return Err(Error::new( + ErrorKind::InvalidData, + "memory map length overflows usize", + )); + } + Ok(len as usize) + }) + } + + /// Configures the anonymous memory map to be suitable for a process or thread stack. + /// + /// This option corresponds to the `MAP_STACK` flag on Linux. + /// + /// This option has no effect on file-backed memory maps. + /// + /// # Example + /// + /// ``` + /// use memmap::MmapOptions; + /// + /// # fn main() -> std::io::Result<()> { + /// let stack = MmapOptions::new().stack().len(4096).map_anon(); + /// # Ok(()) + /// # } + /// ``` + pub fn stack(&mut self) -> &mut Self { + self.stack = true; + self + } + + /// Creates a read-only memory map backed by a file. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with read permissions. + /// + /// # Example + /// + /// ``` + /// use memmap::MmapOptions; + /// use std::fs::File; + /// use std::io::Read; + /// + /// # fn main() -> std::io::Result<()> { + /// let mut file = File::open("README.md")?; + /// + /// let mut contents = Vec::new(); + /// file.read_to_end(&mut contents)?; + /// + /// let mmap = unsafe { + /// MmapOptions::new().map(&file)? + /// }; + /// + /// assert_eq!(&contents[..], &mmap[..]); + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn map(&self, file: &File) -> Result<Mmap> { + MmapInner::map(self.get_len(file)?, file, self.offset).map(|inner| Mmap { inner: inner }) + } + + /// Creates a readable and executable memory map backed by a file. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with read permissions. + pub unsafe fn map_exec(&self, file: &File) -> Result<Mmap> { + MmapInner::map_exec(self.get_len(file)?, file, self.offset) + .map(|inner| Mmap { inner: inner }) + } + + /// Creates a writeable memory map backed by a file. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with read and write permissions. + /// + /// # Example + /// + /// ``` + /// # extern crate memmap; + /// # extern crate tempdir; + /// # + /// use std::fs::OpenOptions; + /// use std::path::PathBuf; + /// + /// use memmap::MmapOptions; + /// # + /// # fn main() -> std::io::Result<()> { + /// # let tempdir = tempdir::TempDir::new("mmap")?; + /// let path: PathBuf = /* path to file */ + /// # tempdir.path().join("map_mut"); + /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; + /// file.set_len(13)?; + /// + /// let mut mmap = unsafe { + /// MmapOptions::new().map_mut(&file)? + /// }; + /// + /// mmap.copy_from_slice(b"Hello, world!"); + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn map_mut(&self, file: &File) -> Result<MmapMut> { + MmapInner::map_mut(self.get_len(file)?, file, self.offset) + .map(|inner| MmapMut { inner: inner }) + } + + /// Creates a copy-on-write memory map backed by a file. + /// + /// Data written to the memory map will not be visible by other processes, + /// and will not be carried through to the underlying file. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with writable permissions. + /// + /// # Example + /// + /// ``` + /// use memmap::MmapOptions; + /// use std::fs::File; + /// use std::io::Write; + /// + /// # fn main() -> std::io::Result<()> { + /// let file = File::open("README.md")?; + /// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? }; + /// (&mut mmap[..]).write_all(b"Hello, world!")?; + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn map_copy(&self, file: &File) -> Result<MmapMut> { + MmapInner::map_copy(self.get_len(file)?, file, self.offset) + .map(|inner| MmapMut { inner: inner }) + } + + /// Creates an anonymous memory map. + /// + /// Note: the memory map length must be configured to be greater than 0 before creating an + /// anonymous memory map using `MmapOptions::len()`. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails. + pub fn map_anon(&self) -> Result<MmapMut> { + MmapInner::map_anon(self.len.unwrap_or(0), self.stack).map(|inner| MmapMut { inner: inner }) + } +} + +/// An immutable memory mapped buffer. +/// +/// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. +/// +/// Use `MmapOptions` to configure and create a file-backed memory map. To create an immutable +/// anonymous memory map, first create a mutable anonymous memory map using `MmapOptions`, and then +/// make it immutable with `MmapMut::make_read_only`. +/// +/// # Example +/// +/// ``` +/// use memmap::MmapOptions; +/// use std::io::Write; +/// use std::fs::File; +/// +/// # fn main() -> std::io::Result<()> { +/// let file = File::open("README.md")?; +/// let mmap = unsafe { MmapOptions::new().map(&file)? }; +/// assert_eq!(b"# memmap", &mmap[0..8]); +/// # Ok(()) +/// # } +/// ``` +/// +/// See `MmapMut` for the mutable version. +pub struct Mmap { + inner: MmapInner, +} + +impl Mmap { + /// Creates a read-only memory map backed by a file. + /// + /// This is equivalent to calling `MmapOptions::new().map(file)`. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with read permissions. + /// + /// # Example + /// + /// ``` + /// use std::fs::File; + /// use std::io::Read; + /// + /// use memmap::Mmap; + /// + /// # fn main() -> std::io::Result<()> { + /// let mut file = File::open("README.md")?; + /// + /// let mut contents = Vec::new(); + /// file.read_to_end(&mut contents)?; + /// + /// let mmap = unsafe { Mmap::map(&file)? }; + /// + /// assert_eq!(&contents[..], &mmap[..]); + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn map(file: &File) -> Result<Mmap> { + MmapOptions::new().map(file) + } + + /// Transition the memory map to be writable. + /// + /// If the memory map is file-backed, the file must have been opened with write permissions. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with writable permissions. + /// + /// # Example + /// + /// ``` + /// # extern crate memmap; + /// # extern crate tempdir; + /// # + /// use memmap::Mmap; + /// use std::ops::DerefMut; + /// use std::io::Write; + /// # use std::fs::OpenOptions; + /// + /// # fn main() -> std::io::Result<()> { + /// # let tempdir = tempdir::TempDir::new("mmap")?; + /// let file = /* file opened with write permissions */ + /// # OpenOptions::new() + /// # .read(true) + /// # .write(true) + /// # .create(true) + /// # .open(tempdir.path() + /// # .join("make_mut"))?; + /// # file.set_len(128)?; + /// let mmap = unsafe { Mmap::map(&file)? }; + /// // ... use the read-only memory map ... + /// let mut mut_mmap = mmap.make_mut()?; + /// mut_mmap.deref_mut().write_all(b"hello, world!")?; + /// # Ok(()) + /// # } + /// ``` + pub fn make_mut(mut self) -> Result<MmapMut> { + self.inner.make_mut()?; + Ok(MmapMut { inner: self.inner }) + } +} + +impl Deref for Mmap { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } + } +} + +impl AsRef<[u8]> for Mmap { + #[inline] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl fmt::Debug for Mmap { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Mmap") + .field("ptr", &self.as_ptr()) + .field("len", &self.len()) + .finish() + } +} + +/// A mutable memory mapped buffer. +/// +/// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous +/// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use +/// `MmapOptions` for creating memory maps. +/// +/// See `Mmap` for the immutable version. +pub struct MmapMut { + inner: MmapInner, +} + +impl MmapMut { + /// Creates a writeable memory map backed by a file. + /// + /// This is equivalent to calling `MmapOptions::new().map_mut(file)`. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file is not open with read and write permissions. + /// + /// # Example + /// + /// ``` + /// # extern crate memmap; + /// # extern crate tempdir; + /// # + /// use std::fs::OpenOptions; + /// use std::path::PathBuf; + /// + /// use memmap::MmapMut; + /// # + /// # fn main() -> std::io::Result<()> { + /// # let tempdir = tempdir::TempDir::new("mmap")?; + /// let path: PathBuf = /* path to file */ + /// # tempdir.path().join("map_mut"); + /// let file = OpenOptions::new() + /// .read(true) + /// .write(true) + /// .create(true) + /// .open(&path)?; + /// file.set_len(13)?; + /// + /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; + /// + /// mmap.copy_from_slice(b"Hello, world!"); + /// # Ok(()) + /// # } + /// ``` + pub unsafe fn map_mut(file: &File) -> Result<MmapMut> { + MmapOptions::new().map_mut(file) + } + + /// Creates an anonymous memory map. + /// + /// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails. + pub fn map_anon(length: usize) -> Result<MmapMut> { + MmapOptions::new().len(length).map_anon() + } + + /// 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 memmap; + /// # extern crate tempdir; + /// # + /// use std::fs::OpenOptions; + /// use std::io::Write; + /// use std::path::PathBuf; + /// + /// use memmap::MmapMut; + /// + /// # fn main() -> std::io::Result<()> { + /// # let tempdir = tempdir::TempDir::new("mmap")?; + /// 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 { MmapMut::map_mut(&file)? }; + /// + /// (&mut mmap[..]).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) + } + + /// Returns an immutable version of this memory mapped buffer. + /// + /// If the memory map is file-backed, the file must have been opened with read permissions. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file has not been opened with read permissions. + /// + /// # Example + /// + /// ``` + /// # extern crate memmap; + /// # + /// use std::io::Write; + /// use std::path::PathBuf; + /// + /// use memmap::{Mmap, MmapMut}; + /// + /// # fn main() -> std::io::Result<()> { + /// let mut mmap = MmapMut::map_anon(128)?; + /// + /// (&mut mmap[..]).write(b"Hello, world!")?; + /// + /// let mmap: Mmap = mmap.make_read_only()?; + /// # Ok(()) + /// # } + /// ``` + pub fn make_read_only(mut self) -> Result<Mmap> { + self.inner.make_read_only()?; + Ok(Mmap { inner: self.inner }) + } + + /// Transition the memory map to be readable and executable. + /// + /// If the memory map is file-backed, the file must have been opened with execute permissions. + /// + /// # Errors + /// + /// This method returns an error when the underlying system call fails, which can happen for a + /// variety of reasons, such as when the file has not been opened with execute permissions. + pub fn make_exec(mut self) -> Result<Mmap> { + self.inner.make_exec()?; + Ok(Mmap { inner: self.inner }) + } +} + +impl Deref for MmapMut { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } + } +} + +impl DerefMut for MmapMut { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.inner.mut_ptr(), self.inner.len()) } + } +} + +impl AsRef<[u8]> for MmapMut { + #[inline] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl AsMut<[u8]> for MmapMut { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.deref_mut() + } +} + +impl fmt::Debug for MmapMut { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("MmapMut") + .field("ptr", &self.as_ptr()) + .field("len", &self.len()) + .finish() + } +} + +#[cfg(test)] +mod test { + + extern crate tempdir; + #[cfg(windows)] + extern crate winapi; + + use std::fs::OpenOptions; + use std::io::{Read, Write}; + #[cfg(windows)] + use std::os::windows::fs::OpenOptionsExt; + use std::sync::Arc; + use std::thread; + + #[cfg(windows)] + use winapi::um::winnt::GENERIC_ALL; + + use super::{Mmap, MmapMut, MmapOptions}; + + #[test] + fn map_file() { + let expected_len = 128; + 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(); + + file.set_len(expected_len as u64).unwrap(); + + let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; + 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[..]); + } + + /// Checks that a 0-length file will not be mapped. + #[test] + fn map_empty_file() { + 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 mmap = unsafe { Mmap::map(&file) }; + assert!(mmap.is_err()); + } + + #[test] + fn map_anon() { + let expected_len = 128; + let mut mmap = MmapMut::map_anon(expected_len).unwrap(); + 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[..]); + } + + #[test] + fn map_anon_zero_len() { + assert!(MmapOptions::new().map_anon().is_err()) + } + + #[test] + fn file_write() { + let tempdir = tempdir::TempDir::new("mmap").unwrap(); + let path = tempdir.path().join("mmap"); + + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&path) + .unwrap(); + file.set_len(128).unwrap(); + + let write = b"abc123"; + let mut read = [0u8; 6]; + + let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; + (&mut mmap[..]).write_all(write).unwrap(); + mmap.flush().unwrap(); + + file.read(&mut read).unwrap(); + assert_eq!(write, &read); + } + + #[test] + fn flush_range() { + 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(); + file.set_len(128).unwrap(); + let write = b"abc123"; + + let mut mmap = unsafe { + MmapOptions::new() + .offset(2) + .len(write.len()) + .map_mut(&file) + .unwrap() + }; + (&mut mmap[..]).write_all(write).unwrap(); + mmap.flush_range(0, write.len()).unwrap(); + } + + #[test] + fn map_copy() { + let tempdir = tempdir::TempDir::new("mmap").unwrap(); + let path = tempdir.path().join("mmap"); + + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&path) + .unwrap(); + file.set_len(128).unwrap(); + + let nulls = b"\0\0\0\0\0\0"; + let write = b"abc123"; + let mut read = [0u8; 6]; + + let mut mmap = unsafe { MmapOptions::new().map_copy(&file).unwrap() }; + + (&mut mmap[..]).write(write).unwrap(); + mmap.flush().unwrap(); + + // The mmap contains the write + (&mmap[..]).read(&mut read).unwrap(); + assert_eq!(write, &read); + + // The file does not contain the write + file.read(&mut read).unwrap(); + assert_eq!(nulls, &read); + + // another mmap does not contain the write + let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; + (&mmap2[..]).read(&mut read).unwrap(); + assert_eq!(nulls, &read); + } + + #[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(); + + // Check inferred length mmap. + let mmap = unsafe { MmapOptions::new().offset(offset).map_mut(&file).unwrap() }; + assert_eq!(len, mmap.len()); + + // Check explicit length mmap. + let mut mmap = unsafe { + MmapOptions::new() + .offset(offset) + .len(len) + .map_mut(&file) + .unwrap() + }; + assert_eq!(len, mmap.len()); + + let zeros = vec![0; len]; + let incr: Vec<_> = (0..len).map(|i| i 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[..]); + } + + #[test] + fn index() { + let mut mmap = MmapMut::map_anon(128).unwrap(); + mmap[0] = 42; + assert_eq!(42, mmap[0]); + } + + #[test] + fn sync_send() { + let mmap = Arc::new(MmapMut::map_anon(129).unwrap()); + thread::spawn(move || { + &mmap[..]; + }); + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn jit_x86(mut mmap: MmapMut) { + use std::mem; + mmap[0] = 0xB8; // mov eax, 0xAB + mmap[1] = 0xAB; + mmap[2] = 0x00; + mmap[3] = 0x00; + mmap[4] = 0x00; + mmap[5] = 0xC3; // ret + + let mmap = mmap.make_exec().expect("make_exec"); + + let jitfn: extern "C" fn() -> u8 = unsafe { mem::transmute(mmap.as_ptr()) }; + assert_eq!(jitfn(), 0xab); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn jit_x86_anon() { + jit_x86(MmapMut::map_anon(4096).unwrap()); + } + + #[test] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn jit_x86_file() { + let tempdir = tempdir::TempDir::new("mmap").unwrap(); + let mut options = OpenOptions::new(); + #[cfg(windows)] + options.access_mode(GENERIC_ALL); + + let file = options + .read(true) + .write(true) + .create(true) + .open(&tempdir.path().join("jit_x86")) + .expect("open"); + + file.set_len(4096).expect("set_len"); + jit_x86(unsafe { MmapMut::map_mut(&file).expect("map_mut") }); + } + + #[test] + fn mprotect_file() { + let tempdir = tempdir::TempDir::new("mmap").unwrap(); + let path = tempdir.path().join("mmap"); + + let mut options = OpenOptions::new(); + #[cfg(windows)] + options.access_mode(GENERIC_ALL); + + let mut file = options + .read(true) + .write(true) + .create(true) + .open(&path) + .expect("open"); + file.set_len(256 as u64).expect("set_len"); + + let mmap = unsafe { MmapMut::map_mut(&file).expect("map_mut") }; + + let mmap = mmap.make_read_only().expect("make_read_only"); + let mut mmap = mmap.make_mut().expect("make_mut"); + + let write = b"abc123"; + let mut read = [0u8; 6]; + + (&mut mmap[..]).write(write).unwrap(); + mmap.flush().unwrap(); + + // The mmap contains the write + (&mmap[..]).read(&mut read).unwrap(); + assert_eq!(write, &read); + + // The file should contain the write + file.read(&mut read).unwrap(); + assert_eq!(write, &read); + + // another mmap should contain the write + let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; + (&mmap2[..]).read(&mut read).unwrap(); + assert_eq!(write, &read); + + let mmap = mmap.make_exec().expect("make_exec"); + + drop(mmap); + } + + #[test] + fn mprotect_copy() { + let tempdir = tempdir::TempDir::new("mmap").unwrap(); + let path = tempdir.path().join("mmap"); + + let mut options = OpenOptions::new(); + #[cfg(windows)] + options.access_mode(GENERIC_ALL); + + let mut file = options + .read(true) + .write(true) + .create(true) + .open(&path) + .expect("open"); + file.set_len(256 as u64).expect("set_len"); + + let mmap = unsafe { MmapOptions::new().map_copy(&file).expect("map_mut") }; + + let mmap = mmap.make_read_only().expect("make_read_only"); + let mut mmap = mmap.make_mut().expect("make_mut"); + + let nulls = b"\0\0\0\0\0\0"; + let write = b"abc123"; + let mut read = [0u8; 6]; + + (&mut mmap[..]).write(write).unwrap(); + mmap.flush().unwrap(); + + // The mmap contains the write + (&mmap[..]).read(&mut read).unwrap(); + assert_eq!(write, &read); + + // The file does not contain the write + file.read(&mut read).unwrap(); + assert_eq!(nulls, &read); + + // another mmap does not contain the write + let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; + (&mmap2[..]).read(&mut read).unwrap(); + assert_eq!(nulls, &read); + + let mmap = mmap.make_exec().expect("make_exec"); + + drop(mmap); + } + + #[test] + fn mprotect_anon() { + let mmap = MmapMut::map_anon(256).expect("map_mut"); + + let mmap = mmap.make_read_only().expect("make_read_only"); + let mmap = mmap.make_mut().expect("make_mut"); + let mmap = mmap.make_exec().expect("make_exec"); + drop(mmap); + } +} diff --git a/third_party/rust/memmap/src/unix.rs b/third_party/rust/memmap/src/unix.rs new file mode 100644 index 0000000000..4838e7e4f7 --- /dev/null +++ b/third_party/rust/memmap/src/unix.rs @@ -0,0 +1,214 @@ +extern crate libc; + +use std::fs::File; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::{io, ptr}; + +#[cfg(any( + all(target_os = "linux", not(target_arch = "mips")), + target_os = "freebsd", + target_os = "android" +))] +const MAP_STACK: libc::c_int = libc::MAP_STACK; + +#[cfg(not(any( + all(target_os = "linux", not(target_arch = "mips")), + target_os = "freebsd", + target_os = "android" +)))] +const MAP_STACK: libc::c_int = 0; + +pub struct MmapInner { + ptr: *mut libc::c_void, + len: usize, +} + +impl MmapInner { + /// Creates a new `MmapInner`. + /// + /// This is a thin wrapper around the `mmap` sytem call. + fn new( + len: usize, + prot: libc::c_int, + flags: libc::c_int, + file: RawFd, + offset: u64, + ) -> io::Result<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", + )); + } + + unsafe { + let ptr = libc::mmap( + ptr::null_mut(), + aligned_len as libc::size_t, + prot, + flags, + file, + aligned_offset as libc::off_t, + ); + + if ptr == libc::MAP_FAILED { + Err(io::Error::last_os_error()) + } else { + Ok(MmapInner { + ptr: ptr.offset(alignment as isize), + len: len, + }) + } + } + } + + pub fn map(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + MmapInner::new( + len, + libc::PROT_READ, + libc::MAP_SHARED, + file.as_raw_fd(), + offset, + ) + } + + pub fn map_exec(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + MmapInner::new( + len, + libc::PROT_READ | libc::PROT_EXEC, + libc::MAP_SHARED, + file.as_raw_fd(), + offset, + ) + } + + pub fn map_mut(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + MmapInner::new( + len, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + file.as_raw_fd(), + offset, + ) + } + + pub fn map_copy(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + MmapInner::new( + len, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE, + file.as_raw_fd(), + offset, + ) + } + + /// Open an anonymous memory map. + pub fn map_anon(len: usize, stack: bool) -> io::Result<MmapInner> { + let stack = if stack { MAP_STACK } else { 0 }; + MmapInner::new( + len, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED | libc::MAP_ANON | stack, + -1, + 0, + ) + } + + pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> { + let alignment = (self.ptr as usize + offset) % page_size(); + let offset = offset as isize - alignment as isize; + let len = len + alignment; + let result = + unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) }; + if result == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { + let alignment = offset % page_size(); + let aligned_offset = offset - alignment; + let aligned_len = len + alignment; + let result = unsafe { + libc::msync( + self.ptr.offset(aligned_offset as isize), + aligned_len as libc::size_t, + libc::MS_ASYNC, + ) + }; + if result == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> { + unsafe { + let alignment = self.ptr as usize % page_size(); + let ptr = self.ptr.offset(-(alignment as isize)); + let len = self.len + alignment; + if libc::mprotect(ptr, len, prot) == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + } + + pub fn make_read_only(&mut self) -> io::Result<()> { + self.mprotect(libc::PROT_READ) + } + + pub fn make_exec(&mut self) -> io::Result<()> { + self.mprotect(libc::PROT_READ | libc::PROT_EXEC) + } + + pub fn make_mut(&mut self) -> io::Result<()> { + self.mprotect(libc::PROT_READ | libc::PROT_WRITE) + } + + #[inline] + pub fn ptr(&self) -> *const u8 { + self.ptr as *const u8 + } + + #[inline] + pub fn mut_ptr(&mut self) -> *mut u8 { + self.ptr as *mut u8 + } + + #[inline] + pub fn len(&self) -> usize { + self.len + } +} + +impl Drop for MmapInner { + fn drop(&mut self) { + let alignment = self.ptr as usize % page_size(); + 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() + ); + } + } +} + +unsafe impl Sync for MmapInner {} +unsafe impl Send for MmapInner {} + +fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} diff --git a/third_party/rust/memmap/src/windows.rs b/third_party/rust/memmap/src/windows.rs new file mode 100644 index 0000000000..d8aa99d255 --- /dev/null +++ b/third_party/rust/memmap/src/windows.rs @@ -0,0 +1,300 @@ +use std::fs::File; +use std::os::raw::c_void; +use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::{io, mem, ptr}; + +use winapi::shared::basetsd::SIZE_T; +use winapi::shared::minwindef::DWORD; +use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; +use winapi::um::memoryapi::{ + CreateFileMappingW, FlushViewOfFile, MapViewOfFile, UnmapViewOfFile, VirtualProtect, + FILE_MAP_ALL_ACCESS, FILE_MAP_COPY, FILE_MAP_EXECUTE, FILE_MAP_READ, FILE_MAP_WRITE, +}; +use winapi::um::sysinfoapi::GetSystemInfo; +use winapi::um::winnt::{ + PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, + PAGE_READWRITE, PAGE_WRITECOPY, +}; + +pub struct MmapInner { + file: Option<File>, + ptr: *mut c_void, + len: usize, + copy: bool, +} + +impl MmapInner { + /// Creates a new `MmapInner`. + /// + /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls. + pub fn new( + file: &File, + protect: DWORD, + access: DWORD, + offset: u64, + len: usize, + copy: bool, + ) -> io::Result<MmapInner> { + let alignment = offset % allocation_granularity() as u64; + let aligned_offset = offset - alignment as u64; + let aligned_len = len + alignment as usize; + + unsafe { + let handle = CreateFileMappingW( + file.as_raw_handle(), + ptr::null_mut(), + protect, + 0, + 0, + ptr::null(), + ); + if handle == ptr::null_mut() { + return Err(io::Error::last_os_error()); + } + + let ptr = MapViewOfFile( + handle, + access, + (aligned_offset >> 16 >> 16) as DWORD, + (aligned_offset & 0xffffffff) as DWORD, + aligned_len as SIZE_T, + ); + CloseHandle(handle); + + if ptr == ptr::null_mut() { + 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: copy, + }) + } + } + } + + pub fn map(len: usize, file: &File, offset: u64) -> 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 mut access = FILE_MAP_READ; + let protection = match (write, exec) { + (true, true) => { + access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE; + PAGE_EXECUTE_READWRITE + } + (true, false) => { + access |= FILE_MAP_WRITE; + PAGE_READWRITE + } + (false, true) => { + access |= FILE_MAP_EXECUTE; + PAGE_EXECUTE_READ + } + (false, false) => PAGE_READONLY, + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; + if write || exec { + inner.make_read_only()?; + } + Ok(inner) + } + + pub fn map_exec(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let write = protection_supported(file.as_raw_handle(), PAGE_READWRITE); + let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE; + let protection = if write { + access |= FILE_MAP_WRITE; + PAGE_EXECUTE_READWRITE + } else { + PAGE_EXECUTE_READ + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; + if write { + inner.make_exec()?; + } + Ok(inner) + } + + pub fn map_mut(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READ); + let mut access = FILE_MAP_READ | FILE_MAP_WRITE; + let protection = if exec { + access |= FILE_MAP_EXECUTE; + PAGE_EXECUTE_READWRITE + } else { + PAGE_READWRITE + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, false)?; + if exec { + inner.make_mut()?; + } + Ok(inner) + } + + pub fn map_copy(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> { + let exec = protection_supported(file.as_raw_handle(), PAGE_EXECUTE_READWRITE); + let mut access = FILE_MAP_COPY; + let protection = if exec { + access |= FILE_MAP_EXECUTE; + PAGE_EXECUTE_WRITECOPY + } else { + PAGE_WRITECOPY + }; + + let mut inner = MmapInner::new(file, protection, access, offset, len, true)?; + if exec { + inner.make_mut()?; + } + Ok(inner) + } + + pub fn map_anon(len: usize, _stack: bool) -> io::Result<MmapInner> { + 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( + INVALID_HANDLE_VALUE, + ptr::null_mut(), + PAGE_EXECUTE_READWRITE, + (len >> 16 >> 16) as DWORD, + (len & 0xffffffff) as DWORD, + ptr::null(), + ); + if handle == ptr::null_mut() { + 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); + + if ptr == ptr::null_mut() { + return Err(io::Error::last_os_error()); + } + + let mut old = 0; + let result = VirtualProtect(ptr, len as SIZE_T, PAGE_READWRITE, &mut old); + if result != 0 { + Ok(MmapInner { + file: None, + ptr: ptr, + len: len as usize, + copy: false, + }) + } else { + Err(io::Error::last_os_error()) + } + } + } + + 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()?; + } + Ok(()) + } + + pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { + let result = unsafe { FlushViewOfFile(self.ptr.offset(offset as isize), len as SIZE_T) }; + if result != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> { + unsafe { + let alignment = self.ptr as usize % allocation_granularity(); + let ptr = self.ptr.offset(-(alignment as isize)); + let aligned_len = self.len as SIZE_T + alignment as SIZE_T; + + let mut old = 0; + let result = VirtualProtect(ptr, aligned_len, protect, &mut old); + + if result != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + } + + pub fn make_read_only(&mut self) -> io::Result<()> { + self.virtual_protect(PAGE_READONLY) + } + + pub fn make_exec(&mut self) -> io::Result<()> { + if self.copy { + self.virtual_protect(PAGE_EXECUTE_WRITECOPY) + } else { + self.virtual_protect(PAGE_EXECUTE_READ) + } + } + + pub fn make_mut(&mut self) -> io::Result<()> { + if self.copy { + self.virtual_protect(PAGE_WRITECOPY) + } else { + self.virtual_protect(PAGE_READWRITE) + } + } + + #[inline] + pub fn ptr(&self) -> *const u8 { + self.ptr as *const u8 + } + + #[inline] + pub fn mut_ptr(&mut self) -> *mut u8 { + self.ptr as *mut u8 + } + + #[inline] + pub fn len(&self) -> usize { + self.len + } +} + +impl Drop for MmapInner { + fn drop(&mut self) { + let alignment = self.ptr as usize % allocation_granularity(); + unsafe { + let ptr = self.ptr.offset(-(alignment as isize)); + assert!( + UnmapViewOfFile(ptr) != 0, + "unable to unmap mmap: {}", + io::Error::last_os_error() + ); + } + } +} + +unsafe impl Sync for MmapInner {} +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 == ptr::null_mut() { + return false; + } + CloseHandle(handle); + true + } +} + +fn allocation_granularity() -> usize { + unsafe { + let mut info = mem::zeroed(); + GetSystemInfo(&mut info); + return info.dwAllocationGranularity as usize; + } +} |