Skip to content

Commit 36aa75e

Browse files
committed
Complete reads and writes synchronously or abort
1 parent 66faaa8 commit 36aa75e

File tree

2 files changed

+137
-66
lines changed

2 files changed

+137
-66
lines changed

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

+35
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ pub const STATUS_SUCCESS: NTSTATUS = 0x00000000;
274274
pub const STATUS_DELETE_PENDING: NTSTATUS = 0xc0000056_u32 as _;
275275
pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
276276

277+
pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
278+
pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
279+
277280
// Equivalent to the `NT_SUCCESS` C preprocessor macro.
278281
// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
279282
pub fn nt_success(status: NTSTATUS) -> bool {
@@ -338,6 +341,12 @@ pub type LPOVERLAPPED_COMPLETION_ROUTINE = unsafe extern "system" fn(
338341
lpOverlapped: *mut OVERLAPPED,
339342
);
340343

344+
type IO_APC_ROUTINE = unsafe extern "system" fn(
345+
ApcContext: *mut c_void,
346+
IoStatusBlock: *mut IO_STATUS_BLOCK,
347+
Reserved: ULONG,
348+
);
349+
341350
#[repr(C)]
342351
#[cfg(not(target_pointer_width = "64"))]
343352
pub struct WSADATA {
@@ -1274,6 +1283,32 @@ compat_fn! {
12741283
) -> NTSTATUS {
12751284
panic!("`NtCreateFile` not available");
12761285
}
1286+
pub fn NtReadFile(
1287+
FileHandle: BorrowedHandle<'_>,
1288+
Event: HANDLE,
1289+
ApcRoutine: Option<IO_APC_ROUTINE>,
1290+
ApcContext: *mut c_void,
1291+
IoStatusBlock: &mut IO_STATUS_BLOCK,
1292+
Buffer: *mut crate::mem::MaybeUninit<u8>,
1293+
Length: ULONG,
1294+
ByteOffset: Option<&LARGE_INTEGER>,
1295+
Key: Option<&ULONG>
1296+
) -> NTSTATUS {
1297+
panic!("`NtReadFile` not available");
1298+
}
1299+
pub fn NtWriteFile(
1300+
FileHandle: BorrowedHandle<'_>,
1301+
Event: HANDLE,
1302+
ApcRoutine: Option<IO_APC_ROUTINE>,
1303+
ApcContext: *mut c_void,
1304+
IoStatusBlock: &mut IO_STATUS_BLOCK,
1305+
Buffer: *const u8,
1306+
Length: ULONG,
1307+
ByteOffset: Option<&LARGE_INTEGER>,
1308+
Key: Option<&ULONG>
1309+
) -> NTSTATUS {
1310+
panic!("`NtWriteFile` not available");
1311+
}
12771312
pub fn RtlNtStatusToDosError(
12781313
Status: NTSTATUS
12791314
) -> ULONG {

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

+102-66
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,10 @@ impl FromRawHandle for Handle {
7474

7575
impl Handle {
7676
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
77-
let mut read = 0;
78-
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
79-
let res = cvt(unsafe {
80-
c::ReadFile(
81-
self.as_handle(),
82-
buf.as_mut_ptr() as c::LPVOID,
83-
len,
84-
&mut read,
85-
ptr::null_mut(),
86-
)
87-
});
77+
let res = unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), None) };
8878

8979
match res {
90-
Ok(_) => Ok(read as usize),
80+
Ok(read) => Ok(read as usize),
9181

9282
// The special treatment of BrokenPipe is to deal with Windows
9383
// pipe semantics, which yields this error when *reading* from
@@ -109,42 +99,23 @@ impl Handle {
10999
}
110100

111101
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
112-
let mut read = 0;
113-
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
114-
let res = unsafe {
115-
let mut overlapped: c::OVERLAPPED = mem::zeroed();
116-
overlapped.Offset = offset as u32;
117-
overlapped.OffsetHigh = (offset >> 32) as u32;
118-
cvt(c::ReadFile(
119-
self.as_handle(),
120-
buf.as_mut_ptr() as c::LPVOID,
121-
len,
122-
&mut read,
123-
&mut overlapped,
124-
))
125-
};
102+
let res =
103+
unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), Some(offset)) };
104+
126105
match res {
127-
Ok(_) => Ok(read as usize),
106+
Ok(read) => Ok(read as usize),
128107
Err(ref e) if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) => Ok(0),
129108
Err(e) => Err(e),
130109
}
131110
}
132111

133112
pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
134-
let mut read = 0;
135-
let len = cmp::min(buf.remaining(), <c::DWORD>::MAX as usize) as c::DWORD;
136-
let res = cvt(unsafe {
137-
c::ReadFile(
138-
self.as_handle(),
139-
buf.unfilled_mut().as_mut_ptr() as c::LPVOID,
140-
len,
141-
&mut read,
142-
ptr::null_mut(),
143-
)
144-
});
113+
let res = unsafe {
114+
self.synchronous_read(buf.unfilled_mut().as_mut_ptr(), buf.remaining(), None)
115+
};
145116

