std\sys\fs/
windows.rs

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