Skip to main content

coreshift_core/
error.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/
4
5//! Shared low-level error types.
6//!
7//! [`CoreError`] is the crate-wide error for Linux and Android primitive
8//! operations. It intentionally stays small: low-level modules surface the
9//! syscall that failed and the raw OS error code, while callers decide how
10//! much policy or recovery to layer on top.
11
12use std::fmt;
13
14/// Error type for low-level system operations.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum CoreError {
17    /// A syscall or libc-style operation failed with the specified OS error.
18    Syscall {
19        /// The raw OS error code.
20        code: i32,
21        /// The name of the failed operation.
22        op: &'static str,
23    },
24    /// An NDK binder operation failed with the specified status code.
25    Binder {
26        /// The NDK binder status code (`binder_status_t`).
27        code: i32,
28        /// The name of the failed operation.
29        op: &'static str,
30    },
31}
32
33impl CoreError {
34    /// Construct a new syscall error.
35    pub fn sys(code: i32, op: &'static str) -> Self {
36        Self::Syscall { code, op }
37    }
38
39    /// Construct a new binder error.
40    pub fn binder(code: i32, op: &'static str) -> Self {
41        Self::Binder { code, op }
42    }
43
44    /// Return the raw OS error code if applicable.
45    pub fn raw_os_error(&self) -> Option<i32> {
46        match self {
47            Self::Syscall { code, .. } => Some(*code),
48            Self::Binder { .. } => None,
49        }
50    }
51
52    /// Convert this low-level error into a standard I/O error.
53    pub fn to_io_error(&self) -> std::io::Error {
54        std::io::Error::from_raw_os_error(self.raw_os_error().unwrap_or(libc::EIO))
55    }
56}
57
58impl From<std::io::Error> for CoreError {
59    fn from(e: std::io::Error) -> Self {
60        Self::sys(
61            e.raw_os_error().unwrap_or(libc::EIO),
62            match e.kind() {
63                std::io::ErrorKind::NotFound => "io:not_found",
64                std::io::ErrorKind::PermissionDenied => "io:permission_denied",
65                std::io::ErrorKind::AlreadyExists => "io:already_exists",
66                std::io::ErrorKind::InvalidInput => "io:invalid_input",
67                _ => "io",
68            },
69        )
70    }
71}
72
73impl fmt::Display for CoreError {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Self::Syscall { code, op } => write!(f, "{op} failed (code={code})"),
77            Self::Binder { code, op } => write!(f, "binder {op} failed (status={code})"),
78        }
79    }
80}
81
82impl std::error::Error for CoreError {}
83
84/// Common errno constants, re-exported so callers need not depend on `libc`.
85pub mod errno {
86    pub const EADDRINUSE: i32 = libc::EADDRINUSE;
87    pub const EPIPE: i32 = libc::EPIPE;
88    pub const EAGAIN: i32 = libc::EAGAIN;
89    pub const EINTR: i32 = libc::EINTR;
90    pub const ENOENT: i32 = libc::ENOENT;
91    pub const EACCES: i32 = libc::EACCES;
92}
93
94#[inline(always)]
95pub(crate) fn syscall_ret(ret: i32, op: &'static str) -> Result<(), CoreError> {
96    if ret == -1 {
97        let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
98        Err(CoreError::sys(code, op))
99    } else {
100        Ok(())
101    }
102}
103
104#[inline(always)]
105pub(crate) fn posix_ret(ret: i32, op: &'static str) -> Result<(), CoreError> {
106    if ret != 0 {
107        Err(CoreError::sys(ret, op))
108    } else {
109        Ok(())
110    }
111}