Skip to content

Commit 79ed2b7

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

19 files changed

+255
-189
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

+3-5
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
//! A dummy implementation for unsupported targets which always returns
1010
//! `Err(Error::UNAVAILABLE)`
1111
use crate::Error;
12-
use core::num::NonZeroU32;
1312

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

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

src/error.rs

+78-44
Original file line numberDiff line numberDiff line change
@@ -5,78 +5,112 @@
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-
1711
/// The error type.
1812
///
1913
/// This type is small and no-std compatible.
2014
#[derive(Copy, Clone, Eq, PartialEq)]
21-
pub struct Error(pub(crate) NonZeroU32);
15+
pub struct Error(NonZeroU32);
2216

17+
// TODO simplify impls with try_from when version >= 1.34
2318
impl Error {
2419
/// An unknown error.
25-
pub const UNKNOWN: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNKNOWN) });
20+
pub(crate) const UNKNOWN: Error = Self(unsafe { NonZeroU32::new_unchecked(u32::max_value()) });
21+
const CUSTOM_START: u32 = 1 << 31;
2622

27-
/// No generator is available.
28-
pub const UNAVAILABLE: Error = Error(unsafe { NonZeroU32::new_unchecked(CODE_UNAVAILABLE) });
23+
pub(crate) fn from_os_error(errno: i32) -> Self {
24+
if errno > 0 {
25+
Self(NonZeroU32::new(errno as u32).unwrap())
26+
} else {
27+
Self::UNKNOWN
28+
}
29+
}
2930

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
31+
pub(crate) fn from_custom_error(custom: u16) -> Self {
32+
Self(NonZeroU32::new(custom as u32 + Self::CUSTOM_START).unwrap())
3833
}
3934

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

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),
48+
pub fn custom_code(&self) -> Option<u16> {
49+
let custom = self.0.get().checked_sub(Self::CUSTOM_START)?;
50+
if custom <= u16::max_value() as u32 {
51+
Some(custom as u16)
52+
} else {
53+
None
5854
}
5955
}
6056
}
6157

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-
}
58+
#[cfg(any(unix, target_os = "redox"))]
59+
fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> {
60+
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
61+
if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
62+
return None;
6863
}
64+
65+
// Take up to trailing null byte
66+
let idx = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
67+
core::str::from_utf8(&buf[..idx]).ok()
68+
}
69+
70+
#[cfg(not(any(unix, target_os = "redox")))]
71+
fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
72+
None
6973
}
7074

71-
impl From<NonZeroU32> for Error {
72-
fn from(code: NonZeroU32) -> Self {
73-
Error(code)
75+
impl fmt::Debug for Error {
76+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77+
let mut dbg = f.debug_struct("Error");
78+
if let Some(errno) = self.raw_os_error() {
79+
dbg.field("os_error", &errno);
80+
let mut buf = [0u8; 128];
81+
if let Some(desc) = os_err_desc(errno, &mut buf) {
82+
dbg.field("description", &desc);
83+
}
84+
} else if let Some(custom) = self.custom_code() {
85+
dbg.field("custom_error", &custom);
86+
if let Some(desc) = crate::imp::custom_description(custom) {
87+
dbg.field("description", &desc);
88+
}
89+
} else {
90+
dbg.field("unknown_error", &self.0);
91+
}
92+
dbg.finish()
7493
}
7594
}
7695

77-
impl From<&Error> for Error {
78-
fn from(error: &Error) -> Self {
79-
*error
96+
impl fmt::Display for Error {
97+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98+
if let Some(errno) = self.raw_os_error() {
99+
let mut buf = [0u8; 128];
100+
if let Some(desc) = os_err_desc(errno, &mut buf) {
101+
f.write_str(desc)
102+
} else {
103+
write!(f, "OS Error: {}", errno)
104+
}
105+
} else if let Some(custom) = self.custom_code() {
106+
if let Some(desc) = crate::imp::custom_description(custom) {
107+
f.write_str(desc)
108+
} else {
109+
write!(f, "Custom Error: {}", custom)
110+
}
111+
} else {
112+
write!(f, "Unknown Error: {}", self.0)
113+
}
80114
}
81115
}
82116

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
}

src/lib.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,26 @@ cfg_if! {
146146
#[cfg(feature = "std")]
147147
extern crate std;
148148

149+
#[allow(dead_code)]
149150
mod error;
150151
pub use crate::error::Error;
151152

152153
#[allow(dead_code)]
153154
mod util;
154-
#[cfg(any(unix, target_os = "redox"))]
155+
#[cfg(any(
156+
target_os = "netbsd",
157+
target_os = "openbsd",
158+
target_os = "android",
159+
target_os = "linux",
160+
target_os = "emscripten",
161+
target_os = "solaris",
162+
target_os = "illumos",
163+
target_os = "macos",
164+
target_os = "freebsd",
165+
target_os = "haiku",
166+
target_os = "redox",
167+
target_os = "dragonfly",
168+
))]
155169
#[allow(dead_code)]
156170
mod util_libc;
157171

@@ -172,7 +186,7 @@ mod use_file;
172186

173187
// System-specific implementations.
174188
//
175-
// These should all provide getrandom_inner with the same signature as getrandom.
189+
// These should all provide getrandom_inner and custom_description.
176190
cfg_if! {
177191
if #[cfg(target_os = "android")] {
178192
#[path = "linux_android.rs"] mod imp;

0 commit comments

Comments
 (0)