rawsys_linux/errno/
mod.rs

1//! Errno: minimal, no_std-friendly Linux error codes
2//!
3//! `Errno` wraps Linux error numbers and offers lightweight conversions from
4//! raw syscall return values. It intentionally avoids depending on `std` by
5//! default so it can be used in `no_std` environments.
6//!
7//! - Use `Errno::from_ret_u32`/`from_ret_u64` to convert raw returns into
8//!   `Result` without panicking or allocating.
9//! - With the `std` feature, `Errno` integrates with `std::io::Error` and can
10//!   retrieve the thread-local errno via `Errno::last()`.
11//! - For convenience, aliases such as `EWOULDBLOCK` map to canonical variants.
12//!
13//! Design intent
14//! - Avoid conflating OS errors with richer I/O errors: conversion from
15//!   `std::io::Error` is fallible (`from_io_error`) because not every I/O error
16//!   is a plain errno.
17//! - Keep formatting cheap: `Display`/`Debug` prefer static names and short
18//!   messages when available.
19//!
20#[macro_use]
21mod macros;
22
23#[allow(clippy::all, clippy::pedantic)]
24mod generated;
25
26#[cfg(feature = "std")]
27mod last;
28
29use core::fmt;
30
31pub use self::generated::Errno;
32
33impl Errno {
34    /// Operation would block. This is the same as [`Errno::EAGAIN`].
35    pub const EWOULDBLOCK: Self = Self::EAGAIN;
36
37    /// Same as [`Errno::EDEADLK`].
38    pub const EDEADLOCK: Self = Self::EDEADLK;
39
40    /// Creates a new `Errno`.
41    pub fn new(num: i32) -> Self {
42        Self(num)
43    }
44
45    /// Converts the `Errno` into a raw `i32`.
46    pub fn into_raw(self) -> i32 {
47        self.0
48    }
49
50    /// Returns true if the error code is valid (i.e., less than 4096).
51    pub fn is_valid(&self) -> bool {
52        self.0 < 4096
53    }
54
55    /// Converts a raw syscall return value to a result.
56    ///
57    /// > Please use [`Errno::from_ret_u32`] or [`Errno::from_ret_u64`].
58    /// > Refactored [`Errno::from_ret`] to handle u32 and u64 explicitly, instead of using usize.
59    #[inline(always)]
60    #[deprecated = "It is recommended to explicitly use u32 or u64."]
61    pub fn from_ret(value: usize) -> Result<usize, Errno> {
62        if value > -4096isize as usize {
63            // Truncation of the error value is guaranteed to never occur due to
64            // the above check. This is the same check that musl uses:
65            // https://git.musl-libc.org/cgit/musl/tree/src/internal/syscall_ret.c?h=v1.1.15
66            Err(Self(-(value as i32)))
67        } else {
68            Ok(value)
69        }
70    }
71
72    /// Rewriting of [`Errno::from_ret`] to use a u32 for pointer width. This function is for platforms where a pointer has a size of 32 bits.
73    #[inline(always)]
74    pub fn from_ret_u32(value: u32) -> Result<u32, Errno> {
75        const THRESHOLD: u32 = u32::MAX - 4095; // == (u32)(-4096)
76        if value > THRESHOLD {
77            // Restore -ret to positive errno code (1..=4095).
78            let code = (u32::MAX - value + 1) as i32;
79            Err(Errno(code))
80        } else {
81            Ok(value)
82        }
83    }
84
85    /// Rewriting of [`Errno::from_ret`] to use a u64 for register width. This function is for platforms where the syscall return register is 64 bits.
86    #[inline(always)]
87    pub fn from_ret_u64(value: u64) -> Result<u64, Errno> {
88        const THRESHOLD: u64 = u64::MAX - 4095; // == (u64)(-4096)
89        if value > THRESHOLD {
90            // Restore -ret to positive errno code (1..=4095).
91            let code = (u64::MAX - value + 1) as i32;
92            Err(Errno(code))
93        } else {
94            Ok(value)
95        }
96    }
97    /// Returns the last error that occurred.
98    #[cfg(feature = "std")]
99    pub fn last() -> Self {
100        Self(unsafe { *last::errno() })
101    }
102
103    /// Converts a value into an `Errno`.
104    #[cfg(feature = "std")]
105    pub fn result<T>(value: T) -> Result<T, Errno>
106    where
107        T: ErrnoSentinel + PartialEq<T>,
108    {
109        if value == T::sentinel() {
110            Err(Self::last())
111        } else {
112            Ok(value)
113        }
114    }
115
116    /// Returns the name of the error. If the internal error code is unknown or
117    /// invalid, `None` is returned.
118    pub fn name(&self) -> Option<&'static str> {
119        self.name_and_description().map(|x| x.0)
120    }
121
122    /// Returns the error description. If the internal error code is unknown or
123    /// invalid, `None` is returned.
124    pub fn description(&self) -> Option<&'static str> {
125        self.name_and_description().map(|x| x.1)
126    }
127
128    /// Converts an `std::io::Error` into an `Errno` if possible. Since an error
129    /// code is just one of the few possible error types that `std::io::Error`
130    /// can represent, this will return `None` if the conversion is not possible.
131    ///
132    /// A `From<std::io::Error>` implementation is not provided because this
133    /// conversion can fail. However, the reverse is possible, so that is
134    /// provided as a `From` implementation.
135    #[cfg(feature = "std")]
136    pub fn from_io_error(err: std::io::Error) -> Option<Self> {
137        err.raw_os_error().map(Self::new)
138    }
139}
140
141impl fmt::Display for Errno {
142    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143        match self.name_and_description() {
144            Some((name, description)) => {
145                write!(f, "{} {name} ({description})", -self.0)
146            }
147            None => {
148                if self.is_valid() {
149                    write!(f, "{}", -self.0)
150                } else {
151                    write!(f, "Invalid errno {:#x}", self.0)
152                }
153            }
154        }
155    }
156}
157
158impl fmt::Debug for Errno {
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        match self.name() {
161            Some(name) => f.write_str(name),
162            None => write!(f, "Errno({})", self.0),
163        }
164    }
165}
166
167#[cfg(feature = "std")]
168impl From<Errno> for std::io::Error {
169    fn from(err: Errno) -> Self {
170        std::io::Error::from_raw_os_error(err.into_raw())
171    }
172}
173
174#[cfg(feature = "std")]
175impl std::error::Error for Errno {}
176
177pub trait ErrnoSentinel: Sized {
178    fn sentinel() -> Self;
179}
180
181impl ErrnoSentinel for isize {
182    fn sentinel() -> Self {
183        -1
184    }
185}
186
187impl ErrnoSentinel for i32 {
188    fn sentinel() -> Self {
189        -1
190    }
191}
192
193impl ErrnoSentinel for i64 {
194    fn sentinel() -> Self {
195        -1
196    }
197}
198
199impl ErrnoSentinel for *mut core::ffi::c_void {
200    fn sentinel() -> Self {
201        -1isize as *mut core::ffi::c_void
202    }
203}
204
205impl ErrnoSentinel for usize {
206    fn sentinel() -> Self {
207        usize::MAX
208    }
209}
210
211#[cfg(test)]
212mod test {
213    use super::*;
214
215    #[test]
216    fn basic() {
217        assert_eq!(Errno::ENOENT.name(), Some("ENOENT"));
218        assert_eq!(
219            Errno::ENOENT.description(),
220            Some("No such file or directory")
221        );
222        #[cfg(feature = "std")]
223        {
224            assert_eq!(
225                format!("{}", Errno::ENOENT),
226                "-2 ENOENT (No such file or directory)"
227            );
228            assert_eq!(format!("{:?}", Errno::ENOENT), "ENOENT");
229        }
230    }
231
232    #[allow(deprecated)]
233    #[test]
234    fn from_ret() {
235        assert_eq!(Errno::from_ret(-2isize as usize), Err(Errno::ENOENT));
236        assert_eq!(Errno::from_ret_u64(-2isize as u64), Err(Errno::ENOENT));
237        assert_eq!(Errno::from_ret_u32(-2isize as u32), Err(Errno::ENOENT));
238        assert_eq!(Errno::from_ret(2), Ok(2));
239        assert_eq!(Errno::from_ret_u32(2), Ok(2));
240        assert_eq!(Errno::from_ret_u64(2), Ok(2));
241    }
242
243    #[cfg(feature = "std")]
244    #[test]
245    fn io_error() {
246        use std::io;
247
248        assert_eq!(
249            io::Error::from(Errno::ENOENT).kind(),
250            io::ErrorKind::NotFound
251        );
252
253        assert_eq!(
254            Errno::from_io_error(io::Error::from_raw_os_error(2)),
255            Some(Errno::ENOENT)
256        );
257
258        assert_eq!(
259            Errno::from_io_error(io::Error::new(io::ErrorKind::Other, "")),
260            None
261        );
262    }
263
264    #[cfg(feature = "std")]
265    #[test]
266    fn last_errno() {
267        assert_eq!(
268            Errno::result(unsafe {
269                libc::open(
270                    b"this_should_not_exist\0".as_ptr() as *const _,
271                    libc::O_RDONLY,
272                )
273            }),
274            Err(Errno::ENOENT)
275        );
276    }
277}