xref: /openbmc/qemu/rust/common/src/errno.rs (revision ccafa85a97e38698b798115bba6c18c849846e25)
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 }
355