mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 10:13:54 +00:00
move io error handling helpers to their own file
This commit is contained in:
parent
c37539d149
commit
88c74529c1
@ -2,7 +2,7 @@ use std::collections::BTreeSet;
|
||||
use std::num::NonZero;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use std::{cmp, io, iter};
|
||||
use std::{cmp, iter};
|
||||
|
||||
use rand::RngCore;
|
||||
use rustc_apfloat::Float;
|
||||
@ -31,65 +31,6 @@ pub enum AccessKind {
|
||||
Write,
|
||||
}
|
||||
|
||||
// This mapping should match `decode_error_kind` in
|
||||
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/mod.rs>.
|
||||
const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
|
||||
use std::io::ErrorKind::*;
|
||||
&[
|
||||
("E2BIG", ArgumentListTooLong),
|
||||
("EADDRINUSE", AddrInUse),
|
||||
("EADDRNOTAVAIL", AddrNotAvailable),
|
||||
("EBUSY", ResourceBusy),
|
||||
("ECONNABORTED", ConnectionAborted),
|
||||
("ECONNREFUSED", ConnectionRefused),
|
||||
("ECONNRESET", ConnectionReset),
|
||||
("EDEADLK", Deadlock),
|
||||
("EDQUOT", FilesystemQuotaExceeded),
|
||||
("EEXIST", AlreadyExists),
|
||||
("EFBIG", FileTooLarge),
|
||||
("EHOSTUNREACH", HostUnreachable),
|
||||
("EINTR", Interrupted),
|
||||
("EINVAL", InvalidInput),
|
||||
("EISDIR", IsADirectory),
|
||||
("ELOOP", FilesystemLoop),
|
||||
("ENOENT", NotFound),
|
||||
("ENOMEM", OutOfMemory),
|
||||
("ENOSPC", StorageFull),
|
||||
("ENOSYS", Unsupported),
|
||||
("EMLINK", TooManyLinks),
|
||||
("ENAMETOOLONG", InvalidFilename),
|
||||
("ENETDOWN", NetworkDown),
|
||||
("ENETUNREACH", NetworkUnreachable),
|
||||
("ENOTCONN", NotConnected),
|
||||
("ENOTDIR", NotADirectory),
|
||||
("ENOTEMPTY", DirectoryNotEmpty),
|
||||
("EPIPE", BrokenPipe),
|
||||
("EROFS", ReadOnlyFilesystem),
|
||||
("ESPIPE", NotSeekable),
|
||||
("ESTALE", StaleNetworkFileHandle),
|
||||
("ETIMEDOUT", TimedOut),
|
||||
("ETXTBSY", ExecutableFileBusy),
|
||||
("EXDEV", CrossesDevices),
|
||||
// The following have two valid options. We have both for the forwards mapping; only the
|
||||
// first one will be used for the backwards mapping.
|
||||
("EPERM", PermissionDenied),
|
||||
("EACCES", PermissionDenied),
|
||||
("EWOULDBLOCK", WouldBlock),
|
||||
("EAGAIN", WouldBlock),
|
||||
]
|
||||
};
|
||||
// This mapping should match `decode_error_kind` in
|
||||
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
|
||||
const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
|
||||
use std::io::ErrorKind::*;
|
||||
// FIXME: this is still incomplete.
|
||||
&[
|
||||
("ERROR_ACCESS_DENIED", PermissionDenied),
|
||||
("ERROR_FILE_NOT_FOUND", NotFound),
|
||||
("ERROR_INVALID_PARAMETER", InvalidInput),
|
||||
]
|
||||
};
|
||||
|
||||
/// Gets an instance for a path.
|
||||
///
|
||||
/// A `None` namespace indicates we are looking for a module.
|
||||
@ -745,130 +686,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
|
||||
}
|
||||
|
||||
/// Get last error variable as a place, lazily allocating thread-local storage for it if
|
||||
/// necessary.
|
||||
fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||
let this = self.eval_context_mut();
|
||||
if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
|
||||
Ok(errno_place.clone())
|
||||
} else {
|
||||
// Allocate new place, set initial value to 0.
|
||||
let errno_layout = this.machine.layouts.u32;
|
||||
let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?;
|
||||
this.write_scalar(Scalar::from_u32(0), &errno_place)?;
|
||||
this.active_thread_mut().last_error = Some(errno_place.clone());
|
||||
Ok(errno_place)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the last error variable.
|
||||
fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let errno_place = this.last_error_place()?;
|
||||
this.write_scalar(scalar, &errno_place)
|
||||
}
|
||||
|
||||
/// Gets the last error variable.
|
||||
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
let errno_place = this.last_error_place()?;
|
||||
this.read_scalar(&errno_place)
|
||||
}
|
||||
|
||||
/// This function tries to produce the most similar OS error from the `std::io::ErrorKind`
|
||||
/// as a platform-specific errnum.
|
||||
fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_ref();
|
||||
let target = &this.tcx.sess.target;
|
||||
if target.families.iter().any(|f| f == "unix") {
|
||||
for &(name, kind) in UNIX_IO_ERROR_TABLE {
|
||||
if err.kind() == kind {
|
||||
return Ok(this.eval_libc(name));
|
||||
}
|
||||
}
|
||||
throw_unsup_format!("unsupported io error: {err}")
|
||||
} else if target.families.iter().any(|f| f == "windows") {
|
||||
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
|
||||
if err.kind() == kind {
|
||||
return Ok(this.eval_windows("c", name));
|
||||
}
|
||||
}
|
||||
throw_unsup_format!("unsupported io error: {err}");
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"converting io::Error into errnum is unsupported for OS {}",
|
||||
target.os
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The inverse of `io_error_to_errnum`.
|
||||
#[allow(clippy::needless_return)]
|
||||
fn try_errnum_to_io_error(
|
||||
&self,
|
||||
errnum: Scalar,
|
||||
) -> InterpResult<'tcx, Option<std::io::ErrorKind>> {
|
||||
let this = self.eval_context_ref();
|
||||
let target = &this.tcx.sess.target;
|
||||
if target.families.iter().any(|f| f == "unix") {
|
||||
let errnum = errnum.to_i32()?;
|
||||
for &(name, kind) in UNIX_IO_ERROR_TABLE {
|
||||
if errnum == this.eval_libc_i32(name) {
|
||||
return Ok(Some(kind));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
} else if target.families.iter().any(|f| f == "windows") {
|
||||
let errnum = errnum.to_u32()?;
|
||||
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
|
||||
if errnum == this.eval_windows("c", name).to_u32()? {
|
||||
return Ok(Some(kind));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"converting errnum into io::Error is unsupported for OS {}",
|
||||
target.os
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the last OS error using a `std::io::ErrorKind`.
|
||||
fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> {
|
||||
self.set_last_error(self.io_error_to_errnum(err)?)
|
||||
}
|
||||
|
||||
/// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place.
|
||||
fn set_last_error_and_return(
|
||||
&mut self,
|
||||
err: impl Into<io::Error>,
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.set_last_error(self.io_error_to_errnum(err.into())?)?;
|
||||
self.write_int(-1, dest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function that consumes an `std::io::Result<T>` and returns an
|
||||
/// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns
|
||||
/// `Ok(-1)` and sets the last OS error accordingly.
|
||||
///
|
||||
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
|
||||
/// functions return different integer types (like `read`, that returns an `i64`).
|
||||
fn try_unwrap_io_result<T: From<i32>>(
|
||||
&mut self,
|
||||
result: std::io::Result<T>,
|
||||
) -> InterpResult<'tcx, T> {
|
||||
match result {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(e) => {
|
||||
self.eval_context_mut().set_last_error_from_io_error(e)?;
|
||||
Ok((-1).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type
|
||||
fn deref_pointer_as(
|
||||
&self,
|
||||
|
@ -150,6 +150,7 @@ pub use crate::range_map::RangeMap;
|
||||
pub use crate::shims::EmulateItemResult;
|
||||
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
|
||||
pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
|
||||
pub use crate::shims::io_error::EvalContextExt as _;
|
||||
pub use crate::shims::os_str::EvalContextExt as _;
|
||||
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
|
||||
pub use crate::shims::time::EvalContextExt as _;
|
||||
|
190
src/tools/miri/src/shims/io_error.rs
Normal file
190
src/tools/miri/src/shims/io_error.rs
Normal file
@ -0,0 +1,190 @@
|
||||
use std::io;
|
||||
|
||||
use crate::*;
|
||||
|
||||
// This mapping should match `decode_error_kind` in
|
||||
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/mod.rs>.
|
||||
const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
|
||||
use std::io::ErrorKind::*;
|
||||
&[
|
||||
("E2BIG", ArgumentListTooLong),
|
||||
("EADDRINUSE", AddrInUse),
|
||||
("EADDRNOTAVAIL", AddrNotAvailable),
|
||||
("EBUSY", ResourceBusy),
|
||||
("ECONNABORTED", ConnectionAborted),
|
||||
("ECONNREFUSED", ConnectionRefused),
|
||||
("ECONNRESET", ConnectionReset),
|
||||
("EDEADLK", Deadlock),
|
||||
("EDQUOT", FilesystemQuotaExceeded),
|
||||
("EEXIST", AlreadyExists),
|
||||
("EFBIG", FileTooLarge),
|
||||
("EHOSTUNREACH", HostUnreachable),
|
||||
("EINTR", Interrupted),
|
||||
("EINVAL", InvalidInput),
|
||||
("EISDIR", IsADirectory),
|
||||
("ELOOP", FilesystemLoop),
|
||||
("ENOENT", NotFound),
|
||||
("ENOMEM", OutOfMemory),
|
||||
("ENOSPC", StorageFull),
|
||||
("ENOSYS", Unsupported),
|
||||
("EMLINK", TooManyLinks),
|
||||
("ENAMETOOLONG", InvalidFilename),
|
||||
("ENETDOWN", NetworkDown),
|
||||
("ENETUNREACH", NetworkUnreachable),
|
||||
("ENOTCONN", NotConnected),
|
||||
("ENOTDIR", NotADirectory),
|
||||
("ENOTEMPTY", DirectoryNotEmpty),
|
||||
("EPIPE", BrokenPipe),
|
||||
("EROFS", ReadOnlyFilesystem),
|
||||
("ESPIPE", NotSeekable),
|
||||
("ESTALE", StaleNetworkFileHandle),
|
||||
("ETIMEDOUT", TimedOut),
|
||||
("ETXTBSY", ExecutableFileBusy),
|
||||
("EXDEV", CrossesDevices),
|
||||
// The following have two valid options. We have both for the forwards mapping; only the
|
||||
// first one will be used for the backwards mapping.
|
||||
("EPERM", PermissionDenied),
|
||||
("EACCES", PermissionDenied),
|
||||
("EWOULDBLOCK", WouldBlock),
|
||||
("EAGAIN", WouldBlock),
|
||||
]
|
||||
};
|
||||
// This mapping should match `decode_error_kind` in
|
||||
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
|
||||
const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
|
||||
use std::io::ErrorKind::*;
|
||||
// FIXME: this is still incomplete.
|
||||
&[
|
||||
("ERROR_ACCESS_DENIED", PermissionDenied),
|
||||
("ERROR_FILE_NOT_FOUND", NotFound),
|
||||
("ERROR_INVALID_PARAMETER", InvalidInput),
|
||||
]
|
||||
};
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Get last error variable as a place, lazily allocating thread-local storage for it if
|
||||
/// necessary.
|
||||
fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||
let this = self.eval_context_mut();
|
||||
if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
|
||||
Ok(errno_place.clone())
|
||||
} else {
|
||||
// Allocate new place, set initial value to 0.
|
||||
let errno_layout = this.machine.layouts.u32;
|
||||
let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?;
|
||||
this.write_scalar(Scalar::from_u32(0), &errno_place)?;
|
||||
this.active_thread_mut().last_error = Some(errno_place.clone());
|
||||
Ok(errno_place)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the last error variable.
|
||||
fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let errno_place = this.last_error_place()?;
|
||||
this.write_scalar(scalar, &errno_place)
|
||||
}
|
||||
|
||||
/// Gets the last error variable.
|
||||
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
let errno_place = this.last_error_place()?;
|
||||
this.read_scalar(&errno_place)
|
||||
}
|
||||
|
||||
/// This function tries to produce the most similar OS error from the `std::io::ErrorKind`
|
||||
/// as a platform-specific errnum.
|
||||
fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_ref();
|
||||
let target = &this.tcx.sess.target;
|
||||
if target.families.iter().any(|f| f == "unix") {
|
||||
for &(name, kind) in UNIX_IO_ERROR_TABLE {
|
||||
if err.kind() == kind {
|
||||
return Ok(this.eval_libc(name));
|
||||
}
|
||||
}
|
||||
throw_unsup_format!("unsupported io error: {err}")
|
||||
} else if target.families.iter().any(|f| f == "windows") {
|
||||
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
|
||||
if err.kind() == kind {
|
||||
return Ok(this.eval_windows("c", name));
|
||||
}
|
||||
}
|
||||
throw_unsup_format!("unsupported io error: {err}");
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"converting io::Error into errnum is unsupported for OS {}",
|
||||
target.os
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The inverse of `io_error_to_errnum`.
|
||||
#[allow(clippy::needless_return)]
|
||||
fn try_errnum_to_io_error(
|
||||
&self,
|
||||
errnum: Scalar,
|
||||
) -> InterpResult<'tcx, Option<std::io::ErrorKind>> {
|
||||
let this = self.eval_context_ref();
|
||||
let target = &this.tcx.sess.target;
|
||||
if target.families.iter().any(|f| f == "unix") {
|
||||
let errnum = errnum.to_i32()?;
|
||||
for &(name, kind) in UNIX_IO_ERROR_TABLE {
|
||||
if errnum == this.eval_libc_i32(name) {
|
||||
return Ok(Some(kind));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
} else if target.families.iter().any(|f| f == "windows") {
|
||||
let errnum = errnum.to_u32()?;
|
||||
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
|
||||
if errnum == this.eval_windows("c", name).to_u32()? {
|
||||
return Ok(Some(kind));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"converting errnum into io::Error is unsupported for OS {}",
|
||||
target.os
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the last OS error using a `std::io::ErrorKind`.
|
||||
fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> {
|
||||
self.set_last_error(self.io_error_to_errnum(err)?)
|
||||
}
|
||||
|
||||
/// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place.
|
||||
fn set_last_error_and_return(
|
||||
&mut self,
|
||||
err: impl Into<io::Error>,
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
this.set_last_error(this.io_error_to_errnum(err.into())?)?;
|
||||
this.write_int(-1, dest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function that consumes an `std::io::Result<T>` and returns an
|
||||
/// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns
|
||||
/// `Ok(-1)` and sets the last OS error accordingly.
|
||||
///
|
||||
/// This function uses `T: From<i32>` instead of `i32` directly because some IO related
|
||||
/// functions return different integer types (like `read`, that returns an `i64`).
|
||||
fn try_unwrap_io_result<T: From<i32>>(
|
||||
&mut self,
|
||||
result: std::io::Result<T>,
|
||||
) -> InterpResult<'tcx, T> {
|
||||
match result {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(e) => {
|
||||
self.eval_context_mut().set_last_error_from_io_error(e)?;
|
||||
Ok((-1).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ mod x86;
|
||||
pub mod env;
|
||||
pub mod extern_static;
|
||||
pub mod foreign_items;
|
||||
pub mod io_error;
|
||||
pub mod os_str;
|
||||
pub mod panic;
|
||||
pub mod time;
|
||||
|
Loading…
Reference in New Issue
Block a user