xref: /drstd/src/std/sys/windows/fs.rs (revision 86982c5e9b2eaa583327251616ee822c36288824)
1 use crate::std::os::windows::prelude::*;
2 
3 use crate::std::borrow::Cow;
4 use crate::std::ffi::OsString;
5 use crate::std::fmt;
6 use crate::std::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
7 use crate::std::mem::{self, MaybeUninit};
8 use crate::std::os::windows::io::{AsHandle, BorrowedHandle};
9 use crate::std::path::{Path, PathBuf};
10 use crate::std::ptr;
11 use crate::std::slice;
12 use crate::std::sync::Arc;
13 use crate::std::sys::handle::Handle;
14 use crate::std::sys::time::SystemTime;
15 use crate::std::sys::{c, cvt, Align8};
16 use crate::std::sys_common::{AsInner, FromInner, IntoInner};
17 use crate::std::thread;
18 use dlibc;
19 
20 use super::path::maybe_verbatim;
21 use super::to_u16s;
22 
23 pub struct File {
24     handle: Handle,
25 }
26 
27 #[derive(Clone)]
28 pub struct FileAttr {
29     attributes: c::DWORD,
30     creation_time: c::FILETIME,
31     last_access_time: c::FILETIME,
32     last_write_time: c::FILETIME,
33     file_size: u64,
34     reparse_tag: c::DWORD,
35     volume_serial_number: Option<u32>,
36     number_of_links: Option<u32>,
37     file_index: Option<u64>,
38 }
39 
40 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
41 pub struct FileType {
42     attributes: c::DWORD,
43     reparse_tag: c::DWORD,
44 }
45 
46 pub struct ReadDir {
47     handle: FindNextFileHandle,
48     root: Arc<PathBuf>,
49     first: Option<c::WIN32_FIND_DATAW>,
50 }
51 
52 struct FindNextFileHandle(c::HANDLE);
53 
54 unsafe impl Send for FindNextFileHandle {}
55 unsafe impl Sync for FindNextFileHandle {}
56 
57 pub struct DirEntry {
58     root: Arc<PathBuf>,
59     data: c::WIN32_FIND_DATAW,
60 }
61 
62 unsafe impl Send for OpenOptions {}
63 unsafe impl Sync for OpenOptions {}
64 
65 #[derive(Clone, Debug)]
66 pub struct OpenOptions {
67     // generic
68     read: bool,
69     write: bool,
70     append: bool,
71     truncate: bool,
72     create: bool,
73     create_new: bool,
74     // system-specific
75     custom_flags: u32,
76     access_mode: Option<c::DWORD>,
77     attributes: c::DWORD,
78     share_mode: c::DWORD,
79     security_qos_flags: c::DWORD,
80     security_attributes: c::LPSECURITY_ATTRIBUTES,
81 }
82 
83 #[derive(Clone, PartialEq, Eq, Debug)]
84 pub struct FilePermissions {
85     attrs: c::DWORD,
86 }
87 
88 #[derive(Copy, Clone, Debug, Default)]
89 pub struct FileTimes {
90     accessed: Option<c::FILETIME>,
91     modified: Option<c::FILETIME>,
92     created: Option<c::FILETIME>,
93 }
94 
95 impl fmt::Debug for c::FILETIME {
96     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97         let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64;
98         f.debug_tuple("FILETIME").field(&time).finish()
99     }
100 }
101 
102 #[derive(Debug)]
103 pub struct DirBuilder;
104 
105 impl fmt::Debug for ReadDir {
106     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107         // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
108         // Thus the result will be e g 'ReadDir("C:\")'
109         fmt::Debug::fmt(&*self.root, f)
110     }
111 }
112 
113 impl Iterator for ReadDir {
114     type Item = io::Result<DirEntry>;
115     fn next(&mut self) -> Option<io::Result<DirEntry>> {
116         if let Some(first) = self.first.take() {
117             if let Some(e) = DirEntry::new(&self.root, &first) {
118                 return Some(Ok(e));
119             }
120         }
121         unsafe {
122             let mut wfd = mem::zeroed();
123             loop {
124                 if c::FindNextFileW(self.handle.0, &mut wfd) == 0 {
125                     if c::GetLastError() == c::ERROR_NO_MORE_FILES {
126                         return None;
127                     } else {
128                         return Some(Err(Error::last_os_error()));
129                     }
130                 }
131                 if let Some(e) = DirEntry::new(&self.root, &wfd) {
132                     return Some(Ok(e));
133                 }
134             }
135         }
136     }
137 }
138 
139 impl Drop for FindNextFileHandle {
140     fn drop(&mut self) {
141         let r = unsafe { c::FindClose(self.0) };
142         debug_assert!(r != 0);
143     }
144 }
145 
146 impl DirEntry {
147     fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
148         match &wfd.cFileName[0..3] {
149             // check for '.' and '..'
150             &[46, 0, ..] | &[46, 46, 0, ..] => return None,
151             _ => {}
152         }
153 
154         Some(DirEntry {
155             root: root.clone(),
156             data: *wfd,
157         })
158     }
159 
160     pub fn path(&self) -> PathBuf {
161         self.root.join(&self.file_name())
162     }
163 
164     pub fn file_name(&self) -> OsString {
165         let filename = super::truncate_utf16_at_nul(&self.data.cFileName);
166         OsString::from_wide(filename)
167     }
168 
169     pub fn file_type(&self) -> io::Result<FileType> {
170         Ok(FileType::new(
171             self.data.dwFileAttributes,
172             /* reparse_tag = */ self.data.dwReserved0,
173         ))
174     }
175 
176     pub fn metadata(&self) -> io::Result<FileAttr> {
177         Ok(self.data.into())
178     }
179 }
180 
181 impl OpenOptions {
182     pub fn new() -> OpenOptions {
183         OpenOptions {
184             // generic
185             read: false,
186             write: false,
187             append: false,
188             truncate: false,
189             create: false,
190             create_new: false,
191             // system-specific
192             custom_flags: 0,
193             access_mode: None,
194             share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,
195             attributes: 0,
196             security_qos_flags: 0,
197             security_attributes: ptr::null_mut(),
198         }
199     }
200 
201     pub fn read(&mut self, read: bool) {
202         self.read = read;
203     }
204     pub fn write(&mut self, write: bool) {
205         self.write = write;
206     }
207     pub fn append(&mut self, append: bool) {
208         self.append = append;
209     }
210     pub fn truncate(&mut self, truncate: bool) {
211         self.truncate = truncate;
212     }
213     pub fn create(&mut self, create: bool) {
214         self.create = create;
215     }
216     pub fn create_new(&mut self, create_new: bool) {
217         self.create_new = create_new;
218     }
219 
220     pub fn custom_flags(&mut self, flags: u32) {
221         self.custom_flags = flags;
222     }
223     pub fn access_mode(&mut self, access_mode: u32) {
224         self.access_mode = Some(access_mode);
225     }
226     pub fn share_mode(&mut self, share_mode: u32) {
227         self.share_mode = share_mode;
228     }
229     pub fn attributes(&mut self, attrs: u32) {
230         self.attributes = attrs;
231     }
232     pub fn security_qos_flags(&mut self, flags: u32) {
233         // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can
234         // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.
235         self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT;
236     }
237     pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) {
238         self.security_attributes = attrs;
239     }
240 
241     fn get_access_mode(&self) -> io::Result<c::DWORD> {
242         const ERROR_INVALID_PARAMETER: i32 = 87;
243 
244         match (self.read, self.write, self.append, self.access_mode) {
245             (.., Some(mode)) => Ok(mode),
246             (true, false, false, None) => Ok(c::GENERIC_READ),
247             (false, true, false, None) => Ok(c::GENERIC_WRITE),
248             (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE),
249             (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA),
250             (true, _, true, None) => {
251                 Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))
252             }
253             (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)),
254         }
255     }
256 
257     fn get_creation_mode(&self) -> io::Result<c::DWORD> {
258         const ERROR_INVALID_PARAMETER: i32 = 87;
259 
260         match (self.write, self.append) {
261             (true, false) => {}
262             (false, false) => {
263                 if self.truncate || self.create || self.create_new {
264                     return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
265                 }
266             }
267             (_, true) => {
268                 if self.truncate && !self.create_new {
269                     return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
270                 }
271             }
272         }
273 
274         Ok(match (self.create, self.truncate, self.create_new) {
275             (false, false, false) => c::OPEN_EXISTING,
276             (true, false, false) => c::OPEN_ALWAYS,
277             (false, true, false) => c::TRUNCATE_EXISTING,
278             (true, true, false) => c::CREATE_ALWAYS,
279             (_, _, true) => c::CREATE_NEW,
280         })
281     }
282 
283     fn get_flags_and_attributes(&self) -> c::DWORD {
284         self.custom_flags
285             | self.attributes
286             | self.security_qos_flags
287             | if self.create_new {
288                 c::FILE_FLAG_OPEN_REPARSE_POINT
289             } else {
290                 0
291             }
292     }
293 }
294 
295 impl File {
296     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
297         let path = maybe_verbatim(path)?;
298         let handle = unsafe {
299             c::CreateFileW(
300                 path.as_ptr(),
301                 opts.get_access_mode()?,
302                 opts.share_mode,
303                 opts.security_attributes,
304                 opts.get_creation_mode()?,
305                 opts.get_flags_and_attributes(),
306                 ptr::null_mut(),
307             )
308         };
309         let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
310         if let Ok(handle) = handle.try_into() {
311             Ok(File {
312                 handle: Handle::from_inner(handle),
313             })
314         } else {
315             Err(Error::last_os_error())
316         }
317     }
318 
319     pub fn fsync(&self) -> io::Result<()> {
320         cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
321         Ok(())
322     }
323 
324     pub fn datasync(&self) -> io::Result<()> {
325         self.fsync()
326     }
327 
328     pub fn truncate(&self, size: u64) -> io::Result<()> {
329         let mut info = c::FILE_END_OF_FILE_INFO {
330             EndOfFile: size as c::LARGE_INTEGER,
331         };
332         let size = mem::size_of_val(&info);
333         cvt(unsafe {
334             c::SetFileInformationByHandle(
335                 self.handle.as_raw_handle(),
336                 c::FileEndOfFileInfo,
337                 &mut info as *mut _ as *mut _,
338                 size as c::DWORD,
339             )
340         })?;
341         Ok(())
342     }
343 
344     #[cfg(not(target_vendor = "uwp"))]
345     pub fn file_attr(&self) -> io::Result<FileAttr> {
346         unsafe {
347             let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
348             cvt(c::GetFileInformationByHandle(
349                 self.handle.as_raw_handle(),
350                 &mut info,
351             ))?;
352             let mut reparse_tag = 0;
353             if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
354                 let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
355                 cvt(c::GetFileInformationByHandleEx(
356                     self.handle.as_raw_handle(),
357                     c::FileAttributeTagInfo,
358                     ptr::addr_of_mut!(attr_tag).cast(),
359                     mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>()
360                         .try_into()
361                         .unwrap(),
362                 ))?;
363                 if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
364                     reparse_tag = attr_tag.ReparseTag;
365                 }
366             }
367             Ok(FileAttr {
368                 attributes: info.dwFileAttributes,
369                 creation_time: info.ftCreationTime,
370                 last_access_time: info.ftLastAccessTime,
371                 last_write_time: info.ftLastWriteTime,
372                 file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32),
373                 reparse_tag,
374                 volume_serial_number: Some(info.dwVolumeSerialNumber),
375                 number_of_links: Some(info.nNumberOfLinks),
376                 file_index: Some(
377                     (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32),
378                 ),
379             })
380         }
381     }
382 
383     #[cfg(target_vendor = "uwp")]
384     pub fn file_attr(&self) -> io::Result<FileAttr> {
385         unsafe {
386             let mut info: c::FILE_BASIC_INFO = mem::zeroed();
387             let size = mem::size_of_val(&info);
388             cvt(c::GetFileInformationByHandleEx(
389                 self.handle.as_raw_handle(),
390                 c::FileBasicInfo,
391                 &mut info as *mut _ as *mut dlibc::c_void,
392                 size as c::DWORD,
393             ))?;
394             let mut attr = FileAttr {
395                 attributes: info.FileAttributes,
396                 creation_time: c::FILETIME {
397                     dwLowDateTime: info.CreationTime as c::DWORD,
398                     dwHighDateTime: (info.CreationTime >> 32) as c::DWORD,
399                 },
400                 last_access_time: c::FILETIME {
401                     dwLowDateTime: info.LastAccessTime as c::DWORD,
402                     dwHighDateTime: (info.LastAccessTime >> 32) as c::DWORD,
403                 },
404                 last_write_time: c::FILETIME {
405                     dwLowDateTime: info.LastWriteTime as c::DWORD,
406                     dwHighDateTime: (info.LastWriteTime >> 32) as c::DWORD,
407                 },
408                 file_size: 0,
409                 reparse_tag: 0,
410                 volume_serial_number: None,
411                 number_of_links: None,
412                 file_index: None,
413             };
414             let mut info: c::FILE_STANDARD_INFO = mem::zeroed();
415             let size = mem::size_of_val(&info);
416             cvt(c::GetFileInformationByHandleEx(
417                 self.handle.as_raw_handle(),
418                 c::FileStandardInfo,
419                 &mut info as *mut _ as *mut dlibc::c_void,
420                 size as c::DWORD,
421             ))?;
422             attr.file_size = info.AllocationSize as u64;
423             attr.number_of_links = Some(info.NumberOfLinks);
424             if attr.file_type().is_reparse_point() {
425                 let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
426                 cvt(c::GetFileInformationByHandleEx(
427                     self.handle.as_raw_handle(),
428                     c::FileAttributeTagInfo,
429                     ptr::addr_of_mut!(attr_tag).cast(),
430                     mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>()
431                         .try_into()
432                         .unwrap(),
433                 ))?;
434                 if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
435                     attr.reparse_tag = attr_tag.ReparseTag;
436                 }
437             }
438             Ok(attr)
439         }
440     }
441 
442     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
443         self.handle.read(buf)
444     }
445 
446     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
447         self.handle.read_vectored(bufs)
448     }
449 
450     #[inline]
451     pub fn is_read_vectored(&self) -> bool {
452         self.handle.is_read_vectored()
453     }
454 
455     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
456         self.handle.read_at(buf, offset)
457     }
458 
459     pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
460         self.handle.read_buf(cursor)
461     }
462 
463     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
464         self.handle.write(buf)
465     }
466 
467     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
468         self.handle.write_vectored(bufs)
469     }
470 
471     #[inline]
472     pub fn is_write_vectored(&self) -> bool {
473         self.handle.is_write_vectored()
474     }
475 
476     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
477         self.handle.write_at(buf, offset)
478     }
479 
480     pub fn flush(&self) -> io::Result<()> {
481         Ok(())
482     }
483 
484     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
485         let (whence, pos) = match pos {
486             // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
487             // integer as `u64`.
488             SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),
489             SeekFrom::End(n) => (c::FILE_END, n),
490             SeekFrom::Current(n) => (c::FILE_CURRENT, n),
491         };
492         let pos = pos as c::LARGE_INTEGER;
493         let mut newpos = 0;
494         cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
495         Ok(newpos as u64)
496     }
497 
498     pub fn duplicate(&self) -> io::Result<File> {
499         Ok(Self {
500             handle: self.handle.try_clone()?,
501         })
502     }
503 
504     // NB: returned pointer is derived from `space`, and has provenance to
505     // match. A raw pointer is returned rather than a reference in order to
506     // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
507     fn reparse_point(
508         &self,
509         space: &mut Align8<[MaybeUninit<u8>]>,
510     ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
511         unsafe {
512             let mut bytes = 0;
513             cvt({
514                 // Grab this in advance to avoid it invalidating the pointer
515                 // we get from `space.0.as_mut_ptr()`.
516                 let len = space.0.len();
517                 c::DeviceIoControl(
518                     self.handle.as_raw_handle(),
519                     c::FSCTL_GET_REPARSE_POINT,
520                     ptr::null_mut(),
521                     0,
522                     space.0.as_mut_ptr().cast(),
523                     len as c::DWORD,
524                     &mut bytes,
525                     ptr::null_mut(),
526                 )
527             })?;
528             const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
529             Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
530         }
531     }
532 
533     fn readlink(&self) -> io::Result<PathBuf> {
534         let mut space =
535             Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
536         let (_bytes, buf) = self.reparse_point(&mut space)?;
537         unsafe {
538             let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
539                 c::IO_REPARSE_TAG_SYMLINK => {
540                     let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
541                         ptr::addr_of_mut!((*buf).rest).cast();
542                     assert!(info.is_aligned());
543                     (
544                         ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
545                         (*info).SubstituteNameOffset / 2,
546                         (*info).SubstituteNameLength / 2,
547                         (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
548                     )
549                 }
550                 c::IO_REPARSE_TAG_MOUNT_POINT => {
551                     let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
552                         ptr::addr_of_mut!((*buf).rest).cast();
553                     assert!(info.is_aligned());
554                     (
555                         ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
556                         (*info).SubstituteNameOffset / 2,
557                         (*info).SubstituteNameLength / 2,
558                         false,
559                     )
560                 }
561                 _ => {
562                     return Err(io::const_io_error!(
563                         io::ErrorKind::Uncategorized,
564                         "Unsupported reparse point type",
565                     ));
566                 }
567             };
568             let subst_ptr = path_buffer.add(subst_off.into());
569             let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
570             // Absolute paths start with an NT internal namespace prefix `\??\`
571             // We should not let it leak through.
572             if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
573                 // Turn `\??\` into `\\?\` (a verbatim path).
574                 subst[1] = b'\\' as u16;
575                 // Attempt to convert to a more user-friendly path.
576                 let user = super::args::from_wide_to_user_path(
577                     subst.iter().copied().chain([0]).collect(),
578                 )?;
579                 Ok(PathBuf::from(OsString::from_wide(
580                     &user.strip_suffix(&[0]).unwrap_or(&user),
581                 )))
582             } else {
583                 Ok(PathBuf::from(OsString::from_wide(subst)))
584             }
585         }
586     }
587 
588     pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
589         let mut info = c::FILE_BASIC_INFO {
590             CreationTime: 0,
591             LastAccessTime: 0,
592             LastWriteTime: 0,
593             ChangeTime: 0,
594             FileAttributes: perm.attrs,
595         };
596         let size = mem::size_of_val(&info);
597         cvt(unsafe {
598             c::SetFileInformationByHandle(
599                 self.handle.as_raw_handle(),
600                 c::FileBasicInfo,
601                 &mut info as *mut _ as *mut _,
602                 size as c::DWORD,
603             )
604         })?;
605         Ok(())
606     }
607 
608     pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
609         let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;
610         if times.accessed.map_or(false, is_zero)
611             || times.modified.map_or(false, is_zero)
612             || times.created.map_or(false, is_zero)
613         {
614             return Err(io::const_io_error!(
615                 io::ErrorKind::InvalidInput,
616                 "Cannot set file timestamp to 0",
617             ));
618         }
619         let is_max =
620             |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX;
621         if times.accessed.map_or(false, is_max)
622             || times.modified.map_or(false, is_max)
623             || times.created.map_or(false, is_max)
624         {
625             return Err(io::const_io_error!(
626                 io::ErrorKind::InvalidInput,
627                 "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",
628             ));
629         }
630         cvt(unsafe {
631             let created = times
632                 .created
633                 .as_ref()
634                 .map(|a| a as *const c::FILETIME)
635                 .unwrap_or(ptr::null());
636             let accessed = times
637                 .accessed
638                 .as_ref()
639                 .map(|a| a as *const c::FILETIME)
640                 .unwrap_or(ptr::null());
641             let modified = times
642                 .modified
643                 .as_ref()
644                 .map(|a| a as *const c::FILETIME)
645                 .unwrap_or(ptr::null());
646             c::SetFileTime(self.as_raw_handle(), created, accessed, modified)
647         })?;
648         Ok(())
649     }
650 
651     /// Get only basic file information such as attributes and file times.
652     fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {
653         unsafe {
654             let mut info: c::FILE_BASIC_INFO = mem::zeroed();
655             let size = mem::size_of_val(&info);
656             cvt(c::GetFileInformationByHandleEx(
657                 self.handle.as_raw_handle(),
658                 c::FileBasicInfo,
659                 &mut info as *mut _ as *mut dlibc::c_void,
660                 size as c::DWORD,
661             ))?;
662             Ok(info)
663         }
664     }
665     /// Delete using POSIX semantics.
666     ///
667     /// Files will be deleted as soon as the handle is closed. This is supported
668     /// for Windows 10 1607 (aka RS1) and later. However some filesystem
669     /// drivers will not support it even then, e.g. FAT32.
670     ///
671     /// If the operation is not supported for this filesystem or OS version
672     /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
673     fn posix_delete(&self) -> io::Result<()> {
674         let mut info = c::FILE_DISPOSITION_INFO_EX {
675             Flags: c::FILE_DISPOSITION_FLAG_DELETE
676                 | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
677                 | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
678         };
679         let size = mem::size_of_val(&info);
680         cvt(unsafe {
681             c::SetFileInformationByHandle(
682                 self.handle.as_raw_handle(),
683                 c::FileDispositionInfoEx,
684                 &mut info as *mut _ as *mut _,
685                 size as c::DWORD,
686             )
687         })?;
688         Ok(())
689     }
690 
691     /// Delete a file using win32 semantics. The file won't actually be deleted
692     /// until all file handles are closed. However, marking a file for deletion
693     /// will prevent anyone from opening a new handle to the file.
694     fn win32_delete(&self) -> io::Result<()> {
695         let mut info = c::FILE_DISPOSITION_INFO {
696             DeleteFile: c::TRUE as _,
697         };
698         let size = mem::size_of_val(&info);
699         cvt(unsafe {
700             c::SetFileInformationByHandle(
701                 self.handle.as_raw_handle(),
702                 c::FileDispositionInfo,
703                 &mut info as *mut _ as *mut _,
704                 size as c::DWORD,
705             )
706         })?;
707         Ok(())
708     }
709 
710     /// Fill the given buffer with as many directory entries as will fit.
711     /// This will remember its position and continue from the last call unless
712     /// `restart` is set to `true`.
713     ///
714     /// The returned bool indicates if there are more entries or not.
715     /// It is an error if `self` is not a directory.
716     ///
717     /// # Symlinks and other reparse points
718     ///
719     /// On Windows a file is either a directory or a non-directory.
720     /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
721     /// So if you open a link (not its target) and iterate the directory,
722     /// you will always iterate an empty directory regardless of the target.
723     fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool> {
724         let class = if restart {
725             c::FileIdBothDirectoryRestartInfo
726         } else {
727             c::FileIdBothDirectoryInfo
728         };
729 
730         unsafe {
731             let result = cvt(c::GetFileInformationByHandleEx(
732                 self.handle.as_raw_handle(),
733                 class,
734                 buffer.as_mut_ptr().cast(),
735                 buffer.capacity() as _,
736             ));
737             match result {
738                 Ok(_) => Ok(true),
739                 Err(e) if e.raw_os_error() == Some(c::ERROR_NO_MORE_FILES as _) => Ok(false),
740                 Err(e) => Err(e),
741             }
742         }
743     }
744 }
745 
746 /// A buffer for holding directory entries.
747 struct DirBuff {
748     buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
749 }
750 impl DirBuff {
751     const BUFFER_SIZE: usize = 1024;
752     fn new() -> Self {
753         Self {
754             // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
755             // initialization.
756             buffer: unsafe { Box::new_uninit().assume_init() },
757         }
758     }
759     fn capacity(&self) -> usize {
760         self.buffer.0.len()
761     }
762     fn as_mut_ptr(&mut self) -> *mut u8 {
763         self.buffer.0.as_mut_ptr().cast()
764     }
765     /// Returns a `DirBuffIter`.
766     fn iter(&self) -> DirBuffIter<'_> {
767         DirBuffIter::new(self)
768     }
769 }
770 impl AsRef<[MaybeUninit<u8>]> for DirBuff {
771     fn as_ref(&self) -> &[MaybeUninit<u8>] {
772         &self.buffer.0
773     }
774 }
775 
776 /// An iterator over entries stored in a `DirBuff`.
777 ///
778 /// Currently only returns file names (UTF-16 encoded).
779 struct DirBuffIter<'a> {
780     buffer: Option<&'a [MaybeUninit<u8>]>,
781     cursor: usize,
782 }
783 impl<'a> DirBuffIter<'a> {
784     fn new(buffer: &'a DirBuff) -> Self {
785         Self {
786             buffer: Some(buffer.as_ref()),
787             cursor: 0,
788         }
789     }
790 }
791 impl<'a> Iterator for DirBuffIter<'a> {
792     type Item = (Cow<'a, [u16]>, bool);
793     fn next(&mut self) -> Option<Self::Item> {
794         use crate::std::mem::size_of;
795         let buffer = &self.buffer?[self.cursor..];
796 
797         // Get the name and next entry from the buffer.
798         // SAFETY:
799         // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
800         //   field (the file name) is unsized. So an offset has to be used to
801         //   get the file name slice.
802         // - The OS has guaranteed initialization of the fields of
803         //   `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
804         //   `FileNameLength` bytes)
805         let (name, is_directory, next_entry) = unsafe {
806             let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
807             // While this is guaranteed to be aligned in documentation for
808             // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
809             // it does not seem that reality is so kind, and assuming this
810             // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
811             // presumably, this can be blamed on buggy filesystem drivers, but who knows.
812             let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize;
813             let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize;
814             let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned();
815             let name = from_maybe_unaligned(
816                 ptr::addr_of!((*info).FileName).cast::<u16>(),
817                 length / size_of::<u16>(),
818             );
819             let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
820 
821             (name, is_directory, next_entry)
822         };
823 
824         if next_entry == 0 {
825             self.buffer = None
826         } else {
827             self.cursor += next_entry
828         }
829 
830         // Skip `.` and `..` pseudo entries.
831         const DOT: u16 = b'.' as u16;
832         match &name[..] {
833             [DOT] | [DOT, DOT] => self.next(),
834             _ => Some((name, is_directory)),
835         }
836     }
837 }
838 
839 unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
840     if p.is_aligned() {
841         Cow::Borrowed(crate::std::slice::from_raw_parts(p, len))
842     } else {
843         Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
844     }
845 }
846 
847 /// Open a link relative to the parent directory, ensure no symlinks are followed.
848 fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
849     // This is implemented using the lower level `NtCreateFile` function as
850     // unfortunately opening a file relative to a parent is not supported by
851     // win32 functions. It is however a fundamental feature of the NT kernel.
852     //
853     // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
854     unsafe {
855         let mut handle = ptr::null_mut();
856         let mut io_status = c::IO_STATUS_BLOCK::PENDING;
857         let mut name_str = c::UNICODE_STRING::from_ref(name);
858         use crate::std::sync::atomic::{AtomicU32, Ordering};
859         // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
860         // tricked into following a symlink. However, it may not be available in
861         // earlier versions of Windows.
862         static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
863         let mut object = c::OBJECT_ATTRIBUTES {
864             ObjectName: &mut name_str,
865             RootDirectory: parent.as_raw_handle(),
866             Attributes: ATTRIBUTES.load(Ordering::Relaxed),
867             ..c::OBJECT_ATTRIBUTES::default()
868         };
869         let status = c::NtCreateFile(
870             &mut handle,
871             access,
872             &mut object,
873             &mut io_status,
874             crate::std::ptr::null_mut(),
875             0,
876             c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
877             c::FILE_OPEN,
878             // If `name` is a symlink then open the link rather than the target.
879             c::FILE_OPEN_REPARSE_POINT,
880             crate::std::ptr::null_mut(),
881             0,
882         );
883         // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
884         if c::nt_success(status) {
885             Ok(File::from_raw_handle(handle))
886         } else if status == c::STATUS_DELETE_PENDING {
887             // We make a special exception for `STATUS_DELETE_PENDING` because
888             // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
889             // very unhelpful.
890             Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as _))
891         } else if status == c::STATUS_INVALID_PARAMETER
892             && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE
893         {
894             // Try without `OBJ_DONT_REPARSE`. See above.
895             ATTRIBUTES.store(0, Ordering::Relaxed);
896             open_link_no_reparse(parent, name, access)
897         } else {
898             Err(io::Error::from_raw_os_error(
899                 c::RtlNtStatusToDosError(status) as _,
900             ))
901         }
902     }
903 }
904 
905 impl AsInner<Handle> for File {
906     #[inline]
907     fn as_inner(&self) -> &Handle {
908         &self.handle
909     }
910 }
911 
912 impl IntoInner<Handle> for File {
913     fn into_inner(self) -> Handle {
914         self.handle
915     }
916 }
917 
918 impl FromInner<Handle> for File {
919     fn from_inner(handle: Handle) -> File {
920         File { handle }
921     }
922 }
923 
924 impl AsHandle for File {
925     fn as_handle(&self) -> BorrowedHandle<'_> {
926         self.as_inner().as_handle()
927     }
928 }
929 
930 impl AsRawHandle for File {
931     fn as_raw_handle(&self) -> RawHandle {
932         self.as_inner().as_raw_handle()
933     }
934 }
935 
936 impl IntoRawHandle for File {
937     fn into_raw_handle(self) -> RawHandle {
938         self.into_inner().into_raw_handle()
939     }
940 }
941 
942 impl FromRawHandle for File {
943     unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
944         Self {
945             handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)),
946         }
947     }
948 }
949 
950 impl fmt::Debug for File {
951     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
952         // FIXME(#24570): add more info here (e.g., mode)
953         let mut b = f.debug_struct("File");
954         b.field("handle", &self.handle.as_raw_handle());
955         if let Ok(path) = get_path(&self) {
956             b.field("path", &path);
957         }
958         b.finish()
959     }
960 }
961 
962 impl FileAttr {
963     pub fn size(&self) -> u64 {
964         self.file_size
965     }
966 
967     pub fn perm(&self) -> FilePermissions {
968         FilePermissions {
969             attrs: self.attributes,
970         }
971     }
972 
973     pub fn attrs(&self) -> u32 {
974         self.attributes
975     }
976 
977     pub fn file_type(&self) -> FileType {
978         FileType::new(self.attributes, self.reparse_tag)
979     }
980 
981     pub fn modified(&self) -> io::Result<SystemTime> {
982         Ok(SystemTime::from(self.last_write_time))
983     }
984 
985     pub fn accessed(&self) -> io::Result<SystemTime> {
986         Ok(SystemTime::from(self.last_access_time))
987     }
988 
989     pub fn created(&self) -> io::Result<SystemTime> {
990         Ok(SystemTime::from(self.creation_time))
991     }
992 
993     pub fn modified_u64(&self) -> u64 {
994         to_u64(&self.last_write_time)
995     }
996 
997     pub fn accessed_u64(&self) -> u64 {
998         to_u64(&self.last_access_time)
999     }
1000 
1001     pub fn created_u64(&self) -> u64 {
1002         to_u64(&self.creation_time)
1003     }
1004 
1005     pub fn volume_serial_number(&self) -> Option<u32> {
1006         self.volume_serial_number
1007     }
1008 
1009     pub fn number_of_links(&self) -> Option<u32> {
1010         self.number_of_links
1011     }
1012 
1013     pub fn file_index(&self) -> Option<u64> {
1014         self.file_index
1015     }
1016 }
1017 impl From<c::WIN32_FIND_DATAW> for FileAttr {
1018     fn from(wfd: c::WIN32_FIND_DATAW) -> Self {
1019         FileAttr {
1020             attributes: wfd.dwFileAttributes,
1021             creation_time: wfd.ftCreationTime,
1022             last_access_time: wfd.ftLastAccessTime,
1023             last_write_time: wfd.ftLastWriteTime,
1024             file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),
1025             reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
1026                 // reserved unless this is a reparse point
1027                 wfd.dwReserved0
1028             } else {
1029                 0
1030             },
1031             volume_serial_number: None,
1032             number_of_links: None,
1033             file_index: None,
1034         }
1035     }
1036 }
1037 
1038 fn to_u64(ft: &c::FILETIME) -> u64 {
1039     (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
1040 }
1041 
1042 impl FilePermissions {
1043     pub fn readonly(&self) -> bool {
1044         self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
1045     }
1046 
1047     pub fn set_readonly(&mut self, readonly: bool) {
1048         if readonly {
1049             self.attrs |= c::FILE_ATTRIBUTE_READONLY;
1050         } else {
1051             self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
1052         }
1053     }
1054 }
1055 
1056 impl FileTimes {
1057     pub fn set_accessed(&mut self, t: SystemTime) {
1058         self.accessed = Some(t.into_inner());
1059     }
1060 
1061     pub fn set_modified(&mut self, t: SystemTime) {
1062         self.modified = Some(t.into_inner());
1063     }
1064 
1065     pub fn set_created(&mut self, t: SystemTime) {
1066         self.created = Some(t.into_inner());
1067     }
1068 }
1069 
1070 impl FileType {
1071     fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
1072         FileType {
1073             attributes: attrs,
1074             reparse_tag,
1075         }
1076     }
1077     pub fn is_dir(&self) -> bool {
1078         !self.is_symlink() && self.is_directory()
1079     }
1080     pub fn is_file(&self) -> bool {
1081         !self.is_symlink() && !self.is_directory()
1082     }
1083     pub fn is_symlink(&self) -> bool {
1084         self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
1085     }
1086     pub fn is_symlink_dir(&self) -> bool {
1087         self.is_symlink() && self.is_directory()
1088     }
1089     pub fn is_symlink_file(&self) -> bool {
1090         self.is_symlink() && !self.is_directory()
1091     }
1092     fn is_directory(&self) -> bool {
1093         self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
1094     }
1095     fn is_reparse_point(&self) -> bool {
1096         self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
1097     }
1098     fn is_reparse_tag_name_surrogate(&self) -> bool {
1099         self.reparse_tag & 0x20000000 != 0
1100     }
1101 }
1102 
1103 impl DirBuilder {
1104     pub fn new() -> DirBuilder {
1105         DirBuilder
1106     }
1107 
1108     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1109         let p = maybe_verbatim(p)?;
1110         cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
1111         Ok(())
1112     }
1113 }
1114 
1115 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
1116     let root = p.to_path_buf();
1117     let star = p.join("*");
1118     let path = maybe_verbatim(&star)?;
1119 
1120     unsafe {
1121         let mut wfd = mem::zeroed();
1122         let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
1123         if find_handle != c::INVALID_HANDLE_VALUE {
1124             Ok(ReadDir {
1125                 handle: FindNextFileHandle(find_handle),
1126                 root: Arc::new(root),
1127                 first: Some(wfd),
1128             })
1129         } else {
1130             Err(Error::last_os_error())
1131         }
1132     }
1133 }
1134 
1135 pub fn unlink(p: &Path) -> io::Result<()> {
1136     let p_u16s = maybe_verbatim(p)?;
1137     cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
1138     Ok(())
1139 }
1140 
1141 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1142     let old = maybe_verbatim(old)?;
1143     let new = maybe_verbatim(new)?;
1144     cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?;
1145     Ok(())
1146 }
1147 
1148 pub fn rmdir(p: &Path) -> io::Result<()> {
1149     let p = maybe_verbatim(p)?;
1150     cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
1151     Ok(())
1152 }
1153 
1154 /// Open a file or directory without following symlinks.
1155 fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
1156     let mut opts = OpenOptions::new();
1157     opts.access_mode(access_mode);
1158     // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
1159     // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
1160     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
1161     File::open(path, &opts)
1162 }
1163 
1164 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
1165     let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
1166 
1167     // Test if the file is not a directory or a symlink to a directory.
1168     if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
1169         return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));
1170     }
1171 
1172     match remove_dir_all_iterative(&file, File::posix_delete) {
1173         Err(e) => {
1174             if let Some(code) = e.raw_os_error() {
1175                 match code as u32 {
1176                     // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
1177                     c::ERROR_NOT_SUPPORTED
1178                     | c::ERROR_INVALID_FUNCTION
1179                     | c::ERROR_INVALID_PARAMETER => {
1180                         remove_dir_all_iterative(&file, File::win32_delete)
1181                     }
1182                     _ => Err(e),
1183                 }
1184             } else {
1185                 Err(e)
1186             }
1187         }
1188         ok => ok,
1189     }
1190 }
1191 
1192 fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> {
1193     // When deleting files we may loop this many times when certain error conditions occur.
1194     // This allows remove_dir_all to succeed when the error is temporary.
1195     const MAX_RETRIES: u32 = 10;
1196 
1197     let mut buffer = DirBuff::new();
1198     let mut dirlist = vec![f.duplicate()?];
1199 
1200     // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
1201     fn copy_handle(f: &File) -> mem::ManuallyDrop<File> {
1202         unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) }
1203     }
1204 
1205     let mut restart = true;
1206     while let Some(dir) = dirlist.last() {
1207         let dir = copy_handle(dir);
1208 
1209         // Fill the buffer and iterate the entries.
1210         let more_data = dir.fill_dir_buff(&mut buffer, restart)?;
1211         restart = false;
1212         for (name, is_directory) in buffer.iter() {
1213             if is_directory {
1214                 let child_dir = open_link_no_reparse(
1215                     &dir,
1216                     &name,
1217                     c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
1218                 );
1219                 // On success, add the handle to the queue.
1220                 // If opening the directory fails we treat it the same as a file
1221                 if let Ok(child_dir) = child_dir {
1222                     dirlist.push(child_dir);
1223                     continue;
1224                 }
1225             }
1226             for i in 1..=MAX_RETRIES {
1227                 let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
1228                 match result {
1229                     Ok(f) => delete(&f)?,
1230                     // Already deleted, so skip.
1231                     Err(e) if e.kind() == io::ErrorKind::NotFound => break,
1232                     // Retry a few times if the file is locked or a delete is already in progress.
1233                     Err(e)
1234                         if i < MAX_RETRIES
1235                             && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _)
1236                                 || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _)) => {}
1237                     // Otherwise return the error.
1238                     Err(e) => return Err(e),
1239                 }
1240                 thread::yield_now();
1241             }
1242         }
1243         // If there were no more files then delete the directory.
1244         if !more_data {
1245             if let Some(dir) = dirlist.pop() {
1246                 // Retry deleting a few times in case we need to wait for a file to be deleted.
1247                 for i in 1..=MAX_RETRIES {
1248                     let result = delete(&dir);
1249                     if let Err(e) = result {
1250                         if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty {
1251                             return Err(e);
1252                         }
1253                         thread::yield_now();
1254                     } else {
1255                         break;
1256                     }
1257                 }
1258             }
1259         }
1260     }
1261     Ok(())
1262 }
1263 
1264 pub fn readlink(path: &Path) -> io::Result<PathBuf> {
1265     // Open the link with no access mode, instead of generic read.
1266     // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
1267     // this is needed for a common case.
1268     let mut opts = OpenOptions::new();
1269     opts.access_mode(0);
1270     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1271     let file = File::open(&path, &opts)?;
1272     file.readlink()
1273 }
1274 
1275 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1276     symlink_inner(original, link, false)
1277 }
1278 
1279 pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
1280     let original = to_u16s(original)?;
1281     let link = maybe_verbatim(link)?;
1282     let flags = if dir {
1283         c::SYMBOLIC_LINK_FLAG_DIRECTORY
1284     } else {
1285         0
1286     };
1287     // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
1288     // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
1289     // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
1290     // added to dwFlags to opt into this behaviour.
1291     let result = cvt(unsafe {
1292         c::CreateSymbolicLinkW(
1293             link.as_ptr(),
1294             original.as_ptr(),
1295             flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1296         ) as c::BOOL
1297     });
1298     if let Err(err) = result {
1299         if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {
1300             // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1301             // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
1302             cvt(unsafe {
1303                 c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL
1304             })?;
1305         } else {
1306             return Err(err);
1307         }
1308     }
1309     Ok(())
1310 }
1311 
1312 #[cfg(not(target_vendor = "uwp"))]
1313 pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1314     let original = maybe_verbatim(original)?;
1315     let link = maybe_verbatim(link)?;
1316     cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
1317     Ok(())
1318 }
1319 
1320 #[cfg(target_vendor = "uwp")]
1321 pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
1322     return Err(io::const_io_error!(
1323         io::ErrorKind::Unsupported,
1324         "hard link are not supported on UWP",
1325     ));
1326 }
1327 
1328 pub fn stat(path: &Path) -> io::Result<FileAttr> {
1329     match metadata(path, ReparsePoint::Follow) {
1330         Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
1331             if let Ok(attrs) = lstat(path) {
1332                 if !attrs.file_type().is_symlink() {
1333                     return Ok(attrs);
1334                 }
1335             }
1336             Err(err)
1337         }
1338         result => result,
1339     }
1340 }
1341 
1342 pub fn lstat(path: &Path) -> io::Result<FileAttr> {
1343     metadata(path, ReparsePoint::Open)
1344 }
1345 
1346 #[repr(u32)]
1347 #[derive(Clone, Copy, PartialEq, Eq)]
1348 enum ReparsePoint {
1349     Follow = 0,
1350     Open = c::FILE_FLAG_OPEN_REPARSE_POINT,
1351 }
1352 impl ReparsePoint {
1353     fn as_flag(self) -> u32 {
1354         self as u32
1355     }
1356 }
1357 
1358 fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
1359     let mut opts = OpenOptions::new();
1360     // No read or write permissions are necessary
1361     opts.access_mode(0);
1362     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());
1363 
1364     // Attempt to open the file normally.
1365     // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
1366     // If the fallback fails for any reason we return the original error.
1367     match File::open(path, &opts) {
1368         Ok(file) => file.file_attr(),
1369         Err(e)
1370             if [
1371                 Some(c::ERROR_SHARING_VIOLATION as _),
1372                 Some(c::ERROR_ACCESS_DENIED as _),
1373             ]
1374             .contains(&e.raw_os_error()) =>
1375         {
1376             // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource.
1377             // One such example is `System Volume Information` as default but can be created as well
1378             // `ERROR_SHARING_VIOLATION` will almost never be returned.
1379             // Usually if a file is locked you can still read some metadata.
1380             // However, there are special system files, such as
1381             // `C:\hiberfil.sys`, that are locked in a way that denies even that.
1382             unsafe {
1383                 let path = maybe_verbatim(path)?;
1384 
1385                 // `FindFirstFileW` accepts wildcard file names.
1386                 // Fortunately wildcards are not valid file names and
1387                 // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
1388                 // therefore it's safe to assume the file name given does not
1389                 // include wildcards.
1390                 let mut wfd = mem::zeroed();
1391                 let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
1392 
1393                 if handle == c::INVALID_HANDLE_VALUE {
1394                     // This can fail if the user does not have read access to the
1395                     // directory.
1396                     Err(e)
1397                 } else {
1398                     // We no longer need the find handle.
1399                     c::FindClose(handle);
1400 
1401                     // `FindFirstFileW` reads the cached file information from the
1402                     // directory. The downside is that this metadata may be outdated.
1403                     let attrs = FileAttr::from(wfd);
1404                     if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
1405                         Err(e)
1406                     } else {
1407                         Ok(attrs)
1408                     }
1409                 }
1410             }
1411         }
1412         Err(e) => Err(e),
1413     }
1414 }
1415 
1416 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1417     let p = maybe_verbatim(p)?;
1418     unsafe {
1419         cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
1420         Ok(())
1421     }
1422 }
1423 
1424 fn get_path(f: &File) -> io::Result<PathBuf> {
1425     super::fill_utf16_buf(
1426         |buf, sz| unsafe {
1427             c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1428         },
1429         |buf| PathBuf::from(OsString::from_wide(buf)),
1430     )
1431 }
1432 
1433 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1434     let mut opts = OpenOptions::new();
1435     // No read or write permissions are necessary
1436     opts.access_mode(0);
1437     // This flag is so we can open directories too
1438     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1439     let f = File::open(p, &opts)?;
1440     get_path(&f)
1441 }
1442 
1443 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1444     unsafe extern "system" fn callback(
1445         _TotalFileSize: c::LARGE_INTEGER,
1446         _TotalBytesTransferred: c::LARGE_INTEGER,
1447         _StreamSize: c::LARGE_INTEGER,
1448         StreamBytesTransferred: c::LARGE_INTEGER,
1449         dwStreamNumber: c::DWORD,
1450         _dwCallbackReason: c::DWORD,
1451         _hSourceFile: c::HANDLE,
1452         _hDestinationFile: c::HANDLE,
1453         lpData: c::LPCVOID,
1454     ) -> c::DWORD {
1455         if dwStreamNumber == 1 {
1456             *(lpData as *mut i64) = StreamBytesTransferred;
1457         }
1458         c::PROGRESS_CONTINUE
1459     }
1460     let pfrom = maybe_verbatim(from)?;
1461     let pto = maybe_verbatim(to)?;
1462     let mut size = 0i64;
1463     cvt(unsafe {
1464         c::CopyFileExW(
1465             pfrom.as_ptr(),
1466             pto.as_ptr(),
1467             Some(callback),
1468             &mut size as *mut _ as *mut _,
1469             ptr::null_mut(),
1470             0,
1471         )
1472     })?;
1473     Ok(size as u64)
1474 }
1475 
1476 #[allow(dead_code)]
1477 pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(
1478     original: P,
1479     junction: Q,
1480 ) -> io::Result<()> {
1481     symlink_junction_inner(original.as_ref(), junction.as_ref())
1482 }
1483 
1484 // Creating a directory junction on windows involves dealing with reparse
1485 // points and the DeviceIoControl function, and this code is a skeleton of
1486 // what can be found here:
1487 //
1488 // http://www.flexhex.com/docs/articles/hard-links.phtml
1489 #[allow(dead_code)]
1490 fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
1491     let d = DirBuilder::new();
1492     d.mkdir(&junction)?;
1493 
1494     let mut opts = OpenOptions::new();
1495     opts.write(true);
1496     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1497     let f = File::open(junction, &opts)?;
1498     let h = f.as_inner().as_raw_handle();
1499     unsafe {
1500         let mut data =
1501             Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
1502         let data_ptr = data.0.as_mut_ptr();
1503         let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize);
1504         let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
1505         // Zero the header to ensure it's fully initialized, including reserved parameters.
1506         *db = mem::zeroed();
1507         let reparse_target_slice = {
1508             let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
1509             // Compute offset in bytes and then divide so that we round down
1510             // rather than hit any UB (admittedly this arithmetic should work
1511             // out so that this isn't necessary)
1512             let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap();
1513             let buf_len_wchars = buf_len_bytes / core::mem::size_of::<c::WCHAR>();
1514             core::slice::from_raw_parts_mut(buf_start, buf_len_wchars)
1515         };
1516 
1517         // FIXME: this conversion is very hacky
1518         let iter = br"\??\"
1519             .iter()
1520             .map(|x| *x as u16)
1521             .chain(original.as_os_str().encode_wide())
1522             .chain(core::iter::once(0));
1523         let mut i = 0;
1524         for c in iter {
1525             if i >= reparse_target_slice.len() {
1526                 return Err(crate::std::io::const_io_error!(
1527                     crate::std::io::ErrorKind::InvalidFilename,
1528                     "Input filename is too long"
1529                 ));
1530             }
1531             reparse_target_slice[i] = c;
1532             i += 1;
1533         }
1534         (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
1535         (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
1536         (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
1537         (*db).ReparseDataLength = (*db).ReparseTargetLength as c::DWORD + 12;
1538 
1539         let mut ret = 0;
1540         cvt(c::DeviceIoControl(
1541             h as *mut _,
1542             c::FSCTL_SET_REPARSE_POINT,
1543             data_ptr.cast(),
1544             (*db).ReparseDataLength + 8,
1545             ptr::null_mut(),
1546             0,
1547             &mut ret,
1548             ptr::null_mut(),
1549         ))
1550         .map(drop)
1551     }
1552 }
1553 
1554 // Try to see if a file exists but, unlike `exists`, report I/O errors.
1555 pub fn try_exists(path: &Path) -> io::Result<bool> {
1556     // Open the file to ensure any symlinks are followed to their target.
1557     let mut opts = OpenOptions::new();
1558     // No read, write, etc access rights are needed.
1559     opts.access_mode(0);
1560     // Backup semantics enables opening directories as well as files.
1561     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1562     match File::open(path, &opts) {
1563         Err(e) => match e.kind() {
1564             // The file definitely does not exist
1565             io::ErrorKind::NotFound => Ok(false),
1566 
1567             // `ERROR_SHARING_VIOLATION` means that the file has been locked by
1568             // another process. This is often temporary so we simply report it
1569             // as the file existing.
1570             _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
1571 
1572             // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
1573             // file exists. However, these types of errors are usually more
1574             // permanent so we report them here.
1575             _ => Err(e),
1576         },
1577         // The file was opened successfully therefore it must exist,
1578         Ok(_) => Ok(true),
1579     }
1580 }
1581