146117
match res {
147-
Ok(_) => {
118+
Ok(read) => {
148119
// Safety: `read` bytes were written to the initialized portion of the buffer
149120
unsafe {
150121
buf.assume_init(read as usize);
@@ -221,18 +192,7 @@ impl Handle {
221192
}
222193

223194
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
224-
let mut amt = 0;
225-
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
226-
cvt(unsafe {
227-
c::WriteFile(
228-
self.as_handle(),
229-
buf.as_ptr() as c::LPVOID,
230-
len,
231-
&mut amt,
232-
ptr::null_mut(),
233-
)
234-
})?;
235-
Ok(amt as usize)
195+
unsafe { self.synchronous_write(&buf, None) }
236196
}
237197

238198
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
@@ -245,21 +205,7 @@ impl Handle {
245205
}
246206

247207
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
248-
let mut written = 0;
249-
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
250-
unsafe {
251-
let mut overlapped: c::OVERLAPPED = mem::zeroed();
252-
overlapped.Offset = offset as u32;
253-
overlapped.OffsetHigh = (offset >> 32) as u32;
254-
cvt(c::WriteFile(
255-
self.as_handle(),
256-
buf.as_ptr() as c::LPVOID,
257-
len,
258-
&mut written,
259-
&mut overlapped,
260-
))?;
261-
}
262-
Ok(written as usize)
208+
unsafe { self.synchronous_write(&buf, Some(offset)) }
263209
}
264210

265211
pub fn try_clone(&self) -> io::Result<Self> {
@@ -274,6 +220,96 @@ impl Handle {
274220
) -> io::Result<Self> {
275221
Ok(Self(self.0.duplicate(access, inherit, options)?))
276222
}
223+
224+
/// Performs a synchronous read.
225+
///
226+
/// If the handle is opened for asynchronous I/O then this abort the process.
227+
/// See #81357.
228+
///
229+
/// If `offset` is `None` then the current file position is used.
230+
unsafe fn synchronous_read(
231+
&self,
232+
buf: *mut mem::MaybeUninit<u8>,
233+
len: usize,
234+
offset: Option<u64>,
235+
) -> io::Result<usize> {
236+
let mut io_status = c::IO_STATUS_BLOCK::default();
237+
238+
// The length is clamped at u32::MAX.
239+
let len = cmp::min(len, c::DWORD::MAX as usize) as c::DWORD;
240+
let status = c::NtReadFile(
241+
self.as_handle(),
242+
ptr::null_mut(),
243+
None,
244+
ptr::null_mut(),
245+
&mut io_status,
246+
buf,
247+
len,
248+
offset.map(|n| n as _).as_ref(),
249+
None,
250+
);
251+
match status {
252+
// If the operation has not completed then abort the process.
253+
// Doing otherwise means that the buffer and stack may be written to
254+
// after this function returns.
255+
c::STATUS_PENDING => {
256+
eprintln!("I/O error: operation failed to complete synchronously");
257+
crate::process::abort();
258+
}
259+
260+
// Return `Ok(0)` when there's nothing more to read.
261+
c::STATUS_END_OF_FILE => Ok(0),
262+
263+
// Success!
264+
status if c::nt_success(status) => Ok(io_status.Information),
265+
266+
status => {
267+
let error = c::RtlNtStatusToDosError(status);
268+
Err(io::Error::from_raw_os_error(error as _))
269+
}
270+
}
271+
}
272+
273+
/// Performs a synchronous write.
274+
///
275+
/// If the handle is opened for asynchronous I/O then this abort the process.
276+
/// See #81357.
277+
///
278+
/// If `offset` is `None` then the current file position is used.
279+
unsafe fn synchronous_write(&self, buf: &[u8], offset: Option<u64>) -> io::Result<usize> {
280+
let mut io_status = c::IO_STATUS_BLOCK::default();
281+
282+
// The length is clamped at u32::MAX.
283+
let len = cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
284+
let status = c::NtWriteFile(
285+
self.as_handle(),
286+
ptr::null_mut(),
287+
None,
288+
ptr::null_mut(),
289+
&mut io_status,
290+
buf.as_ptr(),
291+
len,
292+
offset.map(|n| n as _).as_ref(),
293+
None,
294+
);
295+
match status {
296+
// If the operation has not completed then abort the process.
297+
// Doing otherwise means that the buffer maybe read and the stack
298+
// written to after this function returns.
299+
c::STATUS_PENDING => {
300+
eprintln!("I/O error: operation failed to complete synchronously");
301+
crate::process::abort();
302+
}
303+
304+
// Success!
305+
status if c::nt_success(status) => Ok(io_status.Information),
306+
307+
status => {
308+
let error = c::RtlNtStatusToDosError(status);
309+
Err(io::Error::from_raw_os_error(error as _))
310+
}
311+
}
312+
}
277313
}
278314

279315
impl<'a> Read for &'a Handle {

0 commit comments

Comments
 (0)