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