Skip to content

Commit cd96968

Browse files
committed
Add fallback impl for File::truncate and File::seek
- Fallback for `SetFilePointerEx` based on `SetFilePointer` - Fallback for `SetFileInformationByHandle` with `FileEndOfFileInfo` based on `SetFilePointerEx` and `SetEndOfFile`
1 parent e1ec85f commit cd96968

File tree

5 files changed

+98
-20
lines changed

5 files changed

+98
-20
lines changed

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,52 @@ compat_fn_with_fallback! {
350350
GetSystemTime(&mut st);
351351
crate::sys::cvt(SystemTimeToFileTime(&st, lpSystemTimeAsFileTime)).unwrap();
352352
}
353+
354+
// >= 2000
355+
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointerex
356+
pub fn SetFilePointerEx(
357+
hfile: HANDLE,
358+
lidistancetomove: i64,
359+
lpnewfilepointer: *mut i64,
360+
dwmovemethod: SET_FILE_POINTER_MOVE_METHOD,
361+
) -> BOOL {
362+
let lDistanceToMove = lidistancetomove as i32;
363+
let mut distance_to_move_high = (lidistancetomove >> 32) as i32;
364+
365+
let newPos_low = SetFilePointer(hfile, lDistanceToMove, &mut distance_to_move_high, dwmovemethod);
366+
367+
// since (-1 as u32) could be a valid value for the lower 32 bits of the new file pointer
368+
// position, a call to GetLastError is needed to actually see if it failed
369+
if newPos_low == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR {
370+
return FALSE;
371+
}
372+
373+
if !lpnewfilepointer.is_null() {
374+
*lpnewfilepointer = (distance_to_move_high as i64) << 32 | (newPos_low as i64);
375+
}
376+
377+
TRUE
378+
}
379+
}
380+
381+
compat_fn_lazy! {
382+
pub static KERNEL32: &CStr = c"kernel32" => { load: false, unicows: false };
383+
// >= Vista / Server 2008 (XP / Server 2003 when linking a supported FileExtd.lib)
384+
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle
385+
pub fn SetFileInformationByHandle(
386+
hfile: HANDLE,
387+
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
388+
lpfileinformation: *const ::core::ffi::c_void,
389+
dwbuffersize: u32,
390+
) -> BOOL;
391+
// >= Vista / Server 2008 (XP / Server 2003 when linking a supported FileExtd.lib)
392+
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
393+
pub fn GetFileInformationByHandleEx(
394+
hfile: HANDLE,
395+
fileinformationclass: FILE_INFO_BY_HANDLE_CLASS,
396+
lpfileinformation: *mut ::core::ffi::c_void,
397+
dwbuffersize: u32,
398+
) -> BOOL;
353399
}
354400

