Skip to main content

strat9_syscall/
error.rs

1pub use strat9_abi::errno::*;
2
3use num_enum::{IntoPrimitive, TryFromPrimitive};
4
5const ERRNO_MAX: isize = 4095;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive, thiserror::Error)]
8#[must_use]
9#[repr(usize)]
10pub enum Error {
11    #[error("Operation not permitted")]
12    PermissionDenied = EPERM,
13    #[error("No such file or directory")]
14    NotFound = ENOENT,
15    #[error("No such process")]
16    NoSuchProcess = ESRCH,
17    #[error("Interrupted system call")]
18    Interrupted = EINTR,
19    #[error("Input/output error")]
20    IoError = EIO,
21    #[error("Argument list too long")]
22    ArgumentListTooLong = E2BIG,
23    #[error("Exec format error")]
24    ExecFormatError = ENOEXEC,
25    #[error("Bad file descriptor")]
26    BadHandle = EBADF,
27    #[error("No child processes")]
28    NoChildren = ECHILD,
29    #[error("Resource temporarily unavailable")]
30    Again = EAGAIN,
31    #[error("Cannot allocate memory")]
32    OutOfMemory = ENOMEM,
33    #[error("Permission denied")]
34    AccessDenied = EACCES,
35    #[error("Bad address")]
36    Fault = EFAULT,
37    #[error("File exists")]
38    AlreadyExists = EEXIST,
39    #[error("Not a directory")]
40    NotADirectory = ENOTDIR,
41    #[error("Is a directory")]
42    IsADirectory = EISDIR,
43    #[error("Invalid argument")]
44    InvalidArgument = EINVAL,
45    #[error("Not a typewriter")]
46    NotATty = ENOTTY,
47    #[error("No space left on device")]
48    NoSpace = ENOSPC,
49    #[error("Broken pipe")]
50    Pipe = EPIPE,
51    #[error("Result too large")]
52    RangeError = ERANGE,
53    #[error("File name too long")]
54    NameTooLong = ENAMETOOLONG,
55    #[error("Function not implemented")]
56    NotImplemented = ENOSYS,
57    #[error("Directory not empty")]
58    NotEmpty = ENOTEMPTY,
59    #[error("Too many levels of symbolic links")]
60    SymlinkLoop = ELOOP,
61    #[error("Not supported")]
62    NotSupported = ENOTSUP,
63    #[error("Address already in use")]
64    AddressInUse = EADDRINUSE,
65    #[error("No buffer space available")]
66    QueueFull = ENOBUFS,
67    #[error("Connection timed out")]
68    TimedOut = ETIMEDOUT,
69    #[error("Connection refused")]
70    ConnectionRefused = ECONNREFUSED,
71    #[error("Unknown error (errno={0})")]
72    #[num_enum(catch_all)]
73    Unknown(usize),
74}
75
76impl Error {
77    #[inline]
78    /// Convert a raw errno value into a typed `Error`.
79    pub fn from_errno(errno: usize) -> Self {
80        Self::try_from(errno).unwrap_or(Error::Unknown(errno))
81    }
82
83    #[inline]
84    /// Convert this error to its numeric errno representation.
85    pub fn to_errno(&self) -> usize {
86        usize::from(*self)
87    }
88
89    #[inline]
90    /// Decode a raw syscall return value into `Result`.
91    pub fn demux(ret: usize) -> core::result::Result<usize, Error> {
92        // Strat9 syscall ABI encodes errors as negative errno values in RAX.
93        let ret_s = ret as isize;
94        if ret_s >= -ERRNO_MAX && ret_s < 0 {
95            Err(Error::from_errno((-ret_s) as usize))
96        } else {
97            Ok(ret)
98        }
99    }
100
101    #[inline]
102    /// Return true when retrying later may succeed (`EINTR`/`EAGAIN`).
103    pub fn is_retryable(&self) -> bool {
104        matches!(self, Error::Interrupted | Error::Again)
105    }
106
107    #[inline]
108    /// Return the canonical symbolic errno name.
109    pub fn name(&self) -> &'static str {
110        match self {
111            Error::PermissionDenied => "EPERM",
112            Error::NotFound => "ENOENT",
113            Error::NoSuchProcess => "ESRCH",
114            Error::Interrupted => "EINTR",
115            Error::IoError => "EIO",
116            Error::ArgumentListTooLong => "E2BIG",
117            Error::ExecFormatError => "ENOEXEC",
118            Error::BadHandle => "EBADF",
119            Error::NoChildren => "ECHILD",
120            Error::Again => "EAGAIN",
121            Error::OutOfMemory => "ENOMEM",
122            Error::AccessDenied => "EACCES",
123            Error::Fault => "EFAULT",
124            Error::AlreadyExists => "EEXIST",
125            Error::NotADirectory => "ENOTDIR",
126            Error::IsADirectory => "EISDIR",
127            Error::InvalidArgument => "EINVAL",
128            Error::NotATty => "ENOTTY",
129            Error::NoSpace => "ENOSPC",
130            Error::Pipe => "EPIPE",
131            Error::RangeError => "ERANGE",
132            Error::NameTooLong => "ENAMETOOLONG",
133            Error::NotImplemented => "ENOSYS",
134            Error::NotEmpty => "ENOTEMPTY",
135            Error::SymlinkLoop => "ELOOP",
136            Error::NotSupported => "ENOTSUP",
137            Error::AddressInUse => "EADDRINUSE",
138            Error::QueueFull => "ENOBUFS",
139            Error::TimedOut => "ETIMEDOUT",
140            Error::ConnectionRefused => "ECONNREFUSED",
141            Error::Unknown(_) => "E???",
142        }
143    }
144}
145
146/// Result type used by the syscall API.
147pub type Result<T> = core::result::Result<T, Error>;