library/std/src/sys/fs/windows.rs RUST 1,749 lines View on github.com → Search inside
1#![allow(nonstandard_style)]23use crate::alloc::{Layout, alloc, dealloc};4use crate::borrow::Cow;5use crate::ffi::{OsStr, OsString, c_void};6use crate::fs::TryLockError;7use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};8use crate::mem::{self, MaybeUninit, offset_of};9use crate::os::windows::io::{AsHandle, BorrowedHandle};10use crate::os::windows::prelude::*;11use crate::path::{Path, PathBuf};12use crate::sync::Arc;13use crate::sys::handle::Handle;14use crate::sys::pal::api::{self, WinError, set_file_information_by_handle};15use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul};16use crate::sys::path::{WCStr, maybe_verbatim};17use crate::sys::time::SystemTime;18use crate::sys::{Align8, AsInner, FromInner, IntoInner, c, cvt};19use crate::{fmt, ptr, slice};2021mod dir;22pub use dir::Dir;23mod remove_dir_all;24use remove_dir_all::remove_dir_all_iterative;2526pub struct File {27    handle: Handle,28}2930#[derive(Clone)]31pub struct FileAttr {32    attributes: u32,33    creation_time: c::FILETIME,34    last_access_time: c::FILETIME,35    last_write_time: c::FILETIME,36    change_time: Option<c::FILETIME>,37    file_size: u64,38    reparse_tag: u32,39    volume_serial_number: Option<u32>,40    number_of_links: Option<u32>,41    file_index: Option<u64>,42}4344#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]45pub struct FileType {46    is_directory: bool,47    is_symlink: bool,48}4950pub struct ReadDir {51    handle: Option<FindNextFileHandle>,52    root: Arc<PathBuf>,53    first: Option<c::WIN32_FIND_DATAW>,54}5556struct FindNextFileHandle(c::HANDLE);5758unsafe impl Send for FindNextFileHandle {}59unsafe impl Sync for FindNextFileHandle {}6061pub struct DirEntry {62    root: Arc<PathBuf>,63    data: c::WIN32_FIND_DATAW,64}6566unsafe impl Send for OpenOptions {}67unsafe impl Sync for OpenOptions {}6869#[derive(Clone, Debug)]70pub struct OpenOptions {71    // generic72    read: bool,73    write: bool,74    append: bool,75    truncate: bool,76    create: bool,77    create_new: bool,78    // system-specific79    custom_flags: u32,80    access_mode: Option<u32>,81    attributes: u32,82    share_mode: u32,83    security_qos_flags: u32,84    inherit_handle: bool,85    freeze_last_access_time: bool,86    freeze_last_write_time: bool,87}8889#[derive(Clone, PartialEq, Eq, Debug)]90pub struct FilePermissions {91    attrs: u32,92}9394#[derive(Copy, Clone, Debug, Default)]95pub struct FileTimes {96    accessed: Option<c::FILETIME>,97    modified: Option<c::FILETIME>,98    created: Option<c::FILETIME>,99}100101impl fmt::Debug for c::FILETIME {102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {103        let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64;104        f.debug_tuple("FILETIME").field(&time).finish()105    }106}107108#[derive(Debug)]109pub struct DirBuilder;110111impl fmt::Debug for ReadDir {112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {113        // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.114        // Thus the result will be e g 'ReadDir("C:\")'115        fmt::Debug::fmt(&*self.root, f)116    }117}118119impl Iterator for ReadDir {120    type Item = io::Result<DirEntry>;121    fn next(&mut self) -> Option<io::Result<DirEntry>> {122        let Some(handle) = self.handle.as_ref() else {123            // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle.124            // Simply return `None` because this is only the case when `FindFirstFileExW` in125            // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means126            // no matchhing files can be found.127            return None;128        };129        if let Some(first) = self.first.take() {130            if let Some(e) = DirEntry::new(&self.root, &first) {131                return Some(Ok(e));132            }133        }134        unsafe {135            let mut wfd = mem::zeroed();136            loop {137                if c::FindNextFileW(handle.0, &mut wfd) == 0 {138                    self.handle = None;139                    match api::get_last_error() {140                        WinError::NO_MORE_FILES => return None,141                        WinError { code } => {142                            return Some(Err(Error::from_raw_os_error(code as i32)));143                        }144                    }145                }146                if let Some(e) = DirEntry::new(&self.root, &wfd) {147                    return Some(Ok(e));148                }149            }150        }151    }152}153154impl Drop for FindNextFileHandle {155    fn drop(&mut self) {156        let r = unsafe { c::FindClose(self.0) };157        debug_assert!(r != 0);158    }159}160161impl DirEntry {162    fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {163        match &wfd.cFileName[0..3] {164            // check for '.' and '..'165            &[46, 0, ..] | &[46, 46, 0, ..] => return None,166            _ => {}167        }168169        Some(DirEntry { root: root.clone(), data: *wfd })170    }171172    pub fn path(&self) -> PathBuf {173        self.root.join(self.file_name())174    }175176    pub fn file_name(&self) -> OsString {177        let filename = truncate_utf16_at_nul(&self.data.cFileName);178        OsString::from_wide(filename)179    }180181    pub fn file_type(&self) -> io::Result<FileType> {182        Ok(FileType::new(183            self.data.dwFileAttributes,184            /* reparse_tag = */ self.data.dwReserved0,185        ))186    }187188    pub fn metadata(&self) -> io::Result<FileAttr> {189        Ok(self.data.into())190    }191}192193impl OpenOptions {194    pub fn new() -> OpenOptions {195        OpenOptions {196            // generic197            read: false,198            write: false,199            append: false,200            truncate: false,201            create: false,202            create_new: false,203            // system-specific204            custom_flags: 0,205            access_mode: None,206            share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,207            attributes: 0,208            security_qos_flags: 0,209            inherit_handle: false,210            freeze_last_access_time: false,211            freeze_last_write_time: false,212        }213    }214215    pub fn read(&mut self, read: bool) {216        self.read = read;217    }218    pub fn write(&mut self, write: bool) {219        self.write = write;220    }221    pub fn append(&mut self, append: bool) {222        self.append = append;223    }224    pub fn truncate(&mut self, truncate: bool) {225        self.truncate = truncate;226    }227    pub fn create(&mut self, create: bool) {228        self.create = create;229    }230    pub fn create_new(&mut self, create_new: bool) {231        self.create_new = create_new;232    }233234    pub fn custom_flags(&mut self, flags: u32) {235        self.custom_flags = flags;236    }237    pub fn access_mode(&mut self, access_mode: u32) {238        self.access_mode = Some(access_mode);239    }240    pub fn share_mode(&mut self, share_mode: u32) {241        self.share_mode = share_mode;242    }243    pub fn attributes(&mut self, attrs: u32) {244        self.attributes = attrs;245    }246    pub fn security_qos_flags(&mut self, flags: u32) {247        // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can248        // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.249        self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT;250    }251    pub fn inherit_handle(&mut self, inherit: bool) {252        self.inherit_handle = inherit;253    }254    pub fn freeze_last_access_time(&mut self, freeze: bool) {255        self.freeze_last_access_time = freeze;256    }257    pub fn freeze_last_write_time(&mut self, freeze: bool) {258        self.freeze_last_write_time = freeze;259    }260261    fn get_access_mode(&self) -> io::Result<u32> {262        match (self.read, self.write, self.append, self.access_mode) {263            (.., Some(mode)) => Ok(mode),264            (true, false, false, None) => Ok(c::GENERIC_READ),265            (false, true, false, None) => Ok(c::GENERIC_WRITE),266            (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE),267            (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA),268            (true, _, true, None) => {269                Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))270            }271            (false, false, false, None) => {272                // If no access mode is set, check if any creation flags are set273                // to provide a more descriptive error message274                if self.create || self.create_new || self.truncate {275                    Err(io::Error::new(276                        io::ErrorKind::InvalidInput,277                        "creating or truncating a file requires write or append access",278                    ))279                } else {280                    Err(io::Error::new(281                        io::ErrorKind::InvalidInput,282                        "must specify at least one of read, write, or append access",283                    ))284                }285            }286        }287    }288289    fn get_cmode_disposition(&self) -> io::Result<(u32, u32)> {290        match (self.write, self.append) {291            (true, false) => {}292            (false, false) => {293                if self.truncate || self.create || self.create_new {294                    return Err(io::Error::new(295                        io::ErrorKind::InvalidInput,296                        "creating or truncating a file requires write or append access",297                    ));298                }299            }300            (_, true) => {301                if self.truncate && !self.create_new {302                    return Err(io::Error::new(303                        io::ErrorKind::InvalidInput,304                        "creating or truncating a file requires write or append access",305                    ));306                }307            }308        }309310        Ok(match (self.create, self.truncate, self.create_new) {311            (false, false, false) => (c::OPEN_EXISTING, c::FILE_OPEN),312            (true, false, false) => (c::OPEN_ALWAYS, c::FILE_OPEN_IF),313            (false, true, false) => (c::TRUNCATE_EXISTING, c::FILE_OVERWRITE),314            // `CREATE_ALWAYS` has weird semantics so we emulate it using315            // `OPEN_ALWAYS` and a manual truncation step. See #115745.316            (true, true, false) => (c::OPEN_ALWAYS, c::FILE_OVERWRITE_IF),317            (_, _, true) => (c::CREATE_NEW, c::FILE_CREATE),318        })319    }320321    fn get_creation_mode(&self) -> io::Result<u32> {322        self.get_cmode_disposition().map(|(mode, _)| mode)323    }324325    fn get_disposition(&self) -> io::Result<u32> {326        self.get_cmode_disposition().map(|(_, mode)| mode)327    }328329    fn get_flags_and_attributes(&self) -> u32 {330        self.custom_flags331            | self.attributes332            | self.security_qos_flags333            | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 }334    }335}336337impl File {338    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {339        let path = maybe_verbatim(path)?;340        // SAFETY: maybe_verbatim returns null-terminated strings341        let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };342        Self::open_native(&path, opts)343    }344345    fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result<File> {346        let creation = opts.get_creation_mode()?;347        let sa = c::SECURITY_ATTRIBUTES {348            nLength: size_of::<c::SECURITY_ATTRIBUTES>() as u32,349            lpSecurityDescriptor: ptr::null_mut(),350            bInheritHandle: opts.inherit_handle as c::BOOL,351        };352        let handle = unsafe {353            c::CreateFileW(354                path.as_ptr(),355                opts.get_access_mode()?,356                opts.share_mode,357                if opts.inherit_handle { &sa } else { ptr::null() },358                creation,359                opts.get_flags_and_attributes(),360                ptr::null_mut(),361            )362        };363        let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };364        if let Ok(handle) = OwnedHandle::try_from(handle) {365            if opts.freeze_last_access_time || opts.freeze_last_write_time {366                let file_time =367                    c::FILETIME { dwLowDateTime: 0xFFFFFFFF, dwHighDateTime: 0xFFFFFFFF };368                cvt(unsafe {369                    c::SetFileTime(370                        handle.as_raw_handle(),371                        core::ptr::null(),372                        if opts.freeze_last_access_time { &file_time } else { core::ptr::null() },373                        if opts.freeze_last_write_time { &file_time } else { core::ptr::null() },374                    )375                })?;376            }377            // Manual truncation. See #115745.378            if opts.truncate379                && creation == c::OPEN_ALWAYS380                && api::get_last_error() == WinError::ALREADY_EXISTS381            {382                // This first tries `FileAllocationInfo` but falls back to383                // `FileEndOfFileInfo` in order to support WINE.384                // If WINE gains support for FileAllocationInfo, we should385                // remove the fallback.386                let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };387                set_file_information_by_handle(handle.as_raw_handle(), &alloc)388                    .or_else(|_| {389                        let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 };390                        set_file_information_by_handle(handle.as_raw_handle(), &eof)391                    })392                    .io_result()?;393            }394            Ok(File { handle: Handle::from_inner(handle) })395        } else {396            Err(Error::last_os_error())397        }398    }399400    pub fn fsync(&self) -> io::Result<()> {401        cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;402        Ok(())403    }404405    pub fn datasync(&self) -> io::Result<()> {406        self.fsync()407    }408409    fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> {410        unsafe {411            let mut overlapped: c::OVERLAPPED = mem::zeroed();412            let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null());413            if event.is_null() {414                return Err(io::Error::last_os_error());415            }416            overlapped.hEvent = event;417            let lock_result = cvt(c::LockFileEx(418                self.handle.as_raw_handle(),419                flags,420                0,421                u32::MAX,422                u32::MAX,423                &mut overlapped,424            ));425426            let final_result = match lock_result {427                Ok(_) => Ok(()),428                Err(err) => {429                    if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) {430                        // Wait for the lock to be acquired, and get the lock operation status.431                        // This can happen asynchronously, if the file handle was opened for async IO432                        let mut bytes_transferred = 0;433                        cvt(c::GetOverlappedResult(434                            self.handle.as_raw_handle(),435                            &mut overlapped,436                            &mut bytes_transferred,437                            c::TRUE,438                        ))439                        .map(|_| ())440                    } else {441                        Err(err)442                    }443                }444            };445            c::CloseHandle(overlapped.hEvent);446            final_result447        }448    }449450    pub fn lock(&self) -> io::Result<()> {451        self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK)452    }453454    pub fn lock_shared(&self) -> io::Result<()> {455        self.acquire_lock(0)456    }457458    pub fn try_lock(&self) -> Result<(), TryLockError> {459        let result = cvt(unsafe {460            let mut overlapped = mem::zeroed();461            c::LockFileEx(462                self.handle.as_raw_handle(),463                c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY,464                0,465                u32::MAX,466                u32::MAX,467                &mut overlapped,468            )469        });470471        match result {472            Ok(_) => Ok(()),473            Err(err) if err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => {474                Err(TryLockError::WouldBlock)475            }476            Err(err) => Err(TryLockError::Error(err)),477        }478    }479480    pub fn try_lock_shared(&self) -> Result<(), TryLockError> {481        let result = cvt(unsafe {482            let mut overlapped = mem::zeroed();483            c::LockFileEx(484                self.handle.as_raw_handle(),485                c::LOCKFILE_FAIL_IMMEDIATELY,486                0,487                u32::MAX,488                u32::MAX,489                &mut overlapped,490            )491        });492493        match result {494            Ok(_) => Ok(()),495            Err(err) if err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => {496                Err(TryLockError::WouldBlock)497            }498            Err(err) => Err(TryLockError::Error(err)),499        }500    }501502    pub fn unlock(&self) -> io::Result<()> {503        // Unlock the handle twice because LockFileEx() allows a file handle to acquire504        // both an exclusive and shared lock, in which case the documentation states that:505        // "...two unlock operations are necessary to unlock the region; the first unlock operation506        // unlocks the exclusive lock, the second unlock operation unlocks the shared lock"507        cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?;508        let result =509            cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) });510        match result {511            Ok(_) => Ok(()),512            Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()),513            Err(err) => Err(err),514        }515    }516517    pub fn truncate(&self, size: u64) -> io::Result<()> {518        let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };519        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()520    }521522    #[cfg(not(target_vendor = "uwp"))]523    pub fn file_attr(&self) -> io::Result<FileAttr> {524        unsafe {525            let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();526            cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;527            let mut reparse_tag = 0;528            if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {529                let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();530                cvt(c::GetFileInformationByHandleEx(531                    self.handle.as_raw_handle(),532                    c::FileAttributeTagInfo,533                    (&raw mut attr_tag).cast(),534                    size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),535                ))?;536                if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {537                    reparse_tag = attr_tag.ReparseTag;538                }539            }540            Ok(FileAttr {541                attributes: info.dwFileAttributes,542                creation_time: info.ftCreationTime,543                last_access_time: info.ftLastAccessTime,544                last_write_time: info.ftLastWriteTime,545                change_time: None, // Only available in FILE_BASIC_INFO546                file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32),547                reparse_tag,548                volume_serial_number: Some(info.dwVolumeSerialNumber),549                number_of_links: Some(info.nNumberOfLinks),550                file_index: Some(551                    (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32),552                ),553            })554        }555    }556557    #[cfg(target_vendor = "uwp")]558    pub fn file_attr(&self) -> io::Result<FileAttr> {559        unsafe {560            let mut info: c::FILE_BASIC_INFO = mem::zeroed();561            let size = size_of_val(&info);562            cvt(c::GetFileInformationByHandleEx(563                self.handle.as_raw_handle(),564                c::FileBasicInfo,565                (&raw mut info) as *mut c_void,566                size as u32,567            ))?;568            let mut attr = FileAttr {569                attributes: info.FileAttributes,570                creation_time: c::FILETIME {571                    dwLowDateTime: info.CreationTime as u32,572                    dwHighDateTime: (info.CreationTime >> 32) as u32,573                },574                last_access_time: c::FILETIME {575                    dwLowDateTime: info.LastAccessTime as u32,576                    dwHighDateTime: (info.LastAccessTime >> 32) as u32,577                },578                last_write_time: c::FILETIME {579                    dwLowDateTime: info.LastWriteTime as u32,580                    dwHighDateTime: (info.LastWriteTime >> 32) as u32,581                },582                change_time: Some(c::FILETIME {583                    dwLowDateTime: info.ChangeTime as u32,584                    dwHighDateTime: (info.ChangeTime >> 32) as u32,585                }),586                file_size: 0,587                reparse_tag: 0,588                volume_serial_number: None,589                number_of_links: None,590                file_index: None,591            };592            let mut info: c::FILE_STANDARD_INFO = mem::zeroed();593            let size = size_of_val(&info);594            cvt(c::GetFileInformationByHandleEx(595                self.handle.as_raw_handle(),596                c::FileStandardInfo,597                (&raw mut info) as *mut c_void,598                size as u32,599            ))?;600            attr.file_size = info.AllocationSize as u64;601            attr.number_of_links = Some(info.NumberOfLinks);602            if attr.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {603                let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();604                cvt(c::GetFileInformationByHandleEx(605                    self.handle.as_raw_handle(),606                    c::FileAttributeTagInfo,607                    (&raw mut attr_tag).cast(),608                    size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),609                ))?;610                if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {611                    attr.reparse_tag = attr_tag.ReparseTag;612                }613            }614            Ok(attr)615        }616    }617618    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {619        self.handle.read(buf)620    }621622    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {623        self.handle.read_vectored(bufs)624    }625626    #[inline]627    pub fn is_read_vectored(&self) -> bool {628        self.handle.is_read_vectored()629    }630631    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {632        self.handle.read_at(buf, offset)633    }634635    pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {636        self.handle.read_buf(cursor)637    }638639    pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {640        self.handle.read_buf_at(cursor, offset)641    }642643    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {644        self.handle.write(buf)645    }646647    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {648        self.handle.write_vectored(bufs)649    }650651    #[inline]652    pub fn is_write_vectored(&self) -> bool {653        self.handle.is_write_vectored()654    }655656    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {657        self.handle.write_at(buf, offset)658    }659660    pub fn flush(&self) -> io::Result<()> {661        Ok(())662    }663664    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {665        let (whence, pos) = match pos {666            // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this667            // integer as `u64`.668            SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),669            SeekFrom::End(n) => (c::FILE_END, n),670            SeekFrom::Current(n) => (c::FILE_CURRENT, n),671        };672        let pos = pos as i64;673        let mut newpos = 0;674        cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;675        Ok(newpos as u64)676    }677678    pub fn size(&self) -> Option<io::Result<u64>> {679        let mut result = 0;680        Some(681            cvt(unsafe { c::GetFileSizeEx(self.handle.as_raw_handle(), &mut result) })682                .map(|_| result as u64),683        )684    }685686    pub fn tell(&self) -> io::Result<u64> {687        self.seek(SeekFrom::Current(0))688    }689690    pub fn duplicate(&self) -> io::Result<File> {691        Ok(Self { handle: self.handle.try_clone()? })692    }693694    // NB: returned pointer is derived from `space`, and has provenance to695    // match. A raw pointer is returned rather than a reference in order to696    // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.697    fn reparse_point(698        &self,699        space: &mut Align8<[MaybeUninit<u8>]>,700    ) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> {701        unsafe {702            let mut bytes = 0;703            cvt({704                // Grab this in advance to avoid it invalidating the pointer705                // we get from `space.0.as_mut_ptr()`.706                let len = space.0.len();707                c::DeviceIoControl(708                    self.handle.as_raw_handle(),709                    c::FSCTL_GET_REPARSE_POINT,710                    ptr::null_mut(),711                    0,712                    space.0.as_mut_ptr().cast(),713                    len as u32,714                    &mut bytes,715                    ptr::null_mut(),716                )717            })?;718            const _: () = assert!(align_of::<c::REPARSE_DATA_BUFFER>() <= 8);719            Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))720        }721    }722723    fn readlink(&self) -> io::Result<PathBuf> {724        let mut space =725            Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);726        let (_bytes, buf) = self.reparse_point(&mut space)?;727        unsafe {728            let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {729                c::IO_REPARSE_TAG_SYMLINK => {730                    let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();731                    assert!(info.is_aligned());732                    (733                        (&raw mut (*info).PathBuffer).cast::<u16>(),734                        (*info).SubstituteNameOffset / 2,735                        (*info).SubstituteNameLength / 2,736                        (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,737                    )738                }739                c::IO_REPARSE_TAG_MOUNT_POINT => {740                    let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();741                    assert!(info.is_aligned());742                    (743                        (&raw mut (*info).PathBuffer).cast::<u16>(),744                        (*info).SubstituteNameOffset / 2,745                        (*info).SubstituteNameLength / 2,746                        false,747                    )748                }749                _ => {750                    return Err(io::const_error!(751                        io::ErrorKind::Uncategorized,752                        "Unsupported reparse point type",753                    ));754                }755            };756            let subst_ptr = path_buffer.add(subst_off.into());757            let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);758            // Absolute paths start with an NT internal namespace prefix `\??\`759            // We should not let it leak through.760            if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {761                // Turn `\??\` into `\\?\` (a verbatim path).762                subst[1] = b'\\' as u16;763                // Attempt to convert to a more user-friendly path.764                let user = crate::sys::args::from_wide_to_user_path(765                    subst.iter().copied().chain([0]).collect(),766                )?;767                Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user))))768            } else {769                Ok(PathBuf::from(OsString::from_wide(subst)))770            }771        }772    }773774    pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {775        let info = c::FILE_BASIC_INFO {776            CreationTime: 0,777            LastAccessTime: 0,778            LastWriteTime: 0,779            ChangeTime: 0,780            FileAttributes: perm.attrs,781        };782        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()783    }784785    pub fn set_times(&self, times: FileTimes) -> io::Result<()> {786        let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;787        if times.accessed.map_or(false, is_zero)788            || times.modified.map_or(false, is_zero)789            || times.created.map_or(false, is_zero)790        {791            return Err(io::const_error!(792                io::ErrorKind::InvalidInput,793                "cannot set file timestamp to 0",794            ));795        }796        let is_max = |t: c::FILETIME| t.dwLowDateTime == u32::MAX && t.dwHighDateTime == u32::MAX;797        if times.accessed.map_or(false, is_max)798            || times.modified.map_or(false, is_max)799            || times.created.map_or(false, is_max)800        {801            return Err(io::const_error!(802                io::ErrorKind::InvalidInput,803                "cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",804            ));805        }806        cvt(unsafe {807            let created =808                times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());809            let accessed =810                times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());811            let modified =812                times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());813            c::SetFileTime(self.as_raw_handle(), created, accessed, modified)814        })?;815        Ok(())816    }817818    /// Gets only basic file information such as attributes and file times.819    fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {820        unsafe {821            let mut info: c::FILE_BASIC_INFO = mem::zeroed();822            let size = size_of_val(&info);823            cvt(c::GetFileInformationByHandleEx(824                self.handle.as_raw_handle(),825                c::FileBasicInfo,826                (&raw mut info) as *mut c_void,827                size as u32,828            ))?;829            Ok(info)830        }831    }832833    /// Deletes the file, consuming the file handle to ensure the delete occurs834    /// as immediately as possible.835    /// This attempts to use `posix_delete` but falls back to `win32_delete`836    /// if that is not supported by the filesystem.837    #[allow(unused)]838    fn delete(self) -> Result<(), WinError> {839        // If POSIX delete is not supported for this filesystem then fallback to win32 delete.840        match self.posix_delete() {841            Err(WinError::INVALID_PARAMETER)842            | Err(WinError::NOT_SUPPORTED)843            | Err(WinError::INVALID_FUNCTION) => self.win32_delete(),844            result => result,845        }846    }847848    /// Delete using POSIX semantics.849    ///850    /// Files will be deleted as soon as the handle is closed. This is supported851    /// for Windows 10 1607 (aka RS1) and later. However some filesystem852    /// drivers will not support it even then, e.g. FAT32.853    ///854    /// If the operation is not supported for this filesystem or OS version855    /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.856    #[allow(unused)]857    fn posix_delete(&self) -> Result<(), WinError> {858        let info = c::FILE_DISPOSITION_INFO_EX {859            Flags: c::FILE_DISPOSITION_FLAG_DELETE860                | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS861                | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,862        };863        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)864    }865866    /// Delete a file using win32 semantics. The file won't actually be deleted867    /// until all file handles are closed. However, marking a file for deletion868    /// will prevent anyone from opening a new handle to the file.869    #[allow(unused)]870    fn win32_delete(&self) -> Result<(), WinError> {871        let info = c::FILE_DISPOSITION_INFO { DeleteFile: true };872        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)873    }874875    /// Fill the given buffer with as many directory entries as will fit.876    /// This will remember its position and continue from the last call unless877    /// `restart` is set to `true`.878    ///879    /// The returned bool indicates if there are more entries or not.880    /// It is an error if `self` is not a directory.881    ///882    /// # Symlinks and other reparse points883    ///884    /// On Windows a file is either a directory or a non-directory.885    /// A symlink directory is simply an empty directory with some "reparse" metadata attached.886    /// So if you open a link (not its target) and iterate the directory,887    /// you will always iterate an empty directory regardless of the target.888    #[allow(unused)]889    fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result<bool, WinError> {890        let class =891            if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo };892893        unsafe {894            let result = c::GetFileInformationByHandleEx(895                self.as_raw_handle(),896                class,897                buffer.as_mut_ptr().cast(),898                buffer.capacity() as _,899            );900            if result == 0 {901                let err = api::get_last_error();902                if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) }903            } else {904                Ok(true)905            }906        }907    }908}909910/// A buffer for holding directory entries.911struct DirBuff {912    buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,913}914impl DirBuff {915    const BUFFER_SIZE: usize = 1024;916    fn new() -> Self {917        Self {918            // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need919            // initialization.920            buffer: unsafe { Box::new_uninit().assume_init() },921        }922    }923    fn capacity(&self) -> usize {924        self.buffer.0.len()925    }926    fn as_mut_ptr(&mut self) -> *mut u8 {927        self.buffer.0.as_mut_ptr().cast()928    }929    /// Returns a `DirBuffIter`.930    fn iter(&self) -> DirBuffIter<'_> {931        DirBuffIter::new(self)932    }933}934impl AsRef<[MaybeUninit<u8>]> for DirBuff {935    fn as_ref(&self) -> &[MaybeUninit<u8>] {936        &self.buffer.0937    }938}939940/// An iterator over entries stored in a `DirBuff`.941///942/// Currently only returns file names (UTF-16 encoded).943struct DirBuffIter<'a> {944    buffer: Option<&'a [MaybeUninit<u8>]>,945    cursor: usize,946}947impl<'a> DirBuffIter<'a> {948    fn new(buffer: &'a DirBuff) -> Self {949        Self { buffer: Some(buffer.as_ref()), cursor: 0 }950    }951}952impl<'a> Iterator for DirBuffIter<'a> {953    type Item = (Cow<'a, [u16]>, bool);954    fn next(&mut self) -> Option<Self::Item> {955        let buffer = &self.buffer?[self.cursor..];956957        // Get the name and next entry from the buffer.958        // SAFETY:959        // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last960        //   field (the file name) is unsized. So an offset has to be used to961        //   get the file name slice.962        // - The OS has guaranteed initialization of the fields of963        //   `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least964        //   `FileNameLength` bytes)965        let (name, is_directory, next_entry) = unsafe {966            let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();967            // While this is guaranteed to be aligned in documentation for968            // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info969            // it does not seem that reality is so kind, and assuming this970            // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)971            // presumably, this can be blamed on buggy filesystem drivers, but who knows.972            let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize;973            let length = (&raw const (*info).FileNameLength).read_unaligned() as usize;974            let attrs = (&raw const (*info).FileAttributes).read_unaligned();975            let name = from_maybe_unaligned(976                (&raw const (*info).FileName).cast::<u16>(),977                length / size_of::<u16>(),978            );979            let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;980981            (name, is_directory, next_entry)982        };983984        if next_entry == 0 {985            self.buffer = None986        } else {987            self.cursor += next_entry988        }989990        // Skip `.` and `..` pseudo entries.991        const DOT: u16 = b'.' as u16;992        match &name[..] {993            [DOT] | [DOT, DOT] => self.next(),994            _ => Some((name, is_directory)),995        }996    }997}998999unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {1000    unsafe {1001        if p.is_aligned() {1002            Cow::Borrowed(crate::slice::from_raw_parts(p, len))1003        } else {1004            Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())1005        }1006    }1007}10081009impl AsInner<Handle> for File {1010    #[inline]1011    fn as_inner(&self) -> &Handle {1012        &self.handle1013    }1014}10151016impl IntoInner<Handle> for File {1017    fn into_inner(self) -> Handle {1018        self.handle1019    }1020}10211022impl FromInner<Handle> for File {1023    fn from_inner(handle: Handle) -> File {1024        File { handle }1025    }1026}10271028impl AsHandle for File {1029    fn as_handle(&self) -> BorrowedHandle<'_> {1030        self.as_inner().as_handle()1031    }1032}10331034impl AsRawHandle for File {1035    fn as_raw_handle(&self) -> RawHandle {1036        self.as_inner().as_raw_handle()1037    }1038}10391040impl IntoRawHandle for File {1041    fn into_raw_handle(self) -> RawHandle {1042        self.into_inner().into_raw_handle()1043    }1044}10451046impl FromRawHandle for File {1047    unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {1048        unsafe {1049            Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) }1050        }1051    }1052}10531054fn debug_path_handle<'a, 'b>(1055    handle: BorrowedHandle<'a>,1056    f: &'a mut fmt::Formatter<'b>,1057    name: &str,1058) -> fmt::DebugStruct<'a, 'b> {1059    // FIXME(#24570): add more info here (e.g., mode)1060    let mut b = f.debug_struct(name);1061    b.field("handle", &handle.as_raw_handle());1062    if let Ok(path) = get_path(handle) {1063        b.field("path", &path);1064    }1065    b1066}10671068impl fmt::Debug for File {1069    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {1070        let mut b = debug_path_handle(self.handle.as_handle(), f, "File");1071        b.finish()1072    }1073}10741075impl FileAttr {1076    pub fn size(&self) -> u64 {1077        self.file_size1078    }10791080    pub fn perm(&self) -> FilePermissions {1081        FilePermissions { attrs: self.attributes }1082    }10831084    pub fn attrs(&self) -> u32 {1085        self.attributes1086    }10871088    pub fn file_type(&self) -> FileType {1089        FileType::new(self.attributes, self.reparse_tag)1090    }10911092    pub fn modified(&self) -> io::Result<SystemTime> {1093        Ok(SystemTime::from(self.last_write_time))1094    }10951096    pub fn accessed(&self) -> io::Result<SystemTime> {1097        Ok(SystemTime::from(self.last_access_time))1098    }10991100    pub fn created(&self) -> io::Result<SystemTime> {1101        Ok(SystemTime::from(self.creation_time))1102    }11031104    pub fn modified_u64(&self) -> u64 {1105        to_u64(&self.last_write_time)1106    }11071108    pub fn accessed_u64(&self) -> u64 {1109        to_u64(&self.last_access_time)1110    }11111112    pub fn created_u64(&self) -> u64 {1113        to_u64(&self.creation_time)1114    }11151116    pub fn changed_u64(&self) -> Option<u64> {1117        self.change_time.as_ref().map(|c| to_u64(c))1118    }11191120    pub fn volume_serial_number(&self) -> Option<u32> {1121        self.volume_serial_number1122    }11231124    pub fn number_of_links(&self) -> Option<u32> {1125        self.number_of_links1126    }11271128    pub fn file_index(&self) -> Option<u64> {1129        self.file_index1130    }1131}1132impl From<c::WIN32_FIND_DATAW> for FileAttr {1133    fn from(wfd: c::WIN32_FIND_DATAW) -> Self {1134        FileAttr {1135            attributes: wfd.dwFileAttributes,1136            creation_time: wfd.ftCreationTime,1137            last_access_time: wfd.ftLastAccessTime,1138            last_write_time: wfd.ftLastWriteTime,1139            change_time: None,1140            file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),1141            reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {1142                // reserved unless this is a reparse point1143                wfd.dwReserved01144            } else {1145                01146            },1147            volume_serial_number: None,1148            number_of_links: None,1149            file_index: None,1150        }1151    }1152}11531154fn to_u64(ft: &c::FILETIME) -> u64 {1155    (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)1156}11571158impl FilePermissions {1159    pub fn readonly(&self) -> bool {1160        self.attrs & c::FILE_ATTRIBUTE_READONLY != 01161    }11621163    pub fn set_readonly(&mut self, readonly: bool) {1164        if readonly {1165            self.attrs |= c::FILE_ATTRIBUTE_READONLY;1166        } else {1167            self.attrs &= !c::FILE_ATTRIBUTE_READONLY;1168        }1169    }11701171    pub fn file_attributes(&self) -> u32 {1172        self.attrs as u321173    }1174}11751176impl FromInner<u32> for FilePermissions {1177    fn from_inner(attrs: u32) -> FilePermissions {1178        FilePermissions { attrs }1179    }1180}11811182impl FileTimes {1183    pub fn set_accessed(&mut self, t: SystemTime) {1184        self.accessed = Some(t.into_inner());1185    }11861187    pub fn set_modified(&mut self, t: SystemTime) {1188        self.modified = Some(t.into_inner());1189    }11901191    pub fn set_created(&mut self, t: SystemTime) {1192        self.created = Some(t.into_inner());1193    }1194}11951196impl FileType {1197    fn new(attributes: u32, reparse_tag: u32) -> FileType {1198        let is_directory = attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0;1199        let is_symlink = {1200            let is_reparse_point = attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0;1201            let is_reparse_tag_name_surrogate = reparse_tag & 0x20000000 != 0;1202            is_reparse_point && is_reparse_tag_name_surrogate1203        };1204        FileType { is_directory, is_symlink }1205    }1206    pub fn is_dir(&self) -> bool {1207        !self.is_symlink && self.is_directory1208    }1209    pub fn is_file(&self) -> bool {1210        !self.is_symlink && !self.is_directory1211    }1212    pub fn is_symlink(&self) -> bool {1213        self.is_symlink1214    }1215    pub fn is_symlink_dir(&self) -> bool {1216        self.is_symlink && self.is_directory1217    }1218    pub fn is_symlink_file(&self) -> bool {1219        self.is_symlink && !self.is_directory1220    }1221}12221223impl DirBuilder {1224    pub fn new() -> DirBuilder {1225        DirBuilder1226    }12271228    pub fn mkdir(&self, p: &Path) -> io::Result<()> {1229        let p = maybe_verbatim(p)?;1230        cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;1231        Ok(())1232    }1233}12341235pub fn readdir(p: &Path) -> io::Result<ReadDir> {1236    // We push a `*` to the end of the path which cause the empty path to be1237    // treated as the current directory. So, for consistency with other platforms,1238    // we explicitly error on the empty path.1239    if p.as_os_str().is_empty() {1240        // Return an error code consistent with other ways of opening files.1241        // E.g. fs::metadata or File::open.1242        return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));1243    }1244    let root = p.to_path_buf();1245    let star = p.join("*");1246    let path = maybe_verbatim(&star)?;12471248    unsafe {1249        let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();1250        // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw),1251        // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName1252        // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw)1253        // (which will be always null string value and currently unused) and should be faster.1254        //1255        // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more,1256        // but as we don't know user's use profile of this function, lets be conservative.1257        let find_handle = c::FindFirstFileExW(1258            path.as_ptr(),1259            c::FindExInfoBasic,1260            &mut wfd as *mut _ as _,1261            c::FindExSearchNameMatch,1262            ptr::null(),1263            0,1264        );12651266        if find_handle != c::INVALID_HANDLE_VALUE {1267            Ok(ReadDir {1268                handle: Some(FindNextFileHandle(find_handle)),1269                root: Arc::new(root),1270                first: Some(wfd),1271            })1272        } else {1273            // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function1274            // if no matching files can be found, but not necessarily that the path to find the1275            // files in does not exist.1276            //1277            // Hence, a check for whether the path to search in exists is added when the last1278            // os error returned by Windows is `ERROR_FILE_NOT_FOUND` to handle this scenario.1279            // If that is the case, an empty `ReadDir` iterator is returned as it returns `None`1280            // in the initial `.next()` invocation because `ERROR_NO_MORE_FILES` would have been1281            // returned by the `FindNextFileW` function.1282            //1283            // See issue #120040: https://github.com/rust-lang/rust/issues/120040.1284            let last_error = api::get_last_error();1285            if last_error == WinError::FILE_NOT_FOUND {1286                return Ok(ReadDir { handle: None, root: Arc::new(root), first: None });1287            }12881289            // Just return the error constructed from the raw OS error if the above is not the case.1290            //1291            // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function1292            // when the path to search in does not exist in the first place.1293            Err(Error::from_raw_os_error(last_error.code as i32))1294        }1295    }1296}12971298pub fn unlink(path: &WCStr) -> io::Result<()> {1299    if unsafe { c::DeleteFileW(path.as_ptr()) } == 0 {1300        let err = api::get_last_error();1301        // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove1302        // the file while ignoring the readonly attribute.1303        // This is accomplished by calling the `posix_delete` function on an open file handle.1304        if err == WinError::ACCESS_DENIED {1305            let mut opts = OpenOptions::new();1306            opts.access_mode(c::DELETE);1307            opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);1308            if let Ok(f) = File::open_native(&path, &opts) {1309                if f.posix_delete().is_ok() {1310                    return Ok(());1311                }1312            }1313        }1314        // return the original error if any of the above fails.1315        Err(io::Error::from_raw_os_error(err.code as i32))1316    } else {1317        Ok(())1318    }1319}13201321pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> {1322    if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 {1323        let err = api::get_last_error();1324        // if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move1325        // the file while ignoring the readonly attribute.1326        // This is accomplished by calling `SetFileInformationByHandle` with `FileRenameInfoEx`.1327        if err == WinError::ACCESS_DENIED {1328            let mut opts = OpenOptions::new();1329            opts.access_mode(c::DELETE);1330            opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);1331            let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() };13321333            // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`1334            // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.1335            let Ok(new_len_without_nul_in_bytes): Result<u32, _> =1336                ((new.count_bytes() - 1) * 2).try_into()1337            else {1338                return Err(err).io_result();1339            };1340            let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap();1341            let struct_size = offset + new_len_without_nul_in_bytes + 2;1342            let layout =1343                Layout::from_size_align(struct_size as usize, align_of::<c::FILE_RENAME_INFO>())1344                    .unwrap();13451346            let file_rename_info;1347            // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.1348            unsafe {1349                file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFO>();1350                if file_rename_info.is_null() {1351                    return Err(io::ErrorKind::OutOfMemory.into());1352                }13531354                (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 {1355                    Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS1356                        | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,1357                });13581359                (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut());1360                // Don't include the NULL in the size1361                (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes);13621363                new.as_ptr().copy_to_nonoverlapping(1364                    (&raw mut (*file_rename_info).FileName).cast::<u16>(),1365                    new.count_bytes(),1366                );1367            }13681369            let result = unsafe {1370                c::SetFileInformationByHandle(1371                    f.as_raw_handle(),1372                    c::FileRenameInfoEx,1373                    file_rename_info.cast::<c_void>(),1374                    struct_size,1375                )1376            };1377            unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };1378            if result == 0 {1379                if api::get_last_error() == WinError::DIR_NOT_EMPTY {1380                    return Err(WinError::DIR_NOT_EMPTY).io_result();1381                } else {1382                    return Err(err).io_result();1383                }1384            }1385        } else {1386            return Err(err).io_result();1387        }1388    }1389    Ok(())1390}13911392pub fn rmdir(p: &WCStr) -> io::Result<()> {1393    cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;1394    Ok(())1395}13961397pub fn remove_dir_all(path: &WCStr) -> io::Result<()> {1398    // Open a file or directory without following symlinks.1399    let mut opts = OpenOptions::new();1400    opts.access_mode(c::FILE_LIST_DIRECTORY);1401    // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.1402    // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.1403    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);1404    let file = File::open_native(path, &opts)?;14051406    // Test if the file is not a directory or a symlink to a directory.1407    if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {1408        return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));1409    }14101411    // Remove the directory and all its contents.1412    remove_dir_all_iterative(file).io_result()1413}14141415pub fn readlink(path: &WCStr) -> io::Result<PathBuf> {1416    // Open the link with no access mode, instead of generic read.1417    // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so1418    // this is needed for a common case.1419    let mut opts = OpenOptions::new();1420    opts.access_mode(0);1421    opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);1422    let file = File::open_native(&path, &opts)?;1423    file.readlink()1424}14251426pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {1427    symlink_inner(original, link, false)1428}14291430pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {1431    let original = to_u16s(original)?;1432    let link = maybe_verbatim(link)?;1433    let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };1434    // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 101435    // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the1436    // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be1437    // added to dwFlags to opt into this behavior.1438    let result = cvt(unsafe {1439        c::CreateSymbolicLinkW(1440            link.as_ptr(),1441            original.as_ptr(),1442            flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,1443        ) as c::BOOL1444    });1445    if let Err(err) = result {1446        if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {1447            // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,1448            // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.1449            cvt(unsafe {1450                c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL1451            })?;1452        } else {1453            return Err(err);1454        }1455    }1456    Ok(())1457}14581459#[cfg(not(target_vendor = "uwp"))]1460pub fn link(original: &WCStr, link: &WCStr) -> io::Result<()> {1461    cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;1462    Ok(())1463}14641465#[cfg(target_vendor = "uwp")]1466pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> {1467    return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP"));1468}14691470pub fn stat(path: &WCStr) -> io::Result<FileAttr> {1471    match metadata(path, ReparsePoint::Follow) {1472        Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {1473            if let Ok(attrs) = lstat(path) {1474                if !attrs.file_type().is_symlink() {1475                    return Ok(attrs);1476                }1477            }1478            Err(err)1479        }1480        result => result,1481    }1482}14831484pub fn lstat(path: &WCStr) -> io::Result<FileAttr> {1485    metadata(path, ReparsePoint::Open)1486}14871488#[repr(u32)]1489#[derive(Clone, Copy, PartialEq, Eq)]1490enum ReparsePoint {1491    Follow = 0,1492    Open = c::FILE_FLAG_OPEN_REPARSE_POINT,1493}1494impl ReparsePoint {1495    fn as_flag(self) -> u32 {1496        self as u321497    }1498}14991500fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result<FileAttr> {1501    let mut opts = OpenOptions::new();1502    // No read or write permissions are necessary1503    opts.access_mode(0);1504    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());15051506    // Attempt to open the file normally.1507    // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`.1508    // If the fallback fails for any reason we return the original error.1509    match File::open_native(&path, &opts) {1510        Ok(file) => file.file_attr(),1511        Err(e)1512            if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]1513                .contains(&e.raw_os_error()) =>1514        {1515            // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource.1516            // One such example is `System Volume Information` as default but can be created as well1517            // `ERROR_SHARING_VIOLATION` will almost never be returned.1518            // Usually if a file is locked you can still read some metadata.1519            // However, there are special system files, such as1520            // `C:\hiberfil.sys`, that are locked in a way that denies even that.1521            unsafe {1522                // `FindFirstFileExW` accepts wildcard file names.1523                // Fortunately wildcards are not valid file names and1524                // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)1525                // therefore it's safe to assume the file name given does not1526                // include wildcards.1527                let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();1528                let handle = c::FindFirstFileExW(1529                    path.as_ptr(),1530                    c::FindExInfoBasic,1531                    &mut wfd as *mut _ as _,1532                    c::FindExSearchNameMatch,1533                    ptr::null(),1534                    0,1535                );15361537                if handle == c::INVALID_HANDLE_VALUE {1538                    // This can fail if the user does not have read access to the1539                    // directory.1540                    Err(e)1541                } else {1542                    // We no longer need the find handle.1543                    c::FindClose(handle);15441545                    // `FindFirstFileExW` reads the cached file information from the1546                    // directory. The downside is that this metadata may be outdated.1547                    let attrs = FileAttr::from(wfd);1548                    if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {1549                        Err(e)1550                    } else {1551                        Ok(attrs)1552                    }1553                }1554            }1555        }1556        Err(e) => Err(e),1557    }1558}15591560pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {1561    unsafe {1562        cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;1563        Ok(())1564    }1565}15661567pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> {1568    let mut opts = OpenOptions::new();1569    opts.access_mode(c::FILE_WRITE_ATTRIBUTES);1570    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);1571    let file = File::open_native(p, &opts)?;1572    file.set_times(times)1573}15741575pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> {1576    let mut opts = OpenOptions::new();1577    opts.access_mode(c::FILE_WRITE_ATTRIBUTES);1578    // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior1579    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);1580    let file = File::open_native(p, &opts)?;1581    file.set_times(times)1582}15831584fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {1585    fill_utf16_buf(1586        |buf, sz| unsafe {1587            c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)1588        },1589        |buf| PathBuf::from(OsString::from_wide(buf)),1590    )1591}15921593pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {1594    let mut opts = OpenOptions::new();1595    // No read or write permissions are necessary1596    opts.access_mode(0);1597    // This flag is so we can open directories too1598    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);1599    let f = File::open_native(p, &opts)?;1600    get_path(f.handle)1601}16021603pub fn copy(from: &WCStr, to: &WCStr) -> io::Result<u64> {1604    unsafe extern "system" fn callback(1605        _TotalFileSize: i64,1606        _TotalBytesTransferred: i64,1607        _StreamSize: i64,1608        StreamBytesTransferred: i64,1609        dwStreamNumber: u32,1610        _dwCallbackReason: u32,1611        _hSourceFile: c::HANDLE,1612        _hDestinationFile: c::HANDLE,1613        lpData: *const c_void,1614    ) -> u32 {1615        unsafe {1616            if dwStreamNumber == 1 {1617                *(lpData as *mut i64) = StreamBytesTransferred;1618            }1619            c::PROGRESS_CONTINUE1620        }1621    }1622    let mut size = 0i64;1623    cvt(unsafe {1624        c::CopyFileExW(1625            from.as_ptr(),1626            to.as_ptr(),1627            Some(callback),1628            (&raw mut size) as *mut _,1629            ptr::null_mut(),1630            0,1631        )1632    })?;1633    Ok(size as u64)1634}16351636pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> {1637    // Create and open a new directory in one go.1638    let mut opts = OpenOptions::new();1639    opts.create_new(true);1640    opts.write(true);1641    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS);1642    opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY);16431644    let d = File::open(link, &opts)?;16451646    // We need to get an absolute, NT-style path.1647    let path_bytes = original.as_os_str().as_encoded_bytes();1648    let abs_path: Vec<u16> = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\")1649    {1650        // It's already an absolute path, we just need to convert the prefix to `\??\`1651        let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) };1652        r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()1653    } else {1654        // Get an absolute path and then convert the prefix to `\??\`1655        let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes();1656        if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") {1657            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) };1658            r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()1659        } else if abs_path.starts_with(br"\\.\") {1660            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) };1661            r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()1662        } else if abs_path.starts_with(br"\\") {1663            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) };1664            r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect()1665        } else {1666            return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid"));1667        }1668    };1669    // Defined inline so we don't have to mess about with variable length buffer.1670    #[repr(C)]1671    pub struct MountPointBuffer {1672        ReparseTag: u32,1673        ReparseDataLength: u16,1674        Reserved: u16,1675        SubstituteNameOffset: u16,1676        SubstituteNameLength: u16,1677        PrintNameOffset: u16,1678        PrintNameLength: u16,1679        PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],1680    }1681    let data_len = 12 + (abs_path.len() * 2);1682    if data_len > u16::MAX as usize {1683        return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long"));1684    }1685    let data_len = data_len as u16;1686    let mut header = MountPointBuffer {1687        ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT,1688        ReparseDataLength: data_len,1689        Reserved: 0,1690        SubstituteNameOffset: 0,1691        SubstituteNameLength: (abs_path.len() * 2) as u16,1692        PrintNameOffset: ((abs_path.len() + 1) * 2) as u16,1693        PrintNameLength: 0,1694        PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],1695    };1696    unsafe {1697        let ptr = header.PathBuffer.as_mut_ptr();1698        ptr.copy_from(abs_path.as_ptr().cast_uninit(), abs_path.len());16991700        let mut ret = 0;1701        cvt(c::DeviceIoControl(1702            d.as_raw_handle(),1703            c::FSCTL_SET_REPARSE_POINT,1704            (&raw const header).cast::<c_void>(),1705            data_len as u32 + 8,1706            ptr::null_mut(),1707            0,1708            &mut ret,1709            ptr::null_mut(),1710        ))1711        .map(drop)1712    }1713}17141715// Try to see if a file exists but, unlike `exists`, report I/O errors.1716pub fn exists(path: &WCStr) -> io::Result<bool> {1717    // Open the file to ensure any symlinks are followed to their target.1718    let mut opts = OpenOptions::new();1719    // No read, write, etc access rights are needed.1720    opts.access_mode(0);1721    // Backup semantics enables opening directories as well as files.1722    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);1723    match File::open_native(path, &opts) {1724        Err(e) => match e.kind() {1725            // The file definitely does not exist1726            io::ErrorKind::NotFound => Ok(false),17271728            // `ERROR_SHARING_VIOLATION` means that the file has been locked by1729            // another process. This is often temporary so we simply report it1730            // as the file existing.1731            _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),17321733            // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the1734            // reparse point could not be handled by `CreateFile`.1735            // This can happen for special files such as:1736            // * Unix domain sockets which you need to `connect` to1737            // * App exec links which require using `CreateProcess`1738            _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true),17391740            // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the1741            // file exists. However, these types of errors are usually more1742            // permanent so we report them here.1743            _ => Err(e),1744        },1745        // The file was opened successfully therefore it must exist,1746        Ok(_) => Ok(true),1747    }1748}

