Skip to content

Commit

Permalink
Reimplement Win32 APIs for GetFileInformationByHandleEx and `SetFil…
Browse files Browse the repository at this point in the history
…eInformationByHandle`

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
  • Loading branch information
seritools committed Dec 25, 2024
1 parent 44a13b9 commit 01c6da5
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 48 deletions.
73 changes: 50 additions & 23 deletions library/std/src/sys/pal/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use core::{mem, ptr};
mod windows_sys;
pub use windows_sys::*;

#[cfg(target_vendor = "rust9x")]
pub(crate) mod fileextd;
#[cfg(target_vendor = "rust9x")]
pub(crate) mod wspiapi;

Expand Down Expand Up @@ -436,29 +438,6 @@ compat_fn_with_fallback! {
TRUE
}
}

// >= Vista / Server 2008 (XP / Server 2003 when linking a supported FileExtd.lib)
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle
pub fn SetFileInformationByHandle(
hfile: HANDLE,
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
lpfileinformation: *const ::core::ffi::c_void,
dwbuffersize: u32,
) -> BOOL {
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); };
FALSE
}
// >= Vista / Server 2008 (XP / Server 2003 when linking a supported FileExtd.lib)
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
pub fn GetFileInformationByHandleEx(
hfile: HANDLE,
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
lpfileinformation: *mut ::core::ffi::c_void,
dwbuffersize: u32,
) -> BOOL {
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); };
FALSE
}
}

#[cfg(target_vendor = "rust9x")]
Expand Down Expand Up @@ -704,3 +683,51 @@ compat_fn_with_fallback! {
FALSE
}
}

