| // SPDX-License-Identifier: GPL-2.0-or-later |
| |
| //! Error propagation for QEMU Rust code |
| //! |
| //! This module contains [`Error`], the bridge between Rust errors and |
| //! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) |
| //! struct. |
| //! |
| //! For FFI code, [`Error`] provides functions to simplify conversion between |
| //! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: |
| //! |
| //! * [`ok_or_propagate`](crate::Error::ok_or_propagate), |
| //! [`bool_or_propagate`](crate::Error::bool_or_propagate), |
| //! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build |
| //! a C return value while also propagating an error condition |
| //! |
| //! * [`with_errp`](crate::Error::with_errp) can be used to build a `Result` |
| //! |
| //! This module is most commonly used at the boundary between C and Rust code; |
| //! other code will usually access it through the |
| //! [`utils::Result`](crate::Result) type alias, and will use the |
| //! [`std::error::Error`] interface to let C errors participate in Rust's error |
| //! handling functionality. |
| //! |
| //! Rust code can also create use this module to create an error object that |
| //! will be passed up to C code, though in most cases this will be done |
| //! transparently through the `?` operator. Errors can be constructed from a |
| //! simple error string, from an [`anyhow::Error`] to pass any other Rust error |
| //! type up to C code, or from a combination of the two. |
| //! |
| //! The third case, corresponding to [`Error::with_error`], is the only one that |
| //! requires mentioning [`utils::Error`](crate::Error) explicitly. Similar |
| //! to how QEMU's C code handles errno values, the string and the |
| //! `anyhow::Error` object will be concatenated with `:` as the separator. |
| |
| use std::{ |
| borrow::Cow, |
| ffi::{c_char, c_int, c_void, CStr}, |
| fmt::{self, Display}, |
| ops::Deref, |
| panic, |
| ptr::{self, addr_of_mut}, |
| }; |
| |
| use foreign::{prelude::*, OwnedPointer}; |
| |
| use crate::bindings; |
| |
| pub type Result<T> = std::result::Result<T, Error>; |
| |
| #[derive(Debug)] |
| pub struct Error { |
| cause: anyhow::Error, |
| file: &'static str, |
| line: u32, |
| } |
| |
| impl Deref for Error { |
| type Target = anyhow::Error; |
| |
| fn deref(&self) -> &Self::Target { |
| &self.cause |
| } |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| Display::fmt(&format_args!("{:#}", self.cause), f) |
| } |
| } |
| |
| impl<E> From<E> for Error |
| where |
| anyhow::Error: From<E>, |
| { |
| #[track_caller] |
| fn from(src: E) -> Self { |
| Self::new(anyhow::Error::from(src)) |
| } |
| } |
| |
| impl Error { |
| /// Create a new error from an [`anyhow::Error`]. |
| /// |
| /// This wraps the error with QEMU's location tracking information. |
| /// Most code should use the `?` operator instead of calling this directly. |
| #[track_caller] |
| pub fn new(cause: anyhow::Error) -> Self { |
| let location = panic::Location::caller(); |
| Self { |
| cause, |
| file: location.file(), |
| line: location.line(), |
| } |
| } |
| |
| /// Create a new error from a string message. |
| /// |
| /// This is a convenience wrapper around [`Error::new`] for simple string |
| /// errors. Most code should use the [`ensure!`](crate::ensure) macro |
| /// instead of calling this directly. |
| #[track_caller] |
| pub fn msg(src: impl Into<Cow<'static, str>>) -> Self { |
| Self::new(anyhow::Error::msg(src.into())) |
| } |
| |
| #[track_caller] |
| #[doc(hidden)] |
| #[inline(always)] |
| pub fn format(args: fmt::Arguments) -> Self { |
| // anyhow::Error::msg will allocate anyway, might as well let fmt::format doit. |
| let msg = fmt::format(args); |
| Self::new(anyhow::Error::msg(msg)) |
| } |
| |
| /// Create a new error, prepending `msg` to the |
| /// description of `cause` |
| #[track_caller] |
| pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self { |
| fn do_with_error( |
| msg: Cow<'static, str>, |
| cause: anyhow::Error, |
| location: &'static panic::Location<'static>, |
| ) -> Error { |
| Error { |
| cause: cause.context(msg), |
| file: location.file(), |
| line: location.line(), |
| } |
| } |
| do_with_error(msg.into(), cause.into(), panic::Location::caller()) |
| } |
| |
| /// Consume a result, returning `false` if it is an error and |
| /// `true` if it is successful. The error is propagated into |
| /// `errp` like the C API `error_propagate` would do. |
| /// |
| /// # Safety |
| /// |
| /// `errp` must be a valid argument to `error_propagate`; |
| /// typically it is received from C code and need not be |
| /// checked further at the Rust↔C boundary. |
| pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { |
| // SAFETY: caller guarantees errp is valid |
| unsafe { Self::ok_or_propagate(result, errp) }.is_some() |
| } |
| |
| /// Consume a result, returning a `NULL` pointer if it is an error and |
| /// a C representation of the contents if it is successful. This is |
| /// similar to the C API `error_propagate`, but it panics if `*errp` |
| /// is not `NULL`. |
| /// |
| /// # Safety |
| /// |
| /// `errp` must be a valid argument to `error_propagate`; |
| /// typically it is received from C code and need not be |
| /// checked further at the Rust↔C boundary. |
| /// |
| /// See [`propagate`](Error::propagate) for more information. |
| #[must_use] |
| pub unsafe fn ptr_or_propagate<T: CloneToForeign>( |
| result: Result<T>, |
| errp: *mut *mut bindings::Error, |
| ) -> *mut T::Foreign { |
| // SAFETY: caller guarantees errp is valid |
| unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() |
| } |
| |
| /// Consume a result in the same way as `self.ok()`, but also propagate |
| /// a possible error into `errp`. This is similar to the C API |
| /// `error_propagate`, but it panics if `*errp` is not `NULL`. |
| /// |
| /// # Safety |
| /// |
| /// `errp` must be a valid argument to `error_propagate`; |
| /// typically it is received from C code and need not be |
| /// checked further at the Rust↔C boundary. |
| /// |
| /// See [`propagate`](Error::propagate) for more information. |
| pub unsafe fn ok_or_propagate<T>( |
| result: Result<T>, |
| errp: *mut *mut bindings::Error, |
| ) -> Option<T> { |
| result.map_err(|err| unsafe { err.propagate(errp) }).ok() |
| } |
| |
| /// Equivalent of the C function `error_propagate`. Fill `*errp` |
| /// with the information container in `self` if `errp` is not NULL; |
| /// then consume it. |
| /// |
| /// This is similar to the C API `error_propagate`, but it panics if |
| /// `*errp` is not `NULL`. |
| /// |
| /// # Safety |
| /// |
| /// `errp` must be a valid argument to `error_propagate`; it can be |
| /// `NULL` or it can point to any of: |
| /// * `error_abort` |
| /// * `error_fatal` |
| /// * a local variable of (C) type `Error *` |
| /// |
| /// Typically `errp` is received from C code and need not be |
| /// checked further at the Rust↔C boundary. |
| pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { |
| if errp.is_null() { |
| return; |
| } |
| |
| // SAFETY: caller guarantees that errp and *errp are valid |
| unsafe { |
| assert_eq!(*errp, ptr::null_mut()); |
| bindings::error_propagate(errp, self.clone_to_foreign_ptr()); |
| } |
| } |
| |
| /// Pass a C `Error*` to the closure, and convert the result |
| /// (either the return value of the closure, or the error) |
| /// into a Rust `Result`. |
| /// |
| /// # Safety |
| /// |
| /// One exit from `f`, `c_error` must be unchanged or point to a |
| /// valid C [`struct Error`](bindings::Error). |
| pub unsafe fn with_errp<T, F: FnOnce(&mut *mut bindings::Error) -> T>(f: F) -> Result<T> { |
| let mut c_error: *mut bindings::Error = ptr::null_mut(); |
| |
| // SAFETY: guaranteed by the postcondition of `f` |
| match (f(&mut c_error), unsafe { c_error.into_native() }) { |
| (result, None) => Ok(result), |
| (_, Some(err)) => Err(err), |
| } |
| } |
| } |
| |
| /// Extension trait for `std::result::Result`, providing extra |
| /// methods when the error type can be converted into a QEMU |
| /// Error. |
| pub trait ResultExt { |
| /// The success type `T` in `Result<T, E>`. |
| type OkType; |
| |
| /// Report a fatal error and exit QEMU, or return the success value. |
| /// Note that, unlike [`unwrap()`](std::result::Result::unwrap), this |
| /// is not an abort and can be used for user errors. |
| fn unwrap_fatal(self) -> Self::OkType; |
| } |
| |
| impl<T, E> ResultExt for std::result::Result<T, E> |
| where |
| Error: From<E>, |
| { |
| type OkType = T; |
| |
| fn unwrap_fatal(self) -> T { |
| // SAFETY: errp is valid |
| self.map_err(|err| unsafe { |
| Error::from(err).propagate(addr_of_mut!(bindings::error_fatal)) |
| }) |
| .unwrap() |
| } |
| } |
| |
| impl FreeForeign for Error { |
| type Foreign = bindings::Error; |
| |
| unsafe fn free_foreign(p: *mut bindings::Error) { |
| // SAFETY: caller guarantees p is valid |
| unsafe { |
| bindings::error_free(p); |
| } |
| } |
| } |
| |
| impl CloneToForeign for Error { |
| fn clone_to_foreign(&self) -> OwnedPointer<Self> { |
| // SAFETY: all arguments are controlled by this function |
| unsafe { |
| let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); |
| let err: &mut bindings::Error = &mut *err.cast(); |
| *err = bindings::Error { |
| msg: format!("{self}").clone_to_foreign_ptr(), |
| err_class: bindings::ERROR_CLASS_GENERIC_ERROR, |
| src_len: self.file.len() as c_int, |
| src: self.file.as_ptr().cast::<c_char>(), |
| line: self.line as c_int, |
| func: ptr::null_mut(), |
| hint: ptr::null_mut(), |
| }; |
| OwnedPointer::new(err) |
| } |
| } |
| } |
| |
| impl FromForeign for Error { |
| unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { |
| // SAFETY: caller guarantees c_error is valid |
| unsafe { |
| let error = &*c_error; |
| let file = if error.src_len < 0 { |
| // NUL-terminated |
| CStr::from_ptr(error.src).to_str() |
| } else { |
| // Can become str::from_utf8 with Rust 1.87.0 |
| std::str::from_utf8(std::slice::from_raw_parts( |
| &*error.src.cast::<u8>(), |
| error.src_len as usize, |
| )) |
| }; |
| |
| Error { |
| cause: anyhow::Error::msg(String::cloned_from_foreign(error.msg)), |
| file: file.unwrap(), |
| line: error.line as u32, |
| } |
| } |
| } |
| } |
| |
| /// Ensure that a condition is true, returning an error if it is false. |
| /// |
| /// This macro is similar to [`anyhow::ensure`] but returns a QEMU [`Result`]. |
| /// If the condition evaluates to `false`, the macro returns early with an error |
| /// constructed from the provided message. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use util::{ensure, Result}; |
| /// # fn check_positive(x: i32) -> Result<()> { |
| /// ensure!(x > 0, "value must be positive"); |
| /// # Ok(()) |
| /// # } |
| /// ``` |
| /// |
| /// ``` |
| /// # use util::{ensure, Result}; |
| /// # const MIN: i32 = 123; |
| /// # const MAX: i32 = 456; |
| /// # fn check_range(x: i32) -> Result<()> { |
| /// ensure!( |
| /// x >= MIN && x <= MAX, |
| /// "{} not between {} and {}", |
| /// x, |
| /// MIN, |
| /// MAX |
| /// ); |
| /// # Ok(()) |
| /// # } |
| /// ``` |
| #[macro_export] |
| macro_rules! ensure { |
| ($cond:expr, $fmt:literal, $($arg:tt)*) => { |
| if !$cond { |
| let e = $crate::Error::format(format_args!($fmt, $($arg)*)); |
| return $crate::Result::Err(e); |
| } |
| }; |
| ($cond:expr, $err:expr $(,)?) => { |
| if !$cond { |
| let e = $crate::Error::msg($err); |
| return $crate::Result::Err(e); |
| } |
| }; |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use std::ffi::CStr; |
| |
| use anyhow::anyhow; |
| use common::assert_match; |
| use foreign::OwnedPointer; |
| |
| use super::*; |
| |
| #[track_caller] |
| fn error_for_test(msg: &CStr) -> OwnedPointer<Error> { |
| // SAFETY: all arguments are controlled by this function |
| let location = panic::Location::caller(); |
| unsafe { |
| let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); |
| let err: &mut bindings::Error = &mut *err.cast(); |
| *err = bindings::Error { |
| msg: msg.clone_to_foreign_ptr(), |
| err_class: bindings::ERROR_CLASS_GENERIC_ERROR, |
| src_len: location.file().len() as c_int, |
| src: location.file().as_ptr().cast::<c_char>(), |
| line: location.line() as c_int, |
| func: ptr::null_mut(), |
| hint: ptr::null_mut(), |
| }; |
| OwnedPointer::new(err) |
| } |
| } |
| |
| unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { |
| unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } |
| } |
| |
| #[test] |
| fn test_display() { |
| assert_eq!(&*format!("{}", Error::msg("msg")), "msg"); |
| assert_eq!(&*format!("{}", Error::msg("msg".to_owned())), "msg"); |
| assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); |
| |
| assert_eq!( |
| &*format!("{}", Error::with_error("msg", anyhow!("cause"))), |
| "msg: cause" |
| ); |
| } |
| |
| #[test] |
| fn test_bool_or_propagate() { |
| unsafe { |
| let mut local_err: *mut bindings::Error = ptr::null_mut(); |
| |
| assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); |
| assert_eq!(local_err, ptr::null_mut()); |
| |
| let my_err = Error::msg("msg"); |
| assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); |
| assert_ne!(local_err, ptr::null_mut()); |
| assert_eq!(error_get_pretty(local_err), c"msg"); |
| bindings::error_free(local_err); |
| } |
| } |
| |
| #[test] |
| fn test_ptr_or_propagate() { |
| unsafe { |
| let mut local_err: *mut bindings::Error = ptr::null_mut(); |
| |
| let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); |
| assert_eq!(String::from_foreign(ret), "abc"); |
| assert_eq!(local_err, ptr::null_mut()); |
| |
| let my_err = Error::msg("msg"); |
| assert_eq!( |
| Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err), |
| ptr::null_mut() |
| ); |
| assert_ne!(local_err, ptr::null_mut()); |
| assert_eq!(error_get_pretty(local_err), c"msg"); |
| bindings::error_free(local_err); |
| } |
| } |
| |
| #[test] |
| fn test_with_errp() { |
| unsafe { |
| let result = Error::with_errp(|_errp| true); |
| assert_match!(result, Ok(true)); |
| |
| let err = Error::with_errp(|errp| { |
| *errp = error_for_test(c"msg").into_inner(); |
| false |
| }) |
| .unwrap_err(); |
| assert_eq!(&*format!("{err}"), "msg"); |
| } |
| } |
| } |