Skip to content

Commit ba70371

Browse files
committed
Remove std dependancy for error handling
1 parent 5465e8f commit ba70371

19 files changed

+269
-214
lines changed

src/cloudabi.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,22 @@
88

99
//! Implementation for CloudABI
1010
use crate::Error;
11-
use core::num::NonZeroU32;
1211

1312
extern "C" {
1413
fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16;
1514
}
1615

1716
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1817
let errno = unsafe { cloudabi_sys_random_get(dest.as_mut_ptr(), dest.len()) };
19-
if let Some(code) = NonZeroU32::new(errno as u32) {
20-
error!("cloudabi_sys_random_get failed with code {}", code);
21-
Err(Error::from(code))
18+
if errno != 0 {
19+
error!("cloudabi_sys_random_get: failed with {}", errno);
20+
Err(Error::from_os_error(errno.into()))
2221
} else {
2322
Ok(()) // Zero means success for CloudABI
2423
}
2524
}
2625

2726
#[inline(always)]
28-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
27+
pub fn custom_description(_: u16) -> Option<&'static str> {
2928
None
3029
}

src/dummy.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,14 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
//! A dummy implementation for unsupported targets which always returns
10-
//! `Err(Error::UNAVAILABLE)`
9+
//! A dummy implementation for unsupported targets which always fails
1110
use crate::Error;
12-
use core::num::NonZeroU32;
1311

1412
pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> {
15-
error!("no support for this platform");
16-
Err(Error::UNAVAILABLE)
13+
Err(Error::from_custom_error(0))
1714
}
1815

1916
#[inline(always)]
20-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
21-
None
17+
pub fn custom_description(_: u16) -> Option<&'static str> {
18+
Some("getrandom: this target is not supported")
2219
}

src/error.rs

+88-47
Original file line numberDiff line numberDiff line change
@@ -5,78 +5,119 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8-
use core::convert::From;
98
use core::fmt;
109
use core::num::NonZeroU32;
1110

12-
// A randomly-chosen 24-bit prefix for our codes
13-
pub(crate) const CODE_PREFIX: u32 = 0x57f4c500;
14-
const CODE_UNKNOWN: u32 = CODE_PREFIX | 0x00;
15-
const CODE_UNAVAILABLE: u32 = CODE_PREFIX | 0x01;
16-
17-
/// The error type.
18-
///
19-
/// This type is small and no-std compatible.
11+
/// A small and `no_std` compatible error type. It can indicate failure from
12+
/// either the underlying OS or a custom error reason.
13+
///
14+
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
15+
/// if so, which error code the OS gave the application. If such an error is
16+
/// encountered, please consult with your system documentation.
2017
#[derive(Copy, Clone, Eq, PartialEq)]
21-
pub struct Error(pub(crate) NonZeroU32);
18+
pub struct Error(NonZeroU32);
2219

20+
// This NonZeroU32 has enough room to store 3 types of values:
21+
// - OS Errors: in range [1, 1 << 31) (i.e. positive i32 values)
22+
// - Custom u16 Errors: in range [1 << 31, 1 << 31 + 1 << 16)
23+
// - Unknown Errors: currently just (1 << 32) - 1
24+
// TODO simplify impls with try_from when version >= 1.34
2325
impl Error {
2426
/// An unknown error.
25-
pub const UNKNOWN: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNKNOWN) });
27+
pub(crate) const UNKNOWN: Error = Self(unsafe { NonZeroU32::new_unchecked(u32::max_value()) });
28+
const CUSTOM_START: u32 = 1 << 31;
2629

27-
/// No generator is available.
28-
pub const UNAVAILABLE: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNAVAILABLE) });
30+
pub(crate) fn from_os_error(errno: i32) -> Self {
31+
if errno > 0 {
32+
Self(NonZeroU32::new(errno as u32).unwrap())
33+
} else {
34+
Self::UNKNOWN
35+
}
36+
}
2937

