master
rs 354 lines 10.7 KB
Raw
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 }