#[cfg(target_vendor = "rust9x")]
pub(crate) use fileextd::{
get_file_information_by_handle_ex as GetFileInformationByHandleEx,
set_file_information_by_handle as SetFileInformationByHandle,
};
#[cfg(target_vendor = "rust9x")]
compat_fn_with_fallback! {
pub static NTDLL: &CStr = c"ntdll" => { load: true, unicows: false };
// all NT only
fn NtQueryDirectoryFile(
filehandle: HANDLE,
event: HANDLE,
apcroutine: PIO_APC_ROUTINE,
apccontext: *const core::ffi::c_void,
iostatusblock: *mut IO_STATUS_BLOCK,
fileinformation: *mut core::ffi::c_void,
length: u32,
fileinformationclass: FILE_INFORMATION_CLASS,
returnsingleentry: BOOLEAN,
filename: *const UNICODE_STRING,
restartscan: BOOLEAN
) -> NTSTATUS {
rtabort!("unimplemented")
}
fn NtQueryInformationFile(filehandle: HANDLE,
iostatusblock: *mut IO_STATUS_BLOCK,
fileinformation: *mut core::ffi::c_void,
length: u32,
fileinformationclass: FILE_INFORMATION_CLASS
) -> NTSTATUS {
rtabort!("unimplemented")
}
fn NtSetInformationFile(filehandle: HANDLE,
iostatusblock: *mut IO_STATUS_BLOCK,
fileinformation: *const core::ffi::c_void,
length: u32,
fileinformationclass: FILE_INFORMATION_CLASS
) -> NTSTATUS {
rtabort!("unimplemented")
}
fn NtWaitForSingleObject(handle: HANDLE,
alertable: BOOLEAN,
timeout: *mut i64
) -> NTSTATUS {
rtabort!("unimplemented")
}
}
15 changes: 15 additions & 0 deletions library/std/src/sys/pal/windows/c/bindings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Windows.Wdk.Storage.FileSystem.FILE_CREATE_TREE_CONNECTION
Windows.Wdk.Storage.FileSystem.FILE_DELETE_ON_CLOSE
Windows.Wdk.Storage.FileSystem.FILE_DIRECTORY_FILE
Windows.Wdk.Storage.FileSystem.FILE_DISALLOW_EXCLUSIVE
Windows.Wdk.Storage.FileSystem.FILE_INFORMATION_CLASS
Windows.Wdk.Storage.FileSystem.FILE_NO_COMPRESSION
Windows.Wdk.Storage.FileSystem.FILE_NO_EA_KNOWLEDGE
Windows.Wdk.Storage.FileSystem.FILE_NO_INTERMEDIATE_BUFFERING
Expand All @@ -31,13 +32,25 @@ Windows.Wdk.Storage.FileSystem.FILE_SUPERSEDE
Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_ALERT
Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_NONALERT
Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH
Windows.Wdk.Storage.FileSystem.FileAttributeTagInformation
Windows.Wdk.Storage.FileSystem.FileBasicInformation
Windows.Wdk.Storage.FileSystem.FileDispositionInformation
Windows.Wdk.Storage.FileSystem.FileDispositionInformationEx
Windows.Wdk.Storage.FileSystem.FileEndOfFileInformation
Windows.Wdk.Storage.FileSystem.FileFullDirectoryInformation
Windows.Wdk.Storage.FileSystem.FileFullDirectoryInformation
Windows.Wdk.Storage.FileSystem.FileNameInformation
Windows.Wdk.Storage.FileSystem.NtCreateFile
Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION
Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS
Windows.Wdk.Storage.FileSystem.NtOpenFile
Windows.Wdk.Storage.FileSystem.NtQueryDirectoryFile
Windows.Wdk.Storage.FileSystem.NtQueryInformationFile
Windows.Wdk.Storage.FileSystem.NtReadFile
Windows.Wdk.Storage.FileSystem.NtSetInformationFile
Windows.Wdk.Storage.FileSystem.NtWriteFile
Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE
Windows.Wdk.System.Threading.NtWaitForSingleObject
Windows.Win32.Foundation.BOOL
Windows.Win32.Foundation.BOOLEAN
Windows.Win32.Foundation.CloseHandle
Expand Down Expand Up @@ -2295,13 +2308,15 @@ Windows.Win32.Storage.FileSystem.FILE_FLAG_SEQUENTIAL_SCAN
Windows.Win32.Storage.FileSystem.FILE_FLAG_SESSION_AWARE
Windows.Win32.Storage.FileSystem.FILE_FLAG_WRITE_THROUGH
Windows.Win32.Storage.FileSystem.FILE_FLAGS_AND_ATTRIBUTES
Windows.Win32.Storage.FileSystem.FILE_FULL_DIR_INFO
Windows.Win32.Storage.FileSystem.FILE_GENERIC_EXECUTE
Windows.Win32.Storage.FileSystem.FILE_GENERIC_READ
Windows.Win32.Storage.FileSystem.FILE_GENERIC_WRITE
Windows.Win32.Storage.FileSystem.FILE_ID_BOTH_DIR_INFO
Windows.Win32.Storage.FileSystem.FILE_INFO_BY_HANDLE_CLASS
Windows.Win32.Storage.FileSystem.FILE_IO_PRIORITY_HINT_INFO
Windows.Win32.Storage.FileSystem.FILE_LIST_DIRECTORY
Windows.Win32.Storage.FileSystem.FILE_NAME_INFO
Windows.Win32.Storage.FileSystem.FILE_NAME_NORMALIZED
Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED
Windows.Win32.Storage.FileSystem.FILE_READ_ATTRIBUTES
Expand Down
174 changes: 174 additions & 0 deletions library/std/src/sys/pal/windows/c/fileextd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//! Manual implementation of a subset of `GetFileInformationByHandleEx` and
//! `SetFileInformationByHandle` based on `fileextd.lib` from Microsoft. Many of their later-added
//! APIs do exist all the way back to NT3.1, but were not exposed via a Win32 API until later.
use super::*;

pub(crate) unsafe fn get_file_information_by_handle_ex(
file_handle: HANDLE,
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
lpfileinformation: *mut core::ffi::c_void,
dwbuffersize: u32,
) -> BOOL {
if !crate::sys::compat::checks::is_windows_nt() {
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED) };
return FALSE;
}