30-
/// Extract the error code.
31-
///
32-
/// This may equal one of the codes defined in this library or may be a
33-
/// system error code.
34-
///
35-
/// One may attempt to format this error via the `Display` implementation.
36-
pub fn code(&self) -> NonZeroU32 {
37-
self.0
38+
pub(crate) fn from_custom_error(custom: u16) -> Self {
39+
Self(NonZeroU32::new(custom as u32 + Self::CUSTOM_START).unwrap())
3840
}
3941

40-
pub(crate) fn msg(&self) -> Option<&'static str> {
41-
if let Some(msg) = crate::imp::error_msg_inner(self.0) {
42-
Some(msg)
42+
/// Extract the raw OS error code (if this error came from the OS)
43+
///
44+
/// This method is identical to `std::io::Error::raw_os_error()`, except
45+
/// that it works in `no_std` contexts. If this method returns `None`, the
46+
/// error value can still be formatted via the `Diplay` implementation.
47+
pub fn raw_os_error(&self) -> Option<i32> {
48+
if self.0.get() < Self::CUSTOM_START {
49+
Some(self.0.get() as i32)
4350
} else {
44-
match *self {
45-
Error::UNKNOWN => Some("getrandom: unknown error"),
46-
Error::UNAVAILABLE => Some("getrandom: unavailable"),
47-
_ => None,
48-
}
51+
None
4952
}
5053
}
51-
}
5254

53-
impl fmt::Debug for Error {
54-
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
55-
match self.msg() {
56-
Some(msg) => write!(f, "Error(\"{}\")", msg),
57-
None => write!(f, "Error(0x{:08X})", self.0),
55+
pub fn custom_code(&self) -> Option<u16> {
56+
let custom = self.0.get().checked_sub(Self::CUSTOM_START)?;
57+
if custom <= u16::max_value() as u32 {
58+
Some(custom as u16)
59+
} else {
60+
None
5861
}
5962
}
6063
}
6164

62-
impl fmt::Display for Error {
63-
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
64-
match self.msg() {
65-
Some(msg) => write!(f, "{}", msg),
66-
None => write!(f, "getrandom: unknown code 0x{:08X}", self.0),
67-
}
65+
#[cfg(any(unix, target_os = "redox"))]
66+
fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> {
67+
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
68+
if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
69+
return None;
6870
}
71+
72+
// Take up to trailing null byte
73+
let idx = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
74+
core::str::from_utf8(&buf[..idx]).ok()
75+
}
76+
77+
#[cfg(not(any(unix, target_os = "redox")))]
78+
fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
79+
None
6980
}
7081

71-
impl From<NonZeroU32> for Error {
72-
fn from(code: NonZeroU32) -> Self {
73-
Error(code)
82+
impl fmt::Debug for Error {
83+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84+
let mut dbg = f.debug_struct("Error");
85+
if let Some(errno) = self.raw_os_error() {
86+
dbg.field("os_error", &errno);
87+
let mut buf = [0u8; 128];
88+
if let Some(desc) = os_err_desc(errno, &mut buf) {
89+
dbg.field("description", &desc);
90+
}
91+
} else if let Some(custom) = self.custom_code() {
92+
dbg.field("custom_error", &custom);
93+
if let Some(desc) = crate::imp::custom_description(custom) {
94+
dbg.field("description", &desc);
95+
}
96+
} else {
97+
dbg.field("unknown_error", &self.0);
98+
}
99+
dbg.finish()
74100
}
75101
}
76102

77-
impl From<&Error> for Error {
78-
fn from(error: &Error) -> Self {
79-
*error
103+
impl fmt::Display for Error {
104+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105+
if let Some(errno) = self.raw_os_error() {
106+
let mut buf = [0u8; 128];
107+
if let Some(desc) = os_err_desc(errno, &mut buf) {
108+
f.write_str(desc)
109+
} else {
110+
write!(f, "OS Error: {}", errno)
111+
}
112+
} else if let Some(custom) = self.custom_code() {
113+
if let Some(desc) = crate::imp::custom_description(custom) {
114+
f.write_str(desc)
115+
} else {
116+
write!(f, "Custom Error: {}", custom)
117+
}
118+
} else {
119+
write!(f, "Unknown Error: {}", self.0)
120+
}
80121
}
81122
}
82123

src/error_impls.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,22 @@ extern crate std;
99

1010
use crate::error::Error;
1111
use core::convert::From;
12-
use core::num::NonZeroU32;
1312
use std::{error, io};
1413

1514
impl From<io::Error> for Error {
1615
fn from(err: io::Error) -> Self {
17-
err.raw_os_error()
18-
.and_then(|code| NonZeroU32::new(code as u32))
19-
.map(|code| Error(code))
20-
// in practice this should never happen
21-
.unwrap_or(Error::UNKNOWN)
16+
match err.raw_os_error() {
17+
Some(errno) => Error::from_os_error(errno),
18+
None => Error::UNKNOWN,
19+
}
2220
}
2321
}
2422

2523
impl From<Error> for io::Error {
2624
fn from(err: Error) -> Self {
27-
match err.msg() {
28-
Some(msg) => io::Error::new(io::ErrorKind::Other, msg),
29-
None => io::Error::from_raw_os_error(err.0.get() as i32),
25+
match err.raw_os_error() {
26+
Some(errno) => io::Error::from_raw_os_error(errno),
27+
None => io::Error::new(io::ErrorKind::Other, err),
3028
}
3129
}
3230
}

src/freebsd.rs

+7-14
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@
77
// except according to those terms.
88

99
//! Implementation for FreeBSD
10-
extern crate std;
11-
10+
use crate::util_libc::fill_exact;
1211
use crate::Error;
13-
use core::num::NonZeroU32;
1412
use core::ptr;
15-
use std::io;
1613

17-
fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
14+
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
1815
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
1916
let mut len = buf.len();
2017
let ret = unsafe {
@@ -28,21 +25,17 @@ fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
2825
)
2926
};
3027
if ret == -1 {
31-
error!("freebsd: kern.arandom syscall failed");
32-
return Err(io::Error::last_os_error().into());
28+
-1
29+
} else {
30+
len as libc::ssize_t
3331
}
34-
Ok(len)
3532
}
3633

3734
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
38-
let mut start = 0;
39-
while start < dest.len() {
40-
start += kern_arnd(&mut dest[start..])?;
41-
}
42-
Ok(())
35+
fill_exact(dest, kern_arnd)
4336
}
4437

4538
#[inline(always)]
46-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
39+
pub fn custom_description(_: u16) -> Option<&'static str> {
4740
None
4841
}

