Skip to content

Implement file read/write/seek in Windows #4275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
5 changes: 0 additions & 5 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,9 +544,6 @@ pub struct MiriMachine<'tcx> {
/// Failure rate of compare_exchange_weak, between 0.0 and 1.0
pub(crate) cmpxchg_weak_failure_rate: f64,

/// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded.
pub(crate) mute_stdout_stderr: bool,

/// The probability of the active thread being preempted at the end of each basic block.
pub(crate) preemption_rate: f64,

Expand Down Expand Up @@ -722,7 +719,6 @@ impl<'tcx> MiriMachine<'tcx> {
track_alloc_accesses: config.track_alloc_accesses,
check_alignment: config.check_alignment,
cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
mute_stdout_stderr: config.mute_stdout_stderr,
preemption_rate: config.preemption_rate,
report_progress: config.report_progress,
basic_block_count: 0,
Expand Down Expand Up @@ -925,7 +921,6 @@ impl VisitProvenance for MiriMachine<'_> {
track_alloc_accesses: _,
check_alignment: _,
cmpxchg_weak_failure_rate: _,
mute_stdout_stderr: _,
preemption_rate: _,
report_progress: _,
basic_block_count: _,
Expand Down
24 changes: 24 additions & 0 deletions src/shims/io_error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::io;
use std::io::ErrorKind;

use crate::*;

Expand All @@ -13,6 +14,29 @@ pub enum IoError {
}
pub use self::IoError::*;

impl IoError {
pub(crate) fn into_ntstatus(self) -> i32 {
let raw = match self {
HostError(e) =>
match e.kind() {
// STATUS_MEDIA_WRITE_PROTECTED
ErrorKind::ReadOnlyFilesystem => 0xC00000A2u32,
// STATUS_FILE_INVALID
ErrorKind::InvalidInput => 0xC0000098,
// STATUS_DISK_FULL
ErrorKind::QuotaExceeded => 0xC000007F,
// STATUS_ACCESS_DENIED
ErrorKind::PermissionDenied => 0xC0000022,
// For the default error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
_ => 0xC0000185,
},
// For the default error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
_ => 0xC0000185,
};
raw.cast_signed()
}
}

impl From<io::Error> for IoError {
fn from(value: io::Error) -> Self {
IoError::HostError(value)
Expand Down
110 changes: 48 additions & 62 deletions src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,69 +195,52 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

// File related shims
"NtWriteFile" => {
if !this.frame_in_std() {
throw_unsup_format!(
"`NtWriteFile` support is crude and just enough for stdout to work"
);
}

let [
handle,
_event,
_apc_routine,
_apc_context,
event,
apc_routine,
apc_context,
io_status_block,
buf,
n,
byte_offset,
_key,
key,
] = this.check_shim(abi, sys_conv, link_name, args)?;
let handle = this.read_target_isize(handle)?;
let buf = this.read_pointer(buf)?;
let n = this.read_scalar(n)?.to_u32()?;
let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer
let io_status_block = this
.deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?;

if byte_offset != 0 {
throw_unsup_format!(
"`NtWriteFile` `ByteOffset` parameter is non-null, which is unsupported"
);
}

let written = if handle == -11 || handle == -12 {
// stdout/stderr
use io::Write;

let buf_cont =
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
let res = if this.machine.mute_stdout_stderr {
Ok(buf_cont.len())
} else if handle == -11 {
io::stdout().write(buf_cont)
} else {
io::stderr().write(buf_cont)
};
// We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
res.ok().map(|n| u32::try_from(n).unwrap())
} else {
throw_unsup_format!(
"on Windows, writing to anything except stdout/stderr is not supported"
)
};
// We have to put the result into io_status_block.
if let Some(n) = written {
let io_status_information =
this.project_field_named(&io_status_block, "Information")?;
this.write_scalar(
Scalar::from_target_usize(n.into(), this),
&io_status_information,
)?;
}
// Return whether this was a success. >= 0 is success.
// For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
this.write_scalar(
Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
this.NtWriteFile(
handle,
event,
apc_routine,
apc_context,
io_status_block,
buf,
n,
byte_offset,
key,
dest,
)?;
}
"NtReadFile" => {
let [
handle,
event,
apc_routine,
apc_context,
io_status_block,
buf,
n,
byte_offset,
key,
] = this.check_shim(abi, sys_conv, link_name, args)?;
this.NtReadFile(
handle,
event,
apc_routine,
apc_context,
io_status_block,
buf,
n,
byte_offset,
key,
dest,
)?;
}
Expand Down Expand Up @@ -322,6 +305,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let res = this.DeleteFileW(file_name)?;
this.write_scalar(res, dest)?;
}
"SetFilePointerEx" => {
let [file, distance_to_move, new_file_pointer, move_method] =
this.check_shim(abi, sys_conv, link_name, args)?;
let res =
this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?;
this.write_scalar(res, dest)?;
}

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