|  | // 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"); | 
|  | } | 
|  | } | 
|  | } |