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, errno_from_code, errno_from_zxio_code, error, from_status_like_fdio};
468
469pub trait SourceContext<T, E> {
470 #[track_caller]
488 fn with_source_context(self, context: impl FnOnce() -> String) -> anyhow::Result<T>;
489
490 #[track_caller]
494 fn source_context<C>(self, context: C) -> anyhow::Result<T>
495 where
496 C: Display + Send + Sync + 'static;
497}
498
499impl<T, E> SourceContext<T, E> for Result<T, E>
501where
502 Result<T, E>: anyhow::Context<T, E>,
503{
504 fn with_source_context(self, context: impl FnOnce() -> String) -> anyhow::Result<T> {
505 use anyhow::Context;
506 let caller = std::panic::Location::caller();
507 self.with_context(move || format!("{}, {}", context(), caller))
508 }
509
510 fn source_context<C>(self, context: C) -> anyhow::Result<T>
511 where
512 C: Display + Send + Sync + 'static,
513 {
514 use anyhow::Context;
515 let caller = std::panic::Location::caller();
516 self.context(format!("{}, {}", context, caller))
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523 use std::panic::Location;
524
525 #[test]
526 fn basic_errno_formatting() {
527 let location = std::panic::Location::caller();
528 let errno = Errno { code: ENOENT, location, context: None };
529 assert_eq!(errno.to_string(), format!("ENOENT(2) from {}", location));
530 }
531
532 #[test]
533 fn context_errno_formatting() {
534 let location = std::panic::Location::caller();
535 let errno = Errno { code: ENOENT, location, context: Some("TEST CONTEXT".to_string()) };
536 assert_eq!(
537 errno.to_string(),
538 format!("ENOENT(2) from {}, context: TEST CONTEXT", location)
539 );
540 }
541
542 #[test]
543 fn with_source_context() {
544 let errno = Errno { code: ENOENT, location: std::panic::Location::caller(), context: None };
545 let result: Result<(), Errno> = Err(errno);
546 let error = result.with_source_context(|| format!("42")).unwrap_err();
547 let line_after_error = Location::caller();
548 let expected_prefix =
549 format!("42, {}:{}:", line_after_error.file(), line_after_error.line() - 1,);
550 assert!(
551 error.to_string().starts_with(&expected_prefix),
552 "{} must start with {}",
553 error,
554 expected_prefix
555 );
556 }
557
558 #[test]
559 fn source_context() {
560 let errno = Errno { code: ENOENT, location: std::panic::Location::caller(), context: None };
561 let result: Result<(), Errno> = Err(errno);
562 let error = result.source_context("42").unwrap_err();
563 let line_after_error = Location::caller();
564 let expected_prefix =
565 format!("42, {}:{}:", line_after_error.file(), line_after_error.line() - 1,);
566 assert!(
567 error.to_string().starts_with(&expected_prefix),
568 "{} must start with {}",
569 error,
570 expected_prefix
571 );
572 }
573}