| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | |
| 3 | //! Utility functions to convert `errno` to and from |
| 4 | //! [`io::Error`]/[`io::Result`] |
| 5 | //! |
| 6 | //! QEMU C functions often have a "positive success/negative `errno`" calling |
| 7 | //! convention. This module provides functions to portably convert an integer |
| 8 | //! into an [`io::Result`] and back. |
| 9 | |
| 10 | use std::{ |
| 11 | convert::{self, TryFrom}, |
| 12 | io::{self, ErrorKind}, |
| 13 | }; |
| 14 | |
| 15 | /// An `errno` value that can be converted into an [`io::Error`] |
| 16 | pub struct Errno(pub u16); |
| 17 | |
| 18 | // On Unix, from_raw_os_error takes an errno value and OS errors |
| 19 | // are printed using strerror. On Windows however it takes a |
| 20 | // GetLastError() value; therefore we need to convert errno values |
| 21 | // into io::Error by hand. This is the same mapping that the |
| 22 | // standard library uses to retrieve the kind of OS errors |
| 23 | // (`std::sys::pal::unix::decode_error_kind`). |
| 24 | impl From<Errno> for ErrorKind { |
| 25 | fn from(value: Errno) -> ErrorKind { |
| 26 | use ErrorKind::*; |
| 27 | let Errno(errno) = value; |
| 28 | match i32::from(errno) { |
| 29 | libc::EPERM | libc::EACCES => PermissionDenied, |
| 30 | libc::ENOENT => NotFound, |
| 31 | libc::EINTR => Interrupted, |
| 32 | x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, |
| 33 | libc::ENOMEM => OutOfMemory, |
| 34 | libc::EEXIST => AlreadyExists, |
| 35 | libc::EINVAL => InvalidInput, |
| 36 | libc::EPIPE => BrokenPipe, |
| 37 | libc::EADDRINUSE => AddrInUse, |
| 38 | libc::EADDRNOTAVAIL => AddrNotAvailable, |
| 39 | libc::ECONNABORTED => ConnectionAborted, |
| 40 | libc::ECONNREFUSED => ConnectionRefused, |
| 41 | libc::ECONNRESET => ConnectionReset, |
| 42 | libc::ENOTCONN => NotConnected, |
| 43 | libc::ENOTSUP => Unsupported, |
| 44 | libc::ETIMEDOUT => TimedOut, |
| 45 | _ => Other, |
| 46 | } |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | // This is used on Windows for all io::Errors, but also on Unix if the |
| 51 | // io::Error does not have a raw OS error. This is the reversed |
| 52 | // mapping of the above; EIO is returned for unknown ErrorKinds. |
| 53 | impl From<io::ErrorKind> for Errno { |
| 54 | fn from(value: io::ErrorKind) -> Errno { |
| 55 | use ErrorKind::*; |
| 56 | let errno = match value { |
| 57 | // can be both EPERM or EACCES :( pick one |
| 58 | PermissionDenied => libc::EPERM, |
| 59 | NotFound => libc::ENOENT, |
| 60 | Interrupted => libc::EINTR, |
| 61 | WouldBlock => libc::EAGAIN, |
| 62 | OutOfMemory => libc::ENOMEM, |
| 63 | AlreadyExists => libc::EEXIST, |
| 64 | InvalidInput => libc::EINVAL, |
| 65 | BrokenPipe => libc::EPIPE, |
| 66 | AddrInUse => libc::EADDRINUSE, |
| 67 | AddrNotAvailable => libc::EADDRNOTAVAIL, |
| 68 | ConnectionAborted => libc::ECONNABORTED, |
| 69 | ConnectionRefused => libc::ECONNREFUSED, |
| 70 | ConnectionReset => libc::ECONNRESET, |
| 71 | NotConnected => libc::ENOTCONN, |
| 72 | Unsupported => libc::ENOTSUP, |
| 73 | TimedOut => libc::ETIMEDOUT, |
| 74 | _ => libc::EIO, |
| 75 | }; |
| 76 | Errno(errno as u16) |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | impl From<Errno> for io::Error { |
| 81 | #[cfg(unix)] |
| 82 | fn from(value: Errno) -> io::Error { |
| 83 | let Errno(errno) = value; |
| 84 | io::Error::from_raw_os_error(errno.into()) |
| 85 | } |
| 86 | |
| 87 | #[cfg(windows)] |
| 88 | fn from(value: Errno) -> io::Error { |
| 89 | let error_kind: ErrorKind = value.into(); |
| 90 | error_kind.into() |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | impl From<io::Error> for Errno { |
| 95 | fn from(value: io::Error) -> Errno { |
| 96 | if cfg!(unix) { |
| 97 | if let Some(errno) = value.raw_os_error() { |
| 98 | return Errno(u16::try_from(errno).unwrap()); |
| 99 | } |
| 100 | } |
| 101 | value.kind().into() |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | impl From<convert::Infallible> for Errno { |
| 106 | fn from(_value: convert::Infallible) -> Errno { |
| 107 | panic!("unreachable") |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | /// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] |
| 112 | /// for the "right" set of types. |
| 113 | mod traits { |
| 114 | use super::Errno; |
| 115 | |
| 116 | /// A signed type that can be converted into an |
| 117 | /// [`io::Result`](std::io::Result) |
| 118 | pub trait GetErrno { |
| 119 | /// Unsigned variant of `Self`, used as the type for the `Ok` case. |
| 120 | type Out; |
| 121 | |
| 122 | /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative |
| 123 | fn into_errno_result(self) -> Result<Self::Out, Errno>; |
| 124 | } |
| 125 | |
| 126 | /// A type that can be taken out of an [`io::Result`](std::io::Result) and |
| 127 | /// converted into "positive success/negative `errno`" convention. |
| 128 | pub trait MergeErrno { |
| 129 | /// Signed variant of `Self`, used as the return type of |
| 130 | /// [`into_neg_errno`](super::into_neg_errno). |
| 131 | type Out: From<u16> + std::ops::Neg<Output = Self::Out>; |
| 132 | |
| 133 | /// Return `self`, asserting that it is in range |
| 134 | fn map_ok(self) -> Self::Out; |
| 135 | } |
| 136 | |
| 137 | macro_rules! get_errno { |
| 138 | ($t:ty, $out:ty) => { |
| 139 | impl GetErrno for $t { |
| 140 | type Out = $out; |
| 141 | fn into_errno_result(self) -> Result<Self::Out, Errno> { |
| 142 | match self { |
| 143 | 0.. => Ok(self as $out), |
| 144 | -65535..=-1 => Err(Errno(-self as u16)), |
| 145 | _ => panic!("{self} is not a negative errno"), |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | }; |
| 150 | } |
| 151 | |
| 152 | get_errno!(i32, u32); |
| 153 | get_errno!(i64, u64); |
| 154 | get_errno!(isize, usize); |
| 155 | |
| 156 | macro_rules! merge_errno { |
| 157 | ($t:ty, $out:ty) => { |
| 158 | impl MergeErrno for $t { |
| 159 | type Out = $out; |
| 160 | fn map_ok(self) -> Self::Out { |
| 161 | self.try_into().unwrap() |
| 162 | } |
| 163 | } |
| 164 | }; |
| 165 | } |
| 166 | |
| 167 | merge_errno!(u8, i32); |
| 168 | merge_errno!(u16, i32); |
| 169 | merge_errno!(u32, i32); |
| 170 | merge_errno!(u64, i64); |
| 171 | |
| 172 | impl MergeErrno for () { |
| 173 | type Out = i32; |
| 174 | fn map_ok(self) -> i32 { |
| 175 | 0 |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | use traits::{GetErrno, MergeErrno}; |
| 181 | |
| 182 | /// Convert an integer value into a [`io::Result`]. |
| 183 | /// |
| 184 | /// Positive values are turned into an `Ok` result; negative values |
| 185 | /// are interpreted as negated `errno` and turned into an `Err`. |
| 186 | /// |
| 187 | /// ``` |
| 188 | /// # use common::errno::into_io_result; |
| 189 | /// # use std::io::ErrorKind; |
| 190 | /// let ok = into_io_result(1i32).unwrap(); |
| 191 | /// assert_eq!(ok, 1u32); |
| 192 | /// |
| 193 | /// let err = into_io_result(-1i32).unwrap_err(); // -EPERM |
| 194 | /// assert_eq!(err.kind(), ErrorKind::PermissionDenied); |
| 195 | /// ``` |
| 196 | /// |
| 197 | /// # Panics |
| 198 | /// |
| 199 | /// Since the result is an unsigned integer, negative values must |
| 200 | /// be close to 0; values that are too far away are considered |
| 201 | /// likely overflows and will panic: |
| 202 | /// |
| 203 | /// ```should_panic |
| 204 | /// # use common::errno::into_io_result; |
| 205 | /// # #[allow(dead_code)] |
| 206 | /// let err = into_io_result(-0x1234_5678i32); // panic |
| 207 | /// ``` |
| 208 | pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> { |
| 209 | value.into_errno_result().map_err(Into::into) |
| 210 | } |
| 211 | |
| 212 | /// Convert a [`Result`] into an integer value, using negative `errno` |
| 213 | /// values to report errors. |
| 214 | /// |
| 215 | /// ``` |
| 216 | /// # use common::errno::into_neg_errno; |
| 217 | /// # use std::io::{self, ErrorKind}; |
| 218 | /// let ok: io::Result<()> = Ok(()); |
| 219 | /// assert_eq!(into_neg_errno(ok), 0); |
| 220 | /// |
| 221 | /// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); |
| 222 | /// assert_eq!(into_neg_errno(err), -22); // -EINVAL |
| 223 | /// ``` |
| 224 | /// |
| 225 | /// Since this module also provides the ability to convert [`io::Error`] |
| 226 | /// to an `errno` value, [`io::Result`] is the most commonly used type |
| 227 | /// for the argument of this function: |
| 228 | /// |
| 229 | /// # Panics |
| 230 | /// |
| 231 | /// Since the result is a signed integer, integer `Ok` values must remain |
| 232 | /// positive: |
| 233 | /// |
| 234 | /// ```should_panic |
| 235 | /// # use common::errno::into_neg_errno; |
| 236 | /// # use std::io; |
| 237 | /// let err: io::Result<u32> = Ok(0x8899_AABB); |
| 238 | /// into_neg_errno(err) // panic |
| 239 | /// # ; |
| 240 | /// ``` |
| 241 | pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> T::Out { |
| 242 | match value { |
| 243 | Ok(x) => x.map_ok(), |
| 244 | Err(err) => -T::Out::from(err.into().0), |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | #[cfg(test)] |
| 249 | mod tests { |
| 250 | use std::io::ErrorKind; |
| 251 | |
| 252 | use super::*; |
| 253 | use crate::assert_match; |
| 254 | |
| 255 | #[test] |
| 256 | pub fn test_from_u8() { |
| 257 | let ok: io::Result<_> = Ok(42u8); |
| 258 | assert_eq!(into_neg_errno(ok), 42); |
| 259 | |
| 260 | let err: io::Result<u8> = Err(io::ErrorKind::PermissionDenied.into()); |
| 261 | assert_eq!(into_neg_errno(err), -1); |
| 262 | |
| 263 | if cfg!(unix) { |
| 264 | let os_err: io::Result<u8> = Err(io::Error::from_raw_os_error(10)); |
| 265 | assert_eq!(into_neg_errno(os_err), -10); |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | #[test] |
| 270 | pub fn test_from_u16() { |
| 271 | let ok: io::Result<_> = Ok(1234u16); |
| 272 | assert_eq!(into_neg_errno(ok), 1234); |
| 273 | |
| 274 | let err: io::Result<u16> = Err(io::ErrorKind::PermissionDenied.into()); |
| 275 | assert_eq!(into_neg_errno(err), -1); |
| 276 | |
| 277 | if cfg!(unix) { |
| 278 | let os_err: io::Result<u16> = Err(io::Error::from_raw_os_error(10)); |
| 279 | assert_eq!(into_neg_errno(os_err), -10); |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | #[test] |
| 284 | pub fn test_i32() { |
| 285 | assert_match!(into_io_result(1234i32), Ok(1234)); |
| 286 | |
| 287 | let err = into_io_result(-1i32).unwrap_err(); |
| 288 | #[cfg(unix)] |
| 289 | assert_match!(err.raw_os_error(), Some(1)); |
| 290 | assert_match!(err.kind(), ErrorKind::PermissionDenied); |
| 291 | } |
| 292 | |
| 293 | #[test] |
| 294 | pub fn test_from_u32() { |
| 295 | let ok: io::Result<_> = Ok(1234u32); |
| 296 | assert_eq!(into_neg_errno(ok), 1234); |
| 297 | |
| 298 | let err: io::Result<u32> = Err(io::ErrorKind::PermissionDenied.into()); |
| 299 | assert_eq!(into_neg_errno(err), -1); |
| 300 | |
| 301 | if cfg!(unix) { |
| 302 | let os_err: io::Result<u32> = Err(io::Error::from_raw_os_error(10)); |
| 303 | assert_eq!(into_neg_errno(os_err), -10); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | #[test] |
| 308 | pub fn test_i64() { |
| 309 | assert_match!(into_io_result(1234i64), Ok(1234)); |
| 310 | |
| 311 | let err = into_io_result(-22i64).unwrap_err(); |
| 312 | #[cfg(unix)] |
| 313 | assert_match!(err.raw_os_error(), Some(22)); |
| 314 | assert_match!(err.kind(), ErrorKind::InvalidInput); |
| 315 | } |
| 316 | |
| 317 | #[test] |
| 318 | pub fn test_from_u64() { |
| 319 | let ok: io::Result<_> = Ok(1234u64); |
| 320 | assert_eq!(into_neg_errno(ok), 1234); |
| 321 | |
| 322 | let err: io::Result<u64> = Err(io::ErrorKind::InvalidInput.into()); |
| 323 | assert_eq!(into_neg_errno(err), -22); |
| 324 | |
| 325 | if cfg!(unix) { |
| 326 | let os_err: io::Result<u64> = Err(io::Error::from_raw_os_error(6)); |
| 327 | assert_eq!(into_neg_errno(os_err), -6); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | #[test] |
| 332 | pub fn test_isize() { |
| 333 | assert_match!(into_io_result(1234isize), Ok(1234)); |
| 334 | |
| 335 | let err = into_io_result(-4isize).unwrap_err(); |
| 336 | #[cfg(unix)] |
| 337 | assert_match!(err.raw_os_error(), Some(4)); |
| 338 | assert_match!(err.kind(), ErrorKind::Interrupted); |
| 339 | } |
| 340 | |
| 341 | #[test] |
| 342 | pub fn test_from_unit() { |
| 343 | let ok: io::Result<_> = Ok(()); |
| 344 | assert_eq!(into_neg_errno(ok), 0); |
| 345 | |
| 346 | let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); |
| 347 | assert_eq!(into_neg_errno(err), -12); |
| 348 | |
| 349 | if cfg!(unix) { |
| 350 | let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); |
| 351 | assert_eq!(into_neg_errno(os_err), -2); |
| 352 | } |
| 353 | } |
| 354 | } |