@@ -22,6 +22,7 @@ _qemu_api_rs = static_library(
'src/cell.rs',
'src/chardev.rs',
'src/c_str.rs',
+ 'src/errno.rs',
'src/irq.rs',
'src/memory.rs',
'src/module.rs',
@@ -92,3 +92,31 @@ fn types_must_be_equal<T, U>(_: T)
};
};
}
+
+/// Assert that an expression matches a pattern. This can also be
+/// useful to compare enums that do not implement `Eq`.
+///
+/// # Examples
+///
+/// ```
+/// # use qemu_api::assert_match;
+/// // JoinHandle does not implement `Eq`, therefore the result
+/// // does not either.
+/// let result: Result<std::thread::JoinHandle<()>, u32> = Err(42);
+/// assert_match!(result, Err(42));
+/// ```
+#[macro_export]
+macro_rules! assert_match {
+ ($a:expr, $b:pat) => {
+ assert!(
+ match $a {
+ $b => true,
+ _ => false,
+ },
+ "{} = {:?} does not match {}",
+ stringify!($a),
+ $a,
+ stringify!($b)
+ );
+ };
+}
new file mode 100644
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Module to portably convert `errno` into [`io::Error`], and the
+//! return value of functions returning `-errno` into [`io::Result`].
+
+use std::io;
+#[cfg(windows)]
+use std::io::ErrorKind;
+
+/// An `errno` value that can be converted into an [`io::Error`]
+pub struct Errno(u16);
+
+// On Unix, from_raw_os_error takes an errno value and OS errors
+// are printed using strerror. On Windows however it takes a
+// GetLastError() value; therefore we need to convert errno values
+// into io::Error by hand. For simplicity use ErrorKind and use
+// the standard library's simple-minded mapping of ErrorKind to Error
+// (`impl From<ErrorKind> for io::Error`).
+//
+// Since this is just for Windows, do not bother with using the libc
+// crate or generating the constants from C. Just list here the
+// constants that map to stable error kinds.
+#[cfg(windows)]
+mod libc {
+ pub const EPERM: u16 = 1;
+ pub const ENOENT: u16 = 2;
+ pub const EINTR: u16 = 4;
+ pub const EAGAIN: u16 = 11;
+ pub const ENOMEM: u16 = 12;
+ pub const EACCES: u16 = 13;
+ pub const EEXIST: u16 = 17;
+ pub const EINVAL: u16 = 22;
+ pub const EPIPE: u16 = 32;
+ pub const EADDRINUSE: u16 = 100;
+ pub const EADDRNOTAVAIL: u16 = 101;
+ pub const ECONNABORTED: u16 = 106;
+ pub const ECONNREFUSED: u16 = 107;
+ pub const ECONNRESET: u16 = 108;
+ pub const ENOTCONN: u16 = 126;
+ pub const ENOTSUP: u16 = 129;
+ pub const ETIMEDOUT: u16 = 138;
+ pub const EWOULDBLOCK: u16 = 140;
+}
+
+impl From<Errno> for io::Error {
+ #[cfg(unix)]
+ fn from(value: Errno) -> io::Error {
+ let Errno(errno) = value;
+ io::Error::from_raw_os_error(errno.into())
+ }
+
+ #[cfg(windows)]
+ fn from(value: Errno) -> io::Error {
+ let Errno(errno) = value;
+ let error_kind = match errno {
+ libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
+ libc::ENOENT => ErrorKind::NotFound,
+ libc::EINTR => ErrorKind::Interrupted,
+ // Note that on Windows we know these two are distinct. In general,
+ // it would not be possible to use "|".
+ libc::EAGAIN | libc::EWOULDBLOCK => ErrorKind::WouldBlock,
+ libc::ENOMEM => ErrorKind::OutOfMemory,
+ libc::EEXIST => ErrorKind::AlreadyExists,
+ libc::EINVAL => ErrorKind::InvalidInput,
+ libc::EPIPE => ErrorKind::BrokenPipe,
+ libc::EADDRINUSE => ErrorKind::AddrInUse,
+ libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
+ libc::ECONNABORTED => ErrorKind::ConnectionAborted,
+ libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
+ libc::ECONNRESET => ErrorKind::ConnectionReset,
+ libc::ENOTCONN => ErrorKind::NotConnected,
+ libc::ENOTSUP => ErrorKind::Unsupported,
+ libc::ETIMEDOUT => ErrorKind::TimedOut,
+ _ => ErrorKind::Other,
+ };
+ error_kind.into()
+ }
+}
+
+/// Convert an integer value into a [`Result`], where positive
+/// values are turned into an `Ok` result and negative values are
+/// interpreted as negated errno and turned into an `Err`.
+pub trait GetErrno: Copy {
+ /// Unsigned variant of self, used as the type for the `Ok` case.
+ type Out;
+
+ /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative
+ fn into_errno_result(self) -> Result<Self::Out, Errno>;
+}
+
+macro_rules! get_errno {
+ ($t:ty, $out:ty) => {
+ impl GetErrno for $t {
+ type Out = $out;
+ fn into_errno_result(self) -> Result<Self::Out, Errno> {
+ match self {
+ 0.. => Ok(self as $out),
+ -65535..=-1 => Err(Errno(-self as u16)),
+ _ => panic!("{self} is not a negative errno"),
+ }
+ }
+ }
+ };
+}
+
+get_errno!(i32, u32);
+get_errno!(i64, u64);
+get_errno!(isize, usize);
+
+/// Convert an integer value into a [`io::Result`], where positive
+/// values are turned into an `Ok` result and negative values are
+/// interpreted as negated errno and turn into an `Err`.
+///
+/// ```
+/// # use qemu_api::errno::into_io_result;
+/// # use std::io::ErrorKind;
+///
+/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM
+/// assert_eq!(err.kind(), ErrorKind::PermissionDenied);
+/// ```
+pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
+ Ok(value.into_errno_result()?)
+}
+
+#[cfg(test)]
+mod tests {
+ use std::io::ErrorKind;
+
+ use super::*;
+ use crate::assert_match;
+
+ #[test]
+ pub fn test_i32() {
+ assert_match!(into_io_result(1234i32), Ok(1234));
+
+ let err = into_io_result(-1i32).unwrap_err();
+ #[cfg(unix)]
+ assert_match!(err.raw_os_error(), Some(1));
+ assert_match!(err.kind(), ErrorKind::PermissionDenied);
+ }
+
+ #[test]
+ pub fn test_i64() {
+ assert_match!(into_io_result(1234i64), Ok(1234));
+
+ let err = into_io_result(-22i64).unwrap_err();
+ #[cfg(unix)]
+ assert_match!(err.raw_os_error(), Some(22));
+ assert_match!(err.kind(), ErrorKind::InvalidInput);
+ }
+
+ #[test]
+ pub fn test_isize() {
+ assert_match!(into_io_result(1234isize), Ok(1234));
+
+ let err = into_io_result(-4isize).unwrap_err();
+ #[cfg(unix)]
+ assert_match!(err.raw_os_error(), Some(4));
+ assert_match!(err.kind(), ErrorKind::Interrupted);
+ }
+}
@@ -19,6 +19,7 @@
pub mod callbacks;
pub mod cell;
pub mod chardev;
+pub mod errno;
pub mod irq;
pub mod memory;
pub mod module;
@@ -9,6 +9,8 @@
pub use crate::cell::BqlCell;
pub use crate::cell::BqlRefCell;
+pub use crate::errno;
+
pub use crate::qdev::DeviceMethods;
pub use crate::qom::InterfaceType;
This is an initial, minimal part of the chardev bindings. It is a common convention in QEMU to return a positive value in case of success, and a negated errno value in case of error. Unfortunately, using errno portably in Rust is a bit complicated; on Unix the errno values are supported natively by io::Error, but on Windows they are not and one would have to use the libc crate. Provide a small module that interprets QEMU's convention and does a best-effort translation to io::Error on Windows. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/assertions.rs | 28 ++++++ rust/qemu-api/src/errno.rs | 161 ++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/prelude.rs | 2 + 5 files changed, 193 insertions(+) create mode 100644 rust/qemu-api/src/errno.rs