unsafe {
#[allow(non_upper_case_globals)]
let (class, min_buffer_size, use_directory_api_info): (
FILE_INFORMATION_CLASS,
u32,
Option<bool>,
) = match fileinformationclass {
FileBasicInfo => {
(FileBasicInformation, core::mem::size_of::<FILE_BASIC_INFO>() as u32, None)
}
FileNameInfo => {
(FileNameInformation, core::mem::size_of::<FILE_NAME_INFO>() as u32, None)
}
FileAttributeTagInfo => (
// NT API: 2000 and up
FileAttributeTagInformation,
core::mem::size_of::<FILE_ATTRIBUTE_TAG_INFO>() as u32,
None,
),
// rust9x: replaced with the following two
// FileIdBothDirectoryInfo => (
// // NT API: XP/2003 and up; exposed via Win32 API since Vista
// FileIdBothDirectoryInformation,
// core::mem::size_of::<FILE_ID_BOTH_DIR_INFO>() as u32,
// Some(false),
// ),
// FileIdBothDirectoryRestartInfo => (
// // NT API: XP/2003 and up; exposed via Win32 API since Vista
// FileIdBothDirectoryInformation,
// core::mem::size_of::<FILE_ID_BOTH_DIR_INFO>() as u32,
// Some(true),
// ),
FileFullDirectoryInfo => (
// NT API: at least NT4 but likely 3.1+; exposed via Win32 API since Win8
FileFullDirectoryInformation,
core::mem::size_of::<FILE_FULL_DIR_INFO>() as u32,
Some(false),
),
FileFullDirectoryRestartInfo => (
// NT API: at least NT4 but likely 3.1+; exposed via Win32 API since Win8
FileFullDirectoryInformation,
core::mem::size_of::<FILE_FULL_DIR_INFO>() as u32,
Some(true),
),
_ => {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
};

if dwbuffersize < min_buffer_size {
SetLastError(ERROR_BAD_LENGTH);
return FALSE;
}

let mut io_status_block: IO_STATUS_BLOCK = core::mem::zeroed();
let mut status = if let Some(restart_scan) = use_directory_api_info {
NtQueryDirectoryFile(
file_handle,
core::ptr::null_mut(),
None,
core::ptr::null(),
&mut io_status_block,
lpfileinformation,
dwbuffersize,
class,
false as BOOLEAN,
core::ptr::null(),
restart_scan as BOOLEAN,
)
} else {
NtQueryInformationFile(
file_handle,
&mut io_status_block,
lpfileinformation,
dwbuffersize,
class,
)
};

if status == STATUS_PENDING {
status = NtWaitForSingleObject(file_handle, false as BOOLEAN, core::ptr::null_mut());
}

if status < 0 {
set_last_error_from_ntstatus(status);
return FALSE;
}

// keeping for reference; FileStreamInformation is not used by the standard library
// currently if class == FileStreamInformation && io_status_block.Information == 0 {
// set_last_error_from_ntstatus(STATUS_END_OF_FILE); return FALSE; }
}
TRUE
}

pub(crate) unsafe fn set_file_information_by_handle(
file_handle: HANDLE,
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
lpfileinformation: *const core::ffi::c_void,
dwbuffersize: u32,
) -> BOOL {
if !crate::sys::compat::checks::is_windows_nt() {
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED) };
return FALSE;
}

unsafe {
#[allow(non_upper_case_globals)]
let (class, min_buffer_size): (FILE_INFORMATION_CLASS, u32) = match fileinformationclass {
FileBasicInfo => (FileBasicInformation, core::mem::size_of::<FILE_BASIC_INFO>() as u32),
FileEndOfFileInfo => {
(FileEndOfFileInformation, core::mem::size_of::<FILE_END_OF_FILE_INFO>() as u32)
}
FileDispositionInfo => {
(FileDispositionInformation, core::mem::size_of::<FILE_DISPOSITION_INFO>() as u32)
}
FileDispositionInfoEx => (
// NT API: some Windows 10 version
FileDispositionInformationEx,
core::mem::size_of::<FILE_DISPOSITION_INFO_EX>() as u32,
),
_ => {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
};

if dwbuffersize < min_buffer_size {
SetLastError(ERROR_BAD_LENGTH);
return FALSE;
}

let mut io_status_block: IO_STATUS_BLOCK = core::mem::zeroed();
let status = NtSetInformationFile(
file_handle,
&mut io_status_block,
lpfileinformation,
dwbuffersize,
class,
);

if status < 0 {
set_last_error_from_ntstatus(status);
return FALSE;
}
}

TRUE
}

