summaryrefslogtreecommitdiffstats
path: root/third_party/rust/same-file
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/same-file')
-rw-r--r--third_party/rust/same-file/.cargo-checksum.json1
-rw-r--r--third_party/rust/same-file/COPYING3
-rw-r--r--third_party/rust/same-file/Cargo.lock48
-rw-r--r--third_party/rust/same-file/Cargo.toml29
-rw-r--r--third_party/rust/same-file/LICENSE-MIT21
-rw-r--r--third_party/rust/same-file/README.md49
-rw-r--r--third_party/rust/same-file/UNLICENSE24
-rw-r--r--third_party/rust/same-file/examples/is_same_file.rs11
-rw-r--r--third_party/rust/same-file/examples/is_stderr.rs31
-rw-r--r--third_party/rust/same-file/rustfmt.toml2
-rw-r--r--third_party/rust/same-file/src/lib.rs572
-rw-r--r--third_party/rust/same-file/src/unix.rs112
-rw-r--r--third_party/rust/same-file/src/unknown.rs52
-rw-r--r--third_party/rust/same-file/src/win.rs172
14 files changed, 1127 insertions, 0 deletions
diff --git a/third_party/rust/same-file/.cargo-checksum.json b/third_party/rust/same-file/.cargo-checksum.json
new file mode 100644
index 0000000000..bcc92c50d2
--- /dev/null
+++ b/third_party/rust/same-file/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.lock":"fa40407b035c7abffe97d267d2ff95d22d83e5b916aca876bec49a56a9067c73","Cargo.toml":"991f8df8fa5a259801900a56908cf21a66c5cf7b238bc81ba9bdf348e233252e","LICENSE-MIT":"cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14","README.md":"70c109d9c89b4479016142f2a4ad6963b6fe5793bcdd997add3d3af3d2baf36b","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","examples/is_same_file.rs":"7b3eeb27a15051667d97615fc7a2339cbff5630df3bca6ac19ab81d5be22f329","examples/is_stderr.rs":"e1c5d1a0f36d7aa0020bb5b87c2f45c7176033f03c52cf395be55dd8debfc413","rustfmt.toml":"1ca600239a27401c4a43f363cf3f38183a212affc1f31bff3ae93234bbaec228","src/lib.rs":"b22c2f0b5cad2248f16f4f42add52b2dc0c627631f71ee67a8c38fe305048f85","src/unix.rs":"69abed9fade151247696c6d4a442ef299554f3722e23a2d08053598a52a27d62","src/unknown.rs":"bfde4e9ac88f500c0ccb69165383682ddd24bf7d7ddaf5859426e1fd4b2f9359","src/win.rs":"94f912cc3734f60608d0ee2b0c664afb65fc96e5b0b223a53565fb8998c03fa3"},"package":"93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"} \ No newline at end of file
diff --git a/third_party/rust/same-file/COPYING b/third_party/rust/same-file/COPYING
new file mode 100644
index 0000000000..bb9c20a094
--- /dev/null
+++ b/third_party/rust/same-file/COPYING
@@ -0,0 +1,3 @@
+This project is dual-licensed under the Unlicense and MIT licenses.
+
+You may use this code under the terms of either license.
diff --git a/third_party/rust/same-file/Cargo.lock b/third_party/rust/same-file/Cargo.lock
new file mode 100644
index 0000000000..de49c9df65
--- /dev/null
+++ b/third_party/rust/same-file/Cargo.lock
@@ -0,0 +1,48 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "doc-comment"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+dependencies = [
+ "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
+"checksum winapi 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "890b38836c01d72fdb636d15c9cfc52ec7fd783b330abc93cd1686f4308dfccc"
+"checksum winapi-i686-pc-windows-gnu 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ec6667f60c23eca65c561e63a13d81b44234c2e38a6b6c959025ee907ec614cc"
+"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab"
+"checksum winapi-x86_64-pc-windows-gnu 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98f12c52b2630cd05d2c3ffd8e008f7f48252c042b4871c72aed9dc733b96668"
diff --git a/third_party/rust/same-file/Cargo.toml b/third_party/rust/same-file/Cargo.toml
new file mode 100644
index 0000000000..4f668208e7
--- /dev/null
+++ b/third_party/rust/same-file/Cargo.toml
@@ -0,0 +1,29 @@
+# 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]
+edition = "2018"
+name = "same-file"
+version = "1.0.6"
+authors = ["Andrew Gallant <jamslam@gmail.com>"]
+exclude = ["/.github"]
+description = "A simple crate for determining whether two file paths point to the same file.\n"
+homepage = "https://github.com/BurntSushi/same-file"
+documentation = "https://docs.rs/same-file"
+readme = "README.md"
+keywords = ["same", "file", "equal", "inode"]
+license = "Unlicense/MIT"
+repository = "https://github.com/BurntSushi/same-file"
+[dev-dependencies.doc-comment]
+version = "0.3"
+[target."cfg(windows)".dependencies.winapi-util]
+version = "0.1.1"
diff --git a/third_party/rust/same-file/LICENSE-MIT b/third_party/rust/same-file/LICENSE-MIT
new file mode 100644
index 0000000000..3303149e52
--- /dev/null
+++ b/third_party/rust/same-file/LICENSE-MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Andrew Gallant
+
+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/same-file/README.md b/third_party/rust/same-file/README.md
new file mode 100644
index 0000000000..2c45fdf8dd
--- /dev/null
+++ b/third_party/rust/same-file/README.md
@@ -0,0 +1,49 @@
+same-file
+=========
+A safe and cross platform crate to determine whether two files or directories
+are the same.
+
+[![Build status](https://github.com/BurntSushi/same-file/workflows/ci/badge.svg)](https://github.com/BurntSushi/same-file/actions)
+[![](http://meritbadge.herokuapp.com/same-file)](https://crates.io/crates/same-file)
+
+Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
+
+### Documentation
+
+https://docs.rs/same-file
+
+### Usage
+
+Add this to your `Cargo.toml`:
+
+```toml
+[dependencies]
+same-file = "1"
+```
+
+### Example
+
+The simplest use of this crate is to use the `is_same_file` function, which
+takes two file paths and returns true if and only if they refer to the same
+file:
+
+```rust,no_run
+use same_file::is_same_file;
+
+fn main() {
+ assert!(is_same_file("/bin/sh", "/usr/bin/sh").unwrap());
+}
+```
+
+### Minimum Rust version policy
+
+This crate's minimum supported `rustc` version is `1.34.0`.
+
+The current policy is that the minimum Rust version required to use this crate
+can be increased in minor version updates. For example, if `crate 1.0` requires
+Rust 1.20.0, then `crate 1.0.z` for all values of `z` will also require Rust
+1.20.0 or newer. However, `crate 1.y` for `y > 0` may require a newer minimum
+version of Rust.
+
+In general, this crate will be conservative with respect to the minimum
+supported version of Rust.
diff --git a/third_party/rust/same-file/UNLICENSE b/third_party/rust/same-file/UNLICENSE
new file mode 100644
index 0000000000..68a49daad8
--- /dev/null
+++ b/third_party/rust/same-file/UNLICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to <http://unlicense.org/>
diff --git a/third_party/rust/same-file/examples/is_same_file.rs b/third_party/rust/same-file/examples/is_same_file.rs
new file mode 100644
index 0000000000..6ba948a4c8
--- /dev/null
+++ b/third_party/rust/same-file/examples/is_same_file.rs
@@ -0,0 +1,11 @@
+use same_file::is_same_file;
+use std::io;
+
+fn try_main() -> Result<(), io::Error> {
+ assert!(is_same_file("/bin/sh", "/usr/bin/sh")?);
+ Ok(())
+}
+
+fn main() {
+ try_main().unwrap();
+}
diff --git a/third_party/rust/same-file/examples/is_stderr.rs b/third_party/rust/same-file/examples/is_stderr.rs
new file mode 100644
index 0000000000..1078695d98
--- /dev/null
+++ b/third_party/rust/same-file/examples/is_stderr.rs
@@ -0,0 +1,31 @@
+use std::io;
+use std::process;
+
+use same_file::Handle;
+
+fn main() {
+ if let Err(err) = run() {
+ println!("{}", err);
+ process::exit(1);
+ }
+}
+
+fn run() -> io::Result<()> {
+ // Run with `cargo run --example is_stderr 2> examples/stderr` to see
+ // interesting output.
+ let candidates = &[
+ "examples/is_same_file.rs",
+ "examples/is_stderr.rs",
+ "examples/stderr",
+ ];
+ let stderr_handle = Handle::stderr()?;
+ for candidate in candidates {
+ let handle = Handle::from_path(candidate)?;
+ if stderr_handle == handle {
+ println!("{:?} is stderr!", candidate);
+ } else {
+ println!("{:?} is NOT stderr!", candidate);
+ }
+ }
+ Ok(())
+}
diff --git a/third_party/rust/same-file/rustfmt.toml b/third_party/rust/same-file/rustfmt.toml
new file mode 100644
index 0000000000..aa37a218b9
--- /dev/null
+++ b/third_party/rust/same-file/rustfmt.toml
@@ -0,0 +1,2 @@
+max_width = 79
+use_small_heuristics = "max"
diff --git a/third_party/rust/same-file/src/lib.rs b/third_party/rust/same-file/src/lib.rs
new file mode 100644
index 0000000000..ed7ccf5090
--- /dev/null
+++ b/third_party/rust/same-file/src/lib.rs
@@ -0,0 +1,572 @@
+/*!
+This crate provides a safe and simple **cross platform** way to determine
+whether two file paths refer to the same file or directory.
+
+Most uses of this crate should be limited to the top-level [`is_same_file`]
+function, which takes two file paths and returns true if they refer to the
+same file or directory:
+
+```rust,no_run
+# use std::error::Error;
+use same_file::is_same_file;
+
+# fn try_main() -> Result<(), Box<Error>> {
+assert!(is_same_file("/bin/sh", "/usr/bin/sh")?);
+# Ok(())
+# }
+#
+# fn main() {
+# try_main().unwrap();
+# }
+```
+
+Additionally, this crate provides a [`Handle`] type that permits a more efficient
+equality check depending on your access pattern. For example, if one wanted to
+check whether any path in a list of paths corresponded to the process' stdout
+handle, then one could build a handle once for stdout. The equality check for
+each file in the list then only requires one stat call instead of two. The code
+might look like this:
+
+```rust,no_run
+# use std::error::Error;
+use same_file::Handle;
+
+# fn try_main() -> Result<(), Box<Error>> {
+let candidates = &[
+ "examples/is_same_file.rs",
+ "examples/is_stderr.rs",
+ "examples/stderr",
+];
+let stdout_handle = Handle::stdout()?;
+for candidate in candidates {
+ let handle = Handle::from_path(candidate)?;
+ if stdout_handle == handle {
+ println!("{:?} is stdout!", candidate);
+ } else {
+ println!("{:?} is NOT stdout!", candidate);
+ }
+}
+# Ok(())
+# }
+#
+# fn main() {
+# try_main().unwrap();
+# }
+```
+
+See [`examples/is_stderr.rs`] for a runnable example and compare the output of:
+
+- `cargo run --example is_stderr 2> examples/stderr` and
+- `cargo run --example is_stderr`.
+
+[`is_same_file`]: fn.is_same_file.html
+[`Handle`]: struct.Handle.html
+[`examples/is_stderr.rs`]: https://github.com/BurntSushi/same-file/blob/master/examples/is_same_file.rs
+
+*/
+
+#![allow(bare_trait_objects, unknown_lints)]
+#![deny(missing_docs)]
+
+#[cfg(test)]
+doc_comment::doctest!("../README.md");
+
+use std::fs::File;
+use std::io;
+use std::path::Path;
+
+#[cfg(any(target_os = "redox", unix))]
+use crate::unix as imp;
+#[cfg(not(any(target_os = "redox", unix, windows)))]
+use unknown as imp;
+#[cfg(windows)]
+use win as imp;
+
+#[cfg(any(target_os = "redox", unix))]
+mod unix;
+#[cfg(not(any(target_os = "redox", unix, windows)))]
+mod unknown;
+#[cfg(windows)]
+mod win;
+
+/// A handle to a file that can be tested for equality with other handles.
+///
+/// If two files are the same, then any two handles of those files will compare
+/// equal. If two files are not the same, then any two handles of those files
+/// will compare not-equal.
+///
+/// A handle consumes an open file resource as long as it exists.
+///
+/// Equality is determined by comparing inode numbers on Unix and a combination
+/// of identifier, volume serial, and file size on Windows. Note that it's
+/// possible for comparing two handles to produce a false positive on some
+/// platforms. Namely, two handles can compare equal even if the two handles
+/// *don't* point to the same file. Check the [source] for specific
+/// implementation details.
+///
+/// [source]: https://github.com/BurntSushi/same-file/tree/master/src
+#[derive(Debug, Eq, PartialEq, Hash)]
+pub struct Handle(imp::Handle);
+
+impl Handle {
+ /// Construct a handle from a path.
+ ///
+ /// Note that the underlying [`File`] is opened in read-only mode on all
+ /// platforms.
+ ///
+ /// [`File`]: https://doc.rust-lang.org/std/fs/struct.File.html
+ ///
+ /// # Errors
+ /// This method will return an [`io::Error`] if the path cannot
+ /// be opened, or the file's metadata cannot be obtained.
+ /// The most common reasons for this are: the path does not
+ /// exist, or there were not enough permissions.
+ ///
+ /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
+ ///
+ /// # Examples
+ /// Check that two paths are not the same file:
+ ///
+ /// ```rust,no_run
+ /// # use std::error::Error;
+ /// use same_file::Handle;
+ ///
+ /// # fn try_main() -> Result<(), Box<Error>> {
+ /// let source = Handle::from_path("./source")?;
+ /// let target = Handle::from_path("./target")?;
+ /// assert_ne!(source, target, "The files are the same.");
+ /// # Ok(())
+ /// # }
+ /// #
+ /// # fn main() {
+ /// # try_main().unwrap();
+ /// # }
+ /// ```
+ pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
+ imp::Handle::from_path(p).map(Handle)
+ }
+
+ /// Construct a handle from a file.
+ ///
+ /// # Errors
+ /// This method will return an [`io::Error`] if the metadata for
+ /// the given [`File`] cannot be obtained.
+ ///
+ /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
+ /// [`File`]: https://doc.rust-lang.org/std/fs/struct.File.html
+ ///
+ /// # Examples
+ /// Check that two files are not in fact the same file:
+ ///
+ /// ```rust,no_run
+ /// # use std::error::Error;
+ /// # use std::fs::File;
+ /// use same_file::Handle;
+ ///
+ /// # fn try_main() -> Result<(), Box<Error>> {
+ /// let source = File::open("./source")?;
+ /// let target = File::open("./target")?;
+ ///
+ /// assert_ne!(
+ /// Handle::from_file(source)?,
+ /// Handle::from_file(target)?,
+ /// "The files are the same."
+ /// );
+ /// # Ok(())
+ /// # }
+ /// #
+ /// # fn main() {
+ /// # try_main().unwrap();
+ /// # }
+ /// ```
+ pub fn from_file(file: File) -> io::Result<Handle> {
+ imp::Handle::from_file(file).map(Handle)
+ }
+
+ /// Construct a handle from stdin.
+ ///
+ /// # Errors
+ /// This method will return an [`io::Error`] if stdin cannot
+ /// be opened due to any I/O-related reason.
+ ///
+ /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use std::error::Error;
+ /// use same_file::Handle;
+ ///
+ /// # fn try_main() -> Result<(), Box<Error>> {
+ /// let stdin = Handle::stdin()?;
+ /// let stdout = Handle::stdout()?;
+ /// let stderr = Handle::stderr()?;
+ ///
+ /// if stdin == stdout {
+ /// println!("stdin == stdout");
+ /// }
+ /// if stdin == stderr {
+ /// println!("stdin == stderr");
+ /// }
+ /// if stdout == stderr {
+ /// println!("stdout == stderr");
+ /// }
+ /// #
+ /// # Ok(())
+ /// # }
+ /// #
+ /// # fn main() {
+ /// # try_main().unwrap();
+ /// # }
+ /// ```
+ ///
+ /// The output differs depending on the platform.
+ ///
+ /// On Linux:
+ ///
+ /// ```text
+ /// $ ./example
+ /// stdin == stdout
+ /// stdin == stderr
+ /// stdout == stderr
+ /// $ ./example > result
+ /// $ cat result
+ /// stdin == stderr
+ /// $ ./example > result 2>&1
+ /// $ cat result
+ /// stdout == stderr
+ /// ```
+ ///
+ /// Windows:
+ ///
+ /// ```text
+ /// > example
+ /// > example > result 2>&1
+ /// > type result
+ /// stdout == stderr
+ /// ```
+ pub fn stdin() -> io::Result<Handle> {
+ imp::Handle::stdin().map(Handle)
+ }
+
+ /// Construct a handle from stdout.
+ ///
+ /// # Errors
+ /// This method will return an [`io::Error`] if stdout cannot
+ /// be opened due to any I/O-related reason.
+ ///
+ /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
+ ///
+ /// # Examples
+ /// See the example for [`stdin()`].
+ ///
+ /// [`stdin()`]: #method.stdin
+ pub fn stdout() -> io::Result<Handle> {
+ imp::Handle::stdout().map(Handle)
+ }
+
+ /// Construct a handle from stderr.
+ ///
+ /// # Errors
+ /// This method will return an [`io::Error`] if stderr cannot
+ /// be opened due to any I/O-related reason.
+ ///
+ /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
+ ///
+ /// # Examples
+ /// See the example for [`stdin()`].
+ ///
+ /// [`stdin()`]: #method.stdin
+ pub fn stderr() -> io::Result<Handle> {
+ imp::Handle::stderr().map(Handle)
+ }
+
+ /// Return a reference to the underlying file.
+ ///
+ /// # Examples
+ /// Ensure that the target file is not the same as the source one,
+ /// and copy the data to it:
+ ///
+ /// ```rust,no_run
+ /// # use std::error::Error;
+ /// use std::io::prelude::*;
+ /// use std::io::Write;
+ /// use std::fs::File;
+ /// use same_file::Handle;
+ ///
+ /// # fn try_main() -> Result<(), Box<Error>> {
+ /// let source = File::open("source")?;
+ /// let target = File::create("target")?;
+ ///
+ /// let source_handle = Handle::from_file(source)?;
+ /// let mut target_handle = Handle::from_file(target)?;
+ /// assert_ne!(source_handle, target_handle, "The files are the same.");
+ ///
+ /// let mut source = source_handle.as_file();
+ /// let target = target_handle.as_file_mut();
+ ///
+ /// let mut buffer = Vec::new();
+ /// // data copy is simplified for the purposes of the example
+ /// source.read_to_end(&mut buffer)?;
+ /// target.write_all(&buffer)?;
+ /// #
+ /// # Ok(())
+ /// # }
+ /// #
+ /// # fn main() {
+ /// # try_main().unwrap();
+ /// # }
+ /// ```
+ pub fn as_file(&self) -> &File {
+ self.0.as_file()
+ }
+
+ /// Return a mutable reference to the underlying file.
+ ///
+ /// # Examples
+ /// See the example for [`as_file()`].
+ ///
+ /// [`as_file()`]: #method.as_file
+ pub fn as_file_mut(&mut self) -> &mut File {
+ self.0.as_file_mut()
+ }
+
+ /// Return the underlying device number of this handle.
+ ///
+ /// Note that this only works on unix platforms.
+ #[cfg(any(target_os = "redox", unix))]
+ pub fn dev(&self) -> u64 {
+ self.0.dev()
+ }
+
+ /// Return the underlying inode number of this handle.
+ ///
+ /// Note that this only works on unix platforms.
+ #[cfg(any(target_os = "redox", unix))]
+ pub fn ino(&self) -> u64 {
+ self.0.ino()
+ }
+}
+
+/// Returns true if the two file paths may correspond to the same file.
+///
+/// Note that it's possible for this to produce a false positive on some
+/// platforms. Namely, this can return true even if the two file paths *don't*
+/// resolve to the same file.
+/// # Errors
+/// This function will return an [`io::Error`] if any of the two paths cannot
+/// be opened. The most common reasons for this are: the path does not exist,
+/// or there were not enough permissions.
+///
+/// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
+///
+/// # Example
+///
+/// ```rust,no_run
+/// use same_file::is_same_file;
+///
+/// assert!(is_same_file("./foo", "././foo").unwrap_or(false));
+/// ```
+pub fn is_same_file<P, Q>(path1: P, path2: Q) -> io::Result<bool>
+where
+ P: AsRef<Path>,
+ Q: AsRef<Path>,
+{
+ Ok(Handle::from_path(path1)? == Handle::from_path(path2)?)
+}
+
+#[cfg(test)]
+mod tests {
+ use std::env;
+ use std::error;
+ use std::fs::{self, File};
+ use std::io;
+ use std::path::{Path, PathBuf};
+ use std::result;
+
+ use super::is_same_file;
+
+ type Result<T> = result::Result<T, Box<error::Error + Send + Sync>>;
+
+ /// Create an error from a format!-like syntax.
+ macro_rules! err {
+ ($($tt:tt)*) => {
+ Box::<error::Error + Send + Sync>::from(format!($($tt)*))
+ }
+ }
+
+ /// A simple wrapper for creating a temporary directory that is
+ /// automatically deleted when it's dropped.
+ ///
+ /// We use this in lieu of tempfile because tempfile brings in too many
+ /// dependencies.
+ #[derive(Debug)]
+ struct TempDir(PathBuf);
+
+ impl Drop for TempDir {
+ fn drop(&mut self) {
+ fs::remove_dir_all(&self.0).unwrap();
+ }
+ }
+
+ impl TempDir {
+ /// Create a new empty temporary directory under the system's
+ /// configured temporary directory.
+ fn new() -> Result<TempDir> {
+ #![allow(deprecated)]
+
+ use std::sync::atomic::{
+ AtomicUsize, Ordering, ATOMIC_USIZE_INIT,
+ };
+
+ static TRIES: usize = 100;
+ static COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
+
+ let tmpdir = env::temp_dir();
+ for _ in 0..TRIES {
+ let count = COUNTER.fetch_add(1, Ordering::SeqCst);
+ let path = tmpdir.join("rust-walkdir").join(count.to_string());
+ if path.is_dir() {
+ continue;
+ }
+ fs::create_dir_all(&path).map_err(|e| {
+ err!("failed to create {}: {}", path.display(), e)
+ })?;
+ return Ok(TempDir(path));
+ }
+ Err(err!("failed to create temp dir after {} tries", TRIES))
+ }
+
+ /// Return the underlying path to this temporary directory.
+ fn path(&self) -> &Path {
+ &self.0
+ }
+ }
+
+ fn tmpdir() -> TempDir {
+ TempDir::new().unwrap()
+ }
+
+ #[cfg(unix)]
+ pub fn soft_link_dir<P: AsRef<Path>, Q: AsRef<Path>>(
+ src: P,
+ dst: Q,
+ ) -> io::Result<()> {
+ use std::os::unix::fs::symlink;
+ symlink(src, dst)
+ }
+
+ #[cfg(unix)]
+ pub fn soft_link_file<P: AsRef<Path>, Q: AsRef<Path>>(
+ src: P,
+ dst: Q,
+ ) -> io::Result<()> {
+ soft_link_dir(src, dst)
+ }
+
+ #[cfg(windows)]
+ pub fn soft_link_dir<P: AsRef<Path>, Q: AsRef<Path>>(
+ src: P,
+ dst: Q,
+ ) -> io::Result<()> {
+ use std::os::windows::fs::symlink_dir;
+ symlink_dir(src, dst)
+ }
+
+ #[cfg(windows)]
+ pub fn soft_link_file<P: AsRef<Path>, Q: AsRef<Path>>(
+ src: P,
+ dst: Q,
+ ) -> io::Result<()> {
+ use std::os::windows::fs::symlink_file;
+ symlink_file(src, dst)
+ }
+
+ // These tests are rather uninteresting. The really interesting tests
+ // would stress the edge cases. On Unix, this might be comparing two files
+ // on different mount points with the same inode number. On Windows, this
+ // might be comparing two files whose file indices are the same on file
+ // systems where such things aren't guaranteed to be unique.
+ //
+ // Alas, I don't know how to create those environmental conditions. ---AG
+
+ #[test]
+ fn same_file_trivial() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ File::create(dir.join("a")).unwrap();
+ assert!(is_same_file(dir.join("a"), dir.join("a")).unwrap());
+ }
+
+ #[test]
+ fn same_dir_trivial() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ fs::create_dir(dir.join("a")).unwrap();
+ assert!(is_same_file(dir.join("a"), dir.join("a")).unwrap());
+ }
+
+ #[test]
+ fn not_same_file_trivial() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ File::create(dir.join("a")).unwrap();
+ File::create(dir.join("b")).unwrap();
+ assert!(!is_same_file(dir.join("a"), dir.join("b")).unwrap());
+ }
+
+ #[test]
+ fn not_same_dir_trivial() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ fs::create_dir(dir.join("a")).unwrap();
+ fs::create_dir(dir.join("b")).unwrap();
+ assert!(!is_same_file(dir.join("a"), dir.join("b")).unwrap());
+ }
+
+ #[test]
+ fn same_file_hard() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ File::create(dir.join("a")).unwrap();
+ fs::hard_link(dir.join("a"), dir.join("alink")).unwrap();
+ assert!(is_same_file(dir.join("a"), dir.join("alink")).unwrap());
+ }
+
+ #[test]
+ fn same_file_soft() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ File::create(dir.join("a")).unwrap();
+ soft_link_file(dir.join("a"), dir.join("alink")).unwrap();
+ assert!(is_same_file(dir.join("a"), dir.join("alink")).unwrap());
+ }
+
+ #[test]
+ fn same_dir_soft() {
+ let tdir = tmpdir();
+ let dir = tdir.path();
+
+ fs::create_dir(dir.join("a")).unwrap();
+ soft_link_dir(dir.join("a"), dir.join("alink")).unwrap();
+ assert!(is_same_file(dir.join("a"), dir.join("alink")).unwrap());
+ }
+
+ #[test]
+ fn test_send() {
+ fn assert_send<T: Send>() {}
+ assert_send::<super::Handle>();
+ }
+
+ #[test]
+ fn test_sync() {
+ fn assert_sync<T: Sync>() {}
+ assert_sync::<super::Handle>();
+ }
+}
diff --git a/third_party/rust/same-file/src/unix.rs b/third_party/rust/same-file/src/unix.rs
new file mode 100644
index 0000000000..fb3d19ff8f
--- /dev/null
+++ b/third_party/rust/same-file/src/unix.rs
@@ -0,0 +1,112 @@
+use std::fs::{File, OpenOptions};
+use std::hash::{Hash, Hasher};
+use std::io;
+use std::os::unix::fs::MetadataExt;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::path::Path;
+
+#[derive(Debug)]
+pub struct Handle {
+ file: Option<File>,
+ // If is_std is true, then we don't drop the corresponding File since it
+ // will close the handle.
+ is_std: bool,
+ dev: u64,
+ ino: u64,
+}
+
+impl Drop for Handle {
+ fn drop(&mut self) {
+ if self.is_std {
+ // unwrap() will not panic. Since we were able to open an
+ // std stream successfully, then `file` is guaranteed to be Some()
+ self.file.take().unwrap().into_raw_fd();
+ }
+ }
+}
+
+impl Eq for Handle {}
+
+impl PartialEq for Handle {
+ fn eq(&self, other: &Handle) -> bool {
+ (self.dev, self.ino) == (other.dev, other.ino)
+ }
+}
+
+impl AsRawFd for crate::Handle {
+ fn as_raw_fd(&self) -> RawFd {
+ // unwrap() will not panic. Since we were able to open the
+ // file successfully, then `file` is guaranteed to be Some()
+ self.0.file.as_ref().take().unwrap().as_raw_fd()
+ }
+}
+
+impl IntoRawFd for crate::Handle {
+ fn into_raw_fd(mut self) -> RawFd {
+ // unwrap() will not panic. Since we were able to open the
+ // file successfully, then `file` is guaranteed to be Some()
+ self.0.file.take().unwrap().into_raw_fd()
+ }
+}
+
+impl Hash for Handle {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.dev.hash(state);
+ self.ino.hash(state);
+ }
+}
+
+impl Handle {
+ pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
+ Handle::from_file(OpenOptions::new().read(true).open(p)?)
+ }
+
+ pub fn from_file(file: File) -> io::Result<Handle> {
+ let md = file.metadata()?;
+ Ok(Handle {
+ file: Some(file),
+ is_std: false,
+ dev: md.dev(),
+ ino: md.ino(),
+ })
+ }
+
+ pub fn from_std(file: File) -> io::Result<Handle> {
+ Handle::from_file(file).map(|mut h| {
+ h.is_std = true;
+ h
+ })
+ }
+
+ pub fn stdin() -> io::Result<Handle> {
+ Handle::from_std(unsafe { File::from_raw_fd(0) })
+ }
+
+ pub fn stdout() -> io::Result<Handle> {
+ Handle::from_std(unsafe { File::from_raw_fd(1) })
+ }
+
+ pub fn stderr() -> io::Result<Handle> {
+ Handle::from_std(unsafe { File::from_raw_fd(2) })
+ }
+
+ pub fn as_file(&self) -> &File {
+ // unwrap() will not panic. Since we were able to open the
+ // file successfully, then `file` is guaranteed to be Some()
+ self.file.as_ref().take().unwrap()
+ }
+
+ pub fn as_file_mut(&mut self) -> &mut File {
+ // unwrap() will not panic. Since we were able to open the
+ // file successfully, then `file` is guaranteed to be Some()
+ self.file.as_mut().take().unwrap()
+ }
+
+ pub fn dev(&self) -> u64 {
+ self.dev
+ }
+
+ pub fn ino(&self) -> u64 {
+ self.ino
+ }
+}
diff --git a/third_party/rust/same-file/src/unknown.rs b/third_party/rust/same-file/src/unknown.rs
new file mode 100644
index 0000000000..6bfbdea0d6
--- /dev/null
+++ b/third_party/rust/same-file/src/unknown.rs
@@ -0,0 +1,52 @@
+use std::fs::File;
+use std::io;
+use std::path::Path;
+
+static ERROR_MESSAGE: &str = "same-file is not supported on this platform.";
+// This implementation is to allow same-file to be compiled on
+// unsupported platforms in case it was incidentally included
+// as a transitive, unused dependency
+#[derive(Debug, Hash)]
+pub struct Handle;
+
+impl Eq for Handle {}
+
+impl PartialEq for Handle {
+ fn eq(&self, _other: &Handle) -> bool {
+ unreachable!(ERROR_MESSAGE);
+ }
+}
+
+impl Handle {
+ pub fn from_path<P: AsRef<Path>>(_p: P) -> io::Result<Handle> {
+ error()
+ }
+
+ pub fn from_file(_file: File) -> io::Result<Handle> {
+ error()
+ }
+
+ pub fn stdin() -> io::Result<Handle> {
+ error()
+ }
+
+ pub fn stdout() -> io::Result<Handle> {
+ error()
+ }
+
+ pub fn stderr() -> io::Result<Handle> {
+ error()
+ }
+
+ pub fn as_file(&self) -> &File {
+ unreachable!(ERROR_MESSAGE);
+ }
+
+ pub fn as_file_mut(&self) -> &mut File {
+ unreachable!(ERROR_MESSAGE);
+ }
+}
+
+fn error<T>() -> io::Result<T> {
+ Err(io::Error::new(io::ErrorKind::Other, ERROR_MESSAGE))
+}
diff --git a/third_party/rust/same-file/src/win.rs b/third_party/rust/same-file/src/win.rs
new file mode 100644
index 0000000000..6924739977
--- /dev/null
+++ b/third_party/rust/same-file/src/win.rs
@@ -0,0 +1,172 @@
+use std::fs::File;
+use std::hash::{Hash, Hasher};
+use std::io;
+use std::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle};
+use std::path::Path;
+
+use winapi_util as winutil;
+
+// For correctness, it is critical that both file handles remain open while
+// their attributes are checked for equality. In particular, the file index
+// numbers on a Windows stat object are not guaranteed to remain stable over
+// time.
+//
+// See the docs and remarks on MSDN:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
+//
+// It gets worse. It appears that the index numbers are not always
+// guaranteed to be unique. Namely, ReFS uses 128 bit numbers for unique
+// identifiers. This requires a distinct syscall to get `FILE_ID_INFO`
+// documented here:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/hh802691(v=vs.85).aspx
+//
+// It seems straight-forward enough to modify this code to use
+// `FILE_ID_INFO` when available (minimum Windows Server 2012), but I don't
+// have access to such Windows machines.
+//
+// Two notes.
+//
+// 1. Java's NIO uses the approach implemented here and appears to ignore
+// `FILE_ID_INFO` altogether. So Java's NIO and this code are
+// susceptible to bugs when running on a file system where
+// `nFileIndex{Low,High}` are not unique.
+//
+// 2. LLVM has a bug where they fetch the id of a file and continue to use
+// it even after the handle has been closed, so that uniqueness is no
+// longer guaranteed (when `nFileIndex{Low,High}` are unique).
+// bug report: http://lists.llvm.org/pipermail/llvm-bugs/2014-December/037218.html
+//
+// All said and done, checking whether two files are the same on Windows
+// seems quite tricky. Moreover, even if the code is technically incorrect,
+// it seems like the chances of actually observing incorrect behavior are
+// extremely small. Nevertheless, we mitigate this by checking size too.
+//
+// In the case where this code is erroneous, two files will be reported
+// as equivalent when they are in fact distinct. This will cause the loop
+// detection code to report a false positive, which will prevent descending
+// into the offending directory. As far as failure modes goes, this isn't
+// that bad.
+
+#[derive(Debug)]
+pub struct Handle {
+ kind: HandleKind,
+ key: Option<Key>,
+}
+
+#[derive(Debug)]
+enum HandleKind {
+ /// Used when opening a file or acquiring ownership of a file.
+ Owned(winutil::Handle),
+ /// Used for stdio.
+ Borrowed(winutil::HandleRef),
+}
+
+#[derive(Debug, Eq, PartialEq, Hash)]
+struct Key {
+ volume: u64,
+ index: u64,
+}
+
+impl Eq for Handle {}
+
+impl PartialEq for Handle {
+ fn eq(&self, other: &Handle) -> bool {
+ // Need this branch to satisfy `Eq` since `Handle`s with
+ // `key.is_none()` wouldn't otherwise.
+ if self as *const Handle == other as *const Handle {
+ return true;
+ } else if self.key.is_none() || other.key.is_none() {
+ return false;
+ }
+ self.key == other.key
+ }
+}
+
+impl AsRawHandle for crate::Handle {
+ fn as_raw_handle(&self) -> RawHandle {
+ match self.0.kind {
+ HandleKind::Owned(ref h) => h.as_raw_handle(),
+ HandleKind::Borrowed(ref h) => h.as_raw_handle(),
+ }
+ }
+}
+
+impl IntoRawHandle for crate::Handle {
+ fn into_raw_handle(self) -> RawHandle {
+ match self.0.kind {
+ HandleKind::Owned(h) => h.into_raw_handle(),
+ HandleKind::Borrowed(h) => h.as_raw_handle(),
+ }
+ }
+}
+
+impl Hash for Handle {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.key.hash(state);
+ }
+}
+
+impl Handle {
+ pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
+ let h = winutil::Handle::from_path_any(p)?;
+ let info = winutil::file::information(&h)?;
+ Ok(Handle::from_info(HandleKind::Owned(h), info))
+ }
+
+ pub fn from_file(file: File) -> io::Result<Handle> {
+ let h = winutil::Handle::from_file(file);
+ let info = winutil::file::information(&h)?;
+ Ok(Handle::from_info(HandleKind::Owned(h), info))
+ }
+
+ fn from_std_handle(h: winutil::HandleRef) -> io::Result<Handle> {
+ match winutil::file::information(&h) {
+ Ok(info) => Ok(Handle::from_info(HandleKind::Borrowed(h), info)),
+ // In a Windows console, if there is no pipe attached to a STD
+ // handle, then GetFileInformationByHandle will return an error.
+ // We don't really care. The only thing we care about is that
+ // this handle is never equivalent to any other handle, which is
+ // accomplished by setting key to None.
+ Err(_) => Ok(Handle { kind: HandleKind::Borrowed(h), key: None }),
+ }
+ }
+
+ fn from_info(
+ kind: HandleKind,
+ info: winutil::file::Information,
+ ) -> Handle {
+ Handle {
+ kind: kind,
+ key: Some(Key {
+ volume: info.volume_serial_number(),
+ index: info.file_index(),
+ }),
+ }
+ }
+
+ pub fn stdin() -> io::Result<Handle> {
+ Handle::from_std_handle(winutil::HandleRef::stdin())
+ }
+
+ pub fn stdout() -> io::Result<Handle> {
+ Handle::from_std_handle(winutil::HandleRef::stdout())
+ }
+
+ pub fn stderr() -> io::Result<Handle> {
+ Handle::from_std_handle(winutil::HandleRef::stderr())
+ }
+
+ pub fn as_file(&self) -> &File {
+ match self.kind {
+ HandleKind::Owned(ref h) => h.as_file(),
+ HandleKind::Borrowed(ref h) => h.as_file(),
+ }
+ }
+
+ pub fn as_file_mut(&mut self) -> &mut File {
+ match self.kind {
+ HandleKind::Owned(ref mut h) => h.as_file_mut(),
+ HandleKind::Borrowed(ref mut h) => h.as_file_mut(),
+ }
+ }
+}