Skip to main content

arcbox_fs/
error.rs

1//! Error types for the filesystem service.
2
3use arcbox_error::CommonError;
4use thiserror::Error;
5
6/// Result type alias for filesystem operations.
7pub type Result<T> = std::result::Result<T, FsError>;
8
9/// Errors that can occur during filesystem operations.
10#[derive(Debug, Error)]
11pub enum FsError {
12    /// Common errors shared across `ArcBox` crates.
13    #[error(transparent)]
14    Common(#[from] CommonError),
15
16    /// Invalid path.
17    #[error("invalid path: {0}")]
18    InvalidPath(String),
19
20    /// Operation not supported.
21    #[error("operation not supported: {0}")]
22    NotSupported(String),
23
24    /// FUSE protocol error.
25    #[error("FUSE error: {0}")]
26    Fuse(String),
27
28    /// Cache error.
29    #[error("cache error: {0}")]
30    Cache(String),
31
32    /// Invalid file handle.
33    #[error("invalid file handle: {0}")]
34    InvalidHandle(u64),
35}
36
37impl From<std::io::Error> for FsError {
38    fn from(err: std::io::Error) -> Self {
39        Self::Common(CommonError::from(err))
40    }
41}
42
43impl FsError {
44    /// Creates an I/O error.
45    #[must_use]
46    pub fn io(err: std::io::Error) -> Self {
47        Self::Common(CommonError::Io(err))
48    }
49
50    /// Creates a not found error.
51    #[must_use]
52    pub fn not_found(path: impl Into<String>) -> Self {
53        Self::Common(CommonError::not_found(path))
54    }
55
56    /// Creates a permission denied error.
57    #[must_use]
58    pub fn permission_denied(path: impl Into<String>) -> Self {
59        Self::Common(CommonError::permission_denied(path))
60    }
61
62    /// Returns true if this is a not found error.
63    #[must_use]
64    pub fn is_not_found(&self) -> bool {
65        matches!(self, Self::Common(CommonError::NotFound(_)))
66    }
67
68    /// Converts the error to a POSIX errno.
69    #[must_use]
70    pub fn to_errno(&self) -> i32 {
71        match self {
72            Self::Common(e) => Self::common_to_errno(e),
73            Self::InvalidPath(_) => libc::EINVAL,
74            Self::NotSupported(_) => libc::ENOSYS,
75            Self::Fuse(_) | Self::Cache(_) => libc::EIO,
76            Self::InvalidHandle(_) => libc::EBADF,
77        }
78    }
79
80    /// Converts a CommonError to a Linux-compatible POSIX errno.
81    fn common_to_errno(err: &CommonError) -> i32 {
82        match err {
83            CommonError::Io(e) => {
84                let raw = e.raw_os_error().unwrap_or(libc::EIO);
85                Self::host_errno_to_linux(raw)
86            }
87            CommonError::NotFound(_) => libc::ENOENT,
88            CommonError::PermissionDenied(_) => libc::EACCES,
89            CommonError::AlreadyExists(_) => libc::EEXIST,
90            CommonError::Config(_)
91            | CommonError::InvalidState(_)
92            | CommonError::Timeout(_)
93            | CommonError::Internal(_) => libc::EIO,
94        }
95    }
96
97    /// Translates a host (macOS) errno to a Linux errno.
98    ///
99    /// macOS and Linux share many POSIX errno values (1-34) but diverge
100    /// for platform-specific codes. The FUSE response must use Linux
101    /// errno numbers because the guest kernel interprets them directly.
102    #[cfg(target_os = "macos")]
103    fn host_errno_to_linux(errno: i32) -> i32 {
104        // macOS errno constants that differ from Linux:
105        // macOS ENOATTR (93) → Linux ENODATA (61)
106        // macOS ENOPOLICY (103) → Linux EIO
107        // macOS EQFULL (106) → Linux EIO
108        const MACOS_ENOATTR: i32 = 93;
109        const LINUX_ENODATA: i32 = 61;
110
111        match errno {
112            MACOS_ENOATTR => LINUX_ENODATA,
113            _ => errno,
114        }
115    }
116
117    #[cfg(not(target_os = "macos"))]
118    fn host_errno_to_linux(errno: i32) -> i32 {
119        errno
120    }
121}