Skip to content

Commit

Permalink
Remove std dependancy for error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
josephlr committed Jul 7, 2019
1 parent 5465e8f commit 96e496c
Show file tree
Hide file tree
Showing 19 changed files with 268 additions and 213 deletions.
9 changes: 4 additions & 5 deletions src/cloudabi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,22 @@

//! Implementation for CloudABI
use crate::Error;
use core::num::NonZeroU32;

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

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

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
pub fn custom_description(_: u16) -> Option<&'static str> {
None
}
11 changes: 4 additions & 7 deletions src/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A dummy implementation for unsupported targets which always returns
//! `Err(Error::UNAVAILABLE)`
//! A dummy implementation for unsupported targets which always fails
use crate::Error;
use core::num::NonZeroU32;

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

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
None
pub fn custom_description(_: u16) -> Option<&'static str> {
Some("getrandom: this target is not supported")
}
133 changes: 87 additions & 46 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,78 +5,119 @@
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::convert::From;
use core::fmt;
use core::num::NonZeroU32;

// A randomly-chosen 24-bit prefix for our codes
pub(crate) const CODE_PREFIX: u32 = 0x57f4c500;
const CODE_UNKNOWN: u32 = CODE_PREFIX | 0x00;
const CODE_UNAVAILABLE: u32 = CODE_PREFIX | 0x01;

/// The error type.
/// A small and `no_std` compatible error type. It can indicate failure from
/// either the underlying OS or a custom error reason.
///
/// This type is small and no-std compatible.
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
/// if so, which error code the OS gave the application. If such an error is
/// encountered, please consult with your system documentation.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(pub(crate) NonZeroU32);
pub struct Error(NonZeroU32);

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

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

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

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

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

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.msg() {
Some(msg) => write!(f, "{}", msg),
None => write!(f, "getrandom: unknown code 0x{:08X}", self.0),
}
#[cfg(any(unix, target_os = "redox"))]
fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> {
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
return None;
}

// Take up to trailing null byte
let idx = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
core::str::from_utf8(&buf[..idx]).ok()
}

#[cfg(not(any(unix, target_os = "redox")))]
fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
None
}

impl From<NonZeroU32> for Error {
fn from(code: NonZeroU32) -> Self {
Error(code)
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut dbg = f.debug_struct("Error");
if let Some(errno) = self.raw_os_error() {
dbg.field("os_error", &errno);
let mut buf = [0u8; 128];
if let Some(desc) = os_err_desc(errno, &mut buf) {
dbg.field("description", &desc);
}
} else if let Some(custom) = self.custom_code() {
dbg.field("custom_error", &custom);
if let Some(desc) = crate::imp::custom_description(custom) {
dbg.field("description", &desc);
}
} else {
dbg.field("unknown_error", &self.0);
}
dbg.finish()
}
}

impl From<&Error> for Error {
fn from(error: &Error) -> Self {
*error
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(errno) = self.raw_os_error() {
let mut buf = [0u8; 128];
if let Some(desc) = os_err_desc(errno, &mut buf) {
f.write_str(desc)
} else {
write!(f, "OS Error: {}", errno)
}
} else if let Some(custom) = self.custom_code() {
if let Some(desc) = crate::imp::custom_description(custom) {
f.write_str(desc)
} else {
write!(f, "Custom Error: {}", custom)
}
} else {
write!(f, "Unknown Error: {}", self.0)
}
}
}

Expand Down
16 changes: 7 additions & 9 deletions src/error_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,22 @@ extern crate std;

use crate::error::Error;
use core::convert::From;
use core::num::NonZeroU32;
use std::{error, io};

impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
err.raw_os_error()
.and_then(|code| NonZeroU32::new(code as u32))
.map(|code| Error(code))
// in practice this should never happen
.unwrap_or(Error::UNKNOWN)
match err.raw_os_error() {
Some(errno) => Error::from_os_error(errno),
None => Error::UNKNOWN,
}
}
}

impl From<Error> for io::Error {
fn from(err: Error) -> Self {
match err.msg() {
Some(msg) => io::Error::new(io::ErrorKind::Other, msg),
None => io::Error::from_raw_os_error(err.0.get() as i32),
match err.raw_os_error() {
Some(errno) => io::Error::from_raw_os_error(errno),
None => io::Error::new(io::ErrorKind::Other, err),
}
}
}
Expand Down
21 changes: 7 additions & 14 deletions src/freebsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@
// except according to those terms.

//! Implementation for FreeBSD
extern crate std;

use crate::util_libc::fill_exact;
use crate::Error;
use core::num::NonZeroU32;
use core::ptr;
use std::io;

fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
let mut len = buf.len();
let ret = unsafe {
Expand All @@ -28,21 +25,17 @@ fn kern_arnd(buf: &mut [u8]) -> Result<usize, Error> {
)
};
if ret == -1 {
error!("freebsd: kern.arandom syscall failed");
return Err(io::Error::last_os_error().into());
-1
} else {
len as libc::ssize_t
}
Ok(len)
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
let mut start = 0;
while start < dest.len() {
start += kern_arnd(&mut dest[start..])?;
}
Ok(())
fill_exact(dest, kern_arnd)
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
pub fn custom_description(_: u16) -> Option<&'static str> {
None
}
3 changes: 1 addition & 2 deletions src/fuchsia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

//! Implementation for Fuchsia Zircon
use crate::Error;
use core::num::NonZeroU32;

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

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> {
pub fn custom_description(_: u16) -> Option<&'static str> {
None
}
15 changes: 8 additions & 7 deletions src/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
// except according to those terms.

//! Implementation for iOS
extern crate std;

use crate::Error;
use core::num::NonZeroU32;

const CALL_FAILED: u16 = 0;

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

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

0 comments on commit 96e496c

Please sign in to comment.