| Joel Galenson | 4be0c6d | 2020-07-07 13:20:14 -0700 | [diff] [blame] | 1 | // Copyright 2018 Developers of the Rand project. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| 6 | // option. This file may not be copied, modified, or distributed |
| 7 | // except according to those terms. |
| 8 | use core::fmt; |
| 9 | use core::num::NonZeroU32; |
| 10 | |
| 11 | /// A small and `no_std` compatible error type. |
| 12 | /// |
| 13 | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and |
| 14 | /// if so, which error code the OS gave the application. If such an error is |
| 15 | /// encountered, please consult with your system documentation. |
| 16 | /// |
| 17 | /// Internally this type is a NonZeroU32, with certain values reserved for |
| 18 | /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. |
| 19 | #[derive(Copy, Clone, Eq, PartialEq)] |
| 20 | pub struct Error(NonZeroU32); |
| 21 | |
| 22 | impl Error { |
| 23 | #[deprecated(since = "0.1.7")] |
| 24 | /// Unknown error. |
| 25 | pub const UNKNOWN: Error = UNSUPPORTED; |
| 26 | #[deprecated(since = "0.1.7")] |
| 27 | /// System entropy source is unavailable. |
| 28 | pub const UNAVAILABLE: Error = UNSUPPORTED; |
| 29 | |
| 30 | /// Codes below this point represent OS Errors (i.e. positive i32 values). |
| 31 | /// Codes at or above this point, but below [`Error::CUSTOM_START`] are |
| 32 | /// reserved for use by the `rand` and `getrandom` crates. |
| 33 | pub const INTERNAL_START: u32 = 1 << 31; |
| 34 | |
| 35 | /// Codes at or above this point can be used by users to define their own |
| 36 | /// custom errors. |
| 37 | pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); |
| 38 | |
| 39 | /// Extract the raw OS error code (if this error came from the OS) |
| 40 | /// |
| 41 | /// This method is identical to `std::io::Error::raw_os_error()`, except |
| 42 | /// that it works in `no_std` contexts. If this method returns `None`, the |
| 43 | /// error value can still be formatted via the `Display` implementation. |
| 44 | #[inline] |
| 45 | pub fn raw_os_error(self) -> Option<i32> { |
| 46 | if self.0.get() < Self::INTERNAL_START { |
| 47 | Some(self.0.get() as i32) |
| 48 | } else { |
| 49 | None |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | /// Extract the bare error code. |
| 54 | /// |
| 55 | /// This code can either come from the underlying OS, or be a custom error. |
| 56 | /// Use [`Error::raw_os_error()`] to disambiguate. |
| 57 | #[inline] |
| 58 | pub fn code(self) -> NonZeroU32 { |
| 59 | self.0 |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | cfg_if! { |
| 64 | if #[cfg(unix)] { |
| 65 | fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { |
| 66 | let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; |
| 67 | if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { |
| 68 | return None; |
| 69 | } |
| 70 | |
| 71 | // Take up to trailing null byte |
| 72 | let n = buf.len(); |
| 73 | let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); |
| 74 | core::str::from_utf8(&buf[..idx]).ok() |
| 75 | } |
| 76 | } else if #[cfg(target_os = "wasi")] { |
| 77 | fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> { |
| 78 | wasi::Error::from_raw_error(errno as _) |
| 79 | } |
| 80 | } else { |
| 81 | fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { |
| 82 | None |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | impl fmt::Debug for Error { |
| 88 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 89 | let mut dbg = f.debug_struct("Error"); |
| 90 | if let Some(errno) = self.raw_os_error() { |
| 91 | dbg.field("os_error", &errno); |
| 92 | let mut buf = [0u8; 128]; |
| 93 | if let Some(err) = os_err(errno, &mut buf) { |
| 94 | dbg.field("description", &err); |
| 95 | } |
| 96 | } else if let Some(desc) = internal_desc(*self) { |
| 97 | dbg.field("internal_code", &self.0.get()); |
| 98 | dbg.field("description", &desc); |
| 99 | } else { |
| 100 | dbg.field("unknown_code", &self.0.get()); |
| 101 | } |
| 102 | dbg.finish() |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | impl fmt::Display for Error { |
| 107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 108 | if let Some(errno) = self.raw_os_error() { |
| 109 | let mut buf = [0u8; 128]; |
| 110 | match os_err(errno, &mut buf) { |
| 111 | Some(err) => err.fmt(f), |
| 112 | None => write!(f, "OS Error: {}", errno), |
| 113 | } |
| 114 | } else if let Some(desc) = internal_desc(*self) { |
| 115 | f.write_str(desc) |
| 116 | } else { |
| 117 | write!(f, "Unknown Error: {}", self.0.get()) |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | impl From<NonZeroU32> for Error { |
| 123 | fn from(code: NonZeroU32) -> Self { |
| 124 | Self(code) |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | // TODO: Convert to a function when min_version >= 1.33 |
| 129 | macro_rules! internal_error { |
| 130 | ($n:expr) => { |
| 131 | Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) }) |
| 132 | }; |
| 133 | } |
| 134 | |
| 135 | /// Internal Error constants |
| 136 | pub(crate) const UNSUPPORTED: Error = internal_error!(0); |
| 137 | pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1); |
| 138 | pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2); |
| 139 | pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3); |
| 140 | pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4); |
| 141 | pub(crate) const FAILED_RDRAND: Error = internal_error!(5); |
| 142 | pub(crate) const NO_RDRAND: Error = internal_error!(6); |
| 143 | pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7); |
| 144 | pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8); |
| 145 | pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9); |
| 146 | pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10); |
| 147 | pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11); |
| 148 | |
| 149 | fn internal_desc(error: Error) -> Option<&'static str> { |
| 150 | match error { |
| 151 | UNSUPPORTED => Some("getrandom: this target is not supported"), |
| 152 | ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), |
| 153 | UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), |
| 154 | SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), |
| 155 | RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"), |
| 156 | FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), |
| 157 | NO_RDRAND => Some("RDRAND: instruction not supported"), |
| 158 | BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"), |
| 159 | BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"), |
| 160 | STDWEB_NO_RNG => Some("stdweb: no randomness source available"), |
| 161 | STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"), |
| 162 | RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"), |
| 163 | _ => None, |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | #[cfg(test)] |
| 168 | mod tests { |
| 169 | use super::Error; |
| 170 | use core::mem::size_of; |
| 171 | |
| 172 | #[test] |
| 173 | fn test_size() { |
| 174 | assert_eq!(size_of::<Error>(), 4); |
| 175 | assert_eq!(size_of::<Result<(), Error>>(), 4); |
| 176 | } |
| 177 | } |