std\sys\pal\windows/
fs.rs

1use super::api::{self, WinError};
2use super::{IoResult, to_u16s};
3use crate::alloc::{alloc, handle_alloc_error};
4use crate::borrow::Cow;
5use crate::ffi::{OsStr, OsString, c_void};
6use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
7use crate::mem::{self, MaybeUninit};
8use crate::os::windows::io::{AsHandle, BorrowedHandle};
9use crate::os::windows::prelude::*;
10use crate::path::{Path, PathBuf};
11use crate::sync::Arc;
12use crate::sys::handle::Handle;
13use crate::sys::path::maybe_verbatim;
14use crate::sys::time::SystemTime;
15use crate::sys::{Align8, c, cvt};
16use crate::sys_common::{AsInner, FromInner, IntoInner};
17use crate::{fmt, ptr, slice};
18
19mod remove_dir_all;
20use remove_dir_all::remove_dir_all_iterative;
21
22pub struct File {
23    handle: Handle,
24}
25
26#[derive(Clone)]
27pub struct FileAttr {
28    attributes: u32,
29    creation_time: c::FILETIME,
30    last_access_time: c::FILETIME,
31    last_write_time: c::FILETIME,
32    change_time: Option<c::FILETIME>,
33    file_size: u64,
34    reparse_tag: u32,
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)]
41pub struct FileType {
42    attributes: u32,
43    reparse_tag: u32,
44}
45
46pub struct ReadDir {
47    handle: Option<FindNextFileHandle>,
48    root: Arc<PathBuf>,
49    first: Option<c::WIN32_FIND_DATAW>,
50}
51
52struct FindNextFileHandle(c::HANDLE);
53
54unsafe impl Send for FindNextFileHandle {}
55unsafe impl Sync for FindNextFileHandle {}
56
57pub struct DirEntry {
58    root: Arc<PathBuf>,
59    data: c::WIN32_FIND_DATAW,
60}
61
62unsafe impl Send for OpenOptions {}
63unsafe impl Sync for OpenOptions {}
64
65#[derive(Clone, Debug)]
66pub 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<u32>,
77    attributes: u32,
78    share_mode: u32,
79    security_qos_flags: u32,
80    security_attributes: *mut c::SECURITY_ATTRIBUTES,
81}
82
83#[derive(Clone, PartialEq, Eq, Debug)]
84pub struct FilePermissions {
85    attrs: u32,
86}
87
88#[derive(Copy, Clone, Debug, Default)]
89pub struct FileTimes {
90    accessed: Option<c::FILETIME>,
91    modified: Option<c::FILETIME>,
92    created: Option<c::FILETIME>,
93}
94
95impl 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)]
103pub struct DirBuilder;
104
105impl 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
113impl Iterator for ReadDir {
114    type Item = io::Result<DirEntry>;
115    fn next(&mut self) -> Option<io::Result<DirEntry>> {
116        let Some(handle) = self.handle.as_ref() else {
117            // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle.
118            // Simply return `None` because this is only the case when `FindFirstFileExW` in
119            // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means
120            // no matchhing files can be found.
121            return None;
122        };
123        if let Some(first) = self.first.take() {
124            if let Some(e) = DirEntry::new(&self.root, &first) {
125                return Some(Ok(e));
126            }
127        }
128        unsafe {
129            let mut wfd = mem::zeroed();
130            loop {
131                if c::FindNextFileW(handle.0, &mut wfd) == 0 {
132                    match api::get_last_error() {
133                        WinError::NO_MORE_FILES => return None,
134                        WinError { code } => {
135                            return Some(Err(Error::from_raw_os_error(code as i32)));
136                        }
137                    }
138                }
139                if let Some(e) = DirEntry::new(&self.root, &wfd) {
140                    return Some(Ok(e));
141                }
142            }
143        }
144    }
145}
146
147impl Drop for FindNextFileHandle {
148    fn drop(&mut self) {
149        let r = unsafe { c::FindClose(self.0) };
150        debug_assert!(r != 0);
151    }
152}
153
154impl DirEntry {
155    fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
156        match &wfd.cFileName[0..3] {
157            // check for '.' and '..'
158            &[46, 0, ..] | &[46, 46, 0, ..] => return None,
159            _ => {}
160        }
161
162        Some(DirEntry { root: root.clone(), data: *wfd })
163    }
164
165    pub fn path(&self) -> PathBuf {
166        self.root.join(self.file_name())
167    }
168
169    pub fn file_name(&self) -> OsString {
170        let filename = super::truncate_utf16_at_nul(&self.data.cFileName);
171        OsString::from_wide(filename)
172    }
173
174    pub fn file_type(&self) -> io::Result<FileType> {
175        Ok(FileType::new(
176            self.data.dwFileAttributes,
177            /* reparse_tag = */ self.data.dwReserved0,
178        ))
179    }
180
181    pub fn metadata(&self) -> io::Result<FileAttr> {
182        Ok(self.data.into())
183    }
184}
185
186impl OpenOptions {
187    pub fn new() -> OpenOptions {
188        OpenOptions {
189            // generic
190            read: false,
191            write: false,
192            append: false,
193            truncate: false,
194            create: false,
195            create_new: false,
196            // system-specific
197            custom_flags: 0,
198            access_mode: None,
199            share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,
200            attributes: 0,
201            security_qos_flags: 0,
202            security_attributes: ptr::null_mut(),
203        }
204    }
205
206    pub fn read(&mut self, read: bool) {
207        self.read = read;
208    }
209    pub fn write(&mut self, write: bool) {
210        self.write = write;
211    }
212    pub fn append(&mut self, append: bool) {
213        self.append = append;
214    }
215    pub fn truncate(&mut self, truncate: bool) {
216        self.truncate = truncate;
217    }
218    pub fn create(&mut self, create: bool) {
219        self.create = create;
220    }
221    pub fn create_new(&mut self, create_new: bool) {
222        self.create_new = create_new;
223    }
224
225    pub fn custom_flags(&mut self, flags: u32) {
226        self.custom_flags = flags;
227    }
228    pub fn access_mode(&mut self, access_mode: u32) {
229        self.access_mode = Some(access_mode);
230    }
231    pub fn share_mode(&mut self, share_mode: u32) {
232        self.share_mode = share_mode;
233    }
234    pub fn attributes(&mut self, attrs: u32) {
235        self.attributes = attrs;
236    }
237    pub fn security_qos_flags(&mut self, flags: u32) {
238        // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can
239        // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.
240        self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT;
241    }
242    pub fn security_attributes(&mut self, attrs: *mut c::SECURITY_ATTRIBUTES) {
243        self.security_attributes = attrs;
244    }
245
246    fn get_access_mode(&self) -> io::Result<u32> {
247        match (self.read, self.write, self.append, self.access_mode) {
248            (.., Some(mode)) => Ok(mode),
249            (true, false, false, None) => Ok(c::GENERIC_READ),
250            (false, true, false, None) => Ok(c::GENERIC_WRITE),
251            (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE),
252            (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA),
253            (true, _, true, None) => {
254                Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))
255            }
256            (false, false, false, None) => {
257                Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32))
258            }
259        }
260    }
261
262    fn get_creation_mode(&self) -> io::Result<u32> {
263        match (self.write, self.append) {
264            (true, false) => {}
265            (false, false) => {
266                if self.truncate || self.create || self.create_new {
267                    return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
268                }
269            }
270            (_, true) => {
271                if self.truncate && !self.create_new {
272                    return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
273                }
274            }
275        }
276
277        Ok(match (self.create, self.truncate, self.create_new) {
278            (false, false, false) => c::OPEN_EXISTING,
279            (true, false, false) => c::OPEN_ALWAYS,
280            (false, true, false) => c::TRUNCATE_EXISTING,
281            // `CREATE_ALWAYS` has weird semantics so we emulate it using
282            // `OPEN_ALWAYS` and a manual truncation step. See #115745.
283            (true, true, false) => c::OPEN_ALWAYS,
284            (_, _, true) => c::CREATE_NEW,
285        })
286    }
287
288    fn get_flags_and_attributes(&self) -> u32 {
289        self.custom_flags
290            | self.attributes
291            | self.security_qos_flags
292            | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 }
293    }
294}
295
296impl File {
297    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
298        let path = maybe_verbatim(path)?;
299        Self::open_native(&path, opts)
300    }
301
302    fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> {
303        let creation = opts.get_creation_mode()?;
304        let handle = unsafe {
305            c::CreateFileW(
306                path.as_ptr(),
307                opts.get_access_mode()?,
308                opts.share_mode,
309                opts.security_attributes,
310                creation,
311                opts.get_flags_and_attributes(),
312                ptr::null_mut(),
313            )
314        };
315        let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
316        if let Ok(handle) = OwnedHandle::try_from(handle) {
317            // Manual truncation. See #115745.
318            if opts.truncate
319                && creation == c::OPEN_ALWAYS
320                && api::get_last_error() == WinError::ALREADY_EXISTS
321            {
322                unsafe {
323                    // This first tries `FileAllocationInfo` but falls back to
324                    // `FileEndOfFileInfo` in order to support WINE.
325                    // If WINE gains support for FileAllocationInfo, we should
326                    // remove the fallback.
327                    let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
328                    let result = c::SetFileInformationByHandle(
329                        handle.as_raw_handle(),
330                        c::FileAllocationInfo,
331                        (&raw const alloc).cast::<c_void>(),
332                        mem::size_of::<c::FILE_ALLOCATION_INFO>() as u32,
333                    );
334                    if result == 0 {
335                        let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 };
336                        let result = c::SetFileInformationByHandle(
337                            handle.as_raw_handle(),
338                            c::FileEndOfFileInfo,
339                            (&raw const eof).cast::<c_void>(),
340                            mem::size_of::<c::FILE_END_OF_FILE_INFO>() as u32,
341                        );
342                        if result == 0 {
343                            return Err(io::Error::last_os_error());
344                        }
345                    }
346                }
347            }
348            Ok(File { handle: Handle::from_inner(handle) })
349        } else {
350            Err(Error::last_os_error())
351        }
352    }
353
354    pub fn fsync(&self) -> io::Result<()> {
355        cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
356        Ok(())
357    }
358
359    pub fn datasync(&self) -> io::Result<()> {
360        self.fsync()
361    }
362
363    fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> {
364        unsafe {
365            let mut overlapped: c::OVERLAPPED = mem::zeroed();
366            let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null());
367            if event.is_null() {
368                return Err(io::Error::last_os_error());
369            }
370            overlapped.hEvent = event;
371            let lock_result = cvt(c::LockFileEx(
372                self.handle.as_raw_handle(),
373                flags,
374                0,
375                u32::MAX,
376                u32::MAX,
377                &mut overlapped,
378            ));
379
380            let final_result = match lock_result {
381                Ok(_) => Ok(()),
382                Err(err) => {
383                    if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) {
384                        // Wait for the lock to be acquired, and get the lock operation status.
385                        // This can happen asynchronously, if the file handle was opened for async IO
386                        let mut bytes_transferred = 0;
387                        cvt(c::GetOverlappedResult(
388                            self.handle.as_raw_handle(),
389                            &mut overlapped,
390                            &mut bytes_transferred,
391                            c::TRUE,
392                        ))
393                        .map(|_| ())
394                    } else {
395                        Err(err)
396                    }
397                }
398            };
399            c::CloseHandle(overlapped.hEvent);
400            final_result
401        }
402    }
403
404    pub fn lock(&self) -> io::Result<()> {
405        self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK)
406    }
407
408    pub fn lock_shared(&self) -> io::Result<()> {
409        self.acquire_lock(0)
410    }
411
412    pub fn try_lock(&self) -> io::Result<bool> {
413        let result = cvt(unsafe {
414            let mut overlapped = mem::zeroed();
415            c::LockFileEx(
416                self.handle.as_raw_handle(),
417                c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY,
418                0,
419                u32::MAX,
420                u32::MAX,
421                &mut overlapped,
422            )
423        });
424
425        match result {
426            Ok(_) => Ok(true),
427            Err(err)
428                if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
429                    || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) =>
430            {
431                Ok(false)
432            }
433            Err(err) => Err(err),
434        }
435    }
436
437    pub fn try_lock_shared(&self) -> io::Result<bool> {
438        let result = cvt(unsafe {
439            let mut overlapped = mem::zeroed();
440            c::LockFileEx(
441                self.handle.as_raw_handle(),
442                c::LOCKFILE_FAIL_IMMEDIATELY,
443                0,
444                u32::MAX,
445                u32::MAX,
446                &mut overlapped,
447            )
448        });
449
450        match result {
451            Ok(_) => Ok(true),
452            Err(err)
453                if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
454                    || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) =>
455            {
456                Ok(false)
457            }
458            Err(err) => Err(err),
459        }
460    }
461
462    pub fn unlock(&self) -> io::Result<()> {
463        // Unlock the handle twice because LockFileEx() allows a file handle to acquire
464        // both an exclusive and shared lock, in which case the documentation states that:
465        // "...two unlock operations are necessary to unlock the region; the first unlock operation
466        // unlocks the exclusive lock, the second unlock operation unlocks the shared lock"
467        cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?;
468        let result =
469            cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) });
470        match result {
471            Ok(_) => Ok(()),
472            Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()),
473            Err(err) => Err(err),
474        }
475    }
476
477    pub fn truncate(&self, size: u64) -> io::Result<()> {
478        let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
479        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
480    }
481
482    #[cfg(not(target_vendor = "uwp"))]
483    pub fn file_attr(&self) -> io::Result<FileAttr> {
484        unsafe {
485            let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
486            cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
487            let mut reparse_tag = 0;
488            if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
489                let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
490                cvt(c::GetFileInformationByHandleEx(
491                    self.handle.as_raw_handle(),
492                    c::FileAttributeTagInfo,
493                    (&raw mut attr_tag).cast(),
494                    mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
495                ))?;
496                if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
497                    reparse_tag = attr_tag.ReparseTag;
498                }
499            }
500            Ok(FileAttr {
501                attributes: info.dwFileAttributes,
502                creation_time: info.ftCreationTime,
503                last_access_time: info.ftLastAccessTime,
504                last_write_time: info.ftLastWriteTime,
505                change_time: None, // Only available in FILE_BASIC_INFO
506                file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32),
507                reparse_tag,
508                volume_serial_number: Some(info.dwVolumeSerialNumber),
509                number_of_links: Some(info.nNumberOfLinks),
510                file_index: Some(
511                    (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32),
512                ),
513            })
514        }
515    }
516
517    #[cfg(target_vendor = "uwp")]
518    pub fn file_attr(&self) -> io::Result<FileAttr> {
519        unsafe {
520            let mut info: c::FILE_BASIC_INFO = mem::zeroed();
521            let size = mem::size_of_val(&info);
522            cvt(c::GetFileInformationByHandleEx(
523                self.handle.as_raw_handle(),
524                c::FileBasicInfo,
525                (&raw mut info) as *mut c_void,
526                size as u32,
527            ))?;
528            let mut attr = FileAttr {
529                attributes: info.FileAttributes,
530                creation_time: c::FILETIME {
531                    dwLowDateTime: info.CreationTime as u32,
532                    dwHighDateTime: (info.CreationTime >> 32) as u32,
533                },
534                last_access_time: c::FILETIME {
535                    dwLowDateTime: info.LastAccessTime as u32,
536                    dwHighDateTime: (info.LastAccessTime >> 32) as u32,
537                },
538                last_write_time: c::FILETIME {
539                    dwLowDateTime: info.LastWriteTime as u32,
540                    dwHighDateTime: (info.LastWriteTime >> 32) as u32,
541                },
542                change_time: Some(c::FILETIME {
543                    dwLowDateTime: info.ChangeTime as u32,
544                    dwHighDateTime: (info.ChangeTime >> 32) as u32,
545                }),
546                file_size: 0,
547                reparse_tag: 0,
548                volume_serial_number: None,
549                number_of_links: None,
550                file_index: None,
551            };
552            let mut info: c::FILE_STANDARD_INFO = mem::zeroed();
553            let size = mem::size_of_val(&info);
554            cvt(c::GetFileInformationByHandleEx(
555                self.handle.as_raw_handle(),
556                c::FileStandardInfo,
557                (&raw mut info) as *mut c_void,
558                size as u32,
559            ))?;
560            attr.file_size = info.AllocationSize as u64;
561            attr.number_of_links = Some(info.NumberOfLinks);
562            if attr.file_type().is_reparse_point() {
563                let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
564                cvt(c::GetFileInformationByHandleEx(
565                    self.handle.as_raw_handle(),
566                    c::FileAttributeTagInfo,
567                    (&raw mut attr_tag).cast(),
568                    mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
569                ))?;
570                if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
571                    attr.reparse_tag = attr_tag.ReparseTag;
572                }
573            }
574            Ok(attr)
575        }
576    }
577
578    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
579        self.handle.read(buf)
580    }
581
582    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
583        self.handle.read_vectored(bufs)
584    }
585
586    #[inline]
587    pub fn is_read_vectored(&self) -> bool {
588        self.handle.is_read_vectored()
589    }
590
591    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
592        self.handle.read_at(buf, offset)
593    }
594
595    pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
596        self.handle.read_buf(cursor)
597    }
598
599    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
600        self.handle.write(buf)
601    }
602
603    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
604        self.handle.write_vectored(bufs)
605    }
606
607    #[inline]
608    pub fn is_write_vectored(&self) -> bool {
609        self.handle.is_write_vectored()
610    }
611
612    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
613        self.handle.write_at(buf, offset)
614    }
615
616    pub fn flush(&self) -> io::Result<()> {
617        Ok(())
618    }
619
620    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
621        let (whence, pos) = match pos {
622            // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
623            // integer as `u64`.
624            SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),
625            SeekFrom::End(n) => (c::FILE_END, n),
626            SeekFrom::Current(n) => (c::FILE_CURRENT, n),
627        };
628        let pos = pos as i64;
629        let mut newpos = 0;
630        cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
631        Ok(newpos as u64)
632    }
633
634    pub fn duplicate(&self) -> io::Result<File> {
635        Ok(Self { handle: self.handle.try_clone()? })
636    }
637
638    // NB: returned pointer is derived from `space`, and has provenance to
639    // match. A raw pointer is returned rather than a reference in order to
640    // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
641    fn reparse_point(
642        &self,
643        space: &mut Align8<[MaybeUninit<u8>]>,
644    ) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> {
645        unsafe {
646            let mut bytes = 0;
647            cvt({
648                // Grab this in advance to avoid it invalidating the pointer
649                // we get from `space.0.as_mut_ptr()`.
650                let len = space.0.len();
651                c::DeviceIoControl(
652                    self.handle.as_raw_handle(),
653                    c::FSCTL_GET_REPARSE_POINT,
654                    ptr::null_mut(),
655                    0,
656                    space.0.as_mut_ptr().cast(),
657                    len as u32,
658                    &mut bytes,
659                    ptr::null_mut(),
660                )
661            })?;
662            const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
663            Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
664        }
665    }
666
667    fn readlink(&self) -> io::Result<PathBuf> {
668        let mut space =
669            Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
670        let (_bytes, buf) = self.reparse_point(&mut space)?;
671        unsafe {
672            let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
673                c::IO_REPARSE_TAG_SYMLINK => {
674                    let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
675                    assert!(info.is_aligned());
676                    (
677                        (&raw mut (*info).PathBuffer).cast::<u16>(),
678                        (*info).SubstituteNameOffset / 2,
679                        (*info).SubstituteNameLength / 2,
680                        (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
681                    )
682                }
683                c::IO_REPARSE_TAG_MOUNT_POINT => {
684                    let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
685                    assert!(info.is_aligned());
686                    (
687                        (&raw mut (*info).PathBuffer).cast::<u16>(),
688                        (*info).SubstituteNameOffset / 2,
689                        (*info).SubstituteNameLength / 2,
690                        false,
691                    )
692                }
693                _ => {
694                    return Err(io::const_error!(
695                        io::ErrorKind::Uncategorized,
696                        "Unsupported reparse point type",
697                    ));
698                }
699            };
700            let subst_ptr = path_buffer.add(subst_off.into());
701            let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
702            // Absolute paths start with an NT internal namespace prefix `\??\`
703            // We should not let it leak through.
704            if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
705                // Turn `\??\` into `\\?\` (a verbatim path).
706                subst[1] = b'\\' as u16;
707                // Attempt to convert to a more user-friendly path.
708                let user = super::args::from_wide_to_user_path(
709                    subst.iter().copied().chain([0]).collect(),
710                )?;
711                Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user))))
712            } else {
713                Ok(PathBuf::from(OsString::from_wide(subst)))
714            }
715        }
716    }
717
718    pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
719        let info = c::FILE_BASIC_INFO {
720            CreationTime: 0,
721            LastAccessTime: 0,
722            LastWriteTime: 0,
723            ChangeTime: 0,
724            FileAttributes: perm.attrs,
725        };
726        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
727    }
728
729    pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
730        let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;
731        if times.accessed.map_or(false, is_zero)
732            || times.modified.map_or(false, is_zero)
733            || times.created.map_or(false, is_zero)
734        {
735            return Err(io::const_error!(
736                io::ErrorKind::InvalidInput,
737                "Cannot set file timestamp to 0",
738            ));
739        }
740        let is_max = |t: c::FILETIME| t.dwLowDateTime == u32::MAX && t.dwHighDateTime == u32::MAX;
741        if times.accessed.map_or(false, is_max)
742            || times.modified.map_or(false, is_max)
743            || times.created.map_or(false, is_max)
744        {
745            return Err(io::const_error!(
746                io::ErrorKind::InvalidInput,
747                "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",
748            ));
749        }
750        cvt(unsafe {
751            let created =
752                times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
753            let accessed =
754                times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
755            let modified =
756                times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
757            c::SetFileTime(self.as_raw_handle(), created, accessed, modified)
758        })?;
759        Ok(())
760    }
761
762    /// Gets only basic file information such as attributes and file times.
763    fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {
764        unsafe {
765            let mut info: c::FILE_BASIC_INFO = mem::zeroed();
766            let size = mem::size_of_val(&info);
767            cvt(c::GetFileInformationByHandleEx(
768                self.handle.as_raw_handle(),
769                c::FileBasicInfo,
770                (&raw mut info) as *mut c_void,
771                size as u32,
772            ))?;
773            Ok(info)
774        }
775    }
776
777    /// Deletes the file, consuming the file handle to ensure the delete occurs
778    /// as immediately as possible.
779    /// This attempts to use `posix_delete` but falls back to `win32_delete`
780    /// if that is not supported by the filesystem.
781    #[allow(unused)]
782    fn delete(self) -> Result<(), WinError> {
783        // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
784        match self.posix_delete() {
785            Err(WinError::INVALID_PARAMETER)
786            | Err(WinError::NOT_SUPPORTED)
787            | Err(WinError::INVALID_FUNCTION) => self.win32_delete(),
788            result => result,
789        }
790    }
791
792    /// Delete using POSIX semantics.
793    ///
794    /// Files will be deleted as soon as the handle is closed. This is supported
795    /// for Windows 10 1607 (aka RS1) and later. However some filesystem
796    /// drivers will not support it even then, e.g. FAT32.
797    ///
798    /// If the operation is not supported for this filesystem or OS version
799    /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
800    #[allow(unused)]
801    fn posix_delete(&self) -> Result<(), WinError> {
802        let info = c::FILE_DISPOSITION_INFO_EX {
803            Flags: c::FILE_DISPOSITION_FLAG_DELETE
804                | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
805                | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
806        };
807        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)
808    }
809
810    /// Delete a file using win32 semantics. The file won't actually be deleted
811    /// until all file handles are closed. However, marking a file for deletion
812    /// will prevent anyone from opening a new handle to the file.
813    #[allow(unused)]
814    fn win32_delete(&self) -> Result<(), WinError> {
815        let info = c::FILE_DISPOSITION_INFO { DeleteFile: true };
816        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)
817    }
818
819    /// Fill the given buffer with as many directory entries as will fit.
820    /// This will remember its position and continue from the last call unless
821    /// `restart` is set to `true`.
822    ///
823    /// The returned bool indicates if there are more entries or not.
824    /// It is an error if `self` is not a directory.
825    ///
826    /// # Symlinks and other reparse points
827    ///
828    /// On Windows a file is either a directory or a non-directory.
829    /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
830    /// So if you open a link (not its target) and iterate the directory,
831    /// you will always iterate an empty directory regardless of the target.
832    #[allow(unused)]
833    fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result<bool, WinError> {
834        let class =
835            if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo };
836
837        unsafe {
838            let result = c::GetFileInformationByHandleEx(
839                self.as_raw_handle(),
840                class,
841                buffer.as_mut_ptr().cast(),
842                buffer.capacity() as _,
843            );
844            if result == 0 {
845                let err = api::get_last_error();
846                if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) }
847            } else {
848                Ok(true)
849            }
850        }
851    }
852}
853
854/// A buffer for holding directory entries.
855struct DirBuff {
856    buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
857}
858impl DirBuff {
859    const BUFFER_SIZE: usize = 1024;
860    fn new() -> Self {
861        Self {
862            // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
863            // initialization.
864            buffer: unsafe { Box::new_uninit().assume_init() },
865        }
866    }
867    fn capacity(&self) -> usize {
868        self.buffer.0.len()
869    }
870    fn as_mut_ptr(&mut self) -> *mut u8 {
871        self.buffer.0.as_mut_ptr().cast()
872    }
873    /// Returns a `DirBuffIter`.
874    fn iter(&self) -> DirBuffIter<'_> {
875        DirBuffIter::new(self)
876    }
877}
878impl AsRef<[MaybeUninit<u8>]> for DirBuff {
879    fn as_ref(&self) -> &[MaybeUninit<u8>] {
880        &self.buffer.0
881    }
882}
883
884/// An iterator over entries stored in a `DirBuff`.
885///
886/// Currently only returns file names (UTF-16 encoded).
887struct DirBuffIter<'a> {
888    buffer: Option<&'a [MaybeUninit<u8>]>,
889    cursor: usize,
890}
891impl<'a> DirBuffIter<'a> {
892    fn new(buffer: &'a DirBuff) -> Self {
893        Self { buffer: Some(buffer.as_ref()), cursor: 0 }
894    }
895}
896impl<'a> Iterator for DirBuffIter<'a> {
897    type Item = (Cow<'a, [u16]>, bool);
898    fn next(&mut self) -> Option<Self::Item> {
899        use crate::mem::size_of;
900        let buffer = &self.buffer?[self.cursor..];
901
902        // Get the name and next entry from the buffer.
903        // SAFETY:
904        // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
905        //   field (the file name) is unsized. So an offset has to be used to
906        //   get the file name slice.
907        // - The OS has guaranteed initialization of the fields of
908        //   `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
909        //   `FileNameLength` bytes)
910        let (name, is_directory, next_entry) = unsafe {
911            let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
912            // While this is guaranteed to be aligned in documentation for
913            // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
914            // it does not seem that reality is so kind, and assuming this
915            // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
916            // presumably, this can be blamed on buggy filesystem drivers, but who knows.
917            let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize;
918            let length = (&raw const (*info).FileNameLength).read_unaligned() as usize;
919            let attrs = (&raw const (*info).FileAttributes).read_unaligned();
920            let name = from_maybe_unaligned(
921                (&raw const (*info).FileName).cast::<u16>(),
922                length / size_of::<u16>(),
923            );
924            let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
925
926            (name, is_directory, next_entry)
927        };
928
929        if next_entry == 0 {
930            self.buffer = None
931        } else {
932            self.cursor += next_entry
933        }
934
935        // Skip `.` and `..` pseudo entries.
936        const DOT: u16 = b'.' as u16;
937        match &name[..] {
938            [DOT] | [DOT, DOT] => self.next(),
939            _ => Some((name, is_directory)),
940        }
941    }
942}
943
944unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
945    unsafe {
946        if p.is_aligned() {
947            Cow::Borrowed(crate::slice::from_raw_parts(p, len))
948        } else {
949            Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
950        }
951    }
952}
953
954impl AsInner<Handle> for File {
955    #[inline]
956    fn as_inner(&self) -> &Handle {
957        &self.handle
958    }
959}
960
961impl IntoInner<Handle> for File {
962    fn into_inner(self) -> Handle {
963        self.handle
964    }
965}
966
967impl FromInner<Handle> for File {
968    fn from_inner(handle: Handle) -> File {
969        File { handle }
970    }
971}
972
973impl AsHandle for File {
974    fn as_handle(&self) -> BorrowedHandle<'_> {
975        self.as_inner().as_handle()
976    }
977}
978
979impl AsRawHandle for File {
980    fn as_raw_handle(&self) -> RawHandle {
981        self.as_inner().as_raw_handle()
982    }
983}
984
985impl IntoRawHandle for File {
986    fn into_raw_handle(self) -> RawHandle {
987        self.into_inner().into_raw_handle()
988    }
989}
990
991impl FromRawHandle for File {
992    unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
993        unsafe {
994            Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) }
995        }
996    }
997}
998
999impl fmt::Debug for File {
1000    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1001        // FIXME(#24570): add more info here (e.g., mode)
1002        let mut b = f.debug_struct("File");
1003        b.field("handle", &self.handle.as_raw_handle());
1004        if let Ok(path) = get_path(self) {
1005            b.field("path", &path);
1006        }
1007        b.finish()
1008    }
1009}
1010
1011impl FileAttr {
1012    pub fn size(&self) -> u64 {
1013        self.file_size
1014    }
1015
1016    pub fn perm(&self) -> FilePermissions {
1017        FilePermissions { attrs: self.attributes }
1018    }
1019
1020    pub fn attrs(&self) -> u32 {
1021        self.attributes
1022    }
1023
1024    pub fn file_type(&self) -> FileType {
1025        FileType::new(self.attributes, self.reparse_tag)
1026    }
1027
1028    pub fn modified(&self) -> io::Result<SystemTime> {
1029        Ok(SystemTime::from(self.last_write_time))
1030    }
1031
1032    pub fn accessed(&self) -> io::Result<SystemTime> {
1033        Ok(SystemTime::from(self.last_access_time))
1034    }
1035
1036    pub fn created(&self) -> io::Result<SystemTime> {
1037        Ok(SystemTime::from(self.creation_time))
1038    }
1039
1040    pub fn modified_u64(&self) -> u64 {
1041        to_u64(&self.last_write_time)
1042    }
1043
1044    pub fn accessed_u64(&self) -> u64 {
1045        to_u64(&self.last_access_time)
1046    }
1047
1048    pub fn created_u64(&self) -> u64 {
1049        to_u64(&self.creation_time)
1050    }
1051
1052    pub fn changed_u64(&self) -> Option<u64> {
1053        self.change_time.as_ref().map(|c| to_u64(c))
1054    }
1055
1056    pub fn volume_serial_number(&self) -> Option<u32> {
1057        self.volume_serial_number
1058    }
1059
1060    pub fn number_of_links(&self) -> Option<u32> {
1061        self.number_of_links
1062    }
1063
1064    pub fn file_index(&self) -> Option<u64> {
1065        self.file_index
1066    }
1067}
1068impl From<c::WIN32_FIND_DATAW> for FileAttr {
1069    fn from(wfd: c::WIN32_FIND_DATAW) -> Self {
1070        FileAttr {
1071            attributes: wfd.dwFileAttributes,
1072            creation_time: wfd.ftCreationTime,
1073            last_access_time: wfd.ftLastAccessTime,
1074            last_write_time: wfd.ftLastWriteTime,
1075            change_time: None,
1076            file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),
1077            reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
1078                // reserved unless this is a reparse point
1079                wfd.dwReserved0
1080            } else {
1081                0
1082            },
1083            volume_serial_number: None,
1084            number_of_links: None,
1085            file_index: None,
1086        }
1087    }
1088}
1089
1090fn to_u64(ft: &c::FILETIME) -> u64 {
1091    (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
1092}
1093
1094impl FilePermissions {
1095    pub fn readonly(&self) -> bool {
1096        self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
1097    }
1098
1099    pub fn set_readonly(&mut self, readonly: bool) {
1100        if readonly {
1101            self.attrs |= c::FILE_ATTRIBUTE_READONLY;
1102        } else {
1103            self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
1104        }
1105    }
1106}
1107
1108impl FileTimes {
1109    pub fn set_accessed(&mut self, t: SystemTime) {
1110        self.accessed = Some(t.into_inner());
1111    }
1112
1113    pub fn set_modified(&mut self, t: SystemTime) {
1114        self.modified = Some(t.into_inner());
1115    }
1116
1117    pub fn set_created(&mut self, t: SystemTime) {
1118        self.created = Some(t.into_inner());
1119    }
1120}
1121
1122impl FileType {
1123    fn new(attrs: u32, reparse_tag: u32) -> FileType {
1124        FileType { attributes: attrs, reparse_tag }
1125    }
1126    pub fn is_dir(&self) -> bool {
1127        !self.is_symlink() && self.is_directory()
1128    }
1129    pub fn is_file(&self) -> bool {
1130        !self.is_symlink() && !self.is_directory()
1131    }
1132    pub fn is_symlink(&self) -> bool {
1133        self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
1134    }
1135    pub fn is_symlink_dir(&self) -> bool {
1136        self.is_symlink() && self.is_directory()
1137    }
1138    pub fn is_symlink_file(&self) -> bool {
1139        self.is_symlink() && !self.is_directory()
1140    }
1141    fn is_directory(&self) -> bool {
1142        self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
1143    }
1144    fn is_reparse_point(&self) -> bool {
1145        self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
1146    }
1147    fn is_reparse_tag_name_surrogate(&self) -> bool {
1148        self.reparse_tag & 0x20000000 != 0
1149    }
1150}
1151
1152impl DirBuilder {
1153    pub fn new() -> DirBuilder {
1154        DirBuilder
1155    }
1156
1157    pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1158        let p = maybe_verbatim(p)?;
1159        cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
1160        Ok(())
1161    }
1162}
1163
1164pub fn readdir(p: &Path) -> io::Result<ReadDir> {
1165    // We push a `*` to the end of the path which cause the empty path to be
1166    // treated as the current directory. So, for consistency with other platforms,
1167    // we explicitly error on the empty path.
1168    if p.as_os_str().is_empty() {
1169        // Return an error code consistent with other ways of opening files.
1170        // E.g. fs::metadata or File::open.
1171        return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));
1172    }
1173    let root = p.to_path_buf();
1174    let star = p.join("*");
1175    let path = maybe_verbatim(&star)?;
1176
1177    unsafe {
1178        let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();
1179        // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw),
1180        // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName
1181        // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw)
1182        // (which will be always null string value and currently unused) and should be faster.
1183        //
1184        // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more,
1185        // but as we don't know user's use profile of this function, lets be conservative.
1186        let find_handle = c::FindFirstFileExW(
1187            path.as_ptr(),
1188            c::FindExInfoBasic,
1189            &mut wfd as *mut _ as _,
1190            c::FindExSearchNameMatch,
1191            ptr::null(),
1192            0,
1193        );
1194
1195        if find_handle != c::INVALID_HANDLE_VALUE {
1196            Ok(ReadDir {
1197                handle: Some(FindNextFileHandle(find_handle)),
1198                root: Arc::new(root),
1199                first: Some(wfd),
1200            })
1201        } else {
1202            // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function
1203            // if no matching files can be found, but not necessarily that the path to find the
1204            // files in does not exist.
1205            //
1206            // Hence, a check for whether the path to search in exists is added when the last
1207            // os error returned by Windows is `ERROR_FILE_NOT_FOUND` to handle this scenario.
1208            // If that is the case, an empty `ReadDir` iterator is returned as it returns `None`
1209            // in the initial `.next()` invocation because `ERROR_NO_MORE_FILES` would have been
1210            // returned by the `FindNextFileW` function.
1211            //
1212            // See issue #120040: https://github.com/rust-lang/rust/issues/120040.
1213            let last_error = api::get_last_error();
1214            if last_error == WinError::FILE_NOT_FOUND {
1215                return Ok(ReadDir { handle: None, root: Arc::new(root), first: None });
1216            }
1217
1218            // Just return the error constructed from the raw OS error if the above is not the case.
1219            //
1220            // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function
1221            // when the path to search in does not exist in the first place.
1222            Err(Error::from_raw_os_error(last_error.code as i32))
1223        }
1224    }
1225}
1226
1227pub fn unlink(p: &Path) -> io::Result<()> {
1228    let p_u16s = maybe_verbatim(p)?;
1229    if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 {
1230        let err = api::get_last_error();
1231        // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove
1232        // the file while ignoring the readonly attribute.
1233        // This is accomplished by calling the `posix_delete` function on an open file handle.
1234        if err == WinError::ACCESS_DENIED {
1235            let mut opts = OpenOptions::new();
1236            opts.access_mode(c::DELETE);
1237            opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);
1238            if let Ok(f) = File::open_native(&p_u16s, &opts) {
1239                if f.posix_delete().is_ok() {
1240                    return Ok(());
1241                }
1242            }
1243        }
1244        // return the original error if any of the above fails.
1245        Err(io::Error::from_raw_os_error(err.code as i32))
1246    } else {
1247        Ok(())
1248    }
1249}
1250
1251pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1252    let old = maybe_verbatim(old)?;
1253    let new = maybe_verbatim(new)?;
1254
1255    let new_len_without_nul_in_bytes = (new.len() - 1).try_into().unwrap();
1256
1257    // The last field of FILE_RENAME_INFO, the file name, is unsized,
1258    // and FILE_RENAME_INFO has two padding bytes.
1259    // Therefore we need to make sure to not allocate less than
1260    // size_of::<c::FILE_RENAME_INFO>() bytes, which would be the case with
1261    // 0 or 1 character paths + a null byte.
1262    let struct_size = mem::size_of::<c::FILE_RENAME_INFO>()
1263        .max(mem::offset_of!(c::FILE_RENAME_INFO, FileName) + new.len() * mem::size_of::<u16>());
1264
1265    let struct_size: u32 = struct_size.try_into().unwrap();
1266
1267    let create_file = |extra_access, extra_flags| {
1268        let handle = unsafe {
1269            HandleOrInvalid::from_raw_handle(c::CreateFileW(
1270                old.as_ptr(),
1271                c::SYNCHRONIZE | c::DELETE | extra_access,
1272                c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,
1273                ptr::null(),
1274                c::OPEN_EXISTING,
1275                c::FILE_ATTRIBUTE_NORMAL | c::FILE_FLAG_BACKUP_SEMANTICS | extra_flags,
1276                ptr::null_mut(),
1277            ))
1278        };
1279
1280        OwnedHandle::try_from(handle).map_err(|_| io::Error::last_os_error())
1281    };
1282
1283    // The following code replicates `MoveFileEx`'s behavior as reverse-engineered from its disassembly.
1284    // If `old` refers to a mount point, we move it instead of the target.
1285    let handle = match create_file(c::FILE_READ_ATTRIBUTES, c::FILE_FLAG_OPEN_REPARSE_POINT) {
1286        Ok(handle) => {
1287            let mut file_attribute_tag_info: MaybeUninit<c::FILE_ATTRIBUTE_TAG_INFO> =
1288                MaybeUninit::uninit();
1289
1290            let result = unsafe {
1291                cvt(c::GetFileInformationByHandleEx(
1292                    handle.as_raw_handle(),
1293                    c::FileAttributeTagInfo,
1294                    file_attribute_tag_info.as_mut_ptr().cast(),
1295                    mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
1296                ))
1297            };
1298
1299            if let Err(err) = result {
1300                if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _)
1301                    || err.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as _)
1302                {
1303                    // `GetFileInformationByHandleEx` documents that not all underlying drivers support all file information classes.
1304                    // Since we know we passed the correct arguments, this means the underlying driver didn't understand our request;
1305                    // `MoveFileEx` proceeds by reopening the file without inhibiting reparse point behavior.
1306                    None
1307                } else {
1308                    Some(Err(err))
1309                }
1310            } else {
1311                // SAFETY: The struct has been initialized by GetFileInformationByHandleEx
1312                let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() };
1313                let file_type = FileType::new(
1314                    file_attribute_tag_info.FileAttributes,
1315                    file_attribute_tag_info.ReparseTag,
1316                );
1317
1318                if file_type.is_symlink() {
1319                    // The file is a mount point, junction point or symlink so
1320                    // don't reopen the file so that the link gets renamed.
1321                    Some(Ok(handle))
1322                } else {
1323                    // Otherwise reopen the file without inhibiting reparse point behavior.
1324                    None
1325                }
1326            }
1327        }
1328        // The underlying driver may not support `FILE_FLAG_OPEN_REPARSE_POINT`: Retry without it.
1329        Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None,
1330        Err(err) => Some(Err(err)),
1331    }
1332    .unwrap_or_else(|| create_file(0, 0))?;
1333
1334    let layout = core::alloc::Layout::from_size_align(
1335        struct_size as _,
1336        mem::align_of::<c::FILE_RENAME_INFO>(),
1337    )
1338    .unwrap();
1339
1340    let file_rename_info = unsafe { alloc(layout) } as *mut c::FILE_RENAME_INFO;
1341
1342    if file_rename_info.is_null() {
1343        handle_alloc_error(layout);
1344    }
1345
1346    // SAFETY: file_rename_info is a non-null pointer pointing to memory allocated by the global allocator.
1347    let mut file_rename_info = unsafe { Box::from_raw(file_rename_info) };
1348
1349    // SAFETY: We have allocated enough memory for a full FILE_RENAME_INFO struct and a filename.
1350    unsafe {
1351        (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 {
1352            Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
1353        });
1354
1355        (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut());
1356        (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes);
1357
1358        new.as_ptr()
1359            .copy_to_nonoverlapping((&raw mut (*file_rename_info).FileName) as *mut u16, new.len());
1360    }
1361
1362    // We don't use `set_file_information_by_handle` here as `FILE_RENAME_INFO` is used for both `FileRenameInfo` and `FileRenameInfoEx`.
1363    let result = unsafe {
1364        cvt(c::SetFileInformationByHandle(
1365            handle.as_raw_handle(),
1366            c::FileRenameInfoEx,
1367            (&raw const *file_rename_info).cast::<c_void>(),
1368            struct_size,
1369        ))
1370    };
1371
1372    if let Err(err) = result {
1373        if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) {
1374            // FileRenameInfoEx and FILE_RENAME_FLAG_POSIX_SEMANTICS were added in Windows 10 1607; retry with FileRenameInfo.
1375            file_rename_info.Anonymous.ReplaceIfExists = true;
1376
1377            cvt(unsafe {
1378                c::SetFileInformationByHandle(
1379                    handle.as_raw_handle(),
1380                    c::FileRenameInfo,
1381                    (&raw const *file_rename_info).cast::<c_void>(),
1382                    struct_size,
1383                )
1384            })?;
1385        } else {
1386            return Err(err);
1387        }
1388    }
1389
1390    Ok(())
1391}
1392
1393pub fn rmdir(p: &Path) -> io::Result<()> {
1394    let p = maybe_verbatim(p)?;
1395    cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
1396    Ok(())
1397}
1398
1399pub fn remove_dir_all(path: &Path) -> io::Result<()> {
1400    // Open a file or directory without following symlinks.
1401    let mut opts = OpenOptions::new();
1402    opts.access_mode(c::FILE_LIST_DIRECTORY);
1403    // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
1404    // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
1405    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
1406    let file = File::open(path, &opts)?;
1407
1408    // Test if the file is not a directory or a symlink to a directory.
1409    if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
1410        return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));
1411    }
1412
1413    // Remove the directory and all its contents.
1414    remove_dir_all_iterative(file).io_result()
1415}
1416
1417pub fn readlink(path: &Path) -> io::Result<PathBuf> {
1418    // Open the link with no access mode, instead of generic read.
1419    // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
1420    // this is needed for a common case.
1421    let mut opts = OpenOptions::new();
1422    opts.access_mode(0);
1423    opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
1424    let file = File::open(path, &opts)?;
1425    file.readlink()
1426}
1427
1428pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1429    symlink_inner(original, link, false)
1430}
1431
1432pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
1433    let original = to_u16s(original)?;
1434    let link = maybe_verbatim(link)?;
1435    let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
1436    // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
1437    // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
1438    // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
1439    // added to dwFlags to opt into this behavior.
1440    let result = cvt(unsafe {
1441        c::CreateSymbolicLinkW(
1442            link.as_ptr(),
1443            original.as_ptr(),
1444            flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1445        ) as c::BOOL
1446    });
1447    if let Err(err) = result {
1448        if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {
1449            // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
1450            // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
1451            cvt(unsafe {
1452                c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL
1453            })?;
1454        } else {
1455            return Err(err);
1456        }
1457    }
1458    Ok(())
1459}
1460
1461#[cfg(not(target_vendor = "uwp"))]
1462pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1463    let original = maybe_verbatim(original)?;
1464    let link = maybe_verbatim(link)?;
1465    cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
1466    Ok(())
1467}
1468
1469#[cfg(target_vendor = "uwp")]
1470pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
1471    return Err(
1472        io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP",),
1473    );
1474}
1475
1476pub fn stat(path: &Path) -> io::Result<FileAttr> {
1477    match metadata(path, ReparsePoint::Follow) {
1478        Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
1479            if let Ok(attrs) = lstat(path) {
1480                if !attrs.file_type().is_symlink() {
1481                    return Ok(attrs);
1482                }
1483            }
1484            Err(err)
1485        }
1486        result => result,
1487    }
1488}
1489
1490pub fn lstat(path: &Path) -> io::Result<FileAttr> {
1491    metadata(path, ReparsePoint::Open)
1492}
1493
1494#[repr(u32)]
1495#[derive(Clone, Copy, PartialEq, Eq)]
1496enum ReparsePoint {
1497    Follow = 0,
1498    Open = c::FILE_FLAG_OPEN_REPARSE_POINT,
1499}
1500impl ReparsePoint {
1501    fn as_flag(self) -> u32 {
1502        self as u32
1503    }
1504}
1505
1506fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
1507    let mut opts = OpenOptions::new();
1508    // No read or write permissions are necessary
1509    opts.access_mode(0);
1510    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());
1511
1512    // Attempt to open the file normally.
1513    // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`.
1514    // If the fallback fails for any reason we return the original error.
1515    match File::open(path, &opts) {
1516        Ok(file) => file.file_attr(),
1517        Err(e)
1518            if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]
1519                .contains(&e.raw_os_error()) =>
1520        {
1521            // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource.
1522            // One such example is `System Volume Information` as default but can be created as well
1523            // `ERROR_SHARING_VIOLATION` will almost never be returned.
1524            // Usually if a file is locked you can still read some metadata.
1525            // However, there are special system files, such as
1526            // `C:\hiberfil.sys`, that are locked in a way that denies even that.
1527            unsafe {
1528                let path = maybe_verbatim(path)?;
1529
1530                // `FindFirstFileExW` accepts wildcard file names.
1531                // Fortunately wildcards are not valid file names and
1532                // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
1533                // therefore it's safe to assume the file name given does not
1534                // include wildcards.
1535                let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();
1536                let handle = c::FindFirstFileExW(
1537                    path.as_ptr(),
1538                    c::FindExInfoBasic,
1539                    &mut wfd as *mut _ as _,
1540                    c::FindExSearchNameMatch,
1541                    ptr::null(),
1542                    0,
1543                );
1544
1545                if handle == c::INVALID_HANDLE_VALUE {
1546                    // This can fail if the user does not have read access to the
1547                    // directory.
1548                    Err(e)
1549                } else {
1550                    // We no longer need the find handle.
1551                    c::FindClose(handle);
1552
1553                    // `FindFirstFileExW` reads the cached file information from the
1554                    // directory. The downside is that this metadata may be outdated.
1555                    let attrs = FileAttr::from(wfd);
1556                    if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
1557                        Err(e)
1558                    } else {
1559                        Ok(attrs)
1560                    }
1561                }
1562            }
1563        }
1564        Err(e) => Err(e),
1565    }
1566}
1567
1568pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1569    let p = maybe_verbatim(p)?;
1570    unsafe {
1571        cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
1572        Ok(())
1573    }
1574}
1575
1576fn get_path(f: &File) -> io::Result<PathBuf> {
1577    super::fill_utf16_buf(
1578        |buf, sz| unsafe {
1579            c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1580        },
1581        |buf| PathBuf::from(OsString::from_wide(buf)),
1582    )
1583}
1584
1585pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1586    let mut opts = OpenOptions::new();
1587    // No read or write permissions are necessary
1588    opts.access_mode(0);
1589    // This flag is so we can open directories too
1590    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1591    let f = File::open(p, &opts)?;
1592    get_path(&f)
1593}
1594
1595pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1596    unsafe extern "system" fn callback(
1597        _TotalFileSize: i64,
1598        _TotalBytesTransferred: i64,
1599        _StreamSize: i64,
1600        StreamBytesTransferred: i64,
1601        dwStreamNumber: u32,
1602        _dwCallbackReason: u32,
1603        _hSourceFile: c::HANDLE,
1604        _hDestinationFile: c::HANDLE,
1605        lpData: *const c_void,
1606    ) -> u32 {
1607        unsafe {
1608            if dwStreamNumber == 1 {
1609                *(lpData as *mut i64) = StreamBytesTransferred;
1610            }
1611            c::PROGRESS_CONTINUE
1612        }
1613    }
1614    let pfrom = maybe_verbatim(from)?;
1615    let pto = maybe_verbatim(to)?;
1616    let mut size = 0i64;
1617    cvt(unsafe {
1618        c::CopyFileExW(
1619            pfrom.as_ptr(),
1620            pto.as_ptr(),
1621            Some(callback),
1622            (&raw mut size) as *mut _,
1623            ptr::null_mut(),
1624            0,
1625        )
1626    })?;
1627    Ok(size as u64)
1628}
1629
1630pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> {
1631    // Create and open a new directory in one go.
1632    let mut opts = OpenOptions::new();
1633    opts.create_new(true);
1634    opts.write(true);
1635    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS);
1636    opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY);
1637
1638    let d = File::open(link, &opts)?;
1639
1640    // We need to get an absolute, NT-style path.
1641    let path_bytes = original.as_os_str().as_encoded_bytes();
1642    let abs_path: Vec<u16> = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\")
1643    {
1644        // It's already an absolute path, we just need to convert the prefix to `\??\`
1645        let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) };
1646        r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()
1647    } else {
1648        // Get an absolute path and then convert the prefix to `\??\`
1649        let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes();
1650        if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") {
1651            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) };
1652            r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()
1653        } else if abs_path.starts_with(br"\\.\") {
1654            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) };
1655            r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()
1656        } else if abs_path.starts_with(br"\\") {
1657            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) };
1658            r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect()
1659        } else {
1660            return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid"));
1661        }
1662    };
1663    // Defined inline so we don't have to mess about with variable length buffer.
1664    #[repr(C)]
1665    pub struct MountPointBuffer {
1666        ReparseTag: u32,
1667        ReparseDataLength: u16,
1668        Reserved: u16,
1669        SubstituteNameOffset: u16,
1670        SubstituteNameLength: u16,
1671        PrintNameOffset: u16,
1672        PrintNameLength: u16,
1673        PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
1674    }
1675    let data_len = 12 + (abs_path.len() * 2);
1676    if data_len > u16::MAX as usize {
1677        return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long"));
1678    }
1679    let data_len = data_len as u16;
1680    let mut header = MountPointBuffer {
1681        ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT,
1682        ReparseDataLength: data_len,
1683        Reserved: 0,
1684        SubstituteNameOffset: 0,
1685        SubstituteNameLength: (abs_path.len() * 2) as u16,
1686        PrintNameOffset: ((abs_path.len() + 1) * 2) as u16,
1687        PrintNameLength: 0,
1688        PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
1689    };
1690    unsafe {
1691        let ptr = header.PathBuffer.as_mut_ptr();
1692        ptr.copy_from(abs_path.as_ptr().cast::<MaybeUninit<u16>>(), abs_path.len());
1693
1694        let mut ret = 0;
1695        cvt(c::DeviceIoControl(
1696            d.as_raw_handle(),
1697            c::FSCTL_SET_REPARSE_POINT,
1698            (&raw const header).cast::<c_void>(),
1699            data_len as u32 + 8,
1700            ptr::null_mut(),
1701            0,
1702            &mut ret,
1703            ptr::null_mut(),
1704        ))
1705        .map(drop)
1706    }
1707}
1708
1709// Try to see if a file exists but, unlike `exists`, report I/O errors.
1710pub fn exists(path: &Path) -> io::Result<bool> {
1711    // Open the file to ensure any symlinks are followed to their target.
1712    let mut opts = OpenOptions::new();
1713    // No read, write, etc access rights are needed.
1714    opts.access_mode(0);
1715    // Backup semantics enables opening directories as well as files.
1716    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
1717    match File::open(path, &opts) {
1718        Err(e) => match e.kind() {
1719            // The file definitely does not exist
1720            io::ErrorKind::NotFound => Ok(false),
1721
1722            // `ERROR_SHARING_VIOLATION` means that the file has been locked by
1723            // another process. This is often temporary so we simply report it
1724            // as the file existing.
1725            _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
1726
1727            // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the
1728            // reparse point could not be handled by `CreateFile`.
1729            // This can happen for special files such as:
1730            // * Unix domain sockets which you need to `connect` to
1731            // * App exec links which require using `CreateProcess`
1732            _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true),
1733
1734            // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
1735            // file exists. However, these types of errors are usually more
1736            // permanent so we report them here.
1737            _ => Err(e),
1738        },
1739        // The file was opened successfully therefore it must exist,
1740        Ok(_) => Ok(true),
1741    }
1742}