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