Code quality findings 93

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe impl Send for FindNextFileHandle {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe impl Sync for FindNextFileHandle {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe impl Send for OpenOptions {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe impl Sync for OpenOptions {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let r = unsafe { c::FindClose(self.0) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let handle = unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let result = cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let result = cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) });
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::GetFileSizeEx(self.handle.as_raw_handle(), &mut result) })
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
buffer: unsafe { Box::new_uninit().assume_init() },
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let (name, is_directory, next_entry) = unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
if unsafe { c::DeleteFileW(path.as_ptr()) } == 0 {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let result = unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let result = cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
|buf, sz| unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe extern "system" fn callback(
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
cvt(unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) };
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
match &wfd.cFileName[0..3] {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
subst[1] = b'\\' as u16;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
buffer: Option<&'a [MaybeUninit<u8>]>,
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
match &name[..] {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) };
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) };
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) };
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use crate::os::windows::prelude::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &wfd.cFileName[0..3] {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
(&raw mut info) as *mut c_void,
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
(&raw mut info) as *mut c_void,
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
(&raw mut info) as *mut c_void,
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)]
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
fn as_mut_ptr(&mut self) -> *mut u8 {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &name[..] {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
&mut wfd as *mut _ as _,
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
&mut wfd as *mut _ as _,
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
*(lpData as *mut i64) = StreamBytesTransferred;
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
(&raw mut size) as *mut _,
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match File::open_native(path, &opts) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
Err(e) => match e.kind() {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.