1
1
#[ cfg( feature = "std" ) ]
2
2
extern crate std;
3
3
4
- use core:: { fmt, num :: NonZeroU32 } ;
4
+ use core:: fmt;
5
5
6
6
// This private alias mirrors `std::io::RawOsError`:
7
7
// https://doc.rust-lang.org/std/io/type.RawOsError.html)
8
8
cfg_if:: cfg_if!(
9
9
if #[ cfg( target_os = "uefi" ) ] {
10
+ // See the UEFI spec for more information:
11
+ // https://uefi.org/specs/UEFI/2.10/Apx_D_Status_Codes.html
10
12
type RawOsError = usize ;
13
+ type NonZeroRawOsError = core:: num:: NonZeroUsize ;
14
+ const UEFI_ERROR_FLAG : RawOsError = 1 << ( RawOsError :: BITS - 1 ) ;
11
15
} else {
12
16
type RawOsError = i32 ;
17
+ type NonZeroRawOsError = core:: num:: NonZeroI32 ;
13
18
}
14
19
) ;
15
20
@@ -19,16 +24,16 @@ cfg_if::cfg_if!(
19
24
/// if so, which error code the OS gave the application. If such an error is
20
25
/// encountered, please consult with your system documentation.
21
26
///
22
- /// Internally this type is a NonZeroU32, with certain values reserved for
23
- /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
24
- ///
25
27
/// *If this crate's `"std"` Cargo feature is enabled*, then:
26
28
/// - [`getrandom::Error`][Error] implements
27
29
/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
28
30
/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
29
31
/// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
32
+
33
+ // note: on non-UEFI targets OS errors are represented as negative integers,
34
+ // while on UEFI targets OS errors have the highest bit set to 1.
30
35
#[ derive( Copy , Clone , Eq , PartialEq ) ]
31
- pub struct Error ( NonZeroU32 ) ;
36
+ pub struct Error ( NonZeroRawOsError ) ;
32
37
33
38
impl Error {
34
39
/// This target/platform is not supported by `getrandom`.
@@ -38,29 +43,32 @@ impl Error {
38
43
/// Encountered an unexpected situation which should not happen in practice.
39
44
pub const UNEXPECTED : Error = Self :: new_internal ( 2 ) ;
40
45
41
- /// Codes below this point represent OS Errors (i.e. positive i32 values).
42
- /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
43
- /// reserved for use by the `rand` and `getrandom` crates.
44
- pub const INTERNAL_START : u32 = 1 << 31 ;
46
+ /// Internal errors can be in the range of 2^16..2^17
47
+ const INTERNAL_START : RawOsError = 1 << 16 ;
48
+ /// Custom errors can be in the range of 2^17..(2^17 + 2^16)
49
+ const CUSTOM_START : RawOsError = 1 << 17 ;
45
50
46
- /// Codes at or above this point can be used by users to define their own
47
- /// custom errors.
48
- pub const CUSTOM_START : u32 = ( 1 << 31 ) + ( 1 << 30 ) ;
51
+ /// Creates a new instance of an `Error` from a negative error code.
52
+ #[ cfg( not( target_os = "uefi" ) ) ]
53
+ #[ allow( dead_code) ]
54
+ pub ( super ) fn from_neg_error_code ( code : RawOsError ) -> Self {
55
+ if code < 0 {
56
+ let code = NonZeroRawOsError :: new ( code) . expect ( "`code` is negative" ) ;
57
+ Self ( code)
58
+ } else {
59
+ Error :: UNEXPECTED
60
+ }
61
+ }
49
62
50
- /// Creates a new instance of an `Error` from a particular OS error code.
51
- ///
52
- /// This method is analogous to [`std::io::Error::from_raw_os_error()`][1],
53
- /// except that it works in `no_std` contexts and `code` will be
54
- /// replaced with `Error::UNEXPECTED` if it isn't in the range
55
- /// `1..Error::INTERNAL_START`. Thus, for the result `r`,
56
- /// `r == Self::UNEXPECTED || r.raw_os_error().unsigned_abs() == code`.
57
- ///
58
- /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.from_raw_os_error
63
+ /// Creates a new instance of an `Error` from an UEFI error code.
64
+ #[ cfg( target_os = "uefi" ) ]
59
65
#[ allow( dead_code) ]
60
- pub ( super ) fn from_os_error ( code : u32 ) -> Self {
61
- match NonZeroU32 :: new ( code) {
62
- Some ( code) if code. get ( ) < Self :: INTERNAL_START => Self ( code) ,
63
- _ => Self :: UNEXPECTED ,
66
+ pub ( super ) fn from_uefi_code ( code : RawOsError ) -> Self {
67
+ if code & UEFI_ERROR_FLAG != 0 {
68
+ let code = NonZeroRawOsError :: new ( code) . expect ( "The highest bit of `code` is set to 1" ) ;
69
+ Self ( code)
70
+ } else {
71
+ Self :: UNEXPECTED
64
72
}
65
73
}
66
74
@@ -79,27 +87,53 @@ impl Error {
79
87
#[ inline]
80
88
pub fn raw_os_error ( self ) -> Option < RawOsError > {
81
89
let code = self . 0 . get ( ) ;
82
- if code >= Self :: INTERNAL_START {
83
- return None ;
90
+
91
+ // note: in this method we need to cover only backends which rely on
92
+ // `Error::{from_error_code, from_errno, from_uefi_code}` methods,
93
+ // on all other backends this method always returns `None`.
94
+
95
+ #[ cfg( target_os = "uefi" ) ]
96
+ {
97
+ if code & UEFI_ERROR_FLAG != 0 {
98
+ Some ( code)
99
+ } else {
100
+ None
101
+ }
102
+ }
103
+
104
+ #[ cfg( not( target_os = "uefi" ) ) ]
105
+ {
106
+ // On most targets `std` expects positive error codes while retrieving error strings:
107
+ // - `libc`-based targets use `strerror_r` which expects positive error codes.
108
+ // - Hermit relies on the `hermit-abi` crate, which expects positive error codes:
109
+ // https://docs.rs/hermit-abi/0.4.0/src/hermit_abi/errno.rs.html#400-532
110
+ // - WASIp1 uses the same conventions as `libc`:
111
+ // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/wasi/os.rs#L57-L67
112
+ //
113
+ // The only exception is Solid, `std` expects negative system error codes, see:
114
+ // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/solid/error.rs#L5-L31
115
+ if code >= 0 {
116
+ None
117
+ } else if cfg ! ( not( target_os = "solid_asp3" ) ) {
118
+ code. checked_neg ( )
119
+ } else {
120
+ Some ( code)
121
+ }
84
122
}
85
- let errno = RawOsError :: try_from ( code) . ok ( ) ?;
86
- #[ cfg( target_os = "solid_asp3" ) ]
87
- let errno = -errno;
88
- Some ( errno)
89
123
}
90
124
91
125
/// Creates a new instance of an `Error` from a particular custom error code.
92
126
pub const fn new_custom ( n : u16 ) -> Error {
93
- // SAFETY: code > 0 as CUSTOM_START > 0 and adding n won't overflow a u32 .
94
- let code = Error :: CUSTOM_START + ( n as u32 ) ;
95
- Error ( unsafe { NonZeroU32 :: new_unchecked ( code) } )
127
+ // SAFETY: code > 0 as CUSTOM_START > 0 and adding `n` won't overflow `RawOsError` .
128
+ let code = Error :: CUSTOM_START + ( n as RawOsError ) ;
129
+ Error ( unsafe { NonZeroRawOsError :: new_unchecked ( code) } )
96
130
}
97
131
98
132
/// Creates a new instance of an `Error` from a particular internal error code.
99
133
pub ( crate ) const fn new_internal ( n : u16 ) -> Error {
100
- // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32 .
101
- let code = Error :: INTERNAL_START + ( n as u32 ) ;
102
- Error ( unsafe { NonZeroU32 :: new_unchecked ( code) } )
134
+ // SAFETY: code > 0 as INTERNAL_START > 0 and adding `n` won't overflow `RawOsError` .
135
+ let code = Error :: INTERNAL_START + ( n as RawOsError ) ;
136
+ Error ( unsafe { NonZeroRawOsError :: new_unchecked ( code) } )
103
137
}
104
138
105
139
fn internal_desc ( & self ) -> Option < & ' static str > {
@@ -176,15 +210,3 @@ impl fmt::Display for Error {
176
210
}
177
211
}
178
212
}
179
-
180
- #[ cfg( test) ]
181
- mod tests {
182
- use super :: Error ;
183
- use core:: mem:: size_of;
184
-
185
- #[ test]
186
- fn test_size ( ) {
187
- assert_eq ! ( size_of:: <Error >( ) , 4 ) ;
188
- assert_eq ! ( size_of:: <Result <( ) , Error >>( ) , 4 ) ;
189
- }
190
- }
0 commit comments