1 use crate::os
::windows
::prelude
::*;
3 use crate::ffi
::OsString
;
5 use crate::io
::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}
;
6 use crate::mem
::{self, MaybeUninit}
;
7 use crate::os
::windows
::io
::{AsHandle, BorrowedHandle}
;
8 use crate::path
::{Path, PathBuf}
;
12 use crate::sys
::handle
::Handle
;
13 use crate::sys
::time
::SystemTime
;
14 use crate::sys
::{c, cvt, Align8}
;
15 use crate::sys_common
::{AsInner, FromInner, IntoInner}
;
18 use super::path
::maybe_verbatim
;
28 creation_time
: c
::FILETIME
,
29 last_access_time
: c
::FILETIME
,
30 last_write_time
: c
::FILETIME
,
32 reparse_tag
: c
::DWORD
,
33 volume_serial_number
: Option
<u32>,
34 number_of_links
: Option
<u32>,
35 file_index
: Option
<u64>,
38 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
41 reparse_tag
: c
::DWORD
,
45 handle
: FindNextFileHandle
,
47 first
: Option
<c
::WIN32_FIND_DATAW
>,
50 struct FindNextFileHandle(c
::HANDLE
);
52 unsafe impl Send
for FindNextFileHandle {}
53 unsafe impl Sync
for FindNextFileHandle {}
57 data
: c
::WIN32_FIND_DATAW
,
60 unsafe impl Send
for OpenOptions {}
61 unsafe impl Sync
for OpenOptions {}
63 #[derive(Clone, Debug)]
64 pub struct OpenOptions
{
74 access_mode
: Option
<c
::DWORD
>,
77 security_qos_flags
: c
::DWORD
,
78 security_attributes
: c
::LPSECURITY_ATTRIBUTES
,
81 #[derive(Clone, PartialEq, Eq, Debug)]
82 pub struct FilePermissions
{
86 #[derive(Copy, Clone, Debug, Default)]
87 pub struct FileTimes
{
88 accessed
: Option
<c
::FILETIME
>,
89 modified
: Option
<c
::FILETIME
>,
93 pub struct DirBuilder
;
95 impl fmt
::Debug
for ReadDir
{
96 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
97 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
98 // Thus the result will be e g 'ReadDir("C:\")'
99 fmt
::Debug
::fmt(&*self.root
, f
)
103 impl Iterator
for ReadDir
{
104 type Item
= io
::Result
<DirEntry
>;
105 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
106 if let Some(first
) = self.first
.take() {
107 if let Some(e
) = DirEntry
::new(&self.root
, &first
) {
112 let mut wfd
= mem
::zeroed();
114 if c
::FindNextFileW(self.handle
.0, &mut wfd
) == 0 {
115 if c
::GetLastError() == c
::ERROR_NO_MORE_FILES
{
118 return Some(Err(Error
::last_os_error()));
121 if let Some(e
) = DirEntry
::new(&self.root
, &wfd
) {
129 impl Drop
for FindNextFileHandle
{
131 let r
= unsafe { c::FindClose(self.0) }
;
132 debug_assert
!(r
!= 0);
137 fn new(root
: &Arc
<PathBuf
>, wfd
: &c
::WIN32_FIND_DATAW
) -> Option
<DirEntry
> {
138 match &wfd
.cFileName
[0..3] {
139 // check for '.' and '..'
140 &[46, 0, ..] | &[46, 46, 0, ..] => return None
,
144 Some(DirEntry { root: root.clone(), data: *wfd }
)
147 pub fn path(&self) -> PathBuf
{
148 self.root
.join(&self.file_name())
151 pub fn file_name(&self) -> OsString
{
152 let filename
= super::truncate_utf16_at_nul(&self.data
.cFileName
);
153 OsString
::from_wide(filename
)
156 pub fn file_type(&self) -> io
::Result
<FileType
> {
158 self.data
.dwFileAttributes
,
159 /* reparse_tag = */ self.data
.dwReserved0
,
163 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
169 pub fn new() -> OpenOptions
{
181 share_mode
: c
::FILE_SHARE_READ
| c
::FILE_SHARE_WRITE
| c
::FILE_SHARE_DELETE
,
183 security_qos_flags
: 0,
184 security_attributes
: ptr
::null_mut(),
188 pub fn read(&mut self, read
: bool
) {
191 pub fn write(&mut self, write
: bool
) {
194 pub fn append(&mut self, append
: bool
) {
195 self.append
= append
;
197 pub fn truncate(&mut self, truncate
: bool
) {
198 self.truncate
= truncate
;
200 pub fn create(&mut self, create
: bool
) {
201 self.create
= create
;
203 pub fn create_new(&mut self, create_new
: bool
) {
204 self.create_new
= create_new
;
207 pub fn custom_flags(&mut self, flags
: u32) {
208 self.custom_flags
= flags
;
210 pub fn access_mode(&mut self, access_mode
: u32) {
211 self.access_mode
= Some(access_mode
);
213 pub fn share_mode(&mut self, share_mode
: u32) {
214 self.share_mode
= share_mode
;
216 pub fn attributes(&mut self, attrs
: u32) {
217 self.attributes
= attrs
;
219 pub fn security_qos_flags(&mut self, flags
: u32) {
220 // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can
221 // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.
222 self.security_qos_flags
= flags
| c
::SECURITY_SQOS_PRESENT
;
224 pub fn security_attributes(&mut self, attrs
: c
::LPSECURITY_ATTRIBUTES
) {
225 self.security_attributes
= attrs
;
228 fn get_access_mode(&self) -> io
::Result
<c
::DWORD
> {
229 const ERROR_INVALID_PARAMETER
: i32 = 87;
231 match (self.read
, self.write
, self.append
, self.access_mode
) {
232 (.., Some(mode
)) => Ok(mode
),
233 (true, false, false, None
) => Ok(c
::GENERIC_READ
),
234 (false, true, false, None
) => Ok(c
::GENERIC_WRITE
),
235 (true, true, false, None
) => Ok(c
::GENERIC_READ
| c
::GENERIC_WRITE
),
236 (false, _
, true, None
) => Ok(c
::FILE_GENERIC_WRITE
& !c
::FILE_WRITE_DATA
),
237 (true, _
, true, None
) => {
238 Ok(c
::GENERIC_READ
| (c
::FILE_GENERIC_WRITE
& !c
::FILE_WRITE_DATA
))
240 (false, false, false, None
) => Err(Error
::from_raw_os_error(ERROR_INVALID_PARAMETER
)),
244 fn get_creation_mode(&self) -> io
::Result
<c
::DWORD
> {
245 const ERROR_INVALID_PARAMETER
: i32 = 87;
247 match (self.write
, self.append
) {
250 if self.truncate
|| self.create
|| self.create_new
{
251 return Err(Error
::from_raw_os_error(ERROR_INVALID_PARAMETER
));
255 if self.truncate
&& !self.create_new
{
256 return Err(Error
::from_raw_os_error(ERROR_INVALID_PARAMETER
));
261 Ok(match (self.create
, self.truncate
, self.create_new
) {
262 (false, false, false) => c
::OPEN_EXISTING
,
263 (true, false, false) => c
::OPEN_ALWAYS
,
264 (false, true, false) => c
::TRUNCATE_EXISTING
,
265 (true, true, false) => c
::CREATE_ALWAYS
,
266 (_
, _
, true) => c
::CREATE_NEW
,
270 fn get_flags_and_attributes(&self) -> c
::DWORD
{
273 | self.security_qos_flags
274 | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT }
else { 0 }
279 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
280 let path
= maybe_verbatim(path
)?
;
281 let handle
= unsafe {
284 opts
.get_access_mode()?
,
286 opts
.security_attributes
,
287 opts
.get_creation_mode()?
,
288 opts
.get_flags_and_attributes(),
292 if let Ok(handle
) = handle
.try_into() {
293 Ok(File { handle: Handle::from_inner(handle) }
)
295 Err(Error
::last_os_error())
299 pub fn fsync(&self) -> io
::Result
<()> {
300 cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) }
)?
;
304 pub fn datasync(&self) -> io
::Result
<()> {
308 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
309 let mut info
= c
::FILE_END_OF_FILE_INFO { EndOfFile: size as c::LARGE_INTEGER }
;
310 let size
= mem
::size_of_val(&info
);
312 c
::SetFileInformationByHandle(
313 self.handle
.as_raw_handle(),
314 c
::FileEndOfFileInfo
,
315 &mut info
as *mut _
as *mut _
,
322 #[cfg(not(target_vendor = "uwp"))]
323 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
325 let mut info
: c
::BY_HANDLE_FILE_INFORMATION
= mem
::zeroed();
326 cvt(c
::GetFileInformationByHandle(self.handle
.as_raw_handle(), &mut info
))?
;
327 let mut reparse_tag
= 0;
328 if info
.dwFileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
329 let mut attr_tag
: c
::FILE_ATTRIBUTE_TAG_INFO
= mem
::zeroed();
330 cvt(c
::GetFileInformationByHandleEx(
331 self.handle
.as_raw_handle(),
332 c
::FileAttributeTagInfo
,
333 ptr
::addr_of_mut
!(attr_tag
).cast(),
334 mem
::size_of
::<c
::FILE_ATTRIBUTE_TAG_INFO
>().try_into().unwrap(),
336 if attr_tag
.FileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
337 reparse_tag
= attr_tag
.ReparseTag
;
341 attributes
: info
.dwFileAttributes
,
342 creation_time
: info
.ftCreationTime
,
343 last_access_time
: info
.ftLastAccessTime
,
344 last_write_time
: info
.ftLastWriteTime
,
345 file_size
: (info
.nFileSizeLow
as u64) | ((info
.nFileSizeHigh
as u64) << 32),
347 volume_serial_number
: Some(info
.dwVolumeSerialNumber
),
348 number_of_links
: Some(info
.nNumberOfLinks
),
350 (info
.nFileIndexLow
as u64) | ((info
.nFileIndexHigh
as u64) << 32),
356 #[cfg(target_vendor = "uwp")]
357 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
359 let mut info
: c
::FILE_BASIC_INFO
= mem
::zeroed();
360 let size
= mem
::size_of_val(&info
);
361 cvt(c
::GetFileInformationByHandleEx(
362 self.handle
.as_raw_handle(),
364 &mut info
as *mut _
as *mut libc
::c_void
,
367 let mut attr
= FileAttr
{
368 attributes
: info
.FileAttributes
,
369 creation_time
: c
::FILETIME
{
370 dwLowDateTime
: info
.CreationTime
as c
::DWORD
,
371 dwHighDateTime
: (info
.CreationTime
>> 32) as c
::DWORD
,
373 last_access_time
: c
::FILETIME
{
374 dwLowDateTime
: info
.LastAccessTime
as c
::DWORD
,
375 dwHighDateTime
: (info
.LastAccessTime
>> 32) as c
::DWORD
,
377 last_write_time
: c
::FILETIME
{
378 dwLowDateTime
: info
.LastWriteTime
as c
::DWORD
,
379 dwHighDateTime
: (info
.LastWriteTime
>> 32) as c
::DWORD
,
383 volume_serial_number
: None
,
384 number_of_links
: None
,
387 let mut info
: c
::FILE_STANDARD_INFO
= mem
::zeroed();
388 let size
= mem
::size_of_val(&info
);
389 cvt(c
::GetFileInformationByHandleEx(
390 self.handle
.as_raw_handle(),
392 &mut info
as *mut _
as *mut libc
::c_void
,
395 attr
.file_size
= info
.AllocationSize
as u64;
396 attr
.number_of_links
= Some(info
.NumberOfLinks
);
397 if attr
.file_type().is_reparse_point() {
398 let mut attr_tag
: c
::FILE_ATTRIBUTE_TAG_INFO
= mem
::zeroed();
399 cvt(c
::GetFileInformationByHandleEx(
400 self.handle
.as_raw_handle(),
401 c
::FileAttributeTagInfo
,
402 ptr
::addr_of_mut
!(attr_tag
).cast(),
403 mem
::size_of
::<c
::FILE_ATTRIBUTE_TAG_INFO
>().try_into().unwrap(),
405 if attr_tag
.FileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
406 attr
.reparse_tag
= attr_tag
.ReparseTag
;
413 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
414 self.handle
.read(buf
)
417 pub fn read_vectored(&self, bufs
: &mut [IoSliceMut
<'_
>]) -> io
::Result
<usize> {
418 self.handle
.read_vectored(bufs
)
422 pub fn is_read_vectored(&self) -> bool
{
423 self.handle
.is_read_vectored()
426 pub fn read_at(&self, buf
: &mut [u8], offset
: u64) -> io
::Result
<usize> {
427 self.handle
.read_at(buf
, offset
)
430 pub fn read_buf(&self, cursor
: BorrowedCursor
<'_
>) -> io
::Result
<()> {
431 self.handle
.read_buf(cursor
)
434 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
435 self.handle
.write(buf
)
438 pub fn write_vectored(&self, bufs
: &[IoSlice
<'_
>]) -> io
::Result
<usize> {
439 self.handle
.write_vectored(bufs
)
443 pub fn is_write_vectored(&self) -> bool
{
444 self.handle
.is_write_vectored()
447 pub fn write_at(&self, buf
: &[u8], offset
: u64) -> io
::Result
<usize> {
448 self.handle
.write_at(buf
, offset
)
451 pub fn flush(&self) -> io
::Result
<()> {
455 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
456 let (whence
, pos
) = match pos
{
457 // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
459 SeekFrom
::Start(n
) => (c
::FILE_BEGIN
, n
as i64),
460 SeekFrom
::End(n
) => (c
::FILE_END
, n
),
461 SeekFrom
::Current(n
) => (c
::FILE_CURRENT
, n
),
463 let pos
= pos
as c
::LARGE_INTEGER
;
465 cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) }
)?
;
469 pub fn duplicate(&self) -> io
::Result
<File
> {
470 Ok(Self { handle: self.handle.try_clone()? }
)
473 // NB: returned pointer is derived from `space`, and has provenance to
474 // match. A raw pointer is returned rather than a reference in order to
475 // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
478 space
: &mut Align8
<[MaybeUninit
<u8>]>,
479 ) -> io
::Result
<(c
::DWORD
, *const c
::REPARSE_DATA_BUFFER
)> {
483 // Grab this in advance to avoid it invalidating the pointer
484 // we get from `space.0.as_mut_ptr()`.
485 let len
= space
.0.len();
487 self.handle
.as_raw_handle(),
488 c
::FSCTL_GET_REPARSE_POINT
,
491 space
.0.as_mut_ptr().cast(),
497 const _
: () = assert
!(core
::mem
::align_of
::<c
::REPARSE_DATA_BUFFER
>() <= 8);
498 Ok((bytes
, space
.0.as_ptr().cast
::<c
::REPARSE_DATA_BUFFER
>()))
502 fn readlink(&self) -> io
::Result
<PathBuf
> {
503 let mut space
= Align8([MaybeUninit
::<u8>::uninit(); c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
]);
504 let (_bytes
, buf
) = self.reparse_point(&mut space
)?
;
506 let (path_buffer
, subst_off
, subst_len
, relative
) = match (*buf
).ReparseTag
{
507 c
::IO_REPARSE_TAG_SYMLINK
=> {
508 let info
: *const c
::SYMBOLIC_LINK_REPARSE_BUFFER
=
509 ptr
::addr_of
!((*buf
).rest
).cast();
510 assert
!(info
.is_aligned());
512 ptr
::addr_of
!((*info
).PathBuffer
).cast
::<u16>(),
513 (*info
).SubstituteNameOffset
/ 2,
514 (*info
).SubstituteNameLength
/ 2,
515 (*info
).Flags
& c
::SYMLINK_FLAG_RELATIVE
!= 0,
518 c
::IO_REPARSE_TAG_MOUNT_POINT
=> {
519 let info
: *const c
::MOUNT_POINT_REPARSE_BUFFER
=
520 ptr
::addr_of
!((*buf
).rest
).cast();
521 assert
!(info
.is_aligned());
523 ptr
::addr_of
!((*info
).PathBuffer
).cast
::<u16>(),
524 (*info
).SubstituteNameOffset
/ 2,
525 (*info
).SubstituteNameLength
/ 2,
530 return Err(io
::const_io_error
!(
531 io
::ErrorKind
::Uncategorized
,
532 "Unsupported reparse point type",
536 let subst_ptr
= path_buffer
.add(subst_off
.into());
537 let mut subst
= slice
::from_raw_parts(subst_ptr
, subst_len
as usize);
538 // Absolute paths start with an NT internal namespace prefix `\??\`
539 // We should not let it leak through.
540 if !relative
&& subst
.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
543 Ok(PathBuf
::from(OsString
::from_wide(subst
)))
547 pub fn set_permissions(&self, perm
: FilePermissions
) -> io
::Result
<()> {
548 let mut info
= c
::FILE_BASIC_INFO
{
553 FileAttributes
: perm
.attrs
,
555 let size
= mem
::size_of_val(&info
);
557 c
::SetFileInformationByHandle(
558 self.handle
.as_raw_handle(),
560 &mut info
as *mut _
as *mut _
,
567 pub fn set_times(&self, times
: FileTimes
) -> io
::Result
<()> {
568 let is_zero
= |t
: c
::FILETIME
| t
.dwLowDateTime
== 0 && t
.dwHighDateTime
== 0;
569 if times
.accessed
.map_or(false, is_zero
) || times
.modified
.map_or(false, is_zero
) {
570 return Err(io
::const_io_error
!(
571 io
::ErrorKind
::InvalidInput
,
572 "Cannot set file timestamp to 0",
576 c
::SetFileTime(self.as_handle(), None
, times
.accessed
.as_ref(), times
.modified
.as_ref())
581 /// Get only basic file information such as attributes and file times.
582 fn basic_info(&self) -> io
::Result
<c
::FILE_BASIC_INFO
> {
584 let mut info
: c
::FILE_BASIC_INFO
= mem
::zeroed();
585 let size
= mem
::size_of_val(&info
);
586 cvt(c
::GetFileInformationByHandleEx(
587 self.handle
.as_raw_handle(),
589 &mut info
as *mut _
as *mut libc
::c_void
,
595 /// Delete using POSIX semantics.
597 /// Files will be deleted as soon as the handle is closed. This is supported
598 /// for Windows 10 1607 (aka RS1) and later. However some filesystem
599 /// drivers will not support it even then, e.g. FAT32.
601 /// If the operation is not supported for this filesystem or OS version
602 /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
603 fn posix_delete(&self) -> io
::Result
<()> {
604 let mut info
= c
::FILE_DISPOSITION_INFO_EX
{
605 Flags
: c
::FILE_DISPOSITION_DELETE
606 | c
::FILE_DISPOSITION_POSIX_SEMANTICS
607 | c
::FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE
,
609 let size
= mem
::size_of_val(&info
);
611 c
::SetFileInformationByHandle(
612 self.handle
.as_raw_handle(),
613 c
::FileDispositionInfoEx
,
614 &mut info
as *mut _
as *mut _
,
621 /// Delete a file using win32 semantics. The file won't actually be deleted
622 /// until all file handles are closed. However, marking a file for deletion
623 /// will prevent anyone from opening a new handle to the file.
624 fn win32_delete(&self) -> io
::Result
<()> {
625 let mut info
= c
::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }
;
626 let size
= mem
::size_of_val(&info
);
628 c
::SetFileInformationByHandle(
629 self.handle
.as_raw_handle(),
630 c
::FileDispositionInfo
,
631 &mut info
as *mut _
as *mut _
,
638 /// Fill the given buffer with as many directory entries as will fit.
639 /// This will remember its position and continue from the last call unless
640 /// `restart` is set to `true`.
642 /// The returned bool indicates if there are more entries or not.
643 /// It is an error if `self` is not a directory.
645 /// # Symlinks and other reparse points
647 /// On Windows a file is either a directory or a non-directory.
648 /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
649 /// So if you open a link (not its target) and iterate the directory,
650 /// you will always iterate an empty directory regardless of the target.
651 fn fill_dir_buff(&self, buffer
: &mut DirBuff
, restart
: bool
) -> io
::Result
<bool
> {
653 if restart { c::FileIdBothDirectoryRestartInfo }
else { c::FileIdBothDirectoryInfo }
;
656 let result
= cvt(c
::GetFileInformationByHandleEx(
657 self.handle
.as_raw_handle(),
659 buffer
.as_mut_ptr().cast(),
660 buffer
.capacity() as _
,
664 Err(e
) if e
.raw_os_error() == Some(c
::ERROR_NO_MORE_FILES
as _
) => Ok(false),
671 /// A buffer for holding directory entries.
673 buffer
: Box
<Align8
<[MaybeUninit
<u8>; Self::BUFFER_SIZE
]>>,
676 const BUFFER_SIZE
: usize = 1024;
679 // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
681 buffer
: unsafe { Box::new_uninit().assume_init() }
,
684 fn capacity(&self) -> usize {
687 fn as_mut_ptr(&mut self) -> *mut u8 {
688 self.buffer
.0.as_mut_ptr().cast()
690 /// Returns a `DirBuffIter`.
691 fn iter(&self) -> DirBuffIter
<'_
> {
692 DirBuffIter
::new(self)
695 impl AsRef
<[MaybeUninit
<u8>]> for DirBuff
{
696 fn as_ref(&self) -> &[MaybeUninit
<u8>] {
701 /// An iterator over entries stored in a `DirBuff`.
703 /// Currently only returns file names (UTF-16 encoded).
704 struct DirBuffIter
<'a
> {
705 buffer
: Option
<&'a
[MaybeUninit
<u8>]>,
708 impl<'a
> DirBuffIter
<'a
> {
709 fn new(buffer
: &'a DirBuff
) -> Self {
710 Self { buffer: Some(buffer.as_ref()), cursor: 0 }
713 impl<'a
> Iterator
for DirBuffIter
<'a
> {
714 type Item
= (&'a
[u16], bool
);
715 fn next(&mut self) -> Option
<Self::Item
> {
716 use crate::mem
::size_of
;
717 let buffer
= &self.buffer?
[self.cursor
..];
719 // Get the name and next entry from the buffer.
721 // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
722 // field (the file name) is unsized. So an offset has to be used to
723 // get the file name slice.
724 // - The OS has guaranteed initialization of the fields of
725 // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
726 // `FileNameLength` bytes)
727 let (name
, is_directory
, next_entry
) = unsafe {
728 let info
= buffer
.as_ptr().cast
::<c
::FILE_ID_BOTH_DIR_INFO
>();
729 // Guaranteed to be aligned in documentation for
730 // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
731 assert
!(info
.is_aligned());
732 let next_entry
= (*info
).NextEntryOffset
as usize;
733 let name
= crate::slice
::from_raw_parts(
734 ptr
::addr_of
!((*info
).FileName
).cast
::<u16>(),
735 (*info
).FileNameLength
as usize / size_of
::<u16>(),
737 let is_directory
= ((*info
).FileAttributes
& c
::FILE_ATTRIBUTE_DIRECTORY
) != 0;
739 (name
, is_directory
, next_entry
)
745 self.cursor
+= next_entry
748 // Skip `.` and `..` pseudo entries.
749 const DOT
: u16 = b'
.'
as u16;
751 [DOT
] | [DOT
, DOT
] => self.next(),
752 _
=> Some((name
, is_directory
)),
757 /// Open a link relative to the parent directory, ensure no symlinks are followed.
758 fn open_link_no_reparse(parent
: &File
, name
: &[u16], access
: u32) -> io
::Result
<File
> {
759 // This is implemented using the lower level `NtCreateFile` function as
760 // unfortunately opening a file relative to a parent is not supported by
761 // win32 functions. It is however a fundamental feature of the NT kernel.
763 // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
765 let mut handle
= ptr
::null_mut();
766 let mut io_status
= c
::IO_STATUS_BLOCK
::default();
767 let name_str
= c
::UNICODE_STRING
::from_ref(name
);
768 use crate::sync
::atomic
::{AtomicU32, Ordering}
;
769 // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
770 // tricked into following a symlink. However, it may not be available in
771 // earlier versions of Windows.
772 static ATTRIBUTES
: AtomicU32
= AtomicU32
::new(c
::OBJ_DONT_REPARSE
);
773 let object
= c
::OBJECT_ATTRIBUTES
{
774 ObjectName
: &name_str
,
775 RootDirectory
: parent
.as_raw_handle(),
776 Attributes
: ATTRIBUTES
.load(Ordering
::Relaxed
),
777 ..c
::OBJECT_ATTRIBUTES
::default()
779 let status
= c
::NtCreateFile(
784 crate::ptr
::null_mut(),
786 c
::FILE_SHARE_DELETE
| c
::FILE_SHARE_READ
| c
::FILE_SHARE_WRITE
,
788 // If `name` is a symlink then open the link rather than the target.
789 c
::FILE_OPEN_REPARSE_POINT
,
790 crate::ptr
::null_mut(),
793 // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
794 if c
::nt_success(status
) {
795 Ok(File
::from_raw_handle(handle
))
796 } else if status
== c
::STATUS_DELETE_PENDING
{
797 // We make a special exception for `STATUS_DELETE_PENDING` because
798 // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
800 Err(io
::Error
::from_raw_os_error(c
::ERROR_DELETE_PENDING
as _
))
801 } else if status
== c
::STATUS_INVALID_PARAMETER
802 && ATTRIBUTES
.load(Ordering
::Relaxed
) == c
::OBJ_DONT_REPARSE
804 // Try without `OBJ_DONT_REPARSE`. See above.
805 ATTRIBUTES
.store(0, Ordering
::Relaxed
);
806 open_link_no_reparse(parent
, name
, access
)
808 Err(io
::Error
::from_raw_os_error(c
::RtlNtStatusToDosError(status
) as _
))
813 impl AsInner
<Handle
> for File
{
814 fn as_inner(&self) -> &Handle
{
819 impl IntoInner
<Handle
> for File
{
820 fn into_inner(self) -> Handle
{
825 impl FromInner
<Handle
> for File
{
826 fn from_inner(handle
: Handle
) -> File
{
831 impl AsHandle
for File
{
832 fn as_handle(&self) -> BorrowedHandle
<'_
> {
833 self.as_inner().as_handle()
837 impl AsRawHandle
for File
{
838 fn as_raw_handle(&self) -> RawHandle
{
839 self.as_inner().as_raw_handle()
843 impl IntoRawHandle
for File
{
844 fn into_raw_handle(self) -> RawHandle
{
845 self.into_inner().into_raw_handle()
849 impl FromRawHandle
for File
{
850 unsafe fn from_raw_handle(raw_handle
: RawHandle
) -> Self {
851 Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) }
855 impl fmt
::Debug
for File
{
856 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
857 // FIXME(#24570): add more info here (e.g., mode)
858 let mut b
= f
.debug_struct("File");
859 b
.field("handle", &self.handle
.as_raw_handle());
860 if let Ok(path
) = get_path(&self) {
861 b
.field("path", &path
);
868 pub fn size(&self) -> u64 {
872 pub fn perm(&self) -> FilePermissions
{
873 FilePermissions { attrs: self.attributes }
876 pub fn attrs(&self) -> u32 {
880 pub fn file_type(&self) -> FileType
{
881 FileType
::new(self.attributes
, self.reparse_tag
)
884 pub fn modified(&self) -> io
::Result
<SystemTime
> {
885 Ok(SystemTime
::from(self.last_write_time
))
888 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
889 Ok(SystemTime
::from(self.last_access_time
))
892 pub fn created(&self) -> io
::Result
<SystemTime
> {
893 Ok(SystemTime
::from(self.creation_time
))
896 pub fn modified_u64(&self) -> u64 {
897 to_u64(&self.last_write_time
)
900 pub fn accessed_u64(&self) -> u64 {
901 to_u64(&self.last_access_time
)
904 pub fn created_u64(&self) -> u64 {
905 to_u64(&self.creation_time
)
908 pub fn volume_serial_number(&self) -> Option
<u32> {
909 self.volume_serial_number
912 pub fn number_of_links(&self) -> Option
<u32> {
916 pub fn file_index(&self) -> Option
<u64> {
920 impl From
<c
::WIN32_FIND_DATAW
> for FileAttr
{
921 fn from(wfd
: c
::WIN32_FIND_DATAW
) -> Self {
923 attributes
: wfd
.dwFileAttributes
,
924 creation_time
: wfd
.ftCreationTime
,
925 last_access_time
: wfd
.ftLastAccessTime
,
926 last_write_time
: wfd
.ftLastWriteTime
,
927 file_size
: ((wfd
.nFileSizeHigh
as u64) << 32) | (wfd
.nFileSizeLow
as u64),
928 reparse_tag
: if wfd
.dwFileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
929 // reserved unless this is a reparse point
934 volume_serial_number
: None
,
935 number_of_links
: None
,
941 fn to_u64(ft
: &c
::FILETIME
) -> u64 {
942 (ft
.dwLowDateTime
as u64) | ((ft
.dwHighDateTime
as u64) << 32)
945 impl FilePermissions
{
946 pub fn readonly(&self) -> bool
{
947 self.attrs
& c
::FILE_ATTRIBUTE_READONLY
!= 0
950 pub fn set_readonly(&mut self, readonly
: bool
) {
952 self.attrs
|= c
::FILE_ATTRIBUTE_READONLY
;
954 self.attrs
&= !c
::FILE_ATTRIBUTE_READONLY
;
960 pub fn set_accessed(&mut self, t
: SystemTime
) {
961 self.accessed
= Some(t
.into_inner());
964 pub fn set_modified(&mut self, t
: SystemTime
) {
965 self.modified
= Some(t
.into_inner());
970 fn new(attrs
: c
::DWORD
, reparse_tag
: c
::DWORD
) -> FileType
{
971 FileType { attributes: attrs, reparse_tag }
973 pub fn is_dir(&self) -> bool
{
974 !self.is_symlink() && self.is_directory()
976 pub fn is_file(&self) -> bool
{
977 !self.is_symlink() && !self.is_directory()
979 pub fn is_symlink(&self) -> bool
{
980 self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
982 pub fn is_symlink_dir(&self) -> bool
{
983 self.is_symlink() && self.is_directory()
985 pub fn is_symlink_file(&self) -> bool
{
986 self.is_symlink() && !self.is_directory()
988 fn is_directory(&self) -> bool
{
989 self.attributes
& c
::FILE_ATTRIBUTE_DIRECTORY
!= 0
991 fn is_reparse_point(&self) -> bool
{
992 self.attributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0
994 fn is_reparse_tag_name_surrogate(&self) -> bool
{
995 self.reparse_tag
& 0x20000000 != 0
1000 pub fn new() -> DirBuilder
{
1004 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
1005 let p
= maybe_verbatim(p
)?
;
1006 cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) }
)?
;
1011 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
1012 let root
= p
.to_path_buf();
1013 let star
= p
.join("*");
1014 let path
= maybe_verbatim(&star
)?
;
1017 let mut wfd
= mem
::zeroed();
1018 let find_handle
= c
::FindFirstFileW(path
.as_ptr(), &mut wfd
);
1019 if find_handle
!= c
::INVALID_HANDLE_VALUE
{
1021 handle
: FindNextFileHandle(find_handle
),
1022 root
: Arc
::new(root
),
1026 Err(Error
::last_os_error())
1031 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
1032 let p_u16s
= maybe_verbatim(p
)?
;
1033 cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) }
)?
;
1037 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
1038 let old
= maybe_verbatim(old
)?
;
1039 let new
= maybe_verbatim(new
)?
;
1040 cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) }
)?
;
1044 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
1045 let p
= maybe_verbatim(p
)?
;
1046 cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }
)?
;
1050 /// Open a file or directory without following symlinks.
1051 fn open_link(path
: &Path
, access_mode
: u32) -> io
::Result
<File
> {
1052 let mut opts
= OpenOptions
::new();
1053 opts
.access_mode(access_mode
);
1054 // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
1055 // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
1056 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
| c
::FILE_FLAG_OPEN_REPARSE_POINT
);
1057 File
::open(path
, &opts
)
1060 pub fn remove_dir_all(path
: &Path
) -> io
::Result
<()> {
1061 let file
= open_link(path
, c
::DELETE
| c
::FILE_LIST_DIRECTORY
)?
;
1063 // Test if the file is not a directory or a symlink to a directory.
1064 if (file
.basic_info()?
.FileAttributes
& c
::FILE_ATTRIBUTE_DIRECTORY
) == 0 {
1065 return Err(io
::Error
::from_raw_os_error(c
::ERROR_DIRECTORY
as _
));
1068 match remove_dir_all_iterative(&file
, File
::posix_delete
) {
1070 if let Some(code
) = e
.raw_os_error() {
1072 // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
1073 c
::ERROR_NOT_SUPPORTED
1074 | c
::ERROR_INVALID_FUNCTION
1075 | c
::ERROR_INVALID_PARAMETER
=> {
1076 remove_dir_all_iterative(&file
, File
::win32_delete
)
1088 fn remove_dir_all_iterative(f
: &File
, delete
: fn(&File
) -> io
::Result
<()>) -> io
::Result
<()> {
1089 // When deleting files we may loop this many times when certain error conditions occur.
1090 // This allows remove_dir_all to succeed when the error is temporary.
1091 const MAX_RETRIES
: u32 = 10;
1093 let mut buffer
= DirBuff
::new();
1094 let mut dirlist
= vec
![f
.duplicate()?
];
1096 // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
1097 fn copy_handle(f
: &File
) -> mem
::ManuallyDrop
<File
> {
1098 unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) }
1101 let mut restart
= true;
1102 while let Some(dir
) = dirlist
.last() {
1103 let dir
= copy_handle(dir
);
1105 // Fill the buffer and iterate the entries.
1106 let more_data
= dir
.fill_dir_buff(&mut buffer
, restart
)?
;
1108 for (name
, is_directory
) in buffer
.iter() {
1110 let child_dir
= open_link_no_reparse(
1113 c
::SYNCHRONIZE
| c
::DELETE
| c
::FILE_LIST_DIRECTORY
,
1115 dirlist
.push(child_dir
);
1117 for i
in 1..=MAX_RETRIES
{
1118 let result
= open_link_no_reparse(&dir
, name
, c
::SYNCHRONIZE
| c
::DELETE
);
1120 Ok(f
) => delete(&f
)?
,
1121 // Already deleted, so skip.
1122 Err(e
) if e
.kind() == io
::ErrorKind
::NotFound
=> break,
1123 // Retry a few times if the file is locked or a delete is already in progress.
1126 && (e
.raw_os_error() == Some(c
::ERROR_DELETE_PENDING
as _
)
1128 == Some(c
::ERROR_SHARING_VIOLATION
as _
)) => {}
1129 // Otherwise return the error.
1130 Err(e
) => return Err(e
),
1132 thread
::yield_now();
1136 // If there were no more files then delete the directory.
1138 if let Some(dir
) = dirlist
.pop() {
1139 // Retry deleting a few times in case we need to wait for a file to be deleted.
1140 for i
in 1..=MAX_RETRIES
{
1141 let result
= delete(&dir
);
1142 if let Err(e
) = result
{
1143 if i
== MAX_RETRIES
|| e
.kind() != io
::ErrorKind
::DirectoryNotEmpty
{
1146 thread
::yield_now();
1157 pub fn readlink(path
: &Path
) -> io
::Result
<PathBuf
> {
1158 // Open the link with no access mode, instead of generic read.
1159 // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
1160 // this is needed for a common case.
1161 let mut opts
= OpenOptions
::new();
1162 opts
.access_mode(0);
1163 opts
.custom_flags(c
::FILE_FLAG_OPEN_REPARSE_POINT
| c
::FILE_FLAG_BACKUP_SEMANTICS
);
1164 let file
= File
::open(&path
, &opts
)?
;
1168 pub fn symlink(original
: &Path
, link
: &Path
) -> io
::Result
<()> {
1169 symlink_inner(original
, link
, false)
1172 pub fn symlink_inner(original
: &Path
, link
: &Path
, dir
: bool
) -> io
::Result
<()> {
1173 let original
= to_u16s(original
)?
;
1174 let link
= maybe_verbatim(link
)?
;
1175 let flags
= if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY }
else { 0 }
;
1176 // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
1177 // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
1178 // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
1179 // added to dwFlags to opt into this behaviour.
1180 let result
= cvt(unsafe {
1181 c
::CreateSymbolicLinkW(
1184 flags
| c
::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
,
1187 if let Err(err
) = result
{
1188 if err
.raw_os_error() == Some(c
::ERROR_INVALID_PARAMETER
as i32) {
1189 // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1190 // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
1192 c
::CreateSymbolicLinkW(link
.as_ptr(), original
.as_ptr(), flags
) as c
::BOOL
1201 #[cfg(not(target_vendor = "uwp"))]
1202 pub fn link(original
: &Path
, link
: &Path
) -> io
::Result
<()> {
1203 let original
= maybe_verbatim(original
)?
;
1204 let link
= maybe_verbatim(link
)?
;
1205 cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) }
)?
;
1209 #[cfg(target_vendor = "uwp")]
1210 pub fn link(_original
: &Path
, _link
: &Path
) -> io
::Result
<()> {
1211 return Err(io
::const_io_error
!(
1212 io
::ErrorKind
::Unsupported
,
1213 "hard link are not supported on UWP",
1217 pub fn stat(path
: &Path
) -> io
::Result
<FileAttr
> {
1218 metadata(path
, ReparsePoint
::Follow
)
1221 pub fn lstat(path
: &Path
) -> io
::Result
<FileAttr
> {
1222 metadata(path
, ReparsePoint
::Open
)
1226 #[derive(Clone, Copy, PartialEq, Eq)]
1229 Open
= c
::FILE_FLAG_OPEN_REPARSE_POINT
,
1232 fn as_flag(self) -> u32 {
1237 fn metadata(path
: &Path
, reparse
: ReparsePoint
) -> io
::Result
<FileAttr
> {
1238 let mut opts
= OpenOptions
::new();
1239 // No read or write permissions are necessary
1240 opts
.access_mode(0);
1241 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
| reparse
.as_flag());
1243 // Attempt to open the file normally.
1244 // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
1245 // If the fallback fails for any reason we return the original error.
1246 match File
::open(path
, &opts
) {
1247 Ok(file
) => file
.file_attr(),
1248 Err(e
) if e
.raw_os_error() == Some(c
::ERROR_SHARING_VIOLATION
as _
) => {
1249 // `ERROR_SHARING_VIOLATION` will almost never be returned.
1250 // Usually if a file is locked you can still read some metadata.
1251 // However, there are special system files, such as
1252 // `C:\hiberfil.sys`, that are locked in a way that denies even that.
1254 let path
= maybe_verbatim(path
)?
;
1256 // `FindFirstFileW` accepts wildcard file names.
1257 // Fortunately wildcards are not valid file names and
1258 // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
1259 // therefore it's safe to assume the file name given does not
1260 // include wildcards.
1261 let mut wfd
= mem
::zeroed();
1262 let handle
= c
::FindFirstFileW(path
.as_ptr(), &mut wfd
);
1264 if handle
== c
::INVALID_HANDLE_VALUE
{
1265 // This can fail if the user does not have read access to the
1269 // We no longer need the find handle.
1270 c
::FindClose(handle
);
1272 // `FindFirstFileW` reads the cached file information from the
1273 // directory. The downside is that this metadata may be outdated.
1274 let attrs
= FileAttr
::from(wfd
);
1275 if reparse
== ReparsePoint
::Follow
&& attrs
.file_type().is_symlink() {
1287 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
1288 let p
= maybe_verbatim(p
)?
;
1290 cvt(c
::SetFileAttributesW(p
.as_ptr(), perm
.attrs
))?
;
1295 fn get_path(f
: &File
) -> io
::Result
<PathBuf
> {
1296 super::fill_utf16_buf(
1298 c
::GetFinalPathNameByHandleW(f
.handle
.as_raw_handle(), buf
, sz
, c
::VOLUME_NAME_DOS
)
1300 |buf
| PathBuf
::from(OsString
::from_wide(buf
)),
1304 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
1305 let mut opts
= OpenOptions
::new();
1306 // No read or write permissions are necessary
1307 opts
.access_mode(0);
1308 // This flag is so we can open directories too
1309 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
);
1310 let f
= File
::open(p
, &opts
)?
;
1314 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
1315 unsafe extern "system" fn callback(
1316 _TotalFileSize
: c
::LARGE_INTEGER
,
1317 _TotalBytesTransferred
: c
::LARGE_INTEGER
,
1318 _StreamSize
: c
::LARGE_INTEGER
,
1319 StreamBytesTransferred
: c
::LARGE_INTEGER
,
1320 dwStreamNumber
: c
::DWORD
,
1321 _dwCallbackReason
: c
::DWORD
,
1322 _hSourceFile
: c
::HANDLE
,
1323 _hDestinationFile
: c
::HANDLE
,
1326 if dwStreamNumber
== 1 {
1327 *(lpData
as *mut i64) = StreamBytesTransferred
;
1329 c
::PROGRESS_CONTINUE
1331 let pfrom
= maybe_verbatim(from
)?
;
1332 let pto
= maybe_verbatim(to
)?
;
1333 let mut size
= 0i64;
1339 &mut size
as *mut _
as *mut _
,
1348 pub fn symlink_junction
<P
: AsRef
<Path
>, Q
: AsRef
<Path
>>(
1351 ) -> io
::Result
<()> {
1352 symlink_junction_inner(original
.as_ref(), junction
.as_ref())
1355 // Creating a directory junction on windows involves dealing with reparse
1356 // points and the DeviceIoControl function, and this code is a skeleton of
1357 // what can be found here:
1359 // http://www.flexhex.com/docs/articles/hard-links.phtml
1361 fn symlink_junction_inner(original
: &Path
, junction
: &Path
) -> io
::Result
<()> {
1362 let d
= DirBuilder
::new();
1363 d
.mkdir(&junction
)?
;
1365 let mut opts
= OpenOptions
::new();
1367 opts
.custom_flags(c
::FILE_FLAG_OPEN_REPARSE_POINT
| c
::FILE_FLAG_BACKUP_SEMANTICS
);
1368 let f
= File
::open(junction
, &opts
)?
;
1369 let h
= f
.as_inner().as_raw_handle();
1372 let mut data
= Align8([MaybeUninit
::<u8>::uninit(); c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
]);
1373 let data_ptr
= data
.0.as_mut_ptr();
1374 let db
= data_ptr
.cast
::<c
::REPARSE_MOUNTPOINT_DATA_BUFFER
>();
1375 let buf
= ptr
::addr_of_mut
!((*db
).ReparseTarget
).cast
::<c
::WCHAR
>();
1377 // FIXME: this conversion is very hacky
1379 let v
= v
.iter().map(|x
| *x
as u16);
1380 for c
in v
.chain(original
.as_os_str().encode_wide()) {
1386 (*db
).ReparseTag
= c
::IO_REPARSE_TAG_MOUNT_POINT
;
1387 (*db
).ReparseTargetMaximumLength
= (i
* 2) as c
::WORD
;
1388 (*db
).ReparseTargetLength
= ((i
- 1) * 2) as c
::WORD
;
1389 (*db
).ReparseDataLength
= (*db
).ReparseTargetLength
as c
::DWORD
+ 12;
1392 cvt(c
::DeviceIoControl(
1394 c
::FSCTL_SET_REPARSE_POINT
,
1396 (*db
).ReparseDataLength
+ 8,
1406 // Try to see if a file exists but, unlike `exists`, report I/O errors.
1407 pub fn try_exists(path
: &Path
) -> io
::Result
<bool
> {
1408 // Open the file to ensure any symlinks are followed to their target.
1409 let mut opts
= OpenOptions
::new();
1410 // No read, write, etc access rights are needed.
1411 opts
.access_mode(0);
1412 // Backup semantics enables opening directories as well as files.
1413 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
);
1414 match File
::open(path
, &opts
) {
1415 Err(e
) => match e
.kind() {
1416 // The file definitely does not exist
1417 io
::ErrorKind
::NotFound
=> Ok(false),
1419 // `ERROR_SHARING_VIOLATION` means that the file has been locked by
1420 // another process. This is often temporary so we simply report it
1421 // as the file existing.
1422 _
if e
.raw_os_error() == Some(c
::ERROR_SHARING_VIOLATION
as i32) => Ok(true),
1424 // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
1425 // file exists. However, these types of errors are usually more
1426 // permanent so we report them here.
1429 // The file was opened successfully therefore it must exist,