1#![allow(dead_code)]
6
7use crate::{SA_RESTART, sigaction_t};
8use std::fmt::{Debug, Display, Formatter};
9
10#[derive(Clone, Debug)]
11pub struct Errno {
12 pub code: ErrnoCode,
13 location: &'static std::panic::Location<'static>,
14 context: Option<String>,
15}
16
17impl Errno {
18 #[track_caller]
22 pub fn new(code: ErrnoCode) -> Self {
23 Errno { code, location: std::panic::Location::caller(), context: None }
24 }
25
26 #[track_caller]
27 pub fn with_context(code: ErrnoCode, context: impl ToString) -> Self {
28 Errno { code, location: std::panic::Location::caller(), context: Some(context.to_string()) }
29 }
30
31 pub fn return_value(&self) -> u64 {
32 self.code.return_value()
33 }
34
35 pub fn is_restartable(&self) -> bool {
37 self.code.is_restartable()
38 }
39}
40
41impl PartialEq for Errno {
42 fn eq(&self, other: &Self) -> bool {
43 self.code == other.code
44 }
45}
46
47impl PartialEq<ErrnoCode> for Errno {
48 fn eq(&self, other: &ErrnoCode) -> bool {
49 self.code == *other
50 }
51}
52
53impl Eq for Errno {}
54
55impl Display for Errno {
56 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57 if let Some(context) = &self.context {
58 write!(f, "{} from {}, context: {}", self.code, self.location, context)
59 } else {
60 write!(f, "{} from {}", self.code, self.location)
61 }
62 }
63}
64
65impl std::error::Error for Errno {}
66
67impl From<Errno> for zx_status::Status {
68 fn from(e: Errno) -> Self {
69 match e.code.error_code() {
70 crate::uapi::ENOENT => zx_status::Status::NOT_FOUND,
71 crate::uapi::ENOMEM => zx_status::Status::NO_MEMORY,
72 crate::uapi::EINVAL => zx_status::Status::INVALID_ARGS,
73 crate::uapi::ETIMEDOUT => zx_status::Status::TIMED_OUT,
74 crate::uapi::EBUSY => zx_status::Status::UNAVAILABLE,
75 crate::uapi::EEXIST => zx_status::Status::ALREADY_EXISTS,
76 crate::uapi::EPIPE => zx_status::Status::PEER_CLOSED,
77 crate::uapi::ENAMETOOLONG => zx_status::Status::BAD_PATH,
78 crate::uapi::EIO => zx_status::Status::IO,
79 crate::uapi::EISDIR => zx_status::Status::NOT_FILE,
80 crate::uapi::ENOTDIR => zx_status::Status::NOT_DIR,
81 crate::uapi::EOPNOTSUPP => zx_status::Status::NOT_SUPPORTED,
82 crate::uapi::EBADF => zx_status::Status::BAD_HANDLE,
83 crate::uapi::EACCES => zx_status::Status::ACCESS_DENIED,
84 crate::uapi::EAGAIN => zx_status::Status::SHOULD_WAIT,
85 crate::uapi::EFBIG => zx_status::Status::FILE_BIG,
86 crate::uapi::ENOSPC => zx_status::Status::NO_SPACE,
87 crate::uapi::ENOTEMPTY => zx_status::Status::NOT_EMPTY,
88 crate::uapi::EPROTONOSUPPORT => zx_status::Status::PROTOCOL_NOT_SUPPORTED,
89 crate::uapi::ENETUNREACH => zx_status::Status::ADDRESS_UNREACHABLE,
90 crate::uapi::EADDRINUSE => zx_status::Status::ADDRESS_IN_USE,
91 crate::uapi::ENOTCONN => zx_status::Status::NOT_CONNECTED,
92 crate::uapi::ECONNREFUSED => zx_status::Status::CONNECTION_REFUSED,
93 crate::uapi::ECONNRESET => zx_status::Status::CONNECTION_RESET,
94 crate::uapi::ECONNABORTED => zx_status::Status::CONNECTION_ABORTED,
95 _ => zx_status::Status::NOT_SUPPORTED,
96 }
97 }
98}
99
100#[derive(Eq, PartialEq, Copy, Clone)]
101pub struct ErrnoCode(u32);
102
103impl ErrnoCode {
104 pub const fn from_return_value(retval: u64) -> Self {
105 let retval = retval as i64;
106 if retval >= 0 {
107 return Self(0);
110 }
111 Self(-retval as u32)
112 }
113
114 pub const fn from_error_code(code: i16) -> Self {
115 Self(code as u32)
116 }
117
118 pub const fn return_value(&self) -> u64 {
119 -(self.0 as i32) as u64
120 }
121
122 pub const fn error_code(&self) -> u32 {
123 self.0
124 }
125
126 pub fn is_restartable(&self) -> bool {
128 matches!(*self, ERESTARTSYS | ERESTARTNOINTR | ERESTARTNOHAND | ERESTART_RESTARTBLOCK)
129 }
130
131 pub fn should_restart(&self, sigaction: Option<sigaction_t>) -> bool {
142 let sigaction = sigaction.unwrap_or_default();
145
146 let should_restart_even_if_sigaction_handler_not_null = match *self {
147 ERESTARTSYS => sigaction.sa_flags & SA_RESTART as u64 != 0,
148 ERESTARTNOINTR => true,
149 ERESTARTNOHAND | ERESTART_RESTARTBLOCK => false,
150
151 _ => return false,
153 };
154
155 should_restart_even_if_sigaction_handler_not_null || sigaction.sa_handler.addr == 0
157 }
158}
159
160impl Debug for ErrnoCode {
161 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
162 write!(f, "{}", self)
163 }
164}
165
166impl Display for ErrnoCode {
167 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168 write!(f, "{}({})", self.name(), self.0)
169 }
170}
171
172pub const ERESTARTSYS: ErrnoCode = ErrnoCode(512);
187
188pub const ERESTARTNOINTR: ErrnoCode = ErrnoCode(513);
190
191pub const ERESTARTNOHAND: ErrnoCode = ErrnoCode(514);
193
194pub const ERESTART_RESTARTBLOCK: ErrnoCode = ErrnoCode(516);
197
198pub trait ErrnoResultExt<T> {
200 fn map_eintr(self, make_errno: impl Fn() -> Errno) -> Result<T, Errno>;
202}
203
204impl<T> ErrnoResultExt<T> for Result<T, Errno> {
205 fn map_eintr(self, make_errno: impl Fn() -> Errno) -> Result<T, Errno> {
206 self.map_err(|err| if err == EINTR { make_errno() } else { err })
207 }
208}
209
210macro_rules! errno_codes {
211 ($($name:ident),+) => {
212 $(pub const $name: ErrnoCode = ErrnoCode(crate::uapi::$name);)+
213
214 impl ErrnoCode {
215 fn name(&self) -> &'static str {
216 match self.0 {
217 $(
218 crate::uapi::$name => stringify!($name),
219 )+
220 _ => "unknown error code",
221 }
222 }
223 }
224
225 #[cfg(test)]
226 #[test]
227 fn expected_errno_code_strings() {
228 $(
229 assert_eq!(
230 $name.to_string(),
231 format!("{}({})", stringify!($name), crate::uapi::$name),
232 );
233 )+
234 }
235 };
236}
237
238errno_codes![
239 EPERM,
240 ENOENT,
241 ESRCH,
242 EINTR,
243 EIO,
244 ENXIO,
245 E2BIG,
246 ENOEXEC,
247 EBADF,
248 ECHILD,
249 EAGAIN,
250 ENOMEM,
251 EACCES,
252 EFAULT,
253 ENOTBLK,
254 EBUSY,
255 EEXIST,
256 EXDEV,
257 ENODEV,
258 ENOTDIR,
259 EISDIR,
260 EINVAL,
261 ENFILE,
262 EMFILE,
263 ENOTTY,
264 ETXTBSY,
265 EFBIG,
266 ENOSPC,
267 ESPIPE,
268 EROFS,
269 EMLINK,
270 EPIPE,
271 EDOM,
272 ERANGE,
273 ENAMETOOLONG,
274 ENOLCK,
275 ENOSYS,
276 ENOTEMPTY,
277 ELOOP,
278 ENOMSG,
279 EIDRM,
280 ECHRNG,
281 EL2NSYNC,
282 EL3HLT,
283 EL3RST,
284 ELNRNG,
285 EUNATCH,
286 ENOCSI,
287 EL2HLT,
288 EBADE,
289 EBADR,
290 EXFULL,
291 ENOANO,
292 EBADRQC,
293 EBADSLT,
294 EDEADLOCK,
295 EBFONT,
296 ENOSTR,
297 ENODATA,
298 ETIME,
299 ENOSR,
300 ENONET,
301 ENOPKG,
302 EREMOTE,
303 ENOLINK,
304 EADV,
305 ESRMNT,
306 ECOMM,
307 EPROTO,
308 EMULTIHOP,
309 EDOTDOT,
310 EBADMSG,
311 EOVERFLOW,
312 ENOTUNIQ,
313 EBADFD,
314 EREMCHG,
315 ELIBACC,
316 ELIBBAD,
317 ELIBSCN,
318 ELIBMAX,
319 ELIBEXEC,
320 EILSEQ,
321 ERESTART,
322 ESTRPIPE,
323 EUSERS,
324 ENOTSOCK,
325 EDESTADDRREQ,
326 EMSGSIZE,
327 EPROTOTYPE,
328 ENOPROTOOPT,
329 EPROTONOSUPPORT,
330 ESOCKTNOSUPPORT,
331 EOPNOTSUPP,
332 EPFNOSUPPORT,
333 EAFNOSUPPORT,
334 EADDRINUSE,
335 EADDRNOTAVAIL,
336 ENETDOWN,
337 ENETUNREACH,
338 ENETRESET,
339 ECONNABORTED,
340 ECONNRESET,
341 ENOBUFS,
342 EISCONN,
343 ENOTCONN,
344 ESHUTDOWN,
345 ETOOMANYREFS,
346 ETIMEDOUT,
347 ECONNREFUSED,
348 EHOSTDOWN,
349 EHOSTUNREACH,
350 EALREADY,
351 EINPROGRESS,
352 ESTALE,
353 EUCLEAN,
354 ENOTNAM,
355 ENAVAIL,
356 EISNAM,
357 EREMOTEIO,
358 EDQUOT,
359 ENOMEDIUM,
360 EMEDIUMTYPE,
361 ECANCELED,
362 ENOKEY,
363 EKEYEXPIRED,
364 EKEYREVOKED,
365 EKEYREJECTED,
366 EOWNERDEAD,
367 ENOTRECOVERABLE,
368 ERFKILL,
369 EHWPOISON
370];
371
372pub const ENOTSUP: ErrnoCode = EOPNOTSUPP;
374
375#[macro_export]
379macro_rules! errno {
380 ($err:ident) => {
381 $crate::errors::Errno::new($crate::errors::$err)
382 };
383 ($err:ident, $context:expr) => {
384 $crate::errors::Errno::with_context($crate::errors::$err, $context.to_string())
385 };
386}
387
388#[macro_export]
393macro_rules! error {
394 ($($args:tt)*) => { Err($crate::errno!($($args)*)) };
395}
396
397#[macro_export]
400macro_rules! errno_from_code {
401 ($err:expr) => {{ $crate::errors::Errno::new($crate::errors::ErrnoCode::from_error_code($err)) }};
402}
403
404#[macro_export]
407macro_rules! errno_from_zxio_code {
408 ($err:expr) => {{ $crate::errno_from_code!($err.raw()) }};
409}
410
411#[macro_export]
416macro_rules! from_status_like_fdio {
417 ($status:expr) => {{ $crate::from_status_like_fdio!($status, "") }};
418 ($status:expr, $context:expr) => {{
419 match $status {
420 $crate::__zx_status::Status::NOT_FOUND => $crate::errno!(ENOENT, $context),
421 $crate::__zx_status::Status::NO_MEMORY => $crate::errno!(ENOMEM, $context),
422 $crate::__zx_status::Status::INVALID_ARGS => $crate::errno!(EINVAL, $context),
423 $crate::__zx_status::Status::BUFFER_TOO_SMALL => $crate::errno!(EINVAL, $context),
424 $crate::__zx_status::Status::TIMED_OUT => $crate::errno!(ETIMEDOUT, $context),
425 $crate::__zx_status::Status::UNAVAILABLE => $crate::errno!(EBUSY, $context),
426 $crate::__zx_status::Status::ALREADY_EXISTS => $crate::errno!(EEXIST, $context),
427 $crate::__zx_status::Status::PEER_CLOSED => $crate::errno!(EPIPE, $context),
428 $crate::__zx_status::Status::BAD_STATE => $crate::errno!(EPIPE, $context),
429 $crate::__zx_status::Status::BAD_PATH => $crate::errno!(ENAMETOOLONG, $context),
430 $crate::__zx_status::Status::IO => $crate::errno!(EIO, $context),
431 $crate::__zx_status::Status::NOT_FILE => $crate::errno!(EISDIR, $context),
432 $crate::__zx_status::Status::NOT_DIR => $crate::errno!(ENOTDIR, $context),
433 $crate::__zx_status::Status::NOT_SUPPORTED => $crate::errno!(EOPNOTSUPP, $context),
434 $crate::__zx_status::Status::WRONG_TYPE => $crate::errno!(EOPNOTSUPP, $context),
435 $crate::__zx_status::Status::OUT_OF_RANGE => $crate::errno!(EINVAL, $context),
436 $crate::__zx_status::Status::NO_RESOURCES => $crate::errno!(ENOMEM, $context),
437 $crate::__zx_status::Status::BAD_HANDLE => $crate::errno!(EBADF, $context),
438 $crate::__zx_status::Status::ACCESS_DENIED => $crate::errno!(EACCES, $context),
439 $crate::__zx_status::Status::SHOULD_WAIT => $crate::errno!(EAGAIN, $context),
440 $crate::__zx_status::Status::FILE_BIG => $crate::errno!(EFBIG, $context),
441 $crate::__zx_status::Status::NO_SPACE => $crate::errno!(ENOSPC, $context),
442 $crate::__zx_status::Status::NOT_EMPTY => $crate::errno!(ENOTEMPTY, $context),
443 $crate::__zx_status::Status::IO_REFUSED => $crate::errno!(ECONNREFUSED, $context),
444 $crate::__zx_status::Status::IO_INVALID => $crate::errno!(EIO, $context),
445 $crate::__zx_status::Status::CANCELED => $crate::errno!(EBADF, $context),
446 $crate::__zx_status::Status::PROTOCOL_NOT_SUPPORTED => {
447 $crate::errno!(EPROTONOSUPPORT, $context)
448 }
449 $crate::__zx_status::Status::ADDRESS_UNREACHABLE => {
450 $crate::errno!(ENETUNREACH, $context)
451 }
452 $crate::__zx_status::Status::ADDRESS_IN_USE => $crate::errno!(EADDRINUSE, $context),
453 $crate::__zx_status::Status::NOT_CONNECTED => $crate::errno!(ENOTCONN, $context),
454 $crate::__zx_status::Status::CONNECTION_REFUSED => {
455 $crate::errno!(ECONNREFUSED, $context)
456 }
457 $crate::__zx_status::Status::CONNECTION_RESET => $crate::errno!(ECONNRESET, $context),
458 $crate::__zx_status::Status::CONNECTION_ABORTED => {
459 $crate::errno!(ECONNABORTED, $context)
460 }
461 _ => $crate::errno!(EIO, $context),
462 }
463 }};
464}
465
466pub use errno;
468pub use errno_from_code;
469pub use errno_from_zxio_code;
470pub use error;
471pub use from_status_like_fdio;
472
473pub trait SourceContext<T, E> {
474 #[track_caller]
492 fn with_source_context(self, context: impl FnOnce() -> String) -> anyhow::Result<T>;
493
494 #[track_caller]
498 fn source_context<C>(self, context: C) -> anyhow::Result<T>
499 where
500 C: Display + Send + Sync + 'static;
501}
502
503impl<T, E> SourceContext<T, E> for Result<T, E>
505where
506 Result<T, E>: anyhow::Context<T, E>,
507{
508 fn with_source_context(self, context: impl FnOnce() -> String) -> anyhow::Result<T> {
509 use anyhow::Context;
510 let caller = std::panic::Location::caller();
511 self.with_context(move || format!("{}, {}", context(), caller))
512 }
513
514 fn source_context<C>(self, context: C) -> anyhow::Result<T>
515 where
516 C: Display + Send + Sync + 'static,
517 {
518 use anyhow::Context;
519 let caller = std::panic::Location::caller();
520 self.context(format!("{}, {}", context, caller))
521 }
522}
523
524const MAX_ERRNO: i32 = 4095;
525
526pub fn is_error_return_value(value: i32) -> bool {
527 value < 0 && value >= -MAX_ERRNO
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533 use std::panic::Location;
534
535 #[test]
536 fn basic_errno_formatting() {
537 let location = std::panic::Location::caller();
538 let errno = Errno { code: ENOENT, location, context: None };
539 assert_eq!(errno.to_string(), format!("ENOENT(2) from {}", location));
540 }
541
542 #[test]
543 fn context_errno_formatting() {
544 let location = std::panic::Location::caller();
545 let errno = Errno { code: ENOENT, location, context: Some("TEST CONTEXT".to_string()) };
546 assert_eq!(
547 errno.to_string(),
548 format!("ENOENT(2) from {}, context: TEST CONTEXT", location)
549 );
550 }
551
552 #[test]
553 fn with_source_context() {
554 let errno = Errno { code: ENOENT, location: std::panic::Location::caller(), context: None };
555 let result: Result<(), Errno> = Err(errno);
556 let error = result.with_source_context(|| format!("42")).unwrap_err();
557 let line_after_error = Location::caller();
558 let expected_prefix =
559 format!("42, {}:{}:", line_after_error.file(), line_after_error.line() - 1,);
560 assert!(
561 error.to_string().starts_with(&expected_prefix),
562 "{} must start with {}",
563 error,
564 expected_prefix
565 );
566 }
567
568 #[test]
569 fn source_context() {
570 let errno = Errno { code: ENOENT, location: std::panic::Location::caller(), context: None };
571 let result: Result<(), Errno> = Err(errno);
572 let error = result.source_context("42").unwrap_err();
573 let line_after_error = Location::caller();
574 let expected_prefix =
575 format!("42, {}:{}:", line_after_error.file(), line_after_error.line() - 1,);
576 assert!(
577 error.to_string().starts_with(&expected_prefix),
578 "{} must start with {}",
579 error,
580 expected_prefix
581 );
582 }
583}