Skip to content

Commit 0078f8b

Browse files
committed
Add fallback for IO internals (pipe, handle):
- Add two fallback implementations in stdio redirection: - Add fallback when 'modern' anonymous pipe creation with async io is not available (pre-Vista) - Add fallback for when named pipes are not available (9x/ME, NT before 4.0) Since Windows 9X/ME does not support creating named pipes (only connecting to remote pipes created on NT), we'll have to make do with anonymous pipes, without overlapped I/O. In particular, this means that we'll have to spawn another thread in the case where both stdout and stderr are being piped and read from (`read2`). We also use the fallback implementation on NT before 4.0, as the `Drop` impl of `AsyncPipe` needs to be able to cancel I/O via `CancelIo`. - Add fallbacks for `NtReadFile` and `NtWriteFile` in `synchronous_{read, write}` These might be unsound for handles that _can_ be asynchronous on 9x/ME. See rust-lang#95469 for more info
1 parent 5844d2a commit 0078f8b

7 files changed

Lines changed: 356 additions & 19 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl File {
342342
Self::open_native(&path, opts)
343343
}
344344

345-
fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result<File> {
345+
pub(crate) fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result<File> {
346346
let creation = opts.get_creation_mode()?;
347347
let sa = c::SECURITY_ATTRIBUTES {
348348
nLength: size_of::<c::SECURITY_ATTRIBUTES>() as u32,

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

Lines changed: 115 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,50 @@ unsafe extern "system" {
113113
pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL;
114114
}
115115

116-
windows_link::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile(
117-
filehandle: *mut HANDLE,
118-
desiredaccess: FILE_ACCESS_RIGHTS,
119-
objectattributes: *const OBJECT_ATTRIBUTES,
120-
iostatusblock: *mut IO_STATUS_BLOCK,
121-
shareaccess: FILE_SHARE_MODE,
122-
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
123-
createoptions: NTCREATEFILE_CREATE_OPTIONS,
124-
namedpipetype: u32,
125-
readmode: u32,
126-
completionmode: u32,
127-
maximuminstances: u32,
128-
inboundquota: u32,
129-
outboundquota: u32,
130-
defaulttimeout: *const u64,
131-
) -> NTSTATUS);
116+
cfg_select! {
117+
target_family = "rust9x" => {
118+
compat_fn_with_fallback! {
119+
pub static NTDLL: &CStr = c"ntdll" => { load: false, unicows: false };
120+
// NT only (duh)
121+
pub fn NtCreateNamedPipeFile(
122+
filehandle: *mut HANDLE,
123+
desiredaccess: FILE_ACCESS_RIGHTS,
124+
objectattributes: *const OBJECT_ATTRIBUTES,
125+
iostatusblock: *mut IO_STATUS_BLOCK,
126+
shareaccess: FILE_SHARE_MODE,
127+
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
128+
createoptions: NTCREATEFILE_CREATE_OPTIONS,
129+
namedpipetype: u32,
130+
readmode: u32,
131+
completionmode: u32,
132+
maximuminstances: u32,
133+
inboundquota: u32,
134+
outboundquota: u32,
135+
defaulttimeout: *const u64,
136+
) -> NTSTATUS {
137+
rtabort!("unimplemented")
138+
}
139+
}
140+
}
141+
_ => {
142+
windows_link::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile(
143+
filehandle: *mut HANDLE,
144+
desiredaccess: FILE_ACCESS_RIGHTS,
145+
objectattributes: *const OBJECT_ATTRIBUTES,
146+
iostatusblock: *mut IO_STATUS_BLOCK,
147+
shareaccess: FILE_SHARE_MODE,
148+
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
149+
createoptions: NTCREATEFILE_CREATE_OPTIONS,
150+
namedpipetype: u32,
151+
readmode: u32,
152+
completionmode: u32,
153+
maximuminstances: u32,
154+
inboundquota: u32,
155+
outboundquota: u32,
156+
defaulttimeout: *const u64,
157+
) -> NTSTATUS);
158+
}
159+
}
132160

133161
// Functions that aren't available on every version of Windows that we support,
134162
// but we still use them and just provide some form of a fallback implementation.
@@ -259,6 +287,67 @@ cfg_select! {
259287
windows_link::link_raw_dylib!("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);
260288
windows_link::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
261289
}
290+
target_family = "rust9x" => {
291+
compat_fn_with_fallback! {
292+
pub static NTDLL: &CStr = c"ntdll" => { load: false, unicows: false };
293+
294+
pub fn NtCreateFile(
295+
filehandle: *mut HANDLE,
296+
desiredaccess: FILE_ACCESS_RIGHTS,
297+
objectattributes: *const OBJECT_ATTRIBUTES,
298+
iostatusblock: *mut IO_STATUS_BLOCK,
299+
allocationsize: *const i64,
300+
fileattributes: FILE_FLAGS_AND_ATTRIBUTES,
301+
shareaccess: FILE_SHARE_MODE,
302+
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
303+
createoptions: NTCREATEFILE_CREATE_OPTIONS,
304+
eabuffer: *const core::ffi::c_void,
305+
ealength: u32
306+
) -> NTSTATUS {
307+
STATUS_NOT_IMPLEMENTED
308+
}
309+
310+
pub fn NtReadFile(
311+
filehandle: HANDLE,
312+
event: HANDLE,
313+
apcroutine: PIO_APC_ROUTINE,
314+
apccontext: *const c_void,
315+
iostatusblock: *mut IO_STATUS_BLOCK,
316+
buffer: *mut c_void,
317+
length: u32,
318+
byteoffset: *const i64,
319+
key: *const u32
320+
) -> NTSTATUS {
321+
STATUS_NOT_IMPLEMENTED
322+
}
323+
pub fn NtWriteFile(
324+
filehandle: HANDLE,
325+
event: HANDLE,
326+
apcroutine: PIO_APC_ROUTINE,
327+
apccontext: *const c_void,
328+
iostatusblock: *mut IO_STATUS_BLOCK,
329+
buffer: *const c_void,
330+
length: u32,
331+
byteoffset: *const i64,
332+
key: *const u32
333+
) -> NTSTATUS {
334+
STATUS_NOT_IMPLEMENTED
335+
}
336+
pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 {
337+
ERROR_CALL_NOT_IMPLEMENTED
338+
}
339+
pub fn NtOpenFile(
340+
filehandle: *mut HANDLE,
341+
desiredaccess: u32,
342+
objectattributes: *const OBJECT_ATTRIBUTES,
343+
iostatusblock: *mut IO_STATUS_BLOCK,
344+
shareaccess: u32,
345+
openoptions: u32
346+
) -> NTSTATUS {
347+
STATUS_NOT_IMPLEMENTED
348+
}
349+
}
350+
}
262351
_ => {}
263352
}
264353

@@ -643,3 +732,13 @@ compat_fn_with_fallback! {
643732
rtabort!("unimplemented")
644733
}
645734
}
735+
736+
#[cfg(target_family = "rust9x")]
737+
compat_fn_with_fallback! {
738+
pub static KERNEL32: &CStr = c"kernel32" => { load: false, unicows: false };
739+
// >= 98+, NT4.0
740+
// https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal
741+
pub fn CancelIo(hfile: HANDLE) -> BOOL {
742+
rtabort!("unimplemented")
743+
}
744+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2556,6 +2556,7 @@ WINSOCK_SOCKET_TYPE
25562556
WRITE_DAC
25572557
WRITE_OWNER
25582558
WriteConsoleW
2559+
WriteFile
25592560
WriteFileEx
25602561
WSA_E_CANCELLED
25612562
WSA_E_NO_MORE

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ windows_link::link!("kernel32.dll" "system" fn WakeAllConditionVariable(conditio
152152
windows_link::link!("kernel32.dll" "system" fn WakeConditionVariable(conditionvariable : *mut CONDITION_VARIABLE));
153153
windows_link::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32);
154154
windows_link::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL);
155+
windows_link::link!("kernel32.dll" "system" fn WriteFile(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpnumberofbyteswritten : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
155156
windows_link::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
156157
windows_link::link!("ws2_32.dll" "system" fn accept(s : SOCKET, addr : *mut SOCKADDR, addrlen : *mut i32) -> SOCKET);
157158
windows_link::link!("ws2_32.dll" "system" fn bind(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32);

library/std/src/sys/pal/windows/compat/checks.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ pub fn is_windows_nt() -> bool {
1616
true // let me know once someone ported 9x to 64bit LOL
1717
}
1818

19+
#[inline(always)]
20+
pub fn supports_async_io() -> bool {
21+
unsafe { SUPPORTS_ASYNC_IO }
22+
}
23+
24+
/// Whether the new way (just opening \??\PIPE\ / \Device\NamedPipe without a file/pipe name creates
25+
/// an anon pipe) is supported.
26+
///
27+
/// Prior to Vista (NT 6), kernel32's `CreatePipe` would create a pipe with a unique name via
28+
/// `_sprintf(Buffer, "\\Device\\NamedPipe\\Win32Pipes.%08x.%08x", process_id, global_counter)`;
29+
///
30+
/// see https://github.com/rust-lang/rust/pull/142517
31+
#[inline(always)]
32+
pub fn supports_anon_pipe_autoname() -> bool {
33+
unsafe { SUPPORTS_ANON_PIPE_AUTONAME }
34+
}
35+
1936
pub fn init_rust9x_checks() {
2037
// DO NOT do anything interesting or complicated in this function! DO NOT call
2138
// any Rust functions or CRT functions if those functions touch any global state,
@@ -26,11 +43,18 @@ pub fn init_rust9x_checks() {
2643
init_mutex_kind_check();
2744
}
2845

29-
static mut IS_NT: bool = true;
46+
static mut IS_NT: bool = false;
47+
static mut SUPPORTS_ASYNC_IO: bool = false;
48+
static mut SUPPORTS_ANON_PIPE_AUTONAME: bool = false;
3049

3150
fn init_windows_version_check() {
3251
// according to old MSDN info, the high-order bit is set only on 95/98/ME.
33-
unsafe { IS_NT = c::GetVersion() < 0x8000_0000 };
52+
unsafe {
53+
let version = c::GetVersion();
54+
IS_NT = version < 0x8000_0000;
55+
SUPPORTS_ASYNC_IO = IS_NT && c::CancelIo::available().is_some();
56+
SUPPORTS_ANON_PIPE_AUTONAME = IS_NT && version & 0xFF >= 0x06; // Vista+/NT6+
57+
};
3458
}
3559

3660
#[derive(Clone, Copy, PartialEq)]

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,32 @@ impl Handle {
254254

255255
// The length is clamped at u32::MAX.
256256
let len = cmp::min(len, u32::MAX as usize) as u32;
257+
258+
#[cfg(target_family = "rust9x")]
259+
if !crate::sys::compat::checks::supports_async_io() {
260+
unsafe {
261+
if let Some(offset) = offset {
262+
cvt(c::SetFilePointerEx(
263+
self.as_raw_handle(),
264+
offset as i64,
265+
ptr::null_mut(),
266+
c::FILE_BEGIN,
267+
))?;
268+
}
269+
270+
let mut bytes_read = 0;
271+
cvt(c::ReadFile(
272+
self.as_raw_handle(),
273+
buf.cast(),
274+
len,
275+
&mut bytes_read,
276+
ptr::null_mut(),
277+
))?;
278+
279+
return Ok(bytes_read as usize);
280+
}
281+
}
282+
257283
// SAFETY: It's up to the caller to ensure `buf` is writeable up to
258284
// the provided `len`.
259285
let status = unsafe {
@@ -306,6 +332,32 @@ impl Handle {
306332

307333
// The length is clamped at u32::MAX.
308334
let len = cmp::min(buf.len(), u32::MAX as usize) as u32;
335+
336+
#[cfg(target_family = "rust9x")]
337+
if !crate::sys::compat::checks::supports_async_io() {
338+
unsafe {
339+
if let Some(offset) = offset {
340+
cvt(c::SetFilePointerEx(
341+
self.as_raw_handle(),
342+
offset as i64,
343+
ptr::null_mut(),
344+
c::FILE_BEGIN,
345+
))?;
346+
}
347+
348+
let mut bytes_written = 0;
349+
cvt(c::WriteFile(
350+
self.as_raw_handle(),
351+
buf.as_ptr(),
352+
len,
353+
&mut bytes_written,
354+
ptr::null_mut(),
355+
))?;
356+
357+
return Ok(bytes_written as usize);
358+
}
359+
}
360+
309361
let status = unsafe {
310362
c::NtWriteFile(
311363
self.as_raw_handle(),

0 commit comments

Comments
 (0)