master
rs 462 lines 14.8 KB
Raw
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 //! * [`with_errp`](crate::Error::with_errp) can be used to build a `Result`
18 //!
19 //! This module is most commonly used at the boundary between C and Rust code;
20 //! other code will usually access it through the
21 //! [`utils::Result`](crate::Result) type alias, and will use the
22 //! [`std::error::Error`] interface to let C errors participate in Rust's error
23 //! handling functionality.
24 //!
25 //! Rust code can also create use this module to create an error object that
26 //! will be passed up to C code, though in most cases this will be done
27 //! transparently through the `?` operator. Errors can be constructed from a
28 //! simple error string, from an [`anyhow::Error`] to pass any other Rust error
29 //! type up to C code, or from a combination of the two.
30 //!
31 //! The third case, corresponding to [`Error::with_error`], is the only one that
32 //! requires mentioning [`utils::Error`](crate::Error) explicitly. Similar
33 //! to how QEMU's C code handles errno values, the string and the
34 //! `anyhow::Error` object will be concatenated with `:` as the separator.
35
36 use std::{
37 borrow::Cow,
38 ffi::{c_char, c_int, c_void, CStr},
39 fmt::{self, Display},
40 ops::Deref,
41 panic,
42 ptr::{self, addr_of_mut},
43 };
44
45 use foreign::{prelude::*, OwnedPointer};
46
47 use crate::bindings;
48
49 pub type Result<T> = std::result::Result<T, Error>;
50
51 #[derive(Debug)]
52 pub struct Error {
53 cause: anyhow::Error,
54 file: &'static str,
55 line: u32,
56 }
57
58 impl Deref for Error {
59 type Target = anyhow::Error;
60
61 fn deref(&self) -> &Self::Target {
62 &self.cause
63 }
64 }
65
66 impl Display for Error {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 Display::fmt(&format_args!("{:#}", self.cause), f)
69 }
70 }
71
72 impl<E> From<E> for Error
73 where
74 anyhow::Error: From<E>,
75 {
76 #[track_caller]
77 fn from(src: E) -> Self {
78 Self::new(anyhow::Error::from(src))
79 }
80 }
81
82 impl Error {
83 /// Create a new error from an [`anyhow::Error`].
84 ///
85 /// This wraps the error with QEMU's location tracking information.
86 /// Most code should use the `?` operator instead of calling this directly.
87 #[track_caller]
88 pub fn new(cause: anyhow::Error) -> Self {
89 let location = panic::Location::caller();
90 Self {
91 cause,
92 file: location.file(),
93 line: location.line(),
94 }
95 }
96
97 /// Create a new error from a string message.
98 ///
99 /// This is a convenience wrapper around [`Error::new`] for simple string
100 /// errors. Most code should use the [`ensure!`](crate::ensure) macro
101 /// instead of calling this directly.
102 #[track_caller]
103 pub fn msg(src: impl Into<Cow<'static, str>>) -> Self {
104 Self::new(anyhow::Error::msg(src.into()))
105 }
106
107 #[track_caller]
108 #[doc(hidden)]
109 #[inline(always)]
110 pub fn format(args: fmt::Arguments) -> Self {
111 // anyhow::Error::msg will allocate anyway, might as well let fmt::format doit.
112 let msg = fmt::format(args);
113 Self::new(anyhow::Error::msg(msg))
114 }
115
116 /// Create a new error, prepending `msg` to the
117 /// description of `cause`
118 #[track_caller]
119 pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self {
120 fn do_with_error(
121 msg: Cow<'static, str>,
122 cause: anyhow::Error,
123 location: &'static panic::Location<'static>,
124 ) -> Error {
125 Error {
126 cause: cause.context(msg),
127 file: location.file(),
128 line: location.line(),
129 }
130 }
131 do_with_error(msg.into(), cause.into(), panic::Location::caller())
132 }
133
134 /// Consume a result, returning `false` if it is an error and
135 /// `true` if it is successful. The error is propagated into
136 /// `errp` like the C API `error_propagate` would do.
137 ///
138 /// # Safety
139 ///
140 /// `errp` must be a valid argument to `error_propagate`;
141 /// typically it is received from C code and need not be
142 /// checked further at the Rust↔C boundary.
143 pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool {
144 // SAFETY: caller guarantees errp is valid
145 unsafe { Self::ok_or_propagate(result, errp) }.is_some()
146 }
147
148 /// Consume a result, returning a `NULL` pointer if it is an error and
149 /// a C representation of the contents if it is successful. This is
150 /// similar to the C API `error_propagate`, but it panics if `*errp`
151 /// is not `NULL`.
152 ///
153 /// # Safety
154 ///
155 /// `errp` must be a valid argument to `error_propagate`;
156 /// typically it is received from C code and need not be
157 /// checked further at the Rust↔C boundary.
158 ///
159 /// See [`propagate`](Error::propagate) for more information.
160 #[must_use]
161 pub unsafe fn ptr_or_propagate<T: CloneToForeign>(
162 result: Result<T>,
163 errp: *mut *mut bindings::Error,
164 ) -> *mut T::Foreign {
165 // SAFETY: caller guarantees errp is valid
166 unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr()
167 }
168
169 /// Consume a result in the same way as `self.ok()`, but also propagate
170 /// a possible error into `errp`. This is similar to the C API
171 /// `error_propagate`, but it panics if `*errp` is not `NULL`.
172 ///
173 /// # Safety
174 ///
175 /// `errp` must be a valid argument to `error_propagate`;
176 /// typically it is received from C code and need not be
177 /// checked further at the Rust↔C boundary.
178 ///
179 /// See [`propagate`](Error::propagate) for more information.
180 pub unsafe fn ok_or_propagate<T>(
181 result: Result<T>,
182 errp: *mut *mut bindings::Error,
183 ) -> Option<T> {
184 result.map_err(|err| unsafe { err.propagate(errp) }).ok()
185 }
186
187 /// Equivalent of the C function `error_propagate`. Fill `*errp`
188 /// with the information container in `self` if `errp` is not NULL;
189 /// then consume it.
190 ///
191 /// This is similar to the C API `error_propagate`, but it panics if
192 /// `*errp` is not `NULL`.
193 ///
194 /// # Safety
195 ///
196 /// `errp` must be a valid argument to `error_propagate`; it can be
197 /// `NULL` or it can point to any of:
198 /// * `error_abort`
199 /// * `error_fatal`
200 /// * a local variable of (C) type `Error *`
201 ///
202 /// Typically `errp` is received from C code and need not be
203 /// checked further at the Rust↔C boundary.
204 pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) {
205 if errp.is_null() {
206 return;
207 }
208
209 // SAFETY: caller guarantees that errp and *errp are valid
210 unsafe {
211 assert_eq!(*errp, ptr::null_mut());
212 bindings::error_propagate(errp, self.clone_to_foreign_ptr());
213 }
214 }
215
216 /// Pass a C `Error*` to the closure, and convert the result
217 /// (either the return value of the closure, or the error)
218 /// into a Rust `Result`.
219 ///
220 /// # Safety
221 ///
222 /// One exit from `f`, `c_error` must be unchanged or point to a
223 /// valid C [`struct Error`](bindings::Error).
224 pub unsafe fn with_errp<T, F: FnOnce(&mut *mut bindings::Error) -> T>(f: F) -> Result<T> {
225 let mut c_error: *mut bindings::Error = ptr::null_mut();
226
227 // SAFETY: guaranteed by the postcondition of `f`
228 match (f(&mut c_error), unsafe { c_error.into_native() }) {
229 (result, None) => Ok(result),
230 (_, Some(err)) => Err(err),
231 }
232 }
233 }
234
235 /// Extension trait for `std::result::Result`, providing extra
236 /// methods when the error type can be converted into a QEMU
237 /// Error.
238 pub trait ResultExt {
239 /// The success type `T` in `Result<T, E>`.
240 type OkType;
241
242 /// Report a fatal error and exit QEMU, or return the success value.
243 /// Note that, unlike [`unwrap()`](std::result::Result::unwrap), this
244 /// is not an abort and can be used for user errors.
245 fn unwrap_fatal(self) -> Self::OkType;
246 }
247
248 impl<T, E> ResultExt for std::result::Result<T, E>
249 where
250 Error: From<E>,
251 {
252 type OkType = T;
253
254 fn unwrap_fatal(self) -> T {
255 // SAFETY: errp is valid
256 self.map_err(|err| unsafe {
257 Error::from(err).propagate(addr_of_mut!(bindings::error_fatal))
258 })
259 .unwrap()
260 }
261 }
262
263 impl FreeForeign for Error {
264 type Foreign = bindings::Error;
265
266 unsafe fn free_foreign(p: *mut bindings::Error) {
267 // SAFETY: caller guarantees p is valid
268 unsafe {
269 bindings::error_free(p);
270 }
271 }
272 }
273
274 impl CloneToForeign for Error {
275 fn clone_to_foreign(&self) -> OwnedPointer<Self> {
276 // SAFETY: all arguments are controlled by this function
277 unsafe {
278 let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());
279 let err: &mut bindings::Error = &mut *err.cast();
280 *err = bindings::Error {
281 msg: format!("{self}").clone_to_foreign_ptr(),
282 err_class: bindings::ERROR_CLASS_GENERIC_ERROR,
283 src_len: self.file.len() as c_int,
284 src: self.file.as_ptr().cast::<c_char>(),
285 line: self.line as c_int,
286 func: ptr::null_mut(),
287 hint: ptr::null_mut(),
288 };
289 OwnedPointer::new(err)
290 }
291 }
292 }
293
294 impl FromForeign for Error {
295 unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self {
296 // SAFETY: caller guarantees c_error is valid
297 unsafe {
298 let error = &*c_error;
299 let file = if error.src_len < 0 {
300 // NUL-terminated
301 CStr::from_ptr(error.src).to_str()
302 } else {
303 // Can become str::from_utf8 with Rust 1.87.0
304 std::str::from_utf8(std::slice::from_raw_parts(
305 &*error.src.cast::<u8>(),
306 error.src_len as usize,
307 ))
308 };
309
310 Error {
311 cause: anyhow::Error::msg(String::cloned_from_foreign(error.msg)),
312 file: file.unwrap(),
313 line: error.line as u32,
314 }
315 }
316 }
317 }
318
319 /// Ensure that a condition is true, returning an error if it is false.
320 ///
321 /// This macro is similar to [`anyhow::ensure`] but returns a QEMU [`Result`].
322 /// If the condition evaluates to `false`, the macro returns early with an error
323 /// constructed from the provided message.
324 ///
325 /// # Examples
326 ///
327 /// ```
328 /// # use util::{ensure, Result};
329 /// # fn check_positive(x: i32) -> Result<()> {
330 /// ensure!(x > 0, "value must be positive");
331 /// # Ok(())
332 /// # }
333 /// ```
334 ///
335 /// ```
336 /// # use util::{ensure, Result};
337 /// # const MIN: i32 = 123;
338 /// # const MAX: i32 = 456;
339 /// # fn check_range(x: i32) -> Result<()> {
340 /// ensure!(
341 /// x >= MIN && x <= MAX,
342 /// "{} not between {} and {}",
343 /// x,
344 /// MIN,
345 /// MAX
346 /// );
347 /// # Ok(())
348 /// # }
349 /// ```
350 #[macro_export]
351 macro_rules! ensure {
352 ($cond:expr, $fmt:literal, $($arg:tt)*) => {
353 if !$cond {
354 let e = $crate::Error::format(format_args!($fmt, $($arg)*));
355 return $crate::Result::Err(e);
356 }
357 };
358 ($cond:expr, $err:expr $(,)?) => {
359 if !$cond {
360 let e = $crate::Error::msg($err);
361 return $crate::Result::Err(e);
362 }
363 };
364 }
365
366 #[cfg(test)]
367 mod tests {
368 use std::ffi::CStr;
369
370 use anyhow::anyhow;
371 use common::assert_match;
372 use foreign::OwnedPointer;
373
374 use super::*;
375
376 #[track_caller]
377 fn error_for_test(msg: &CStr) -> OwnedPointer<Error> {
378 // SAFETY: all arguments are controlled by this function
379 let location = panic::Location::caller();
380 unsafe {
381 let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());
382 let err: &mut bindings::Error = &mut *err.cast();
383 *err = bindings::Error {
384 msg: msg.clone_to_foreign_ptr(),
385 err_class: bindings::ERROR_CLASS_GENERIC_ERROR,
386 src_len: location.file().len() as c_int,
387 src: location.file().as_ptr().cast::<c_char>(),
388 line: location.line() as c_int,
389 func: ptr::null_mut(),
390 hint: ptr::null_mut(),
391 };
392 OwnedPointer::new(err)
393 }
394 }
395
396 unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr {
397 unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) }
398 }
399
400 #[test]
401 fn test_display() {
402 assert_eq!(&*format!("{}", Error::msg("msg")), "msg");
403 assert_eq!(&*format!("{}", Error::msg("msg".to_owned())), "msg");
404 assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg");
405
406 assert_eq!(
407 &*format!("{}", Error::with_error("msg", anyhow!("cause"))),
408 "msg: cause"
409 );
410 }
411
412 #[test]
413 fn test_bool_or_propagate() {
414 unsafe {
415 let mut local_err: *mut bindings::Error = ptr::null_mut();
416
417 assert!(Error::bool_or_propagate(Ok(()), &mut local_err));
418 assert_eq!(local_err, ptr::null_mut());
419
420 let my_err = Error::msg("msg");
421 assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err));
422 assert_ne!(local_err, ptr::null_mut());
423 assert_eq!(error_get_pretty(local_err), c"msg");
424 bindings::error_free(local_err);
425 }
426 }
427
428 #[test]
429 fn test_ptr_or_propagate() {
430 unsafe {
431 let mut local_err: *mut bindings::Error = ptr::null_mut();
432
433 let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err);
434 assert_eq!(String::from_foreign(ret), "abc");
435 assert_eq!(local_err, ptr::null_mut());
436
437 let my_err = Error::msg("msg");
438 assert_eq!(
439 Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err),
440 ptr::null_mut()
441 );
442 assert_ne!(local_err, ptr::null_mut());
443 assert_eq!(error_get_pretty(local_err), c"msg");
444 bindings::error_free(local_err);
445 }
446 }
447
448 #[test]
449 fn test_with_errp() {
450 unsafe {
451 let result = Error::with_errp(|_errp| true);
452 assert_match!(result, Ok(true));
453
454 let err = Error::with_errp(|errp| {
455 *errp = error_for_test(c"msg").into_inner();
456 false
457 })
458 .unwrap_err();
459 assert_eq!(&*format!("{err}"), "msg");
460 }
461 }
462 }