diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 6841adaa31beb6..b873c7884c67b8 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -9,7 +9,7 @@ use crate::{bindings, c_types}; use alloc::{alloc::AllocError, collections::TryReserveError}; use core::convert::From; use core::fmt; -use core::num::TryFromIntError; +use core::num::{NonZeroI16, TryFromIntError}; use core::str::{self, Utf8Error}; /// Generic integer kernel error. @@ -21,44 +21,55 @@ use core::str::{self, Utf8Error}; /// /// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`). #[derive(Clone, Copy, PartialEq, Eq)] -pub struct Error(c_types::c_int); +pub struct Error(NonZeroI16); + +/// DO NOT use this macro outside of Error const initializations. +/// +/// # Safety +/// +/// The parameter must be a valid kernel error number. +macro_rules! kernel_const_to_error { + ($($tt:tt)*) => {{ + Error(unsafe {NonZeroI16::new_unchecked(-($($tt)* as i16))}) + }}; +} impl Error { /// Invalid argument. - pub const EINVAL: Self = Error(-(bindings::EINVAL as i32)); + pub const EINVAL: Self = kernel_const_to_error!(bindings::EINVAL); /// Out of memory. - pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32)); + pub const ENOMEM: Self = kernel_const_to_error!(bindings::ENOMEM); /// Bad address. - pub const EFAULT: Self = Error(-(bindings::EFAULT as i32)); + pub const EFAULT: Self = kernel_const_to_error!(bindings::EFAULT); /// Illegal seek. - pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32)); + pub const ESPIPE: Self = kernel_const_to_error!(bindings::ESPIPE); /// Try again. - pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32)); + pub const EAGAIN: Self = kernel_const_to_error!(bindings::EAGAIN); /// Device or resource busy. - pub const EBUSY: Self = Error(-(bindings::EBUSY as i32)); + pub const EBUSY: Self = kernel_const_to_error!(bindings::EBUSY); /// Restart the system call. - pub const ERESTARTSYS: Self = Error(-(bindings::ERESTARTSYS as i32)); + pub const ERESTARTSYS: Self = kernel_const_to_error!(bindings::ERESTARTSYS); /// Operation not permitted. - pub const EPERM: Self = Error(-(bindings::EPERM as i32)); + pub const EPERM: Self = kernel_const_to_error!(bindings::EPERM); /// No such process. - pub const ESRCH: Self = Error(-(bindings::ESRCH as i32)); + pub const ESRCH: Self = kernel_const_to_error!(bindings::ESRCH); /// No such file or directory. - pub const ENOENT: Self = Error(-(bindings::ENOENT as i32)); + pub const ENOENT: Self = kernel_const_to_error!(bindings::ENOENT); /// Interrupted system call. - pub const EINTR: Self = Error(-(bindings::EINTR as i32)); + pub const EINTR: Self = kernel_const_to_error!(bindings::EINTR); /// Bad file number. - pub const EBADF: Self = Error(-(bindings::EBADF as i32)); + pub const EBADF: Self = kernel_const_to_error!(bindings::EBADF); /// Creates an [`Error`] from a kernel error code. /// @@ -76,7 +87,8 @@ impl Error { // INVARIANT: the check above ensures the type invariant // will hold. - Error(errno) + let nzi16_errno = NonZeroI16::new(errno as i16).unwrap(); + Error(nzi16_errno) } /// Creates an [`Error`] from a kernel error code. @@ -87,12 +99,15 @@ impl Error { pub(crate) unsafe fn from_kernel_errno_unchecked(errno: c_types::c_int) -> Error { // INVARIANT: the contract ensures the type invariant // will hold. - Error(errno) + // + // Safety: `errno` must not be zero, which is guaranteed by the contract + // of this function. + Error(unsafe { NonZeroI16::new_unchecked(errno as i16) }) } /// Returns the kernel error code. pub fn to_kernel_errno(self) -> c_types::c_int { - self.0 + self.0.get().into() } } @@ -102,11 +117,14 @@ impl fmt::Debug for Error { fn rust_helper_errname(err: c_types::c_int) -> *const c_types::c_char; } // SAFETY: FFI call. - let name = unsafe { rust_helper_errname(-self.0) }; + let name = unsafe { rust_helper_errname(-self.to_kernel_errno()) }; if name.is_null() { // Print out number if no name can be found. - return f.debug_tuple("Error").field(&-self.0).finish(); + return f + .debug_tuple("Error") + .field(&-self.to_kernel_errno()) + .finish(); } // SAFETY: `'static` string from C, and is not NULL.