]> git.proxmox.com Git - rustc.git/blob - vendor/same-file/src/win.rs
New upstream version 1.32.0~beta.2+dfsg1
[rustc.git] / vendor / same-file / src / win.rs
1 use std::fs::File;
2 use std::hash::{Hash, Hasher};
3 use std::io;
4 use std::os::windows::io::{
5 AsRawHandle, IntoRawHandle, RawHandle,
6 };
7 use std::path::Path;
8
9 use winapi_util as winutil;
10
11 // For correctness, it is critical that both file handles remain open while
12 // their attributes are checked for equality. In particular, the file index
13 // numbers on a Windows stat object are not guaranteed to remain stable over
14 // time.
15 //
16 // See the docs and remarks on MSDN:
17 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
18 //
19 // It gets worse. It appears that the index numbers are not always
20 // guaranteed to be unique. Namely, ReFS uses 128 bit numbers for unique
21 // identifiers. This requires a distinct syscall to get `FILE_ID_INFO`
22 // documented here:
23 // https://msdn.microsoft.com/en-us/library/windows/desktop/hh802691(v=vs.85).aspx
24 //
25 // It seems straight-forward enough to modify this code to use
26 // `FILE_ID_INFO` when available (minimum Windows Server 2012), but I don't
27 // have access to such Windows machines.
28 //
29 // Two notes.
30 //
31 // 1. Java's NIO uses the approach implemented here and appears to ignore
32 // `FILE_ID_INFO` altogether. So Java's NIO and this code are
33 // susceptible to bugs when running on a file system where
34 // `nFileIndex{Low,High}` are not unique.
35 //
36 // 2. LLVM has a bug where they fetch the id of a file and continue to use
37 // it even after the handle has been closed, so that uniqueness is no
38 // longer guaranteed (when `nFileIndex{Low,High}` are unique).
39 // bug report: http://lists.llvm.org/pipermail/llvm-bugs/2014-December/037218.html
40 //
41 // All said and done, checking whether two files are the same on Windows
42 // seems quite tricky. Moreover, even if the code is technically incorrect,
43 // it seems like the chances of actually observing incorrect behavior are
44 // extremely small. Nevertheless, we mitigate this by checking size too.
45 //
46 // In the case where this code is erroneous, two files will be reported
47 // as equivalent when they are in fact distinct. This will cause the loop
48 // detection code to report a false positive, which will prevent descending
49 // into the offending directory. As far as failure modes goes, this isn't
50 // that bad.
51
52 #[derive(Debug)]
53 pub struct Handle {
54 kind: HandleKind,
55 key: Option<Key>,
56 }
57
58 #[derive(Debug)]
59 enum HandleKind {
60 /// Used when opening a file or acquiring ownership of a file.
61 Owned(winutil::Handle),
62 /// Used for stdio.
63 Borrowed(winutil::HandleRef),
64 }
65
66 #[derive(Debug, Eq, PartialEq, Hash)]
67 struct Key {
68 volume: u64,
69 index: u64,
70 }
71
72 impl Eq for Handle {}
73
74 impl PartialEq for Handle {
75 fn eq(&self, other: &Handle) -> bool {
76 // Need this branch to satisfy `Eq` since `Handle`s with
77 // `key.is_none()` wouldn't otherwise.
78 if self as *const Handle == other as *const Handle {
79 return true;
80 } else if self.key.is_none() || other.key.is_none() {
81 return false;
82 }
83 self.key == other.key
84 }
85 }
86
87 impl AsRawHandle for ::Handle {
88 fn as_raw_handle(&self) -> RawHandle {
89 match self.0.kind {
90 HandleKind::Owned(ref h) => h.as_raw_handle(),
91 HandleKind::Borrowed(ref h) => h.as_raw_handle(),
92 }
93 }
94 }
95
96 impl IntoRawHandle for ::Handle {
97 fn into_raw_handle(self) -> RawHandle {
98 match self.0.kind {
99 HandleKind::Owned(h) => h.into_raw_handle(),
100 HandleKind::Borrowed(h) => h.as_raw_handle(),
101 }
102 }
103 }
104
105 impl Hash for Handle {
106 fn hash<H: Hasher>(&self, state: &mut H) {
107 self.key.hash(state);
108 }
109 }
110
111 impl Handle {
112 pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
113 let h = winutil::Handle::from_path_any(p)?;
114 let info = winutil::file::information(&h)?;
115 Ok(Handle::from_info(HandleKind::Owned(h), info))
116 }
117
118 pub fn from_file(file: File) -> io::Result<Handle> {
119 let h = winutil::Handle::from_file(file);
120 let info = winutil::file::information(&h)?;
121 Ok(Handle::from_info(HandleKind::Owned(h), info))
122 }
123
124 fn from_std_handle(h: winutil::HandleRef) -> io::Result<Handle> {
125 match winutil::file::information(&h) {
126 Ok(info) => Ok(Handle::from_info(HandleKind::Borrowed(h), info)),
127 // In a Windows console, if there is no pipe attached to a STD
128 // handle, then GetFileInformationByHandle will return an error.
129 // We don't really care. The only thing we care about is that
130 // this handle is never equivalent to any other handle, which is
131 // accomplished by setting key to None.
132 Err(_) => Ok(Handle { kind: HandleKind::Borrowed(h), key: None }),
133 }
134 }
135
136 fn from_info(
137 kind: HandleKind,
138 info: winutil::file::Information,
139 ) -> Handle {
140 Handle {
141 kind: kind,
142 key: Some(Key {
143 volume: info.volume_serial_number(),
144 index: info.file_index(),
145 }),
146 }
147 }
148
149 pub fn stdin() -> io::Result<Handle> {
150 Handle::from_std_handle(winutil::HandleRef::stdin())
151 }
152
153 pub fn stdout() -> io::Result<Handle> {
154 Handle::from_std_handle(winutil::HandleRef::stdout())
155 }
156
157 pub fn stderr() -> io::Result<Handle> {
158 Handle::from_std_handle(winutil::HandleRef::stderr())
159 }
160
161 pub fn as_file(&self) -> &File {
162 match self.kind {
163 HandleKind::Owned(ref h) => h.as_file(),
164 HandleKind::Borrowed(ref h) => h.as_file(),
165 }
166 }
167
168 pub fn as_file_mut(&mut self) -> &mut File {
169 match self.kind {
170 HandleKind::Owned(ref mut h) => h.as_file_mut(),
171 HandleKind::Borrowed(ref mut h) => h.as_file_mut(),
172 }
173 }
174 }