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 ("EMLINK", TooManyLinks),
83 ("ENAMETOOLONG", InvalidFilename),
84 ("ENETDOWN", NetworkDown),
85 ("ENETUNREACH", NetworkUnreachable),
86 ("ENOTCONN", NotConnected),
87 ("ENOTDIR", NotADirectory),
88 ("ENOTEMPTY", DirectoryNotEmpty),
89 ("EPIPE", BrokenPipe),
90 ("EROFS", ReadOnlyFilesystem),
91 ("ESPIPE", NotSeekable),
92 ("ESTALE", StaleNetworkFileHandle),
93 ("ETIMEDOUT", TimedOut),
94 ("ETXTBSY", ExecutableFileBusy),
95 ("EXDEV", CrossesDevices),
96 ("EINPROGRESS", InProgress),
97 #[cfg(not(bootstrap))]
98 ("EIO", InputOutputError),
99 ("EPERM", PermissionDenied),
102 ("EACCES", PermissionDenied),
103 ("EWOULDBLOCK", WouldBlock),
104 ("EAGAIN", WouldBlock),
105 ("ENOSYS", Unsupported),
106 ("EOPNOTSUPP", Unsupported),
107 ("ENOTSUP", Unsupported),
108 #[cfg(not(bootstrap))]
109 ("EMFILE", TooManyOpenFiles),
110 #[cfg(not(bootstrap))]
111 ("ENFILE", TooManyOpenFiles),
112 ]
113};
114#[cfg(unix)]
117const UNIX_ERRNO_TABLE: &[(&str, libc::c_int)] = &[
118 ("E2BIG", libc::E2BIG),
119 ("EACCES", libc::EACCES),
120 ("EADDRINUSE", libc::EADDRINUSE),
121 ("EADDRNOTAVAIL", libc::EADDRNOTAVAIL),
122 ("EAFNOSUPPORT", libc::EAFNOSUPPORT),
123 ("EAGAIN", libc::EAGAIN),
124 ("EALREADY", libc::EALREADY),
125 ("EBADF", libc::EBADF),
126 ("EBADMSG", libc::EBADMSG),
127 ("EBUSY", libc::EBUSY),
128 ("ECANCELED", libc::ECANCELED),
129 ("ECHILD", libc::ECHILD),
130 ("ECONNABORTED", libc::ECONNABORTED),
131 ("ECONNREFUSED", libc::ECONNREFUSED),
132 ("ECONNRESET", libc::ECONNRESET),
133 ("EDEADLK", libc::EDEADLK),
134 ("EDESTADDRREQ", libc::EDESTADDRREQ),
135 ("EDOM", libc::EDOM),
136 ("EDQUOT", libc::EDQUOT),
137 ("EEXIST", libc::EEXIST),
138 ("EFAULT", libc::EFAULT),
139 ("EFBIG", libc::EFBIG),
140 ("EHOSTUNREACH", libc::EHOSTUNREACH),
141 ("EIDRM", libc::EIDRM),
142 ("EILSEQ", libc::EILSEQ),
143 ("EINPROGRESS", libc::EINPROGRESS),
144 ("EINTR", libc::EINTR),
145 ("EINVAL", libc::EINVAL),
146 ("EIO", libc::EIO),
147 ("EISCONN", libc::EISCONN),
148 ("EISDIR", libc::EISDIR),
149 ("ELOOP", libc::ELOOP),
150 ("EMFILE", libc::EMFILE),
151 ("EMLINK", libc::EMLINK),
152 ("EMSGSIZE", libc::EMSGSIZE),
153 ("EMULTIHOP", libc::EMULTIHOP),
154 ("ENAMETOOLONG", libc::ENAMETOOLONG),
155 ("ENETDOWN", libc::ENETDOWN),
156 ("ENETRESET", libc::ENETRESET),
157 ("ENETUNREACH", libc::ENETUNREACH),
158 ("ENFILE", libc::ENFILE),
159 ("ENOBUFS", libc::ENOBUFS),
160 ("ENODEV", libc::ENODEV),
161 ("ENOENT", libc::ENOENT),
162 ("ENOEXEC", libc::ENOEXEC),
163 ("ENOLCK", libc::ENOLCK),
164 ("ENOLINK", libc::ENOLINK),
165 ("ENOMEM", libc::ENOMEM),
166 ("ENOMSG", libc::ENOMSG),
167 ("ENOPROTOOPT", libc::ENOPROTOOPT),
168 ("ENOSPC", libc::ENOSPC),
169 ("ENOSYS", libc::ENOSYS),
170 ("ENOTCONN", libc::ENOTCONN),
171 ("ENOTDIR", libc::ENOTDIR),
172 ("ENOTEMPTY", libc::ENOTEMPTY),
173 ("ENOTRECOVERABLE", libc::ENOTRECOVERABLE),
174 ("ENOTSOCK", libc::ENOTSOCK),
175 ("ENOTSUP", libc::ENOTSUP),
176 ("ENOTTY", libc::ENOTTY),
177 ("ENXIO", libc::ENXIO),
178 ("EOPNOTSUPP", libc::EOPNOTSUPP),
179 ("EOVERFLOW", libc::EOVERFLOW),
180 ("EOWNERDEAD", libc::EOWNERDEAD),
181 ("EPERM", libc::EPERM),
182 ("EPIPE", libc::EPIPE),
183 ("EPROTO", libc::EPROTO),
184 ("EPROTONOSUPPORT", libc::EPROTONOSUPPORT),
185 ("EPROTOTYPE", libc::EPROTOTYPE),
186 ("ERANGE", libc::ERANGE),
187 ("EROFS", libc::EROFS),
188 ("ESOCKTNOSUPPORT", libc::ESOCKTNOSUPPORT),
189 ("ESPIPE", libc::ESPIPE),
190 ("ESRCH", libc::ESRCH),
191 ("ESTALE", libc::ESTALE),
192 ("ETIMEDOUT", libc::ETIMEDOUT),
193 ("ETXTBSY", libc::ETXTBSY),
194 ("EWOULDBLOCK", libc::EWOULDBLOCK),
195 ("EXDEV", libc::EXDEV),
196];
197const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
200 use std::io::ErrorKind::*;
201 &[
206 ("WSAEADDRINUSE", AddrInUse),
207 ("WSAEADDRNOTAVAIL", AddrNotAvailable),
208 ("ERROR_ALREADY_EXISTS", AlreadyExists),
209 ("ERROR_FILE_EXISTS", AlreadyExists),
210 ("ERROR_NO_DATA", BrokenPipe),
211 ("WSAECONNABORTED", ConnectionAborted),
212 ("WSAECONNREFUSED", ConnectionRefused),
213 ("WSAECONNRESET", ConnectionReset),
214 ("ERROR_NOT_SAME_DEVICE", CrossesDevices),
215 ("ERROR_POSSIBLE_DEADLOCK", Deadlock),
216 ("ERROR_DIR_NOT_EMPTY", DirectoryNotEmpty),
217 ("ERROR_CANT_RESOLVE_FILENAME", FilesystemLoop),
218 ("ERROR_DISK_QUOTA_EXCEEDED", QuotaExceeded),
219 ("WSAEDQUOT", QuotaExceeded),
220 ("ERROR_FILE_TOO_LARGE", FileTooLarge),
221 ("ERROR_HOST_UNREACHABLE", HostUnreachable),
222 ("WSAEHOSTUNREACH", HostUnreachable),
223 ("ERROR_INVALID_NAME", InvalidFilename),
224 ("ERROR_BAD_PATHNAME", InvalidFilename),
225 ("ERROR_FILENAME_EXCED_RANGE", InvalidFilename),
226 ("ERROR_INVALID_PARAMETER", InvalidInput),
227 ("WSAEINVAL", InvalidInput),
228 ("ERROR_DIRECTORY_NOT_SUPPORTED", IsADirectory),
229 ("WSAENETDOWN", NetworkDown),
230 ("ERROR_NETWORK_UNREACHABLE", NetworkUnreachable),
231 ("WSAENETUNREACH", NetworkUnreachable),
232 ("ERROR_DIRECTORY", NotADirectory),
233 ("WSAENOTCONN", NotConnected),
234 ("ERROR_FILE_NOT_FOUND", NotFound),
235 ("ERROR_PATH_NOT_FOUND", NotFound),
236 ("ERROR_INVALID_DRIVE", NotFound),
237 ("ERROR_BAD_NETPATH", NotFound),
238 ("ERROR_BAD_NET_NAME", NotFound),
239 ("ERROR_SEEK_ON_DEVICE", NotSeekable),
240 ("ERROR_NOT_ENOUGH_MEMORY", OutOfMemory),
241 ("ERROR_OUTOFMEMORY", OutOfMemory),
242 ("ERROR_ACCESS_DENIED", PermissionDenied),
243 ("WSAEACCES", PermissionDenied),
244 ("ERROR_WRITE_PROTECT", ReadOnlyFilesystem),
245 ("ERROR_BUSY", ResourceBusy),
246 ("ERROR_DISK_FULL", StorageFull),
247 ("ERROR_HANDLE_DISK_FULL", StorageFull),
248 ("WAIT_TIMEOUT", TimedOut),
249 ("WSAETIMEDOUT", TimedOut),
250 ("ERROR_DRIVER_CANCEL_TIMEOUT", TimedOut),
251 ("ERROR_OPERATION_ABORTED", TimedOut),
252 ("ERROR_SERVICE_REQUEST_TIMEOUT", TimedOut),
253 ("ERROR_COUNTER_TIMEOUT", TimedOut),
254 ("ERROR_TIMEOUT", TimedOut),
255 ("ERROR_RESOURCE_CALL_TIMED_OUT", TimedOut),
256 ("ERROR_CTX_MODEM_RESPONSE_TIMEOUT", TimedOut),
257 ("ERROR_CTX_CLIENT_QUERY_TIMEOUT", TimedOut),
258 ("FRS_ERR_SYSVOL_POPULATE_TIMEOUT", TimedOut),
259 ("ERROR_DS_TIMELIMIT_EXCEEDED", TimedOut),
260 ("DNS_ERROR_RECORD_TIMED_OUT", TimedOut),
261 ("ERROR_IPSEC_IKE_TIMED_OUT", TimedOut),
262 ("ERROR_RUNLEVEL_SWITCH_TIMEOUT", TimedOut),
263 ("ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT", TimedOut),
264 ("ERROR_TOO_MANY_LINKS", TooManyLinks),
265 ("ERROR_CALL_NOT_IMPLEMENTED", Unsupported),
266 ("WSAEWOULDBLOCK", WouldBlock),
267 #[cfg(not(bootstrap))]
268 ("ERROR_TOO_MANY_OPEN_FILES", TooManyOpenFiles),
269 #[cfg(not(bootstrap))]
270 ("ERROR_IO_DEVICE", InputOutputError),
271 ]
272};
273
274impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
275pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
276 fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
279 let this = self.eval_context_mut();
280 if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
281 interp_ok(errno_place.clone())
282 } else {
283 let errno_layout = this.machine.layouts.u32;
285 let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?;
286 this.write_scalar(Scalar::from_u32(0), &errno_place)?;
287 this.active_thread_mut().last_error = Some(errno_place.clone());
288 interp_ok(errno_place)
289 }
290 }
291
292 fn io_error_to_errnum(&mut self, err: impl Into<IoError>) -> InterpResult<'tcx, Scalar> {
293 let this = self.eval_context_mut();
294 interp_ok(match err.into() {
295 HostError(err) => this.host_error_to_errnum(err)?,
296 LibcError(name) => this.eval_libc(name),
297 WindowsError(name) => this.eval_windows("c", name),
298 Raw(val) => val,
299 })
300 }
301
302 fn set_last_error(&mut self, err: impl Into<IoError>) -> InterpResult<'tcx> {
304 let this = self.eval_context_mut();
305 let errno = this.io_error_to_errnum(err)?;
306 let errno_place = this.last_error_place()?;
307 this.write_scalar(errno, &errno_place)
308 }
309
310 fn set_errno_and_return_neg1(
312 &mut self,
313 err: impl Into<IoError>,
314 dest: &MPlaceTy<'tcx>,
315 ) -> InterpResult<'tcx> {
316 let this = self.eval_context_mut();
317 this.set_last_error(err)?;
318 this.write_int(-1, dest)?;
319 interp_ok(())
320 }
321
322 fn set_errno_and_return_neg1_i32(
324 &mut self,
325 err: impl Into<IoError>,
326 ) -> InterpResult<'tcx, Scalar> {
327 let this = self.eval_context_mut();
328 this.set_last_error(err)?;
329 interp_ok(Scalar::from_i32(-1))
330 }
331
332 fn set_errno_and_return_neg1_i64(
334 &mut self,
335 err: impl Into<IoError>,
336 ) -> InterpResult<'tcx, Scalar> {
337 let this = self.eval_context_mut();
338 this.set_last_error(err)?;
339 interp_ok(Scalar::from_i64(-1))
340 }
341
342 fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
344 let this = self.eval_context_mut();
345 let errno_place = this.last_error_place()?;
346 this.read_scalar(&errno_place)
347 }
348
349 fn host_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> {
352 let this = self.eval_context_ref();
353 let target = &this.tcx.sess.target;
354
355 if target.families.iter().any(|f| f == "unix") {
356 #[cfg(unix)]
359 if let Some(host_errno) = err.raw_os_error() {
360 for &(name, errno) in UNIX_ERRNO_TABLE {
361 if host_errno == errno {
362 return interp_ok(this.eval_libc(name));
363 }
364 }
365 }
366 for &(name, kind) in UNIX_IO_ERROR_TABLE {
368 if err.kind() == kind {
369 return interp_ok(this.eval_libc(name));
370 }
371 }
372 throw_unsup_format!("unsupported io error: {err}")
373 } else if target.families.iter().any(|f| f == "windows") {
374 for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
375 if err.kind() == kind {
376 return interp_ok(this.eval_windows("c", name));
377 }
378 }
379 throw_unsup_format!("unsupported io error: {err}");
380 } else {
381 throw_unsup_format!(
382 "converting io::Error into errnum is unsupported for OS {}",
383 target.os
384 )
385 }
386 }
387
388 #[expect(clippy::needless_return)]
392 fn try_errnum_to_io_error(
393 &self,
394 target_errnum: Scalar,
395 ) -> InterpResult<'tcx, Option<io::Error>> {
396 let this = self.eval_context_ref();
397 let target = &this.tcx.sess.target;
398 if target.families.iter().any(|f| f == "unix") {
399 let target_errnum = target_errnum.to_i32()?;
400 #[cfg(unix)]
404 for &(name, errno) in UNIX_ERRNO_TABLE {
405 if target_errnum == this.eval_libc_i32(name) {
406 return interp_ok(Some(io::Error::from_raw_os_error(errno)));
407 }
408 }
409 for &(name, kind) in UNIX_IO_ERROR_TABLE {
411 if target_errnum == this.eval_libc_i32(name) {
412 return interp_ok(Some(kind.into()));
413 }
414 }
415 return interp_ok(None);
416 } else if target.families.iter().any(|f| f == "windows") {
417 let target_errnum = target_errnum.to_u32()?;
418 for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
419 if target_errnum == this.eval_windows("c", name).to_u32()? {
420 return interp_ok(Some(kind.into()));
421 }
422 }
423 return interp_ok(None);
424 } else {
425 throw_unsup_format!(
426 "converting errnum into io::Error is unsupported for OS {}",
427 target.os
428 )
429 }
430 }
431
432 fn try_unwrap_io_result<T: From<i32>>(
439 &mut self,
440 result: std::io::Result<T>,
441 ) -> InterpResult<'tcx, T> {
442 match result {
443 Ok(ok) => interp_ok(ok),
444 Err(e) => {
445 self.eval_context_mut().set_last_error(e)?;
446 interp_ok((-1).into())
447 }
448 }
449 }
450}