1use std::io;
2use std::io::ErrorKind;
3
4use crate::*;
5
6#[derive(Debug)]
9pub enum IoError {
10 LibcError(&'static str),
11 WindowsError(&'static str),
12 HostError(io::Error),
13 Raw(Scalar),
14}
15pub use self::IoError::*;
16
17impl IoError {
18 pub(crate) fn into_ntstatus(self) -> i32 {
19 let raw = match self {
20 HostError(e) =>
21 match e.kind() {
22 ErrorKind::ReadOnlyFilesystem => 0xC00000A2u32,
24 ErrorKind::InvalidInput => 0xC0000098,
26 ErrorKind::QuotaExceeded => 0xC000007F,
28 ErrorKind::PermissionDenied => 0xC0000022,
30 _ => 0xC0000185,
32 },
33 _ => 0xC0000185,
35 };
36 raw.cast_signed()
37 }
38}
39
40impl From<io::Error> for IoError {
41 fn from(value: io::Error) -> Self {
42 IoError::HostError(value)
43 }
44}
45
46impl From<io::ErrorKind> for IoError {
47 fn from(value: io::ErrorKind) -> Self {
48 IoError::HostError(value.into())
49 }
50}
51
52impl From<Scalar> for IoError {
53 fn from(value: Scalar) -> Self {
54 IoError::Raw(value)
55 }
56}
57
58const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
61 use std::io::ErrorKind::*;
62 &[
63 ("E2BIG", ArgumentListTooLong),
64 ("EADDRINUSE", AddrInUse),
65 ("EADDRNOTAVAIL", AddrNotAvailable),
66 ("EBUSY", ResourceBusy),
67 ("ECONNABORTED", ConnectionAborted),
68 ("ECONNREFUSED", ConnectionRefused),
69 ("ECONNRESET", ConnectionReset),
70 ("EDEADLK", Deadlock),
71 ("EDQUOT", QuotaExceeded),
72 ("EEXIST", AlreadyExists),
73 ("EFBIG", FileTooLarge),
74 ("EHOSTUNREACH", HostUnreachable),
75 ("EINTR", Interrupted),
76 ("EINVAL", InvalidInput),
77 ("EISDIR", IsADirectory),
78 ("ELOOP", FilesystemLoop),
79 ("ENOENT", NotFound),
80 ("ENOMEM", OutOfMemory),
81 ("ENOSPC", StorageFull),
82 ("ENOSYS", Unsupported),
83 ("EMLINK", TooManyLinks),
84 ("ENAMETOOLONG", InvalidFilename),
85 ("ENETDOWN", NetworkDown),
86 ("ENETUNREACH", NetworkUnreachable),
87 ("ENOTCONN", NotConnected),
88 ("ENOTDIR", NotADirectory),
89 ("ENOTEMPTY", DirectoryNotEmpty),
90 ("EPIPE", BrokenPipe),
91 ("EROFS", ReadOnlyFilesystem),
92 ("ESPIPE", NotSeekable),
93 ("ESTALE", StaleNetworkFileHandle),
94 ("ETIMEDOUT", TimedOut),
95 ("ETXTBSY", ExecutableFileBusy),
96 ("EXDEV", CrossesDevices),
97 ("EINPROGRESS", InProgress),
98 ("EPERM", PermissionDenied),
101 ("EACCES", PermissionDenied),
102 ("EWOULDBLOCK", WouldBlock),
103 ("EAGAIN", WouldBlock),
104 ]
105};
106const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
109 use std::io::ErrorKind::*;
110 &[
115 ("WSAEADDRINUSE", AddrInUse),
116 ("WSAEADDRNOTAVAIL", AddrNotAvailable),
117 ("ERROR_ALREADY_EXISTS", AlreadyExists),
118 ("ERROR_FILE_EXISTS", AlreadyExists),
119 ("ERROR_NO_DATA", BrokenPipe),
120 ("WSAECONNABORTED", ConnectionAborted),
121 ("WSAECONNREFUSED", ConnectionRefused),
122 ("WSAECONNRESET", ConnectionReset),
123 ("ERROR_NOT_SAME_DEVICE", CrossesDevices),
124 ("ERROR_POSSIBLE_DEADLOCK", Deadlock),
125 ("ERROR_DIR_NOT_EMPTY", DirectoryNotEmpty),
126 ("ERROR_CANT_RESOLVE_FILENAME", FilesystemLoop),
127 ("ERROR_DISK_QUOTA_EXCEEDED", QuotaExceeded),
128 ("WSAEDQUOT", QuotaExceeded),
129 ("ERROR_FILE_TOO_LARGE", FileTooLarge),
130 ("ERROR_HOST_UNREACHABLE", HostUnreachable),
131 ("WSAEHOSTUNREACH", HostUnreachable),
132 ("ERROR_INVALID_NAME", InvalidFilename),
133 ("ERROR_BAD_PATHNAME", InvalidFilename),
134 ("ERROR_FILENAME_EXCED_RANGE", InvalidFilename),
135 ("ERROR_INVALID_PARAMETER", InvalidInput),
136 ("WSAEINVAL", InvalidInput),
137 ("ERROR_DIRECTORY_NOT_SUPPORTED", IsADirectory),
138 ("WSAENETDOWN", NetworkDown),
139 ("ERROR_NETWORK_UNREACHABLE", NetworkUnreachable),
140 ("WSAENETUNREACH", NetworkUnreachable),
141 ("ERROR_DIRECTORY", NotADirectory),
142 ("WSAENOTCONN", NotConnected),
143 ("ERROR_FILE_NOT_FOUND", NotFound),
144 ("ERROR_PATH_NOT_FOUND", NotFound),
145 ("ERROR_INVALID_DRIVE", NotFound),
146 ("ERROR_BAD_NETPATH", NotFound),
147 ("ERROR_BAD_NET_NAME", NotFound),
148 ("ERROR_SEEK_ON_DEVICE", NotSeekable),
149 ("ERROR_NOT_ENOUGH_MEMORY", OutOfMemory),
150 ("ERROR_OUTOFMEMORY", OutOfMemory),
151 ("ERROR_ACCESS_DENIED", PermissionDenied),
152 ("WSAEACCES", PermissionDenied),
153 ("ERROR_WRITE_PROTECT", ReadOnlyFilesystem),
154 ("ERROR_BUSY", ResourceBusy),
155 ("ERROR_DISK_FULL", StorageFull),
156 ("ERROR_HANDLE_DISK_FULL", StorageFull),
157 ("WAIT_TIMEOUT", TimedOut),
158 ("WSAETIMEDOUT", TimedOut),
159 ("ERROR_DRIVER_CANCEL_TIMEOUT", TimedOut),
160 ("ERROR_OPERATION_ABORTED", TimedOut),
161 ("ERROR_SERVICE_REQUEST_TIMEOUT", TimedOut),
162 ("ERROR_COUNTER_TIMEOUT", TimedOut),
163 ("ERROR_TIMEOUT", TimedOut),
164 ("ERROR_RESOURCE_CALL_TIMED_OUT", TimedOut),
165 ("ERROR_CTX_MODEM_RESPONSE_TIMEOUT", TimedOut),
166 ("ERROR_CTX_CLIENT_QUERY_TIMEOUT", TimedOut),
167 ("FRS_ERR_SYSVOL_POPULATE_TIMEOUT", TimedOut),
168 ("ERROR_DS_TIMELIMIT_EXCEEDED", TimedOut),
169 ("DNS_ERROR_RECORD_TIMED_OUT", TimedOut),
170 ("ERROR_IPSEC_IKE_TIMED_OUT", TimedOut),
171 ("ERROR_RUNLEVEL_SWITCH_TIMEOUT", TimedOut),
172 ("ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT", TimedOut),
173 ("ERROR_TOO_MANY_LINKS", TooManyLinks),
174 ("ERROR_CALL_NOT_IMPLEMENTED", Unsupported),
175 ("WSAEWOULDBLOCK", WouldBlock),
176 ]
177};
178
179impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
180pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
181 fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
184 let this = self.eval_context_mut();
185 if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
186 interp_ok(errno_place.clone())
187 } else {
188 let errno_layout = this.machine.layouts.u32;
190 let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?;
191 this.write_scalar(Scalar::from_u32(0), &errno_place)?;
192 this.active_thread_mut().last_error = Some(errno_place.clone());
193 interp_ok(errno_place)
194 }
195 }
196
197 fn set_last_error(&mut self, err: impl Into<IoError>) -> InterpResult<'tcx> {
199 let this = self.eval_context_mut();
200 let errno = match err.into() {
201 HostError(err) => this.io_error_to_errnum(err)?,
202 LibcError(name) => this.eval_libc(name),
203 WindowsError(name) => this.eval_windows("c", name),
204 Raw(val) => val,
205 };
206 let errno_place = this.last_error_place()?;
207 this.write_scalar(errno, &errno_place)
208 }
209
210 fn set_last_error_and_return(
212 &mut self,
213 err: impl Into<IoError>,
214 dest: &MPlaceTy<'tcx>,
215 ) -> InterpResult<'tcx> {
216 let this = self.eval_context_mut();
217 this.set_last_error(err)?;
218 this.write_int(-1, dest)?;
219 interp_ok(())
220 }
221
222 fn set_last_error_and_return_i32(
224 &mut self,
225 err: impl Into<IoError>,
226 ) -> InterpResult<'tcx, Scalar> {
227 let this = self.eval_context_mut();
228 this.set_last_error(err)?;
229 interp_ok(Scalar::from_i32(-1))
230 }
231
232 fn set_last_error_and_return_i64(
234 &mut self,
235 err: impl Into<IoError>,
236 ) -> InterpResult<'tcx, Scalar> {
237 let this = self.eval_context_mut();
238 this.set_last_error(err)?;
239 interp_ok(Scalar::from_i64(-1))
240 }
241
242 fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
244 let this = self.eval_context_mut();
245 let errno_place = this.last_error_place()?;
246 this.read_scalar(&errno_place)
247 }
248
249 fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> {
252 let this = self.eval_context_ref();
253 let target = &this.tcx.sess.target;
254 if target.families.iter().any(|f| f == "unix") {
255 for &(name, kind) in UNIX_IO_ERROR_TABLE {
256 if err.kind() == kind {
257 return interp_ok(this.eval_libc(name));
258 }
259 }
260 throw_unsup_format!("unsupported io error: {err}")
261 } else if target.families.iter().any(|f| f == "windows") {
262 for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
263 if err.kind() == kind {
264 return interp_ok(this.eval_windows("c", name));
265 }
266 }
267 throw_unsup_format!("unsupported io error: {err}");
268 } else {
269 throw_unsup_format!(
270 "converting io::Error into errnum is unsupported for OS {}",
271 target.os
272 )
273 }
274 }
275
276 #[expect(clippy::needless_return)]
278 fn try_errnum_to_io_error(
279 &self,
280 errnum: Scalar,
281 ) -> InterpResult<'tcx, Option<std::io::ErrorKind>> {
282 let this = self.eval_context_ref();
283 let target = &this.tcx.sess.target;
284 if target.families.iter().any(|f| f == "unix") {
285 let errnum = errnum.to_i32()?;
286 for &(name, kind) in UNIX_IO_ERROR_TABLE {
287 if errnum == this.eval_libc_i32(name) {
288 return interp_ok(Some(kind));
289 }
290 }
291 return interp_ok(None);
292 } else if target.families.iter().any(|f| f == "windows") {
293 let errnum = errnum.to_u32()?;
294 for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
295 if errnum == this.eval_windows("c", name).to_u32()? {
296 return interp_ok(Some(kind));
297 }
298 }
299 return interp_ok(None);
300 } else {
301 throw_unsup_format!(
302 "converting errnum into io::Error is unsupported for OS {}",
303 target.os
304 )
305 }
306 }
307
308 fn try_unwrap_io_result<T: From<i32>>(
315 &mut self,
316 result: std::io::Result<T>,
317 ) -> InterpResult<'tcx, T> {
318 match result {
319 Ok(ok) => interp_ok(ok),
320 Err(e) => {
321 self.eval_context_mut().set_last_error(e)?;
322 interp_ok((-1).into())
323 }
324 }
325 }
326}