src/fuchsia.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
//! Implementation for Fuchsia Zircon
1010
use crate::Error;
11-
use core::num::NonZeroU32;
1211

1312
#[link(name = "zircon")]
1413
extern "C" {
@@ -21,6 +20,6 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
2120
}
2221

2322
#[inline(always)]
24-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
23+
pub fn custom_description(_: u16) -> Option<&'static str> {
2524
None
2625
}

src/ios.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
// except according to those terms.
88

99
//! Implementation for iOS
10-
extern crate std;
11-
1210
use crate::Error;
13-
use core::num::NonZeroU32;
11+
12+
const CALL_FAILED: u16 = 0;
1413

1514
// TODO: Make extern once extern_types feature is stabilized. See:
1615
// https://github.com/rust-lang/rust/issues/43467
@@ -27,14 +26,16 @@ extern "C" {
2726
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
2827
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
2928
if ret == -1 {
30-
error!("SecRandomCopyBytes call failed");
31-
Err(Error::UNKNOWN)
29+
Err(Error::from_custom_error(CALL_FAILED))
3230
} else {
3331
Ok(())
3432
}
3533
}
3634

3735
#[inline(always)]
38-
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
39-
None
36+
pub fn custom_description(custom: u16) -> Option<&'static str> {
37+
match custom {
38+
CALL_FAILED => Some("SecRandomCopyBytes: call failed"),
39+
_ => None,
40+
}
4041
}

0 commit comments

Comments
 (0)