Skip to content

Commit fec3dc5

Browse files
committed
Reimplement Win32 APIs for GetFileInformationByHandleEx and SetFileInformationByHandle
Reimplement them on-top of native NT APIs as done in `fileextd.lib`. This allows more file system APIs to work on XP and below. Affected APIs: - File truncation (can now set end of file without multiple SetFilePointerEx calls on NT) - Cleaner fallback impl for reparse tags - remove_dir_all: Modern impl now supported on all NT-based systems
1 parent f708cc1 commit fec3dc5

6 files changed

Lines changed: 321 additions & 50 deletions

File tree

library/std/src/sys/fs/windows.rs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ impl File {
591591

592592
#[cfg(target_family = "rust9x")]
593593
pub fn truncate_inner(handle: RawHandle, size: u64) -> io::Result<()> {
594-
if c::SetFileInformationByHandle::available().is_some() {
594+
if crate::sys::compat::checks::is_windows_nt() {
595595
let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
596596
api::set_file_information_by_handle(handle, &info).io_result()
597597
} else {
@@ -620,22 +620,20 @@ impl File {
620620
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
621621
let mut reparse_tag = 0;
622622
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
623-
#[cfg(target_family = "rust9x")]
624-
let f = c::GetFileInformationByHandleEx::available();
625-
#[cfg(not(target_family = "rust9x"))]
626-
let f = Some(c::GetFileInformationByHandleEx);
627-
628-
if let Some(f) = f {
629-
let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
630-
cvt(f(
631-
self.handle.as_raw_handle(),
632-
c::FileAttributeTagInfo,
633-
(&raw mut attr_tag).cast(),
634-
size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
635-
))?;
636-
if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
637-
reparse_tag = attr_tag.ReparseTag;
638-
}
623+
let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
624+
// rust9x: If a reparse point attribute is returned, we must be on NT-based Windows
625+
// with support for FileAttributeTagInformation, so our
626+
// fallback implementation of `GetFileInformationByHandleEx` will work without
627+
// further checks.
628+
629+
cvt(c::GetFileInformationByHandleEx(
630+
self.handle.as_raw_handle(),
631+
c::FileAttributeTagInfo,
632+
(&raw mut attr_tag).cast(),
633+
mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
634+
))?;
635+
if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
636+
reparse_tag = attr_tag.ReparseTag;
639637
}
640638
}
641639
Ok(FileAttr {
@@ -988,6 +986,10 @@ impl File {
988986
/// you will always iterate an empty directory regardless of the target.
989987
#[allow(unused)]
990988
fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result<bool, WinError> {
989+
#[cfg(target_family = "rust9x")]
990+
let class =
991+
if restart { c::FileFullDirectoryRestartInfo } else { c::FileFullDirectoryInfo };
992+
#[cfg(not(target_family = "rust9x"))]
991993
let class =
992994
if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo };
993995

@@ -1064,6 +1066,9 @@ impl<'a> Iterator for DirBuffIter<'a> {
10641066
// `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
10651067
// `FileNameLength` bytes)
10661068
let (name, is_directory, next_entry) = unsafe {
1069+
#[cfg(target_family = "rust9x")]
1070+
let info = buffer.as_ptr().cast::<c::FILE_FULL_DIR_INFO>();
1071+
#[cfg(not(target_family = "rust9x"))]
10671072
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
10681073
// While this is guaranteed to be aligned in documentation for
10691074
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
@@ -1503,12 +1508,10 @@ pub fn remove_dir_all(p: &Path) -> io::Result<()> {
15031508
use crate::sys::path::with_native_path;
15041509

15051510
with_native_path(p, &|path| {
1506-
// if the modern file/directory APIs are not available, we'll fall back to the old (unsafe, see
1507-
// https://github.com/rust-lang/rust/pull/93112) directory removal implementation
1508-
if !(c::NtOpenFile::available().is_some()
1509-
&& c::GetFileInformationByHandleEx::available().is_some()
1510-
&& c::SetFileInformationByHandle::available().is_some())
1511-
{
1511+
// if the modern file/directory APIs are not available (9x/Me), we'll fall back to the old
1512+
// (unsafe, see https://github.com/rust-lang/rust/pull/93112) directory removal
1513+
// implementation
1514+
if !crate::sys::compat::checks::is_windows_nt() {
15121515
let filetype = lstat(path)?.file_type();
15131516
if filetype.is_symlink() {
15141517
// On Windows symlinks to files and directories are removed differently.

library/std/src/sys/io/is_terminal/windows.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool {
4242
// Safety: buffer length is fixed.
4343
let res = unsafe {
4444
#[cfg(target_family = "rust9x")]
45-
let Some(fun) = c::GetFileInformationByHandleEx::available() else { return false };
46-
#[cfg(not(target_family = "rust9x"))]
47-
let fun = c::GetFileInformationByHandleEx;
48-
fun(
45+
if !crate::sys::compat::checks::is_windows_nt() {
46+
return false;
47+
}
48+
49+
c::GetFileInformationByHandleEx(
4950
handle.as_raw_handle(),
5051
c::FileNameInfo,
5152
(&raw mut name_info) as *mut c_void,

library/std/src/sys/pal/windows/c.rs

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use core::ptr;
1212
mod windows_sys;
1313
pub use windows_sys::*;
1414

15+
#[cfg(target_family = "rust9x")]
16+
pub(crate) mod fileextd;
1517
#[cfg(target_family = "rust9x")]
1618
pub(crate) mod wspiapi;
1719

@@ -485,29 +487,6 @@ compat_fn_with_fallback! {
485487
TRUE
486488
}
487489
}
488-
489-
// >= Vista / Server 2008 (XP / Server 2003 when linking a supported FileExtd.lib)
490-
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle
491-
pub fn SetFileInformationByHandle(
492-
hfile: HANDLE,
493-
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
494-
lpfileinformation: *const ::core::ffi::c_void,
495-
dwbuffersize: u32,
496-
) -> BOOL {
497-
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); };
498-
FALSE
499-
}
500-
// >= Vista / Server 2008 (XP / Server 2003 when linking a supported FileExtd.lib)
501-
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
502-
pub fn GetFileInformationByHandleEx(
503-
hfile: HANDLE,
504-
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
505-
lpfileinformation: *mut ::core::ffi::c_void,
506-
dwbuffersize: u32,
507-
) -> BOOL {
508-
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); };
509-
FALSE
510-
}
511490
}
512491

513492
#[cfg(target_family = "rust9x")]
@@ -769,3 +748,55 @@ compat_fn_with_fallback! {
769748
FALSE
770749
}
771750
}
751+
752+
#[cfg(target_family = "rust9x")]
753+
pub(crate) use fileextd::{
754+
get_file_information_by_handle_ex as GetFileInformationByHandleEx,
755+
set_file_information_by_handle as SetFileInformationByHandle,
756+
};
757+
758+
#[cfg(target_family = "rust9x")]
759+
compat_fn_with_fallback! {
760+
pub static NTDLL: &CStr = c"ntdll" => { load: false, unicows: false };
761+
// all NT only
762+
fn NtQueryDirectoryFile(
763+
filehandle: HANDLE,
764+
event: HANDLE,
765+
apcroutine: PIO_APC_ROUTINE,
766+
apccontext: *const core::ffi::c_void,
767+
iostatusblock: *mut IO_STATUS_BLOCK,
768+
fileinformation: *mut core::ffi::c_void,
769+
length: u32,
770+
fileinformationclass: FILE_INFORMATION_CLASS,
771+
returnsingleentry: bool,
772+
filename: *const UNICODE_STRING,
773+
restartscan: bool
774+
) -> NTSTATUS {
775+
rtabort!("unimplemented")
776+
}
777+
fn NtQueryInformationFile(
778+
filehandle: HANDLE,
779+
iostatusblock: *mut IO_STATUS_BLOCK,
780+
fileinformation: *mut core::ffi::c_void,
781+
length: u32,
782+
fileinformationclass: FILE_INFORMATION_CLASS
783+
) -> NTSTATUS {
784+
rtabort!("unimplemented")
785+
}
786+
fn NtSetInformationFile(
787+
filehandle: HANDLE,
788+
iostatusblock: *mut IO_STATUS_BLOCK,
789+
fileinformation: *const core::ffi::c_void,
790+
length: u32,
791+
fileinformationclass: FILE_INFORMATION_CLASS
792+
) -> NTSTATUS {
793+
rtabort!("unimplemented")
794+
}
795+
fn NtWaitForSingleObject(
796+
handle: HANDLE,
797+
alertable: bool,
798+
timeout: *mut i64
799+
) -> NTSTATUS {
800+
rtabort!("unimplemented")
801+
}
802+
}

library/std/src/sys/pal/windows/c/bindings.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,13 +2047,16 @@ FILE_FLAG_SEQUENTIAL_SCAN
20472047
FILE_FLAG_SESSION_AWARE
20482048
FILE_FLAG_WRITE_THROUGH
20492049
FILE_FLAGS_AND_ATTRIBUTES
2050+
FILE_FULL_DIR_INFO
20502051
FILE_GENERIC_EXECUTE
20512052
FILE_GENERIC_READ
20522053
FILE_GENERIC_WRITE
20532054
FILE_ID_BOTH_DIR_INFO
20542055
FILE_INFO_BY_HANDLE_CLASS
2056+
FILE_INFORMATION_CLASS
20552057
FILE_IO_PRIORITY_HINT_INFO
20562058
FILE_LIST_DIRECTORY
2059+
FILE_NAME_INFO
20572060
FILE_NAME_NORMALIZED
20582061
FILE_NAME_OPENED
20592062
FILE_NO_EA_KNOWLEDGE
@@ -2109,13 +2112,19 @@ FILE_WRITE_THROUGH
21092112
FileAlignmentInfo
21102113
FileAllocationInfo
21112114
FileAttributeTagInfo
2115+
FileAttributeTagInformation
21122116
FileBasicInfo
2117+
FileBasicInformation
21132118
FileCaseSensitiveInfo
21142119
FileCompressionInfo
21152120
FileDispositionInfo
21162121
FileDispositionInfoEx
2122+
FileDispositionInformation
2123+
FileDispositionInformationEx
21172124
FileEndOfFileInfo
2125+
FileEndOfFileInformation
21182126
FileFullDirectoryInfo
2127+
FileFullDirectoryInformation
21192128
FileFullDirectoryRestartInfo
21202129
FileIdBothDirectoryInfo
21212130
FileIdBothDirectoryRestartInfo
@@ -2124,6 +2133,7 @@ FileIdExtdDirectoryRestartInfo
21242133
FileIdInfo
21252134
FileIoPriorityHintInfo
21262135
FileNameInfo
2136+
FileNameInformation
21272137
FileNormalizedNameInfo
21282138
FileRemoteProtocolInfo
21292139
FileRenameInfo
@@ -2324,8 +2334,12 @@ NtCreateFile
23242334
NTCREATEFILE_CREATE_DISPOSITION
23252335
NTCREATEFILE_CREATE_OPTIONS
23262336
NtOpenFile
2337+
NtQueryDirectoryFile
2338+
NtQueryInformationFile
23272339
NtReadFile
2340+
NtSetInformationFile
23282341
NTSTATUS
2342+
NtWaitForSingleObject
23292343
NtWriteFile
23302344
OBJ_CASE_INSENSITIVE
23312345
OBJ_DONT_REPARSE

0 commit comments

Comments
 (0)