1 // SPDX-License-Identifier: GPL-2.0-or-later 2 3 //! Error propagation for QEMU Rust code 4 //! 5 //! This module contains [`Error`], the bridge between Rust errors and 6 //! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) 7 //! struct. 8 //! 9 //! For FFI code, [`Error`] provides functions to simplify conversion between 10 //! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: 11 //! 12 //! * [`ok_or_propagate`](crate::Error::ok_or_propagate), 13 //! [`bool_or_propagate`](crate::Error::bool_or_propagate), 14 //! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build 15 //! a C return value while also propagating an error condition 16 //! 17 //! * [`err_or_else`](crate::Error::err_or_else) and 18 //! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` 19 //! 20 //! This module is most commonly used at the boundary between C and Rust code; 21 //! other code will usually access it through the 22 //! [`qemu_api::Result`](crate::Result) type alias, and will use the 23 //! [`std::error::Error`] interface to let C errors participate in Rust's error 24 //! handling functionality. 25 //! 26 //! Rust code can also create use this module to create an error object that 27 //! will be passed up to C code, though in most cases this will be done 28 //! transparently through the `?` operator. Errors can be constructed from a 29 //! simple error string, from an [`anyhow::Error`] to pass any other Rust error 30 //! type up to C code, or from a combination of the two. 31 //! 32 //! The third case, corresponding to [`Error::with_error`], is the only one that 33 //! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar 34 //! to how QEMU's C code handles errno values, the string and the 35 //! `anyhow::Error` object will be concatenated with `:` as the separator. 36 37 use std::{ 38 borrow::Cow, 39 ffi::{c_char, c_int, c_void, CStr}, 40 fmt::{self, Display}, 41 panic, ptr, 42 }; 43 44 use foreign::{prelude::*, OwnedPointer}; 45 46 use crate::bindings; 47 48 pub type Result<T> = std::result::Result<T, Error>; 49 50 #[derive(Debug)] 51 pub struct Error { 52 msg: Option<Cow<'static, str>>, 53 /// Appends the print string of the error to the msg if not None 54 cause: Option<anyhow::Error>, 55 file: &'static str, 56 line: u32, 57 } 58 59 impl std::error::Error for Error { 60 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 61 self.cause.as_ref().map(AsRef::as_ref) 62 } 63 64 #[allow(deprecated)] 65 fn description(&self) -> &str { 66 self.msg 67 .as_deref() 68 .or_else(|| self.cause.as_deref().map(std::error::Error::description)) 69 .expect("no message nor cause?") 70 } 71 } 72 73 impl Display for Error { 74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 75 let mut prefix = ""; 76 if let Some(ref msg) = self.msg { 77 write!(f, "{msg}")?; 78 prefix = ": "; 79 } 80 if let Some(ref cause) = self.cause { 81 write!(f, "{prefix}{cause}")?; 82 } else if prefix.is_empty() { 83 panic!("no message nor cause?"); 84 } 85 Ok(()) 86 } 87 } 88 89 impl From<String> for Error { 90 #[track_caller] 91 fn from(msg: String) -> Self { 92 let location = panic::Location::caller(); 93 Error { 94 msg: Some(Cow::Owned(msg)), 95 cause: None, 96 file: location.file(), 97 line: location.line(), 98 } 99 } 100 } 101 102 impl From<&'static str> for Error { 103 #[track_caller] 104 fn from(msg: &'static str) -> Self { 105 let location = panic::Location::caller(); 106 Error { 107 msg: Some(Cow::Borrowed(msg)), 108 cause: None, 109 file: location.file(), 110 line: location.line(), 111 } 112 } 113 } 114 115 impl From<anyhow::Error> for Error { 116 #[track_caller] 117 fn from(error: anyhow::Error) -> Self { 118 let location = panic::Location::caller(); 119 Error { 120 msg: None, 121 cause: Some(error), 122 file: location.file(), 123 line: location.line(), 124 } 125 } 126 } 127 128 impl Error { 129 /// Create a new error, prepending `msg` to the 130 /// description of `cause` 131 #[track_caller] 132 pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self { 133 let location = panic::Location::caller(); 134 Error { 135 msg: Some(msg.into()), 136 cause: Some(cause.into()), 137 file: location.file(), 138 line: location.line(), 139 } 140 } 141 142 /// Consume a result, returning `false` if it is an error and 143 /// `true` if it is successful. The error is propagated into 144 /// `errp` like the C API `error_propagate` would do. 145 /// 146 /// # Safety 147 /// 148 /// `errp` must be a valid argument to `error_propagate`; 149 /// typically it is received from C code and need not be 150 /// checked further at the Rust↔C boundary. 151 pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { 152 // SAFETY: caller guarantees errp is valid 153 unsafe { Self::ok_or_propagate(result, errp) }.is_some() 154 } 155 156 /// Consume a result, returning a `NULL` pointer if it is an error and 157 /// a C representation of the contents if it is successful. This is 158 /// similar to the C API `error_propagate`, but it panics if `*errp` 159 /// is not `NULL`. 160 /// 161 /// # Safety 162 /// 163 /// `errp` must be a valid argument to `error_propagate`; 164 /// typically it is received from C code and need not be 165 /// checked further at the Rust↔C boundary. 166 /// 167 /// See [`propagate`](Error::propagate) for more information. 168 #[must_use] 169 pub unsafe fn ptr_or_propagate<T: CloneToForeign>( 170 result: Result<T>, 171 errp: *mut *mut bindings::Error, 172 ) -> *mut T::Foreign { 173 // SAFETY: caller guarantees errp is valid 174 unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() 175 } 176 177 /// Consume a result in the same way as `self.ok()`, but also propagate 178 /// a possible error into `errp`. This is similar to the C API 179 /// `error_propagate`, but it panics if `*errp` is not `NULL`. 180 /// 181 /// # Safety 182 /// 183 /// `errp` must be a valid argument to `error_propagate`; 184 /// typically it is received from C code and need not be 185 /// checked further at the Rust↔C boundary. 186 /// 187 /// See [`propagate`](Error::propagate) for more information. 188 pub unsafe fn ok_or_propagate<T>( 189 result: Result<T>, 190 errp: *mut *mut bindings::Error, 191 ) -> Option<T> { 192 result.map_err(|err| unsafe { err.propagate(errp) }).ok() 193 } 194 195 /// Equivalent of the C function `error_propagate`. Fill `*errp` 196 /// with the information container in `self` if `errp` is not NULL; 197 /// then consume it. 198 /// 199 /// This is similar to the C API `error_propagate`, but it panics if 200 /// `*errp` is not `NULL`. 201 /// 202 /// # Safety 203 /// 204 /// `errp` must be a valid argument to `error_propagate`; it can be 205 /// `NULL` or it can point to any of: 206 /// * `error_abort` 207 /// * `error_fatal` 208 /// * a local variable of (C) type `Error *` 209 /// 210 /// Typically `errp` is received from C code and need not be 211 /// checked further at the Rust↔C boundary. 212 pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { 213 if errp.is_null() { 214 return; 215 } 216 217 // SAFETY: caller guarantees that errp and *errp are valid 218 unsafe { 219 assert_eq!(*errp, ptr::null_mut()); 220 bindings::error_propagate(errp, self.clone_to_foreign_ptr()); 221 } 222 } 223 224 /// Convert a C `Error*` into a Rust `Result`, using 225 /// `Ok(())` if `c_error` is NULL. Free the `Error*`. 226 /// 227 /// # Safety 228 /// 229 /// `c_error` must be `NULL` or valid; typically it was initialized 230 /// with `ptr::null_mut()` and passed by reference to a C function. 231 pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { 232 // SAFETY: caller guarantees c_error is valid 233 unsafe { Self::err_or_else(c_error, || ()) } 234 } 235 236 /// Convert a C `Error*` into a Rust `Result`, calling `f()` to 237 /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. 238 /// 239 /// # Safety 240 /// 241 /// `c_error` must be `NULL` or point to a valid C [`struct 242 /// Error`](bindings::Error); typically it was initialized with 243 /// `ptr::null_mut()` and passed by reference to a C function. 244 pub unsafe fn err_or_else<T, F: FnOnce() -> T>( 245 c_error: *mut bindings::Error, 246 f: F, 247 ) -> Result<T> { 248 // SAFETY: caller guarantees c_error is valid 249 let err = unsafe { Option::<Self>::from_foreign(c_error) }; 250 match err { 251 None => Ok(f()), 252 Some(err) => Err(err), 253 } 254 } 255 } 256 257 impl FreeForeign for Error { 258 type Foreign = bindings::Error; 259 260 unsafe fn free_foreign(p: *mut bindings::Error) { 261 // SAFETY: caller guarantees p is valid 262 unsafe { 263 bindings::error_free(p); 264 } 265 } 266 } 267 268 impl CloneToForeign for Error { 269 fn clone_to_foreign(&self) -> OwnedPointer<Self> { 270 // SAFETY: all arguments are controlled by this function 271 unsafe { 272 let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); 273 let err: &mut bindings::Error = &mut *err.cast(); 274 *err = bindings::Error { 275 msg: format!("{self}").clone_to_foreign_ptr(), 276 err_class: bindings::ERROR_CLASS_GENERIC_ERROR, 277 src_len: self.file.len() as c_int, 278 src: self.file.as_ptr().cast::<c_char>(), 279 line: self.line as c_int, 280 func: ptr::null_mut(), 281 hint: ptr::null_mut(), 282 }; 283 OwnedPointer::new(err) 284 } 285 } 286 } 287 288 impl FromForeign for Error { 289 unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { 290 // SAFETY: caller guarantees c_error is valid 291 unsafe { 292 let error = &*c_error; 293 let file = if error.src_len < 0 { 294 // NUL-terminated 295 CStr::from_ptr(error.src).to_str() 296 } else { 297 // Can become str::from_utf8 with Rust 1.87.0 298 std::str::from_utf8(std::slice::from_raw_parts( 299 &*error.src.cast::<u8>(), 300 error.src_len as usize, 301 )) 302 }; 303 304 Error { 305 msg: FromForeign::cloned_from_foreign(error.msg), 306 cause: None, 307 file: file.unwrap(), 308 line: error.line as u32, 309 } 310 } 311 } 312 } 313 314 #[cfg(test)] 315 mod tests { 316 use std::ffi::CStr; 317 318 use anyhow::anyhow; 319 use foreign::OwnedPointer; 320 321 use super::*; 322 use crate::{assert_match, bindings}; 323 324 #[track_caller] 325 fn error_for_test(msg: &CStr) -> OwnedPointer<Error> { 326 // SAFETY: all arguments are controlled by this function 327 let location = panic::Location::caller(); 328 unsafe { 329 let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); 330 let err: &mut bindings::Error = &mut *err.cast(); 331 *err = bindings::Error { 332 msg: msg.clone_to_foreign_ptr(), 333 err_class: bindings::ERROR_CLASS_GENERIC_ERROR, 334 src_len: location.file().len() as c_int, 335 src: location.file().as_ptr().cast::<c_char>(), 336 line: location.line() as c_int, 337 func: ptr::null_mut(), 338 hint: ptr::null_mut(), 339 }; 340 OwnedPointer::new(err) 341 } 342 } 343 344 unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { 345 unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } 346 } 347 348 #[test] 349 #[allow(deprecated)] 350 fn test_description() { 351 use std::error::Error; 352 353 assert_eq!(super::Error::from("msg").description(), "msg"); 354 assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); 355 } 356 357 #[test] 358 fn test_display() { 359 assert_eq!(&*format!("{}", Error::from("msg")), "msg"); 360 assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); 361 assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); 362 363 assert_eq!( 364 &*format!("{}", Error::with_error("msg", anyhow!("cause"))), 365 "msg: cause" 366 ); 367 } 368 369 #[test] 370 fn test_bool_or_propagate() { 371 unsafe { 372 let mut local_err: *mut bindings::Error = ptr::null_mut(); 373 374 assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); 375 assert_eq!(local_err, ptr::null_mut()); 376 377 let my_err = Error::from("msg"); 378 assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); 379 assert_ne!(local_err, ptr::null_mut()); 380 assert_eq!(error_get_pretty(local_err), c"msg"); 381 bindings::error_free(local_err); 382 } 383 } 384 385 #[test] 386 fn test_ptr_or_propagate() { 387 unsafe { 388 let mut local_err: *mut bindings::Error = ptr::null_mut(); 389 390 let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); 391 assert_eq!(String::from_foreign(ret), "abc"); 392 assert_eq!(local_err, ptr::null_mut()); 393 394 let my_err = Error::from("msg"); 395 assert_eq!( 396 Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err), 397 ptr::null_mut() 398 ); 399 assert_ne!(local_err, ptr::null_mut()); 400 assert_eq!(error_get_pretty(local_err), c"msg"); 401 bindings::error_free(local_err); 402 } 403 } 404 405 #[test] 406 fn test_err_or_unit() { 407 unsafe { 408 let result = Error::err_or_unit(ptr::null_mut()); 409 assert_match!(result, Ok(())); 410 411 let err = error_for_test(c"msg"); 412 let err = Error::err_or_unit(err.into_inner()).unwrap_err(); 413 assert_eq!(&*format!("{err}"), "msg"); 414 } 415 } 416 } 417