summaryrefslogtreecommitdiffstats
path: root/third_party/rust/same-file/src/win.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/same-file/src/win.rs
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/same-file/src/win.rs')
-rw-r--r--third_party/rust/same-file/src/win.rs209
1 files changed, 209 insertions, 0 deletions
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..30966d52a5
--- /dev/null
+++ b/third_party/rust/same-file/src/win.rs
@@ -0,0 +1,209 @@
+use std::fs::{File, OpenOptions};
+use std::hash::{Hash, Hasher};
+use std::io;
+use std::mem;
+use std::os::windows::fs::OpenOptionsExt;
+use std::os::windows::io::{
+ AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle,
+};
+use std::path::Path;
+
+use winapi::shared::minwindef::DWORD;
+use winapi::um::fileapi::{
+ BY_HANDLE_FILE_INFORMATION,
+ GetFileInformationByHandle,
+};
+use winapi::um::processenv::GetStdHandle;
+use winapi::um::winbase::{
+ FILE_FLAG_BACKUP_SEMANTICS,
+ STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE,
+};
+
+// 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 {
+ 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,
+ key: Option<Key>,
+}
+
+#[derive(Debug, Eq, PartialEq, Hash)]
+struct Key {
+ volume: DWORD,
+ idx_high: DWORD,
+ idx_low: DWORD,
+}
+
+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_handle();
+ }
+ }
+}
+
+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 ::Handle {
+ fn as_raw_handle(&self) -> RawHandle {
+ // 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_handle()
+ }
+}
+
+impl IntoRawHandle for ::Handle {
+ fn into_raw_handle(mut self) -> RawHandle {
+ // 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_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 file = OpenOptions::new()
+ .read(true)
+ // Necessary in order to support opening directory paths.
+ .custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
+ .open(p)?;
+ Handle::from_file(file)
+ }
+
+ pub fn from_file(file: File) -> io::Result<Handle> {
+ file_info(&file).map(|info| Handle::from_file_info(file, false, info))
+ }
+
+ fn from_std_handle(file: File) -> io::Result<Handle> {
+ match file_info(&file) {
+ Ok(info) => Ok(Handle::from_file_info(file, true, 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 { file: Some(file), is_std: true, key: None }),
+ }
+ }
+
+ fn from_file_info(
+ file: File,
+ is_std: bool,
+ info: BY_HANDLE_FILE_INFORMATION,
+ ) -> Handle {
+ Handle {
+ file: Some(file),
+ is_std: is_std,
+ key: Some(Key {
+ volume: info.dwVolumeSerialNumber,
+ idx_high: info.nFileIndexHigh,
+ idx_low: info.nFileIndexLow,
+ }),
+ }
+ }
+
+ pub fn stdin() -> io::Result<Handle> {
+ Handle::from_std_handle(unsafe {
+ File::from_raw_handle(GetStdHandle(STD_INPUT_HANDLE))
+ })
+ }
+
+ pub fn stdout() -> io::Result<Handle> {
+ Handle::from_std_handle(unsafe {
+ File::from_raw_handle(GetStdHandle(STD_OUTPUT_HANDLE))
+ })
+ }
+
+ pub fn stderr() -> io::Result<Handle> {
+ Handle::from_std_handle(unsafe {
+ File::from_raw_handle(GetStdHandle(STD_ERROR_HANDLE))
+ })
+ }
+
+ 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()
+ }
+}
+
+fn file_info(file: &File) -> io::Result<BY_HANDLE_FILE_INFORMATION> {
+ let (r, info) = unsafe {
+ let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed();
+ (GetFileInformationByHandle(file.as_raw_handle(), &mut info), info)
+ };
+ if r == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(info)
+ }
+}