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;
so, use the libc crate.
This is a set of utility functions that are used by both chardev and
block layer bindings.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
docs/devel/rust.rst | 1 +
rust/qemu-api/meson.build | 4 +
rust/qemu-api/src/assertions.rs | 28 +++
rust/qemu-api/src/errno.rs | 343 ++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 1 +
rust/qemu-api/src/prelude.rs | 2 +
6 files changed, 379 insertions(+)
create mode 100644 rust/qemu-api/src/errno.rs
diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 90958e5a306..c75dccdbb7c 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -179,6 +179,7 @@ module status
``callbacks`` complete
``cell`` stable
``c_str`` complete
+``errno`` complete
``irq`` complete
``memory`` stable
``module`` complete
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 2e9c1078b9b..bcf1cf780f3 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -2,6 +2,8 @@ _qemu_api_cfg = run_command(rustc_args,
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
capture: true, check: true).stdout().strip().splitlines()
+libc_dep = dependency('libc-0.2-rs')
+
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
if rustc.version().version_compare('>=1.77.0')
_qemu_api_cfg += ['--cfg', 'has_offset_of']
@@ -22,6 +24,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',
@@ -39,6 +42,7 @@ _qemu_api_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _qemu_api_cfg,
+ dependencies: libc_dep,
)
rust.test('rust-qemu-api-tests', _qemu_api_rs,
diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs
index fa1a18de6fe..104dec39774 100644
--- a/rust/qemu-api/src/assertions.rs
+++ b/rust/qemu-api/src/assertions.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)
+ );
+ };
+}
diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs
new file mode 100644
index 00000000000..bfd972df951
--- /dev/null
+++ b/rust/qemu-api/src/errno.rs
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Utility functions to convert `errno` to and from
+//! [`io::Error`]/[`io::Result`]
+//!
+//! QEMU C functions often have a "positive success/negative `errno`" calling
+//! convention. This module provides functions to portably convert an integer
+//! into an [`io::Result`] and back.
+
+use std::{convert::TryFrom, io, io::ErrorKind};
+
+/// An `errno` value that can be converted into an [`io::Error`]
+pub struct Errno(pub 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. This is the same mapping that the
+// standard library uses to retrieve the kind of OS errors
+// (`std::sys::pal::unix::decode_error_kind`).
+impl From<Errno> for ErrorKind {
+ fn from(value: Errno) -> ErrorKind {
+ let Errno(errno) = value;
+ match i32::from(errno) {
+ libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
+ libc::ENOENT => ErrorKind::NotFound,
+ libc::EINTR => ErrorKind::Interrupted,
+ x if x == libc::EAGAIN || x == 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,
+ }
+ }
+}
+
+// This is used on Windows for all io::Errors, but also on Unix if the
+// io::Error does not have a raw OS error. This is the reversed
+// mapping of the above.
+impl From<io::ErrorKind> for Errno {
+ fn from(value: io::ErrorKind) -> Errno {
+ let errno = match value {
+ // can be both EPERM or EACCES :( pick one
+ ErrorKind::PermissionDenied => libc::EPERM,
+ ErrorKind::NotFound => libc::ENOENT,
+ ErrorKind::Interrupted => libc::EINTR,
+ ErrorKind::WouldBlock => libc::EAGAIN,
+ ErrorKind::OutOfMemory => libc::ENOMEM,
+ ErrorKind::AlreadyExists => libc::EEXIST,
+ ErrorKind::InvalidInput => libc::EINVAL,
+ ErrorKind::BrokenPipe => libc::EPIPE,
+ ErrorKind::AddrInUse => libc::EADDRINUSE,
+ ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
+ ErrorKind::ConnectionAborted => libc::ECONNABORTED,
+ ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
+ ErrorKind::ConnectionReset => libc::ECONNRESET,
+ ErrorKind::NotConnected => libc::ENOTCONN,
+ ErrorKind::Unsupported => libc::ENOTSUP,
+ ErrorKind::TimedOut => libc::ETIMEDOUT,
+ _ => libc::EIO,
+ };
+ Errno(errno as u16)
+ }
+}
+
+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 error_kind: ErrorKind = value.into();
+ error_kind.into()
+ }
+}
+
+impl From<io::Error> for Errno {
+ fn from(value: io::Error) -> Errno {
+ if cfg!(unix) {
+ if let Some(errno) = value.raw_os_error() {
+ return Errno(u16::try_from(errno).unwrap());
+ }
+ }
+ value.kind().into()
+ }
+}
+
+/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`]
+/// for the "right" set of types.
+mod traits {
+ use super::Errno;
+
+ /// A signed type that can be converted into an
+ /// [`io::Result`](std::io::Result)
+ pub trait GetErrno {
+ /// 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>;
+ }
+
+ /// A type that can be taken out of an [`io::Result`](std::io::Result) and
+ /// converted into "positive success/negative `errno`" convention.
+ pub trait MergeErrno {
+ /// Signed variant of `Self`, used as the return type of
+ /// [`into_neg_errno`](super::into_neg_errno).
+ type Out: From<u16> + std::ops::Neg<Output = Self::Out>;
+
+ /// Return `self`, asserting that it is in range
+ fn map_ok(self) -> Self::Out;
+ }
+
+ 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);
+
+ macro_rules! merge_errno {
+ ($t:ty, $out:ty) => {
+ impl MergeErrno for $t {
+ type Out = $out;
+ fn map_ok(self) -> Self::Out {
+ self.try_into().unwrap()
+ }
+ }
+ };
+ }
+
+ merge_errno!(u8, i32);
+ merge_errno!(u16, i32);
+ merge_errno!(u32, i32);
+ merge_errno!(u64, i64);
+
+ impl MergeErrno for () {
+ type Out = i32;
+ fn map_ok(self) -> i32 {
+ 0
+ }
+ }
+}
+
+use traits::{GetErrno, MergeErrno};
+
+/// Convert an integer value into a [`io::Result`].
+///
+/// Positive values are turned into an `Ok` result; negative values
+/// are interpreted as negated `errno` and turned into an `Err`.
+///
+/// ```
+/// # use qemu_api::errno::into_io_result;
+/// # use std::io::ErrorKind;
+/// let ok = into_io_result(1i32).unwrap();
+/// assert_eq!(ok, 1u32);
+///
+/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM
+/// assert_eq!(err.kind(), ErrorKind::PermissionDenied);
+/// ```
+///
+/// # Panics
+///
+/// Since the result is an unsigned integer, negative values must
+/// be close to 0; values that are too far away are considered
+/// likely overflows and will panic:
+///
+/// ```should_panic
+/// # use qemu_api::errno::into_io_result;
+/// # #[allow(dead_code)]
+/// let err = into_io_result(-0x1234_5678i32); // panic
+/// ```
+pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
+ value.into_errno_result().map_err(Into::into)
+}
+
+/// Convert a [`Result`] into an integer value, using negative `errno`
+/// values to report errors.
+///
+/// ```
+/// # use qemu_api::errno::into_neg_errno;
+/// # use std::io::{self, ErrorKind};
+/// let ok: io::Result<()> = Ok(());
+/// assert_eq!(into_neg_errno(ok), 0);
+///
+/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into());
+/// assert_eq!(into_neg_errno(err), -22); // -EINVAL
+/// ```
+///
+/// Since this module also provides the ability to convert [`io::Error`]
+/// to an `errno` value, [`io::Result`] is the most commonly used type
+/// for the argument of this function:
+///
+/// # Panics
+///
+/// Since the result is a signed integer, integer `Ok` values must remain
+/// positive:
+///
+/// ```should_panic
+/// # use qemu_api::errno::into_neg_errno;
+/// # use std::io;
+/// let err: io::Result<u32> = Ok(0x8899_AABB);
+/// into_neg_errno(err) // panic
+/// # ;
+/// ```
+pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> T::Out {
+ match value {
+ Ok(x) => x.map_ok(),
+ Err(err) => -T::Out::from(err.into().0),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::io::ErrorKind;
+
+ use super::*;
+ use crate::assert_match;
+
+ #[test]
+ pub fn test_from_u8() {
+ let ok: io::Result<_> = Ok(42u8);
+ assert_eq!(into_neg_errno(ok), 42);
+
+ let err: io::Result<u8> = Err(io::ErrorKind::PermissionDenied.into());
+ assert_eq!(into_neg_errno(err), -1);
+
+ if cfg!(unix) {
+ let os_err: io::Result<u8> = Err(io::Error::from_raw_os_error(10));
+ assert_eq!(into_neg_errno(os_err), -10);
+ }
+ }
+
+ #[test]
+ pub fn test_from_u16() {
+ let ok: io::Result<_> = Ok(1234u16);
+ assert_eq!(into_neg_errno(ok), 1234);
+
+ let err: io::Result<u16> = Err(io::ErrorKind::PermissionDenied.into());
+ assert_eq!(into_neg_errno(err), -1);
+
+ if cfg!(unix) {
+ let os_err: io::Result<u16> = Err(io::Error::from_raw_os_error(10));
+ assert_eq!(into_neg_errno(os_err), -10);
+ }
+ }
+
+ #[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_from_u32() {
+ let ok: io::Result<_> = Ok(1234u32);
+ assert_eq!(into_neg_errno(ok), 1234);
+
+ let err: io::Result<u32> = Err(io::ErrorKind::PermissionDenied.into());
+ assert_eq!(into_neg_errno(err), -1);
+
+ if cfg!(unix) {
+ let os_err: io::Result<u32> = Err(io::Error::from_raw_os_error(10));
+ assert_eq!(into_neg_errno(os_err), -10);
+ }
+ }
+
+ #[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_from_u64() {
+ let ok: io::Result<_> = Ok(1234u64);
+ assert_eq!(into_neg_errno(ok), 1234);
+
+ let err: io::Result<u64> = Err(io::ErrorKind::InvalidInput.into());
+ assert_eq!(into_neg_errno(err), -22);
+
+ if cfg!(unix) {
+ let os_err: io::Result<u64> = Err(io::Error::from_raw_os_error(6));
+ assert_eq!(into_neg_errno(os_err), -6);
+ }
+ }
+
+ #[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);
+ }
+
+ #[test]
+ pub fn test_from_unit() {
+ let ok: io::Result<_> = Ok(());
+ assert_eq!(into_neg_errno(ok), 0);
+
+ let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into());
+ assert_eq!(into_neg_errno(err), -12);
+
+ if cfg!(unix) {
+ let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2));
+ assert_eq!(into_neg_errno(os_err), -2);
+ }
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index ed1a8f9a2b4..05f38b51d30 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -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;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index fbf0ee23e0b..634acf37a85 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -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;
--
2.48.1
Hi Paolo, > 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; > so, use the libc crate. I'm a bit confused. The doc of error.h just said the negative value for failure: • integer-valued functions return non-negative / negative. Why do we need to using libc's -errno for Windows as well? Converting `io::Error::last_os_error().raw_os_error().unwrap()` to a negative value seems compatible with Windows, except it returns Windows error codes. > This is a set of utility functions that are used by both chardev and > block layer bindings. ... > +// 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. This is the same mapping that the > +// standard library uses to retrieve the kind of OS errors > +// (`std::sys::pal::unix::decode_error_kind`). > +impl From<Errno> for ErrorKind { > + fn from(value: Errno) -> ErrorKind { What about `use ErrorKind::*;` to oimt the following "ErrorKind::" prefix? > + let Errno(errno) = value; > + match i32::from(errno) { Maybe `match i32::from(errno.0)` ? > + libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, > + libc::ENOENT => ErrorKind::NotFound, > + libc::EINTR => ErrorKind::Interrupted, > + x if x == libc::EAGAIN || x == 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, Are these errno cases specifically selected? It seems to have fewer than `decode_error_kind` lists. Why not support all the cases `decode_error_kind` mentions? Or do we need to try to cover as many errno cases as possible from rust/kernel/error.rs? > + } > + } > +} > + > +// This is used on Windows for all io::Errors, but also on Unix if the > +// io::Error does not have a raw OS error. This is the reversed > +// mapping of the above. Maybe: This is the "almost" reversed (except the default case) mapping ? > +impl From<io::ErrorKind> for Errno { > + fn from(value: io::ErrorKind) -> Errno { `use ErrorKind::*;` could save some words, too. > + let errno = match value { > + // can be both EPERM or EACCES :( pick one > + ErrorKind::PermissionDenied => libc::EPERM, > + ErrorKind::NotFound => libc::ENOENT, > + ErrorKind::Interrupted => libc::EINTR, > + ErrorKind::WouldBlock => libc::EAGAIN, > + ErrorKind::OutOfMemory => libc::ENOMEM, > + ErrorKind::AlreadyExists => libc::EEXIST, > + ErrorKind::InvalidInput => libc::EINVAL, > + ErrorKind::BrokenPipe => libc::EPIPE, > + ErrorKind::AddrInUse => libc::EADDRINUSE, > + ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL, > + ErrorKind::ConnectionAborted => libc::ECONNABORTED, > + ErrorKind::ConnectionRefused => libc::ECONNREFUSED, > + ErrorKind::ConnectionReset => libc::ECONNRESET, > + ErrorKind::NotConnected => libc::ENOTCONN, > + ErrorKind::Unsupported => libc::ENOTSUP, > + ErrorKind::TimedOut => libc::ETIMEDOUT, > + _ => libc::EIO, > + }; > + Errno(errno as u16) > + } > +} > + > +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()) Maybe `io::Error::from_raw_os_error(value.0.into())`? > + } > + > + #[cfg(windows)] > + fn from(value: Errno) -> io::Error { > + let error_kind: ErrorKind = value.into(); > + error_kind.into() Even this works: fn from(value: Errno) -> io::Error { - let error_kind: ErrorKind = value.into(); - error_kind.into() + value.into() However, it's less readability, so I still prefer your current codes. :-) Thanks, Zhao
© 2016 - 2025 Red Hat, Inc.