From 22a9a8b76ec7a40eb5fdbe04e69aa8ac15b5a0f7 Mon Sep 17 00:00:00 2001 From: klensy Date: Sun, 20 Oct 2024 15:13:36 +0300 Subject: [PATCH 1/3] replace FindFirstFileW with FindFirstFileExW and regenerate bindings --- library/std/src/sys/pal/windows/c/bindings.txt | 4 +++- library/std/src/sys/pal/windows/c/windows_sys.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 9c2e4500da068..192c95fd203c6 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2337,7 +2337,9 @@ Windows.Win32.Storage.FileSystem.FileStandardInfo Windows.Win32.Storage.FileSystem.FileStorageInfo Windows.Win32.Storage.FileSystem.FileStreamInfo Windows.Win32.Storage.FileSystem.FindClose -Windows.Win32.Storage.FileSystem.FindFirstFileW +Windows.Win32.Storage.FileSystem.FindExInfoBasic +Windows.Win32.Storage.FileSystem.FindExSearchNameMatch +Windows.Win32.Storage.FileSystem.FindFirstFileExW Windows.Win32.Storage.FileSystem.FindNextFileW Windows.Win32.Storage.FileSystem.FlushFileBuffers Windows.Win32.Storage.FileSystem.GetFileAttributesW diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index ab5f8919d7af6..52444c2c009ed 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -26,7 +26,7 @@ windows_targets::link!("kernel32.dll" "system" fn DeviceIoControl(hdevice : HAND windows_targets::link!("kernel32.dll" "system" fn DuplicateHandle(hsourceprocesshandle : HANDLE, hsourcehandle : HANDLE, htargetprocesshandle : HANDLE, lptargethandle : *mut HANDLE, dwdesiredaccess : u32, binherithandle : BOOL, dwoptions : DUPLICATE_HANDLE_OPTIONS) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn ExitProcess(uexitcode : u32) -> !); windows_targets::link!("kernel32.dll" "system" fn FindClose(hfindfile : HANDLE) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn FindFirstFileW(lpfilename : PCWSTR, lpfindfiledata : *mut WIN32_FIND_DATAW) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn FindFirstFileExW(lpfilename : PCWSTR, finfolevelid : FINDEX_INFO_LEVELS, lpfindfiledata : *mut core::ffi::c_void, fsearchop : FINDEX_SEARCH_OPS, lpsearchfilter : *const core::ffi::c_void, dwadditionalflags : FIND_FIRST_EX_FLAGS) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn FindNextFileW(hfindfile : HANDLE, lpfindfiledata : *mut WIN32_FIND_DATAW) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn FlushFileBuffers(hfile : HANDLE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32); @@ -2501,6 +2501,9 @@ pub const FILE_WRITE_ATTRIBUTES: FILE_ACCESS_RIGHTS = 256u32; pub const FILE_WRITE_DATA: FILE_ACCESS_RIGHTS = 2u32; pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32; pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; +pub type FINDEX_INFO_LEVELS = i32; +pub type FINDEX_SEARCH_OPS = i32; +pub type FIND_FIRST_EX_FLAGS = u32; pub const FIONBIO: i32 = -2147195266i32; #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] @@ -2565,6 +2568,8 @@ pub const FileRenameInfoEx: FILE_INFO_BY_HANDLE_CLASS = 22i32; pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32; pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32; pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32; +pub const FindExInfoBasic: FINDEX_INFO_LEVELS = 1i32; +pub const FindExSearchNameMatch: FINDEX_SEARCH_OPS = 0i32; pub type GENERIC_ACCESS_RIGHTS = u32; pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32; pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32; @@ -3307,7 +3312,6 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } - #[cfg(target_arch = "arm")] #[repr(C)] pub struct WSADATA { From 8abe67c949d55dfaf4196fcc89e0c9b4b02c439e Mon Sep 17 00:00:00 2001 From: klensy Date: Sun, 20 Oct 2024 16:02:00 +0300 Subject: [PATCH 2/3] replace FindFirstFileW with FindFirstFileExW and apply optimization --- library/std/src/sys/pal/windows/fs.rs | 29 +++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index aab471e28eaeb..6ec1f68855aab 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1047,8 +1047,22 @@ pub fn readdir(p: &Path) -> io::Result { let path = maybe_verbatim(&star)?; unsafe { - let mut wfd = mem::zeroed(); - let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); + let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); + // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw), + // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName + // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw) + // (which will be always null string value and currently unused) and should be faster. + // + // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more, + // but as we don't know user's use profile of this function, lets be conservative. + let find_handle = c::FindFirstFileExW( + path.as_ptr(), + c::FindExInfoBasic, + &mut wfd as *mut _ as _, + c::FindExSearchNameMatch, + ptr::null(), + 0, + ); if find_handle != c::INVALID_HANDLE_VALUE { Ok(ReadDir { @@ -1242,8 +1256,15 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) // therefore it's safe to assume the file name given does not // include wildcards. - let mut wfd = mem::zeroed(); - let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); + let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); + let handle = c::FindFirstFileExW( + path.as_ptr(), + c::FindExInfoBasic, + &mut wfd as *mut _ as _, + c::FindExSearchNameMatch, + ptr::null(), + 0, + ); if handle == c::INVALID_HANDLE_VALUE { // This can fail if the user does not have read access to the From 2920ed0999ff462c13d9991e3a2a81b234836571 Mon Sep 17 00:00:00 2001 From: klensy Date: Sun, 20 Oct 2024 18:23:46 +0300 Subject: [PATCH 3/3] fix docs --- library/std/src/fs.rs | 2 +- library/std/src/sys/pal/windows/fs.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 675140ff18f5f..8a0d2a7f5cfcf 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2618,7 +2618,7 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { /// # Platform-specific behavior /// /// This function currently corresponds to the `opendir` function on Unix -/// and the `FindFirstFile` function on Windows. Advancing the iterator +/// and the `FindFirstFileEx` function on Windows. Advancing the iterator /// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows. /// Note that, this [may change in the future][changes]. /// diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 6ec1f68855aab..b237fa481e287 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -114,7 +114,7 @@ impl Iterator for ReadDir { fn next(&mut self) -> Option> { if self.handle.0 == c::INVALID_HANDLE_VALUE { // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle. - // Simply return `None` because this is only the case when `FindFirstFileW` in + // Simply return `None` because this is only the case when `FindFirstFileExW` in // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means // no matchhing files can be found. return None; @@ -1071,7 +1071,7 @@ pub fn readdir(p: &Path) -> io::Result { first: Some(wfd), }) } else { - // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileW` function + // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function // if no matching files can be found, but not necessarily that the path to find the // files in does not exist. // @@ -1093,7 +1093,7 @@ pub fn readdir(p: &Path) -> io::Result { // Just return the error constructed from the raw OS error if the above is not the case. // - // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileW` function + // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function // when the path to search in does not exist in the first place. Err(Error::from_raw_os_error(last_error.code as i32)) } @@ -1234,7 +1234,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); // Attempt to open the file normally. - // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`. + // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`. // If the fallback fails for any reason we return the original error. match File::open(path, &opts) { Ok(file) => file.file_attr(), @@ -1251,7 +1251,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { unsafe { let path = maybe_verbatim(path)?; - // `FindFirstFileW` accepts wildcard file names. + // `FindFirstFileExW` accepts wildcard file names. // Fortunately wildcards are not valid file names and // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) // therefore it's safe to assume the file name given does not @@ -1274,7 +1274,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { // We no longer need the find handle. c::FindClose(handle); - // `FindFirstFileW` reads the cached file information from the + // `FindFirstFileExW` reads the cached file information from the // directory. The downside is that this metadata may be outdated. let attrs = FileAttr::from(wfd); if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {