xref: /qemu/rust/qemu-api/src/error.rs (revision 96215036f47403438c7c7869b7cd419bd7a11f82)
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 {
source(&self) -> Option<&(dyn std::error::Error + 'static)>60     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
61         self.cause.as_ref().map(AsRef::as_ref)
62     }
63 
64     #[allow(deprecated)]
description(&self) -> &str65     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 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result74     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]
from(msg: String) -> Self91     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]
from(msg: &'static str) -> Self104     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]
from(error: anyhow::Error) -> Self117     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]
with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self132     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.
bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool151     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]
ptr_or_propagate<T: CloneToForeign>( result: Result<T>, errp: *mut *mut bindings::Error, ) -> *mut T::Foreign169     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.
ok_or_propagate<T>( result: Result<T>, errp: *mut *mut bindings::Error, ) -> Option<T>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.
propagate(self, errp: *mut *mut bindings::Error)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.
err_or_unit(c_error: *mut bindings::Error) -> Result<()>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.
err_or_else<T, F: FnOnce() -> T>( c_error: *mut bindings::Error, f: F, ) -> Result<T>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 
free_foreign(p: *mut bindings::Error)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 {
clone_to_foreign(&self) -> OwnedPointer<Self>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 {
cloned_from_foreign(c_error: *const bindings::Error) -> Self289     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]
error_for_test(msg: &CStr) -> OwnedPointer<Error>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 
error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr344     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)]
test_description()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]
test_display()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]
test_bool_or_propagate()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]
test_ptr_or_propagate()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]
test_err_or_unit()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