// 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 = std::result::Result; #[derive(Debug)] pub struct Error { msg: Option>, /// Appends the print string of the error to the msg if not None cause: Option, 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 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 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>, cause: impl Into) -> 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( result: Result, 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( result: Result, errp: *mut *mut bindings::Error, ) -> Option { 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>( c_error: *mut bindings::Error, f: F, ) -> Result { // SAFETY: caller guarantees c_error is valid let err = unsafe { Option::::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 { // SAFETY: all arguments are controlled by this function unsafe { let err: *mut c_void = libc::malloc(std::mem::size_of::()); 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::(), 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::(), 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 { // 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::()); 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::(), 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::(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"); } } }