Skip to content

Commit eabd82d

Browse files
committed
Implement file read/write on Windows
1 parent 13f80f2 commit eabd82d

File tree

8 files changed

+460
-81
lines changed

8 files changed

+460
-81
lines changed

src/machine.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -544,9 +544,6 @@ pub struct MiriMachine<'tcx> {
544544
/// Failure rate of compare_exchange_weak, between 0.0 and 1.0
545545
pub(crate) cmpxchg_weak_failure_rate: f64,
546546

547-
/// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded.
548-
pub(crate) mute_stdout_stderr: bool,
549-
550547
/// The probability of the active thread being preempted at the end of each basic block.
551548
pub(crate) preemption_rate: f64,
552549

@@ -722,7 +719,6 @@ impl<'tcx> MiriMachine<'tcx> {
722719
track_alloc_accesses: config.track_alloc_accesses,
723720
check_alignment: config.check_alignment,
724721
cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
725-
mute_stdout_stderr: config.mute_stdout_stderr,
726722
preemption_rate: config.preemption_rate,
727723
report_progress: config.report_progress,
728724
basic_block_count: 0,
@@ -925,7 +921,6 @@ impl VisitProvenance for MiriMachine<'_> {
925921
track_alloc_accesses: _,
926922
check_alignment: _,
927923
cmpxchg_weak_failure_rate: _,
928-
mute_stdout_stderr: _,
929924
preemption_rate: _,
930925
report_progress: _,
931926
basic_block_count: _,

src/shims/io_error.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::io;
2+
use std::io::ErrorKind;
23

34
use crate::*;
45

@@ -13,6 +14,29 @@ pub enum IoError {
1314
}
1415
pub use self::IoError::*;
1516

17+
impl IoError {
18+
pub(crate) fn into_ntstatus(self) -> i32 {
19+
let raw = match self {
20+
HostError(e) =>
21+
match e.kind() {
22+
// STATUS_MEDIA_WRITE_PROTECTED
23+
ErrorKind::ReadOnlyFilesystem => 0xC00000A2u32,
24+
// STATUS_FILE_INVALID
25+
ErrorKind::InvalidInput => 0xC0000098,
26+
// STATUS_DISK_FULL
27+
ErrorKind::QuotaExceeded => 0xC000007F,
28+
// STATUS_ACCESS_DENIED
29+
ErrorKind::PermissionDenied => 0xC0000022,
30+
// For the default error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
31+
_ => 0xC0000185,
32+
},
33+
// For the default error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
34+
_ => 0xC0000185,
35+
};
36+
raw.cast_signed()
37+
}
38+
}
39+
1640
impl From<io::Error> for IoError {
1741
fn from(value: io::Error) -> Self {
1842
IoError::HostError(value)

src/shims/windows/foreign_items.rs

Lines changed: 48 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -195,69 +195,52 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
195195

196196
// File related shims
197197
"NtWriteFile" => {
198-
if !this.frame_in_std() {
199-
throw_unsup_format!(
200-
"`NtWriteFile` support is crude and just enough for stdout to work"
201-
);
202-
}
203-
204198
let [
205199
handle,
206-
_event,
207-
_apc_routine,
208-
_apc_context,
200+
event,
201+
apc_routine,
202+
apc_context,
209203
io_status_block,
210204
buf,
211205
n,
212206
byte_offset,
213-
_key,
207+
key,
214208
] = this.check_shim(abi, sys_conv, link_name, args)?;
215-
let handle = this.read_target_isize(handle)?;
216-
let buf = this.read_pointer(buf)?;
217-
let n = this.read_scalar(n)?.to_u32()?;
218-
let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer
219-
let io_status_block = this
220-
.deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?;
221-
222-
if byte_offset != 0 {
223-
throw_unsup_format!(
224-
"`NtWriteFile` `ByteOffset` parameter is non-null, which is unsupported"
225-
);
226-
}
227-
228-
let written = if handle == -11 || handle == -12 {
229-
// stdout/stderr
230-
use io::Write;
231-
232-
let buf_cont =
233-
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
234-
let res = if this.machine.mute_stdout_stderr {
235-
Ok(buf_cont.len())
236-
} else if handle == -11 {
237-
io::stdout().write(buf_cont)
238-
} else {
239-
io::stderr().write(buf_cont)
240-
};
241-
// We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
242-
res.ok().map(|n| u32::try_from(n).unwrap())
243-
} else {
244-
throw_unsup_format!(
245-
"on Windows, writing to anything except stdout/stderr is not supported"
246-
)
247-
};
248-
// We have to put the result into io_status_block.
249-
if let Some(n) = written {
250-
let io_status_information =
251-
this.project_field_named(&io_status_block, "Information")?;
252-
this.write_scalar(
253-
Scalar::from_target_usize(n.into(), this),
254-
&io_status_information,
255-
)?;
256-
}
257-
// Return whether this was a success. >= 0 is success.
258-
// For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
259-
this.write_scalar(
260-
Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
209+
this.NtWriteFile(
210+
handle,
211+
event,
212+
apc_routine,
213+
apc_context,
214+
io_status_block,
215+
buf,
216+
n,
217+
byte_offset,
218+
key,
219+
dest,
220+
)?;
221+
}
222+
"NtReadFile" => {
223+
let [
224+
handle,
225+
event,
226+
apc_routine,
227+
apc_context,
228+
io_status_block,
229+
buf,
230+
n,
231+
byte_offset,
232+
key,
233+
] = this.check_shim(abi, sys_conv, link_name, args)?;
234+
this.NtReadFile(
235+
handle,
236+
event,
237+
apc_routine,
238+
apc_context,
239+
io_status_block,
240+
buf,
241+
n,
242+
byte_offset,
243+
key,
261244
dest,
262245
)?;
263246
}
@@ -322,6 +305,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
322305
let res = this.DeleteFileW(file_name)?;
323306
this.write_scalar(res, dest)?;
324307
}
308+
"SetFilePointerEx" => {
309+
let [file, distance_to_move, new_file_pointer, move_method] =
310+
this.check_shim(abi, sys_conv, link_name, args)?;
311+
let res =
312+
this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?;
313+
this.write_scalar(res, dest)?;
314+
}
325315

326316
// Allocation
327317
"HeapAlloc" => {
@@ -700,12 +690,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
700690
}
701691
"GetStdHandle" => {
702692
let [which] = this.check_shim(abi, sys_conv, link_name, args)?;
703-
let which = this.read_scalar(which)?.to_i32()?;
704-
// We just make this the identity function, so we know later in `NtWriteFile` which
705-
// one it is. This is very fake, but libtest needs it so we cannot make it a
706-
// std-only shim.
707-
// FIXME: this should return real HANDLEs when io support is added
708-
this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
693+
let res = this.GetStdHandle(which)?;
694+
this.write_scalar(res, dest)?;
709695
}
710696
"CloseHandle" => {
711697
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;

0 commit comments

Comments
 (0)