diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/same-file | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/same-file')
-rw-r--r-- | third_party/rust/same-file/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/same-file/COPYING | 3 | ||||
-rw-r--r-- | third_party/rust/same-file/Cargo.lock | 48 | ||||
-rw-r--r-- | third_party/rust/same-file/Cargo.toml | 29 | ||||
-rw-r--r-- | third_party/rust/same-file/LICENSE-MIT | 21 | ||||
-rw-r--r-- | third_party/rust/same-file/README.md | 49 | ||||
-rw-r--r-- | third_party/rust/same-file/UNLICENSE | 24 | ||||
-rw-r--r-- | third_party/rust/same-file/examples/is_same_file.rs | 11 | ||||
-rw-r--r-- | third_party/rust/same-file/examples/is_stderr.rs | 31 | ||||
-rw-r--r-- | third_party/rust/same-file/rustfmt.toml | 2 | ||||
-rw-r--r-- | third_party/rust/same-file/src/lib.rs | 572 | ||||
-rw-r--r-- | third_party/rust/same-file/src/unix.rs | 112 | ||||
-rw-r--r-- | third_party/rust/same-file/src/unknown.rs | 52 | ||||
-rw-r--r-- | third_party/rust/same-file/src/win.rs | 172 |
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(), + } + } +} |