1 use std
::fs
::{File, OpenOptions}
;
4 use std
::os
::windows
::fs
::OpenOptionsExt
;
5 use std
::os
::windows
::io
::{
6 AsRawHandle
, FromRawHandle
, IntoRawHandle
, RawHandle
,
10 use kernel32
::{GetFileInformationByHandle, GetStdHandle}
;
11 use winapi
::fileapi
::BY_HANDLE_FILE_INFORMATION
;
12 use winapi
::minwindef
::DWORD
;
13 use winapi
::winbase
::{
14 FILE_FLAG_BACKUP_SEMANTICS
,
15 STD_INPUT_HANDLE
, STD_OUTPUT_HANDLE
, STD_ERROR_HANDLE
,
18 // For correctness, it is critical that both file handles remain open while
19 // their attributes are checked for equality. In particular, the file index
20 // numbers on a Windows stat object are not guaranteed to remain stable over
23 // See the docs and remarks on MSDN:
24 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx
26 // It gets worse. It appears that the index numbers are not always
27 // guaranteed to be unqiue. Namely, ReFS uses 128 bit numbers for unique
28 // identifiers. This requires a distinct syscall to get `FILE_ID_INFO`
30 // https://msdn.microsoft.com/en-us/library/windows/desktop/hh802691(v=vs.85).aspx
32 // It seems straight-forward enough to modify this code to use
33 // `FILE_ID_INFO` when available (minimum Windows Server 2012), but I don't
34 // have access to such Windows machines.
38 // 1. Java's NIO uses the approach implemented here and appears to ignore
39 // `FILE_ID_INFO` altogether. So Java's NIO and this code are
40 // susceptible to bugs when running on a file system where
41 // `nFileIndex{Low,High}` are not unique.
43 // 2. LLVM has a bug where they fetch the id of a file and continue to use
44 // it even after the handle has been closed, so that uniqueness is no
45 // longer guaranteed (when `nFileIndex{Low,High}` are unique).
46 // bug report: http://lists.llvm.org/pipermail/llvm-bugs/2014-December/037218.html
48 // All said and done, checking whether two files are the same on Windows
49 // seems quite tricky. Moreover, even if the code is technically incorrect,
50 // it seems like the chances of actually observing incorrect behavior are
51 // extremely small. Nevertheless, we mitigate this by checking size too.
53 // In the case where this code is erroneous, two files will be reported
54 // as equivalent when they are in fact distinct. This will cause the loop
55 // detection code to report a false positive, which will prevent descending
56 // into the offending directory. As far as failure modes goes, this isn't
62 // If is_std is true, then we don't drop the corresponding File since it
63 // will close the handle.
68 #[derive(Debug, Eq, PartialEq)]
75 impl Drop
for Handle
{
78 self.file
.take().unwrap().into_raw_handle();
85 impl PartialEq
for Handle
{
86 fn eq(&self, other
: &Handle
) -> bool
{
87 if self.key
.is_none() || other
.key
.is_none() {
94 impl AsRawHandle
for ::Handle
{
95 fn as_raw_handle(&self) -> RawHandle
{
96 self.0.file
.as_ref().take().unwrap().as_raw_handle()
100 impl IntoRawHandle
for ::Handle
{
101 fn into_raw_handle(mut self) -> RawHandle
{
102 self.0.file
.take().unwrap().into_raw_handle()
107 pub fn from_path
<P
: AsRef
<Path
>>(p
: P
) -> io
::Result
<Handle
> {
108 let file
= try
!(OpenOptions
::new()
110 // Necessary in order to support opening directory paths.
111 .custom_flags(FILE_FLAG_BACKUP_SEMANTICS
)
113 Handle
::from_file(file
)
116 pub fn from_file(file
: File
) -> io
::Result
<Handle
> {
117 file_info(&file
).map(|info
| Handle
::from_file_info(file
, false, info
))
120 fn from_std_handle(file
: File
) -> io
::Result
<Handle
> {
121 match file_info(&file
) {
122 Ok(info
) => Ok(Handle
::from_file_info(file
, true, info
)),
123 // In a Windows console, if there is no pipe attached to a STD
124 // handle, then GetFileInformationByHandle will return an error.
125 // We don't really care. The only thing we care about is that
126 // this handle is never equivalent to any other handle, which is
127 // accomplished by setting key to None.
128 Err(_
) => Ok(Handle { file: Some(file), is_std: true, key: None }
),
135 info
: BY_HANDLE_FILE_INFORMATION
,
141 volume
: info
.dwVolumeSerialNumber
,
142 idx_high
: info
.nFileIndexHigh
,
143 idx_low
: info
.nFileIndexLow
,
148 pub fn stdin() -> io
::Result
<Handle
> {
149 Handle
::from_std_handle(unsafe {
150 File
::from_raw_handle(GetStdHandle(STD_INPUT_HANDLE
))
154 pub fn stdout() -> io
::Result
<Handle
> {
155 Handle
::from_std_handle(unsafe {
156 File
::from_raw_handle(GetStdHandle(STD_OUTPUT_HANDLE
))
160 pub fn stderr() -> io
::Result
<Handle
> {
161 Handle
::from_std_handle(unsafe {
162 File
::from_raw_handle(GetStdHandle(STD_ERROR_HANDLE
))
166 pub fn as_file(&self) -> &File
{
167 self.file
.as_ref().take().unwrap()
170 pub fn as_file_mut(&mut self) -> &mut File
{
171 self.file
.as_mut().take().unwrap()
175 fn file_info(file
: &File
) -> io
::Result
<BY_HANDLE_FILE_INFORMATION
> {
176 let (r
, info
) = unsafe {
177 let mut info
: BY_HANDLE_FILE_INFORMATION
= mem
::zeroed();
178 (GetFileInformationByHandle(file
.as_raw_handle(), &mut info
), info
)
181 Err(io
::Error
::last_os_error())