Skip to main content

error_code/
posix.rs

1use crate::{Category, MessageBuf, ErrorCode};
2use crate::utils::write_fallback_code;
3use crate::types::c_int;
4
5use core::{ptr, str};
6
7/// Posix error category, suitable for all environments.
8///
9/// In presence of OS, it means it identifies POSIX error codes.
10pub static POSIX_CATEGORY: Category = Category {
11    name: "PosixError",
12    message,
13    equivalent,
14    is_would_block,
15};
16
17fn equivalent(code: c_int, other: &ErrorCode) -> bool {
18    ptr::eq(&POSIX_CATEGORY, other.category()) && code == other.raw_code()
19}
20
21//Reference
22//https://github.com/rust-lang/rust/blob/0844f35a32c98882d77e39da8c2a872ec74615cd/library/std/src/sys/io/error/unix.rs#L6
23//
24//newlib can technically be on bare metal so add it as exception
25#[cfg(any(target_env = "newlib", not(any(target_os = "unknown", target_os = "hermit", target_os = "vxworks", target_os = "dragonfly"))))]
26pub(crate) fn get_last_error() -> c_int {
27    extern "C" {
28        #[cfg_attr(
29            any(
30                target_os = "linux",
31                target_os = "emscripten",
32                target_os = "fuchsia",
33                target_os = "l4re",
34                target_os = "hurd",
35                target_os = "teeos",
36                target_os = "wasi"
37            ),
38            link_name = "__errno_location"
39        )]
40        #[cfg_attr(
41            any(
42                target_os = "netbsd",
43                target_os = "openbsd",
44                target_os = "cygwin",
45                target_os = "android",
46                target_os = "redox",
47                target_os = "nuttx",
48                target_env = "newlib"
49            ),
50            link_name = "__errno"
51        )]
52        #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
53        #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
54        #[cfg_attr(target_os = "qnx", link_name = "__get_errno_ptr")]
55        #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")]
56        #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
57        #[cfg_attr(target_os = "aix", link_name = "_Errno")]
58        #[cfg_attr(target_os = "windows", link_name = "_errno")]
59        fn errno_location() -> *mut c_int;
60    }
61
62    unsafe {
63        *(errno_location())
64    }
65}
66
67#[cfg(target_os = "dragonfly")]
68pub(crate) fn get_last_error() -> c_int {
69    //Rust uses thread local, but it might be better just use __errno_location() location for portability sake
70    //Referenece: https://github.com/WebAssembly/wasi-libc/blob/355422cd5effd01fde5dc069ae4c34828c1e85f1/libc-bottom-half/sources/__errno_location.c#L6
71    extern "C" {
72        #[thread_local]
73        static errno: c_int;
74    }
75
76    errno
77}
78
79#[cfg(target_os = "vxworks")]
80pub(crate) fn get_last_error() -> c_int {
81    extern "C" {
82        pub fn errnoGet() -> c_int;
83    }
84
85    unsafe {
86        errnoGet()
87    }
88}
89
90#[cfg(target_os = "hermit")]
91pub(crate) fn get_last_error() -> c_int {
92    extern "C" {
93        #[link_name = "sys_get_errno"]
94        pub fn get_errno() -> c_int;
95    }
96
97    unsafe {
98        get_errno()
99    }
100}
101
102#[cfg(all(target_os = "unknown", not(target_env = "newlib")))]
103pub(crate) fn get_last_error() -> c_int {
104    0
105}
106
107pub(crate) fn message(_code: c_int, out: &mut MessageBuf) -> &str {
108    #[cfg(any(windows, target_os = "wasi", all(unix, not(target_env = "gnu"))))]
109    extern "C" {
110        ///Only GNU impl is thread unsafe
111        fn strerror(code: c_int) -> *const i8;
112        fn strlen(text: *const i8) -> usize;
113    }
114
115    #[cfg(all(unix, target_env = "gnu"))]
116    extern "C" {
117        fn strerror_l(code: c_int, locale: *mut i8) -> *const i8;
118        fn strlen(text: *const i8) -> usize;
119    }
120
121    #[cfg(all(unix, target_env = "gnu"))]
122    #[inline]
123    unsafe fn strerror(code: c_int) -> *const i8 {
124        strerror_l(code, ptr::null_mut())
125    }
126
127    #[cfg(any(windows, unix, target_os = "wasi"))]
128    {
129        let err = unsafe {
130            strerror(_code)
131        };
132
133        if !err.is_null() {
134            let err_len = unsafe {
135                core::cmp::min(out.len(), strlen(err) as _)
136            };
137
138            let err_slice = unsafe {
139                ptr::copy_nonoverlapping(err as *const u8, out.as_mut_ptr() as *mut u8, err_len);
140                core::slice::from_raw_parts(out.as_ptr() as *const u8, err_len)
141            };
142
143            if let Ok(msg) = str::from_utf8(err_slice) {
144                return msg
145            }
146        }
147    }
148
149    write_fallback_code(out, _code)
150}
151
152#[cfg(not(any(windows, unix, target_os = "wasi")))]
153pub(crate) fn is_would_block(_: c_int) -> bool {
154    false
155}
156
157#[cfg(any(windows, unix, target_os = "wasi"))]
158pub(crate) fn is_would_block(code: c_int) -> bool {
159    code == crate::defs::EWOULDBLOCK || code == crate::defs::EAGAIN
160}