summaryrefslogtreecommitdiff
path: root/getrandom/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'getrandom/src/error.rs')
-rw-r--r--getrandom/src/error.rs178
1 files changed, 178 insertions, 0 deletions
diff --git a/getrandom/src/error.rs b/getrandom/src/error.rs
new file mode 100644
index 0000000..b2cb9a8
--- /dev/null
+++ b/getrandom/src/error.rs
@@ -0,0 +1,178 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use core::fmt;
+use core::num::NonZeroU32;
+
+/// A small and `no_std` compatible error type.
+///
+/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
+/// if so, which error code the OS gave the application. If such an error is
+/// encountered, please consult with your system documentation.
+///
+/// Internally this type is a NonZeroU32, with certain values reserved for
+/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Error(NonZeroU32);
+
+impl Error {
+ #[deprecated(since = "0.1.7")]
+ /// Unknown error.
+ pub const UNKNOWN: Error = UNSUPPORTED;
+ #[deprecated(since = "0.1.7")]
+ /// System entropy source is unavailable.
+ pub const UNAVAILABLE: Error = UNSUPPORTED;
+
+ /// Codes below this point represent OS Errors (i.e. positive i32 values).
+ /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
+ /// reserved for use by the `rand` and `getrandom` crates.
+ pub const INTERNAL_START: u32 = 1 << 31;
+
+ /// Codes at or above this point can be used by users to define their own
+ /// custom errors.
+ pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
+
+ /// Extract the raw OS error code (if this error came from the OS)
+ ///
+ /// This method is identical to `std::io::Error::raw_os_error()`, except
+ /// that it works in `no_std` contexts. If this method returns `None`, the
+ /// error value can still be formatted via the `Display` implementation.
+ #[inline]
+ pub fn raw_os_error(self) -> Option<i32> {
+ if self.0.get() < Self::INTERNAL_START {
+ Some(self.0.get() as i32)
+ } else {
+ None
+ }
+ }
+
+ /// Extract the bare error code.
+ ///
+ /// This code can either come from the underlying OS, or be a custom error.
+ /// Use [`Error::raw_os_error()`] to disambiguate.
+ #[inline]
+ pub fn code(self) -> NonZeroU32 {
+ self.0
+ }
+}
+
+cfg_if! {
+ if #[cfg(unix)] {
+ fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> {
+ let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
+ if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
+ return None;
+ }
+
+ // Take up to trailing null byte
+ let n = buf.len();
+ let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
+ core::str::from_utf8(&buf[..idx]).ok()
+ }
+ } else if #[cfg(target_os = "wasi")] {
+ fn os_err_desc(errno: i32, _buf: &mut [u8]) -> Option<&str> {
+ core::num::NonZeroU16::new(errno as u16)
+ .and_then(wasi::wasi_unstable::error_str)
+ }
+ } else {
+ fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
+ None
+ }
+ }
+}
+
+impl fmt::Debug for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut dbg = f.debug_struct("Error");
+ if let Some(errno) = self.raw_os_error() {
+ dbg.field("os_error", &errno);
+ let mut buf = [0u8; 128];
+ if let Some(desc) = os_err_desc(errno, &mut buf) {
+ dbg.field("description", &desc);
+ }
+ } else if let Some(desc) = internal_desc(*self) {
+ dbg.field("internal_code", &self.0.get());
+ dbg.field("description", &desc);
+ } else {
+ dbg.field("unknown_code", &self.0.get());
+ }
+ dbg.finish()
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(errno) = self.raw_os_error() {
+ let mut buf = [0u8; 128];
+ match os_err_desc(errno, &mut buf) {
+ Some(desc) => f.write_str(desc),
+ None => write!(f, "OS Error: {}", errno),
+ }
+ } else if let Some(desc) = internal_desc(*self) {
+ f.write_str(desc)
+ } else {
+ write!(f, "Unknown Error: {}", self.0.get())
+ }
+ }
+}
+
+impl From<NonZeroU32> for Error {
+ fn from(code: NonZeroU32) -> Self {
+ Self(code)
+ }
+}
+
+// TODO: Convert to a function when min_version >= 1.33
+macro_rules! internal_error {
+ ($n:expr) => {
+ Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) })
+ };
+}
+
+/// Internal Error constants
+pub(crate) const UNSUPPORTED: Error = internal_error!(0);
+pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1);
+pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2);
+pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3);
+pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4);
+pub(crate) const FAILED_RDRAND: Error = internal_error!(5);
+pub(crate) const NO_RDRAND: Error = internal_error!(6);
+pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7);
+pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8);
+pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9);
+pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10);
+pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11);
+
+fn internal_desc(error: Error) -> Option<&'static str> {
+ match error {
+ UNSUPPORTED => Some("getrandom: this target is not supported"),
+ ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
+ UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"),
+ SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"),
+ RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"),
+ FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
+ NO_RDRAND => Some("RDRAND: instruction not supported"),
+ BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"),
+ BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"),
+ STDWEB_NO_RNG => Some("stdweb: no randomness source available"),
+ STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"),
+ RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"),
+ _ => None,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Error;
+ use core::mem::size_of;
+
+ #[test]
+ fn test_size() {
+ assert_eq!(size_of::<Error>(), 4);
+ assert_eq!(size_of::<Result<(), Error>>(), 4);
+ }
+}