Skip to main content

std/sys/io/error/
unix.rs

1use crate::ffi::{CStr, c_char, c_int};
2use crate::io;
3
4unsafe extern "C" {
5    #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
6    #[cfg_attr(
7        any(
8            target_os = "linux",
9            target_os = "emscripten",
10            target_os = "fuchsia",
11            target_os = "l4re",
12            target_os = "hurd",
13        ),
14        link_name = "__errno_location"
15    )]
16    #[cfg_attr(
17        any(
18            target_os = "netbsd",
19            target_os = "openbsd",
20            target_os = "cygwin",
21            target_os = "android",
22            target_os = "redox",
23            target_os = "nuttx",
24            target_env = "newlib"
25        ),
26        link_name = "__errno"
27    )]
28    #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
29    #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
30    #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")]
31    #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
32    #[cfg_attr(target_os = "aix", link_name = "_Errno")]
33    // SAFETY: this will always return the same pointer on a given thread.
34    #[unsafe(ffi_const)]
35    pub safe fn errno_location() -> *mut c_int;
36}
37
38/// Returns the platform-specific value of errno
39#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
40#[inline]
41pub fn errno() -> i32 {
42    unsafe { (*errno_location()) as i32 }
43}
44
45/// Sets the platform-specific value of errno
46// needed for readdir and syscall!
47#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))]
48#[allow(dead_code)] // but not all target cfgs actually end up using it
49#[inline]
50pub fn set_errno(e: i32) {
51    unsafe { *errno_location() = e as c_int }
52}
53
54#[cfg(target_os = "vxworks")]
55#[inline]
56pub fn errno() -> i32 {
57    unsafe { libc::errnoGet() }
58}
59
60#[cfg(target_os = "rtems")]
61#[inline]
62pub fn errno() -> i32 {
63    unsafe extern "C" {
64        #[thread_local]
65        static _tls_errno: c_int;
66    }
67
68    unsafe { _tls_errno as i32 }
69}
70
71#[cfg(target_os = "dragonfly")]
72#[inline]
73pub fn errno() -> i32 {
74    unsafe extern "C" {
75        #[thread_local]
76        static errno: c_int;
77    }
78
79    unsafe { errno as i32 }
80}
81
82#[cfg(target_os = "dragonfly")]
83#[allow(dead_code)]
84#[inline]
85pub fn set_errno(e: i32) {
86    unsafe extern "C" {
87        #[thread_local]
88        static mut errno: c_int;
89    }
90
91    unsafe {
92        errno = e;
93    }
94}
95
96#[inline]
97pub fn is_interrupted(errno: i32) -> bool {
98    errno == libc::EINTR
99}
100
101pub fn decode_error_kind(errno: i32) -> io::ErrorKind {
102    use io::ErrorKind::*;
103    match errno as libc::c_int {
104        libc::E2BIG => ArgumentListTooLong,
105        libc::EADDRINUSE => AddrInUse,
106        libc::EADDRNOTAVAIL => AddrNotAvailable,
107        libc::EBUSY => ResourceBusy,
108        libc::ECONNABORTED => ConnectionAborted,
109        libc::ECONNREFUSED => ConnectionRefused,
110        libc::ECONNRESET => ConnectionReset,
111        libc::EDEADLK => Deadlock,
112        libc::EDQUOT => QuotaExceeded,
113        libc::EEXIST => AlreadyExists,
114        libc::EFBIG => FileTooLarge,
115        libc::EHOSTUNREACH => HostUnreachable,
116        libc::EINTR => Interrupted,
117        libc::EINVAL => InvalidInput,
118        libc::EISDIR => IsADirectory,
119        libc::ELOOP => FilesystemLoop,
120        libc::ENOENT => NotFound,
121        libc::ENOMEM => OutOfMemory,
122        libc::ENOSPC => StorageFull,
123        libc::ENOSYS => Unsupported,
124        libc::EMLINK => TooManyLinks,
125        libc::ENAMETOOLONG => InvalidFilename,
126        libc::ENETDOWN => NetworkDown,
127        libc::ENETUNREACH => NetworkUnreachable,
128        libc::ENOTCONN => NotConnected,
129        libc::ENOTDIR => NotADirectory,
130        #[cfg(not(target_os = "aix"))]
131        libc::ENOTEMPTY => DirectoryNotEmpty,
132        libc::EPIPE => BrokenPipe,
133        libc::EROFS => ReadOnlyFilesystem,
134        libc::ESPIPE => NotSeekable,
135        libc::ESTALE => StaleNetworkFileHandle,
136        libc::ETIMEDOUT => TimedOut,
137        libc::ETXTBSY => ExecutableFileBusy,
138        libc::EXDEV => CrossesDevices,
139        libc::EINPROGRESS => InProgress,
140        libc::EOPNOTSUPP => Unsupported,
141
142        libc::EACCES | libc::EPERM => PermissionDenied,
143
144        // These two constants can have the same value on some systems,
145        // but different values on others, so we can't use a match
146        // clause
147        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock,
148
149        _ => Uncategorized,
150    }
151}
152
153/// Gets a detailed string description for the given error number.
154pub fn error_string(errno: i32) -> String {
155    const TMPBUF_SZ: usize = 128;
156
157    unsafe extern "C" {
158        #[cfg_attr(
159            all(
160                any(
161                    target_os = "linux",
162                    target_os = "hurd",
163                    target_env = "newlib",
164                    target_os = "cygwin"
165                ),
166                not(target_env = "ohos")
167            ),
168            link_name = "__xpg_strerror_r"
169        )]
170        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int;
171    }
172
173    let mut buf = [0 as c_char; TMPBUF_SZ];
174
175    let p = buf.as_mut_ptr();
176    unsafe {
177        if strerror_r(errno as c_int, p, buf.len()) < 0 {
178            panic!("strerror_r failure");
179        }
180
181        let p = p as *const _;
182        // We can't always expect a UTF-8 environment. When we don't get that luxury,
183        // it's better to give a low-quality error message than none at all.
184        String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into()
185    }
186}