2 use std
::hash
::{Hash, Hasher}
;
4 use std
::os
::windows
::io
::{
5 AsRawHandle
, IntoRawHandle
, RawHandle
,
9 use winapi_util
as winutil
;
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
16 // See the docs and remarks on MSDN:
17 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
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`
23 // https://msdn.microsoft.com/en-us/library/windows/desktop/hh802691(v=vs.85).aspx
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.
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.
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
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.
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
60 /// Used when opening a file or acquiring ownership of a file.
61 Owned(winutil
::Handle
),
63 Borrowed(winutil
::HandleRef
),
66 #[derive(Debug, Eq, PartialEq, Hash)]
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
{
80 } else if self.key
.is_none() || other
.key
.is_none() {
87 impl AsRawHandle
for ::Handle
{
88 fn as_raw_handle(&self) -> RawHandle
{
90 HandleKind
::Owned(ref h
) => h
.as_raw_handle(),
91 HandleKind
::Borrowed(ref h
) => h
.as_raw_handle(),
96 impl IntoRawHandle
for ::Handle
{
97 fn into_raw_handle(self) -> RawHandle
{
99 HandleKind
::Owned(h
) => h
.into_raw_handle(),
100 HandleKind
::Borrowed(h
) => h
.as_raw_handle(),
105 impl Hash
for Handle
{
106 fn hash
<H
: Hasher
>(&self, state
: &mut H
) {
107 self.key
.hash(state
);
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
))
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
))
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 }
),
138 info
: winutil
::file
::Information
,
143 volume
: info
.volume_serial_number(),
144 index
: info
.file_index(),
149 pub fn stdin() -> io
::Result
<Handle
> {
150 Handle
::from_std_handle(winutil
::HandleRef
::stdin())
153 pub fn stdout() -> io
::Result
<Handle
> {
154 Handle
::from_std_handle(winutil
::HandleRef
::stdout())
157 pub fn stderr() -> io
::Result
<Handle
> {
158 Handle
::from_std_handle(winutil
::HandleRef
::stderr())
161 pub fn as_file(&self) -> &File
{
163 HandleKind
::Owned(ref h
) => h
.as_file(),
164 HandleKind
::Borrowed(ref h
) => h
.as_file(),
168 pub fn as_file_mut(&mut self) -> &mut File
{
170 HandleKind
::Owned(ref mut h
) => h
.as_file_mut(),
171 HandleKind
::Borrowed(ref mut h
) => h
.as_file_mut(),