11#[ cfg( feature = "std" ) ]
22extern crate std;
33
4- use core:: { fmt, num :: NonZeroU32 } ;
4+ use core:: fmt;
55
66// This private alias mirrors `std::io::RawOsError`:
77// https://doc.rust-lang.org/std/io/type.RawOsError.html)
88cfg_if:: cfg_if!(
99 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
1012 type RawOsError = usize ;
13+ type NonZeroRawOsError = core:: num:: NonZeroUsize ;
14+ const UEFI_ERROR_FLAG : RawOsError = 1 << ( RawOsError :: BITS - 1 ) ;
1115 } else {
1216 type RawOsError = i32 ;
17+ type NonZeroRawOsError = core:: num:: NonZeroI32 ;
1318 }
1419) ;
1520
@@ -19,16 +24,16 @@ cfg_if::cfg_if!(
1924/// if so, which error code the OS gave the application. If such an error is
2025/// encountered, please consult with your system documentation.
2126///
22- /// Internally this type is a NonZeroU32, with certain values reserved for
23- /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
24- ///
2527/// *If this crate's `"std"` Cargo feature is enabled*, then:
2628/// - [`getrandom::Error`][Error] implements
2729/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
2830/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
2931/// [`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.
3035#[ derive( Copy , Clone , Eq , PartialEq ) ]
31- pub struct Error ( NonZeroU32 ) ;
36+ pub struct Error ( NonZeroRawOsError ) ;
3237
3338impl Error {
3439 /// This target/platform is not supported by `getrandom`.
@@ -38,29 +43,32 @@ impl Error {
3843 /// Encountered an unexpected situation which should not happen in practice.
3944 pub const UNEXPECTED : Error = Self :: new_internal ( 2 ) ;
4045
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 ;
4550
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+ }
4962
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" ) ]
5965 #[ 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
6472 }
6573 }
6674
@@ -79,27 +87,53 @@ impl Error {
7987 #[ inline]
8088 pub fn raw_os_error ( self ) -> Option < RawOsError > {
8189 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+ }
84122 }
85- let errno = RawOsError :: try_from ( code) . ok ( ) ?;
86- #[ cfg( target_os = "solid_asp3" ) ]
87- let errno = -errno;
88- Some ( errno)
89123 }
90124
91125 /// Creates a new instance of an `Error` from a particular custom error code.
92126 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) } )
96130 }
97131
98132 /// Creates a new instance of an `Error` from a particular internal error code.
99133 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) } )
103137 }
104138
105139 fn internal_desc ( & self ) -> Option < & ' static str > {
@@ -176,15 +210,3 @@ impl fmt::Display for Error {
176210 }
177211 }
178212}
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