355401
compat_fn_optional! {

library/std/src/sys/windows/c/windows_sys.lst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,3 +2612,8 @@ Windows.Win32.System.Threading.CONDITION_VARIABLE
26122612
// system time fallback
26132613
Windows.Win32.System.SystemInformation.GetSystemTime
26142614
Windows.Win32.System.Time.SystemTimeToFileTime
2615+
2616+
// seek and truncate fallbacks
2617+
Windows.Win32.Storage.FileSystem.SetFilePointer
2618+
Windows.Win32.Storage.FileSystem.SetEndOfFile
2619+
Windows.Win32.Storage.FileSystem.INVALID_SET_FILE_POINTER

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@ extern "system" {
516516
pub fn SetCurrentDirectoryW(lppathname: PCWSTR) -> BOOL;
517517
}
518518
#[link(name = "kernel32")]
519+
extern "system" {
520+
pub fn SetEndOfFile(hfile: HANDLE) -> BOOL;
521+
}
522+
#[link(name = "kernel32")]
519523
extern "system" {
520524
pub fn SetEnvironmentVariableW(lpname: PCWSTR, lpvalue: PCWSTR) -> BOOL;
521525
}
@@ -536,6 +540,15 @@ extern "system" {
536540
) -> BOOL;
537541
}
538542
#[link(name = "kernel32")]
543+
extern "system" {
544+
pub fn SetFilePointer(
545+
hfile: HANDLE,
546+
ldistancetomove: i32,
547+
lpdistancetomovehigh: *mut i32,
548+
dwmovemethod: SET_FILE_POINTER_MOVE_METHOD,
549+
) -> u32;
550+
}
551+
#[link(name = "kernel32")]
539552
extern "system" {
540553
pub fn SetFilePointerEx(
541554
hfile: HANDLE,
@@ -3585,6 +3598,7 @@ impl ::core::clone::Clone for INIT_ONCE {
35853598
}
35863599
pub const INIT_ONCE_INIT_FAILED: u32 = 4u32;
35873600
pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32;
3601+
pub const INVALID_SET_FILE_POINTER: u32 = 4294967295u32;
35883602
pub const INVALID_SOCKET: SOCKET = -1i32 as _;
35893603
#[repr(C)]
35903604
pub struct IN_ADDR {

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

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -308,22 +308,7 @@ impl File {
308308
&& creation == c::OPEN_ALWAYS
309309
&& unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS
310310
{
311-
unsafe {
312-
// This originally used `FileAllocationInfo` instead of
313-
// `FileEndOfFileInfo` but that wasn't supported by WINE.
314-
// It's arguable which fits the semantics of `OpenOptions`
315-
// better so let's just use the more widely supported method.
316-
let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 };
317-
let result = c::SetFileInformationByHandle(
318-
handle.as_raw_handle(),
319-
c::FileEndOfFileInfo,
320-
ptr::addr_of!(eof).cast::<c_void>(),
321-
mem::size_of::<c::FILE_END_OF_FILE_INFO>() as u32,
322-
);
323-
if result == 0 {
324-
return Err(io::Error::last_os_error());
325-
}
326-
}
311+
Self::truncate_inner(handle.as_raw_handle(), 0)?
327312
}
328313
Ok(File { handle: Handle::from_inner(handle) })
329314
} else {
@@ -341,8 +326,30 @@ impl File {
341326
}
342327

343328
pub fn truncate(&self, size: u64) -> io::Result<()> {
344-
let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
345-
api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
329+
Self::truncate_inner(self.handle.as_raw_handle(), size)
330+
}
331+
332+
pub fn truncate_inner(handle: RawHandle, size: u64) -> io::Result<()> {
333+
if c::SetFileInformationByHandle::option().is_some() {
334+
let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
335+
api::set_file_information_by_handle(handle, &info).io_result()
336+
} else {
337+
let mut saved_pos = 0i64;
338+
unsafe {
339+
// get current file pointer position
340+
cvt(c::SetFilePointerEx(handle, 0, &mut saved_pos, c::FILE_CURRENT))?;
341+
342+
// seek to new end position
343+
cvt(c::SetFilePointerEx(handle, size as i64, ptr::null_mut(), c::FILE_BEGIN))?;
344+
345+
// set current position as end of file
346+
cvt(c::SetEndOfFile(handle))?;
347+
348+
// go back to saved position
349+
cvt(c::SetFilePointerEx(handle, saved_pos, ptr::null_mut(), c::FILE_BEGIN))?;
350+
}
351+
Ok(())
352+
}
346353
}
347354

348355
#[cfg(not(target_vendor = "uwp"))]
@@ -351,7 +358,9 @@ impl File {
351358
let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
352359
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
353360
let mut reparse_tag = 0;
354-
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
361+
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
362+
&& c::GetFileInformationByHandleEx::option().is_some()
363+
{
355364
let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
356365
cvt(c::GetFileInformationByHandleEx(
357366
self.handle.as_raw_handle(),

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool {
121121
return false;
122122
}
123123

124+
let Some(get_file_info_ex) = c::GetFileInformationByHandleEx::option() else {
125+
return false;
126+
};
127+
124128
/// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack
125129
/// allocate
126130
///
@@ -133,7 +137,7 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool {
133137
}
134138
let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] };
135139
// Safety: buffer length is fixed.
136-
let res = c::GetFileInformationByHandleEx(
140+
let res = get_file_info_ex(
137141
handle,
138142
c::FileNameInfo,
139143
&mut name_info as *mut _ as *mut c_void,

0 commit comments

Comments
 (0)