| // 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 |
| //! |
| //! * [`err_or_else`](crate::Error::err_or_else) and |
| //! [`err_or_unit`](crate::Error::err_or_unit) 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 |
| //! [`qemu_api::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 [`qemu_api::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}, |
| panic, ptr, |
| }; |
| |
| use foreign::{prelude::*, OwnedPointer}; |
| |
| use crate::bindings; |
| |
| pub type Result<T> = std::result::Result<T, Error>; |
| |
| #[derive(Debug)] |
| pub struct Error { |
| msg: Option<Cow<'static, str>>, |
| /// Appends the print string of the error to the msg if not None |
| cause: Option<anyhow::Error>, |
| file: &'static str, |
| line: u32, |
| } |
| |
| impl std::error::Error for Error { |
| fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| self.cause.as_ref().map(AsRef::as_ref) |
| } |
| |
| #[allow(deprecated)] |
| fn description(&self) -> &str { |
| self.msg |
| .as_deref() |
| .or_else(|| self.cause.as_deref().map(std::error::Error::description)) |
| .expect("no message nor cause?") |
| } |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| let mut prefix = ""; |
| if let Some(ref msg) = self.msg { |
| write!(f, "{msg}")?; |
| prefix = ": "; |
| } |
| if let Some(ref cause) = self.cause { |
| write!(f, "{prefix}{cause}")?; |
| } else if prefix.is_empty() { |
| panic!("no message nor cause?"); |
| } |
| Ok(()) |
| } |
| } |
| |
| impl From<String> for Error { |
| #[track_caller] |
| fn from(msg: String) -> Self { |
| let location = panic::Location::caller(); |
| Error { |
| msg: Some(Cow::Owned(msg)), |
| cause: None, |
| file: location.file(), |
| line: location.line(), |
| } |
| } |
| } |
| |
| impl From<&'static str> for Error { |
| #[track_caller] |
| fn from(msg: &'static str) -> Self { |
| let location = panic::Location::caller(); |
| Error { |
| msg: Some(Cow::Borrowed(msg)), |
| cause: None, |
| file: location.file(), |
| line: location.line(), |
| } |
| } |
| } |
| |
| impl From<anyhow::Error> for Error { |
| #[track_caller] |
| fn from(error: anyhow::Error) -> Self { |
| let location = panic::Location::caller(); |
| Error { |
| msg: None, |
| cause: Some(error), |
| file: location.file(), |
| line: location.line(), |
| } |
| } |
| } |
| |
| impl Error { |
| /// 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 { |
| let location = panic::Location::caller(); |
| Error { |
| msg: Some(msg.into()), |
| cause: Some(cause.into()), |
| file: location.file(), |
| line: location.line(), |
| } |
| } |
| |
| /// 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()); |
| } |
| } |
| |
| /// Convert a C `Error*` into a Rust `Result`, using |
| /// `Ok(())` if `c_error` is NULL. Free the `Error*`. |
| /// |
| /// # Safety |
| /// |
| /// `c_error` must be `NULL` or valid; typically it was initialized |
| /// with `ptr::null_mut()` and passed by reference to a C function. |
| pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { |
| // SAFETY: caller guarantees c_error is valid |
| unsafe { Self::err_or_else(c_error, || ()) } |
| } |
| |
| /// Convert a C `Error*` into a Rust `Result`, calling `f()` to |
| /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. |
| /// |
| /// # Safety |
| /// |
| /// `c_error` must be `NULL` or point to a valid C [`struct |
| /// Error`](bindings::Error); typically it was initialized with |
| /// `ptr::null_mut()` and passed by reference to a C function. |
| pub unsafe fn err_or_else<T, F: FnOnce() -> T>( |
| c_error: *mut bindings::Error, |
| f: F, |
| ) -> Result<T> { |
| // SAFETY: caller guarantees c_error is valid |
| let err = unsafe { Option::<Self>::from_foreign(c_error) }; |
| match err { |
| None => Ok(f()), |
| Some(err) => Err(err), |
| } |
| } |
| } |
| |
| 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 { |
| msg: FromForeign::cloned_from_foreign(error.msg), |
| cause: None, |
| file: file.unwrap(), |
| line: error.line as u32, |
| } |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use std::ffi::CStr; |
| |
| use anyhow::anyhow; |
| use foreign::OwnedPointer; |
| |
| use super::*; |
| use crate::{assert_match, bindings}; |
| |
| #[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] |
| #[allow(deprecated)] |
| fn test_description() { |
| use std::error::Error; |
| |
| assert_eq!(super::Error::from("msg").description(), "msg"); |
| assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); |
| } |
| |
| #[test] |
| fn test_display() { |
| assert_eq!(&*format!("{}", Error::from("msg")), "msg"); |
| assert_eq!(&*format!("{}", Error::from("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::from("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::from("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_err_or_unit() { |
| unsafe { |
| let result = Error::err_or_unit(ptr::null_mut()); |
| assert_match!(result, Ok(())); |
| |
| let err = error_for_test(c"msg"); |
| let err = Error::err_or_unit(err.into_inner()).unwrap_err(); |
| assert_eq!(&*format!("{err}"), "msg"); |
| } |
| } |
| } |