fn set_last_error_from_ntstatus(status: NTSTATUS) {
unsafe {
let error = RtlNtStatusToDosError(status);
SetLastError(error);
}
}
34 changes: 34 additions & 0 deletions library/std/src/sys/pal/windows/c/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ windows_targets::link!("kernel32.dll" "system" fn WriteFile(hfile : HANDLE, lpbu
windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtQueryDirectoryFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, fileinformation : *mut core::ffi::c_void, length : u32, fileinformationclass : FILE_INFORMATION_CLASS, returnsingleentry : BOOLEAN, filename : *const UNICODE_STRING, restartscan : BOOLEAN) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtQueryInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fileinformation : *mut core::ffi::c_void, length : u32, fileinformationclass : FILE_INFORMATION_CLASS) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtSetInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fileinformation : *const core::ffi::c_void, length : u32, fileinformationclass : FILE_INFORMATION_CLASS) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtWaitForSingleObject(handle : HANDLE, alertable : BOOLEAN, timeout : *mut i64) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL);
Expand Down Expand Up @@ -2477,6 +2481,22 @@ pub const FILE_FLAG_RANDOM_ACCESS: FILE_FLAGS_AND_ATTRIBUTES = 268435456u32;
pub const FILE_FLAG_SEQUENTIAL_SCAN: FILE_FLAGS_AND_ATTRIBUTES = 134217728u32;
pub const FILE_FLAG_SESSION_AWARE: FILE_FLAGS_AND_ATTRIBUTES = 8388608u32;
pub const FILE_FLAG_WRITE_THROUGH: FILE_FLAGS_AND_ATTRIBUTES = 2147483648u32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FILE_FULL_DIR_INFO {
pub NextEntryOffset: u32,
pub FileIndex: u32,
pub CreationTime: i64,
pub LastAccessTime: i64,
pub LastWriteTime: i64,
pub ChangeTime: i64,
pub EndOfFile: i64,
pub AllocationSize: i64,
pub FileAttributes: u32,
pub FileNameLength: u32,
pub EaSize: u32,
pub FileName: [u16; 1],
}
pub const FILE_GENERIC_EXECUTE: FILE_ACCESS_RIGHTS = 1179808u32;
pub const FILE_GENERIC_READ: FILE_ACCESS_RIGHTS = 1179785u32;
pub const FILE_GENERIC_WRITE: FILE_ACCESS_RIGHTS = 1179926u32;
Expand All @@ -2499,13 +2519,20 @@ pub struct FILE_ID_BOTH_DIR_INFO {
pub FileId: i64,
pub FileName: [u16; 1],
}
pub type FILE_INFORMATION_CLASS = i32;
pub type FILE_INFO_BY_HANDLE_CLASS = i32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FILE_IO_PRIORITY_HINT_INFO {
pub PriorityHint: PRIORITY_HINT,
}
pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FILE_NAME_INFO {
pub FileNameLength: u32,
pub FileName: [u16; 1],
}
pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32;
pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32;
pub const FILE_NON_DIRECTORY_FILE: NTCREATEFILE_CREATE_OPTIONS = 64u32;
Expand Down Expand Up @@ -2602,13 +2629,19 @@ pub const FSCTL_SET_REPARSE_POINT: u32 = 589988u32;
pub const FileAlignmentInfo: FILE_INFO_BY_HANDLE_CLASS = 17i32;
pub const FileAllocationInfo: FILE_INFO_BY_HANDLE_CLASS = 5i32;
pub const FileAttributeTagInfo: FILE_INFO_BY_HANDLE_CLASS = 9i32;
pub const FileAttributeTagInformation: FILE_INFORMATION_CLASS = 35i32;
pub const FileBasicInfo: FILE_INFO_BY_HANDLE_CLASS = 0i32;
pub const FileBasicInformation: FILE_INFORMATION_CLASS = 4i32;
pub const FileCaseSensitiveInfo: FILE_INFO_BY_HANDLE_CLASS = 23i32;
pub const FileCompressionInfo: FILE_INFO_BY_HANDLE_CLASS = 8i32;
pub const FileDispositionInfo: FILE_INFO_BY_HANDLE_CLASS = 4i32;
pub const FileDispositionInfoEx: FILE_INFO_BY_HANDLE_CLASS = 21i32;
pub const FileDispositionInformation: FILE_INFORMATION_CLASS = 13i32;
pub const FileDispositionInformationEx: FILE_INFORMATION_CLASS = 64i32;
pub const FileEndOfFileInfo: FILE_INFO_BY_HANDLE_CLASS = 6i32;
pub const FileEndOfFileInformation: FILE_INFORMATION_CLASS = 20i32;
pub const FileFullDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 14i32;
pub const FileFullDirectoryInformation: FILE_INFORMATION_CLASS = 2i32;
pub const FileFullDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 15i32;
pub const FileIdBothDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 10i32;
pub const FileIdBothDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 11i32;
Expand All @@ -2617,6 +2650,7 @@ pub const FileIdExtdDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 20i32;
pub const FileIdInfo: FILE_INFO_BY_HANDLE_CLASS = 18i32;
pub const FileIoPriorityHintInfo: FILE_INFO_BY_HANDLE_CLASS = 12i32;
pub const FileNameInfo: FILE_INFO_BY_HANDLE_CLASS = 2i32;
pub const FileNameInformation: FILE_INFORMATION_CLASS = 9i32;
pub const FileNormalizedNameInfo: FILE_INFO_BY_HANDLE_CLASS = 24i32;
pub const FileRemoteProtocolInfo: FILE_INFO_BY_HANDLE_CLASS = 13i32;
pub const FileRenameInfo: FILE_INFO_BY_HANDLE_CLASS = 3i32;
Expand Down
Loading

0 comments on commit 01c6da5

Please sign in to comment.