diff options
| author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-03 10:20:59 +0800 |
|---|---|---|
| committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-03 10:20:59 +0800 |
| commit | 8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0 (patch) | |
| tree | 6059933b4c0205cdfb5f597f94a2ad2d0b4debfd /rust/qemu-api/src/errno.rs | |
| parent | 70fc2bde913ef8a6b77ded6831534d9c79541e17 (diff) | |
| parent | a4e749780bd20593c0c386612a51bf4d64a80132 (diff) | |
| download | focaccia-qemu-8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0.tar.gz focaccia-qemu-8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0.zip | |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* qom: Use command line syntax for default values in help
* i386: support cache topology with machine's configuration
* rust: fix duplicate symbols from monitor-fd.c
* rust: add module to convert between success/-errno and io::Result
* rust: move class_init implementation from trait to method
* pvg: configuration improvements
* kvm guestmemfd: replace assertion with error
* riscv: cleanups
* target/i386/hvf: cleanups to emulation
* target/i386: add Zhaoxin and Yongfeng CPU model
# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAme+10sUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroMkRwf/eT0gVbE3u0TS6EVZwjGZPHEOEyy/
# gl39SlTT97HxoAClE4PRcdkn7YR3f30hytHghc4qhou+Eh/7Mj2Ox7l7+CyaaCS/
# fxowsOVMBV7++PkyKRPxIMamKzD8Bo0eGwWe+CJijA0zt9PSI/YEwRV0pf/s6KCW
# pOya2f+aNbAo3O5RWtIKSISgbSVvuVzDcDHyfydmOHuvGr2NHAM8UfZYD+41qy5B
# 81PYlvK6HgvhaCboqCUADULkte96Xmc4p2ggk0ZNiy0ho46rs78SMyBh5sXR2S3I
# moiQHpJXyV5TcI7HmwvcW7s0/cpdKm/wmPOjb6otu9InWh/ON1nnURsTEQ==
# =V/fm
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 26 Feb 2025 16:56:43 HKT
# gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg: issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (34 commits)
target/i386: Mask CMPLegacy bit in CPUID[0x80000001].ECX for Zhaoxin CPUs
target/i386: Introduce Zhaoxin Yongfeng CPU model
target/i386: Add CPUID leaf 0xC000_0001 EDX definitions
target/i386: Add support for Zhaoxin CPU vendor identification
target/riscv: move 128-bit check to TCG realize
target/riscv: remove unused macro DEFINE_CPU
i386/cpu: add has_caches flag to check smp_cache configuration
i386/pc: Support cache topology in -machine for PC machine
i386/cpu: Update cache topology with machine's configuration
i386/cpu: Support module level cache topology
rust: qom: get rid of ClassInitImpl
rust: pl011, qemu_api tests: do not use ClassInitImpl
rust: qom: add ObjectImpl::CLASS_INIT
rust: add SysBusDeviceImpl
rust: add IsA bounds to QOM implementation traits
target/i386/hvf: drop some dead code
target/i386/hvf: move and rename simulate_{rdmsr, wrmsr}
target/i386/hvf: move and rename {load, store}_regs
target/i386/hvf: use x86_segment in x86_decode.c
target/i386/hvf: fix the declaration of hvf_handle_io
...
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/qemu-api/src/errno.rs')
| -rw-r--r-- | rust/qemu-api/src/errno.rs | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs new file mode 100644 index 0000000000..18d101448b --- /dev/null +++ b/rust/qemu-api/src/errno.rs @@ -0,0 +1,345 @@ +// 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 { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => 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; EIO is returned for unknown ErrorKinds. +impl From<io::ErrorKind> for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + 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); + } + } +} |