/rust/registry/src/index.crates.io-1949cf8c6b5b557f/getrandom-0.4.1/src/error.rs
Line | Count | Source |
1 | | #[cfg(feature = "std")] |
2 | | extern crate std; |
3 | | |
4 | | use core::fmt; |
5 | | |
6 | | cfg_if::cfg_if!( |
7 | | if #[cfg(target_os = "uefi")] { |
8 | | // See the UEFI spec for more information: |
9 | | // https://uefi.org/specs/UEFI/2.10/Apx_D_Status_Codes.html |
10 | | |
11 | | /// Raw error code. |
12 | | /// |
13 | | /// This alias mirrors unstable [`std::io::RawOsError`]. |
14 | | /// |
15 | | /// [`std::io::RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html |
16 | | pub type RawOsError = usize; |
17 | | type NonZeroRawOsError = core::num::NonZeroUsize; |
18 | | const UEFI_ERROR_FLAG: RawOsError = 1 << (RawOsError::BITS - 1); |
19 | | } else { |
20 | | /// Raw error code. |
21 | | /// |
22 | | /// This alias mirrors unstable [`std::io::RawOsError`]. |
23 | | /// |
24 | | /// [`std::io::RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html |
25 | | pub type RawOsError = i32; |
26 | | type NonZeroRawOsError = core::num::NonZeroI32; |
27 | | } |
28 | | ); |
29 | | |
30 | | /// A small and `no_std` compatible error type |
31 | | /// |
32 | | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and |
33 | | /// if so, which error code the OS gave the application. If such an error is |
34 | | /// encountered, please consult with your system documentation. |
35 | | /// |
36 | | /// *If this crate's `"std"` Cargo feature is enabled*, then: |
37 | | /// - [`getrandom::Error`][Error] implements |
38 | | /// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) |
39 | | /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements |
40 | | /// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html). |
41 | | |
42 | | // note: on non-UEFI targets OS errors are represented as negative integers, |
43 | | // while on UEFI targets OS errors have the highest bit set to 1. |
44 | | #[derive(Copy, Clone, Eq, PartialEq)] |
45 | | pub struct Error(NonZeroRawOsError); |
46 | | |
47 | | impl Error { |
48 | | /// This target/platform is not supported by `getrandom`. |
49 | | pub const UNSUPPORTED: Error = Self::new_internal(0); |
50 | | /// The platform-specific `errno` returned a non-positive value. |
51 | | pub const ERRNO_NOT_POSITIVE: Error = Self::new_internal(1); |
52 | | /// Encountered an unexpected situation which should not happen in practice. |
53 | | pub const UNEXPECTED: Error = Self::new_internal(2); |
54 | | |
55 | | /// Internal errors can be in the range of 2^16..2^17 |
56 | | const INTERNAL_START: RawOsError = 1 << 16; |
57 | | /// Custom errors can be in the range of 2^17..(2^17 + 2^16) |
58 | | const CUSTOM_START: RawOsError = 1 << 17; |
59 | | |
60 | | /// Creates a new `Error` instance from a positive error code. |
61 | | /// |
62 | | /// Returns [`Error::ERRNO_NOT_POSITIVE`] for zero and negative error codes. |
63 | | #[cfg(not(target_os = "uefi"))] |
64 | | #[allow(dead_code)] |
65 | 0 | pub(super) fn from_errno(errno: i32) -> Self { |
66 | 0 | if errno > 0 { |
67 | 0 | let code = errno |
68 | 0 | .checked_neg() |
69 | 0 | .expect("Positive number can be always negated"); |
70 | 0 | Error::from_neg_error_code(code) |
71 | | } else { |
72 | 0 | Error::ERRNO_NOT_POSITIVE |
73 | | } |
74 | 0 | } |
75 | | |
76 | | /// Creates a new `Error` instance from a negative error code. |
77 | | /// |
78 | | /// Returns [`Error::UNEXPECTED`] for zero and positive error codes. |
79 | | #[cfg(not(target_os = "uefi"))] |
80 | | #[allow(dead_code)] |
81 | 0 | pub(super) fn from_neg_error_code(code: RawOsError) -> Self { |
82 | 0 | if code < 0 { |
83 | 0 | let code = NonZeroRawOsError::new(code).expect("`code` is negative"); |
84 | 0 | Self(code) |
85 | | } else { |
86 | 0 | Error::UNEXPECTED |
87 | | } |
88 | 0 | } |
89 | | |
90 | | /// Creates a new instance of an `Error` from an UEFI error code. |
91 | | #[cfg(target_os = "uefi")] |
92 | | #[allow(dead_code)] |
93 | | pub(super) fn from_uefi_code(code: RawOsError) -> Self { |
94 | | if code & UEFI_ERROR_FLAG != 0 { |
95 | | let code = NonZeroRawOsError::new(code).expect("The highest bit of `code` is set to 1"); |
96 | | Self(code) |
97 | | } else { |
98 | | Self::UNEXPECTED |
99 | | } |
100 | | } |
101 | | |
102 | | /// Extract the raw OS error code (if this error came from the OS) |
103 | | /// |
104 | | /// This method is identical to [`std::io::Error::raw_os_error()`][1], except |
105 | | /// that it works in `no_std` contexts. On most targets this method returns |
106 | | /// `Option<i32>`, but some platforms (e.g. UEFI) may use a different primitive |
107 | | /// type like `usize`. Consult with the [`RawOsError`] docs for more information. |
108 | | /// |
109 | | /// If this method returns `None`, the error value can still be formatted via |
110 | | /// the `Display` implementation. |
111 | | /// |
112 | | /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error |
113 | | /// [`RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html |
114 | | #[inline] |
115 | 0 | pub fn raw_os_error(self) -> Option<RawOsError> { |
116 | 0 | let code = self.0.get(); |
117 | | |
118 | | // note: in this method we need to cover only backends which rely on |
119 | | // `Error::{from_error_code, from_errno, from_uefi_code}` methods, |
120 | | // on all other backends this method always returns `None`. |
121 | | |
122 | | #[cfg(target_os = "uefi")] |
123 | | { |
124 | | if code & UEFI_ERROR_FLAG != 0 { |
125 | | Some(code) |
126 | | } else { |
127 | | None |
128 | | } |
129 | | } |
130 | | |
131 | | #[cfg(not(target_os = "uefi"))] |
132 | | { |
133 | | // On most targets `std` expects positive error codes while retrieving error strings: |
134 | | // - `libc`-based targets use `strerror_r` which expects positive error codes. |
135 | | // - Hermit relies on the `hermit-abi` crate, which expects positive error codes: |
136 | | // https://docs.rs/hermit-abi/0.4.0/src/hermit_abi/errno.rs.html#400-532 |
137 | | // - WASIp1 uses the same conventions as `libc`: |
138 | | // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/wasi/os.rs#L57-L67 |
139 | | // |
140 | | // The only exception is Solid, `std` expects negative system error codes, see: |
141 | | // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/solid/error.rs#L5-L31 |
142 | 0 | if code >= 0 { |
143 | 0 | None |
144 | 0 | } else if cfg!(not(target_os = "solid_asp3")) { |
145 | 0 | code.checked_neg() |
146 | | } else { |
147 | 0 | Some(code) |
148 | | } |
149 | | } |
150 | 0 | } |
151 | | |
152 | | /// Creates a new instance of an `Error` from a particular custom error code. |
153 | 0 | pub const fn new_custom(n: u16) -> Error { |
154 | | // SAFETY: code > 0 as CUSTOM_START > 0 and adding `n` won't overflow `RawOsError`. |
155 | 0 | let code = Error::CUSTOM_START + (n as RawOsError); |
156 | 0 | Error(unsafe { NonZeroRawOsError::new_unchecked(code) }) |
157 | 0 | } |
158 | | |
159 | | /// Creates a new instance of an `Error` from a particular internal error code. |
160 | 0 | pub(crate) const fn new_internal(n: u16) -> Error { |
161 | | // SAFETY: code > 0 as INTERNAL_START > 0 and adding `n` won't overflow `RawOsError`. |
162 | 0 | let code = Error::INTERNAL_START + (n as RawOsError); |
163 | 0 | Error(unsafe { NonZeroRawOsError::new_unchecked(code) }) |
164 | 0 | } |
165 | | |
166 | 0 | fn internal_desc(&self) -> Option<&'static str> { |
167 | 0 | let desc = match *self { |
168 | 0 | Error::UNSUPPORTED => "getrandom: this target is not supported", |
169 | 0 | Error::ERRNO_NOT_POSITIVE => "errno: did not return a positive value", |
170 | 0 | Error::UNEXPECTED => "unexpected situation", |
171 | | #[cfg(any( |
172 | | target_os = "ios", |
173 | | target_os = "visionos", |
174 | | target_os = "watchos", |
175 | | target_os = "tvos", |
176 | | ))] |
177 | | Error::IOS_RANDOM_GEN => "SecRandomCopyBytes: iOS Security framework failure", |
178 | | #[cfg(all(windows, target_vendor = "win7"))] |
179 | | Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure", |
180 | | #[cfg(all( |
181 | | feature = "wasm_js", |
182 | | target_arch = "wasm32", |
183 | | any(target_os = "unknown", target_os = "none") |
184 | | ))] |
185 | | Error::WEB_CRYPTO => "Web Crypto API is unavailable", |
186 | | #[cfg(target_os = "vxworks")] |
187 | | Error::VXWORKS_RAND_SECURE => "randSecure: VxWorks RNG module is not initialized", |
188 | | |
189 | | #[cfg(any( |
190 | | getrandom_backend = "rdrand", |
191 | | all(target_arch = "x86_64", target_env = "sgx") |
192 | | ))] |
193 | | Error::FAILED_RDRAND => "RDRAND: failed multiple times: CPU issue likely", |
194 | | #[cfg(any( |
195 | | getrandom_backend = "rdrand", |
196 | | all(target_arch = "x86_64", target_env = "sgx") |
197 | | ))] |
198 | | Error::NO_RDRAND => "RDRAND: instruction not supported", |
199 | | |
200 | | #[cfg(getrandom_backend = "rndr")] |
201 | | Error::RNDR_FAILURE => "RNDR: Could not generate a random number", |
202 | | #[cfg(getrandom_backend = "rndr")] |
203 | | Error::RNDR_NOT_AVAILABLE => "RNDR: Register not supported", |
204 | 0 | _ => return None, |
205 | | }; |
206 | 0 | Some(desc) |
207 | 0 | } |
208 | | } |
209 | | |
210 | | impl core::error::Error for Error {} |
211 | | |
212 | | impl fmt::Debug for Error { |
213 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
214 | 0 | let mut dbg = f.debug_struct("Error"); |
215 | 0 | if let Some(errno) = self.raw_os_error() { |
216 | 0 | dbg.field("os_error", &errno); |
217 | 0 | #[cfg(feature = "std")] |
218 | 0 | dbg.field("description", &std::io::Error::from_raw_os_error(errno)); |
219 | 0 | } else if let Some(desc) = self.internal_desc() { |
220 | 0 | dbg.field("internal_code", &self.0.get()); |
221 | 0 | dbg.field("description", &desc); |
222 | 0 | } else { |
223 | 0 | dbg.field("unknown_code", &self.0.get()); |
224 | 0 | } |
225 | 0 | dbg.finish() |
226 | 0 | } |
227 | | } |
228 | | |
229 | | impl fmt::Display for Error { |
230 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
231 | 0 | if let Some(errno) = self.raw_os_error() { |
232 | | cfg_if! { |
233 | | if #[cfg(feature = "std")] { |
234 | | std::io::Error::from_raw_os_error(errno).fmt(f) |
235 | | } else { |
236 | 0 | write!(f, "OS Error: {errno}") |
237 | | } |
238 | | } |
239 | 0 | } else if let Some(desc) = self.internal_desc() { |
240 | 0 | f.write_str(desc) |
241 | | } else { |
242 | 0 | write!(f, "Unknown Error: {}", self.0.get()) |
243 | | } |
244 | 0 | } |
245 | | } |