From 6d48778991e06c9a5886be03be053f2d577f8d7e Mon Sep 17 00:00:00 2001 From: Lukasz Czerniawski Date: Fri, 2 Aug 2024 17:39:35 +0200 Subject: [PATCH] Fix NtQueryInformationFile and get ChangeTime from single sys call --- ext/fs/std_fs.rs | 54 +++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 5d12726547..8a031e55de 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -821,35 +821,6 @@ fn stat_extra( Ok(info.dwVolumeSerialNumber as u64) } - unsafe fn get_change_time( - handle: winapi::shared::ntdef::HANDLE, - ) -> std::io::Result { - use winapi::shared::minwindef::FALSE; - use winapi::um::fileapi::FILE_BASIC_INFO; - use winapi::um::minwinbase::FileBasicInfo; - use winapi::um::winbase::GetFileInformationByHandleEx; - - let file_info = { - let mut file_info = std::mem::MaybeUninit::::zeroed(); - if GetFileInformationByHandleEx( - handle, - FileBasicInfo, - &mut file_info as *mut _ as *mut _, - std::mem::size_of::() as u32, - ) == FALSE - { - return Err(std::io::Error::last_os_error()); - } - - file_info.assume_init() - }; - - let change_time = file_info.ChangeTime.QuadPart(); - let unix_time_msec = windows_time_to_unix_time_msec(change_time); - - Ok(unix_time_msec as u64) - } - const WINDOWS_TICK: i64 = 10_000; // 100-nanosecond intervals in a millisecond const SEC_TO_UNIX_EPOCH: i64 = 11_644_473_600; // Seconds between Windows epoch and Unix epoch @@ -859,23 +830,36 @@ fn stat_extra( } use windows_sys::Wdk::Storage::FileSystem::FILE_ALL_INFORMATION; + use windows_sys::Win32::Foundation::NTSTATUS; unsafe fn query_file_information( handle: winapi::shared::ntdef::HANDLE, - ) -> std::io::Result { + ) -> Result { + use windows_sys::Win32::Foundation::RtlNtStatusToDosError; + use windows_sys::Win32::Foundation::ERROR_MORE_DATA; + use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; use windows_sys::Wdk::Storage::FileSystem::NtQueryInformationFile; let mut info = std::mem::MaybeUninit::::zeroed(); + let mut io_status_block = std::mem::MaybeUninit::::zeroed(); let status = NtQueryInformationFile( handle as _, - std::ptr::null_mut(), + io_status_block.as_mut_ptr(), info.as_mut_ptr() as *mut _, std::mem::size_of::() as _, 18, /* FileAllInformation */ ); if status < 0 { - return Err(std::io::Error::last_os_error()); + let converted_status = RtlNtStatusToDosError(status); + + // If error more data is returned, then it means that the buffer is too small to get full filename information + // to have that we should retry. However, since we only use BasicInformation and StandardInformation, it is fine to ignore it + // since struct is populated with other data anyway. + + if converted_status != ERROR_MORE_DATA { + return Err(converted_status as NTSTATUS); + } } Ok(info.assume_init()) @@ -900,11 +884,10 @@ fn stat_extra( let result = get_dev(file_handle); fsstat.dev = result?; - fsstat.ctime = get_change_time(file_handle).ok(); - - CloseHandle(file_handle); if let Ok(file_info) = query_file_information(file_handle) { + fsstat.ctime = Some(windows_time_to_unix_time_msec(&file_info.BasicInformation.ChangeTime) as u64); + if file_info.BasicInformation.FileAttributes & winapi::um::winnt::FILE_ATTRIBUTE_REPARSE_POINT != 0 @@ -937,6 +920,7 @@ fn stat_extra( } } + CloseHandle(file_handle); Ok(()) } }