1use ninep_client::ClientError;
2use ninep_proto::P9_ENOTLEADER;
3
4#[derive(Debug, Clone)]
11pub enum ZeroFsError {
12 NotFound {
14 path: String,
16 },
17 PermissionDenied {
19 path: String,
21 },
22 NotPermitted {
25 path: String,
27 },
28 AlreadyExists {
30 path: String,
32 },
33 NotADirectory {
35 path: String,
37 },
38 IsADirectory {
40 path: String,
42 },
43 DirectoryNotEmpty {
45 path: String,
47 },
48 NameTooLong {
50 name: String,
52 },
53 InvalidArgument {
56 message: String,
58 },
59 TooManySymlinks {
61 path: String,
63 },
64 Closed,
66 ConnectFailed {
68 message: String,
70 },
71 NotLeader {
73 path: String,
75 },
76 Stale {
79 path: String,
81 },
82 Io {
84 errno: i32,
86 path: String,
88 message: String,
90 },
91 Protocol {
93 message: String,
95 },
96}
97
98impl std::fmt::Display for ZeroFsError {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 match self {
101 Self::NotFound { path } => write!(f, "not found: {path}"),
102 Self::PermissionDenied { path } => write!(f, "permission denied: {path}"),
103 Self::NotPermitted { path } => write!(f, "operation not permitted: {path}"),
104 Self::AlreadyExists { path } => write!(f, "already exists: {path}"),
105 Self::NotADirectory { path } => write!(f, "not a directory: {path}"),
106 Self::IsADirectory { path } => write!(f, "is a directory: {path}"),
107 Self::DirectoryNotEmpty { path } => write!(f, "directory not empty: {path}"),
108 Self::NameTooLong { name } => write!(f, "name too long: {name}"),
109 Self::InvalidArgument { message } => write!(f, "invalid argument: {message}"),
110 Self::TooManySymlinks { path } => {
111 write!(f, "too many levels of symbolic links: {path}")
112 }
113 Self::Closed => write!(f, "handle is closed"),
114 Self::ConnectFailed { message } => write!(f, "connection failed: {message}"),
115 Self::NotLeader { path } => write!(f, "not the leader (re-route): {path}"),
116 Self::Stale { path } => {
117 write!(
118 f,
119 "stale handle (fsync: prior writes may not be durable): {path}"
120 )
121 }
122 Self::Io {
123 errno,
124 path,
125 message,
126 } => write!(f, "i/o error (errno {errno}): {path}: {message}"),
127 Self::Protocol { message } => write!(f, "protocol error: {message}"),
128 }
129 }
130}
131
132impl std::error::Error for ZeroFsError {}
133
134impl From<ZeroFsError> for std::io::Error {
138 fn from(e: ZeroFsError) -> Self {
139 let kind = std::io::Error::from_raw_os_error(e.to_errno()).kind();
140 std::io::Error::new(kind, e)
141 }
142}
143
144impl ZeroFsError {
145 pub fn to_errno(&self) -> i32 {
147 match self {
148 Self::NotFound { .. } => crate::linux::ENOENT,
149 Self::PermissionDenied { .. } => crate::linux::EACCES,
150 Self::NotPermitted { .. } => crate::linux::EPERM,
151 Self::AlreadyExists { .. } => crate::linux::EEXIST,
152 Self::NotADirectory { .. } => crate::linux::ENOTDIR,
153 Self::IsADirectory { .. } => crate::linux::EISDIR,
154 Self::DirectoryNotEmpty { .. } => crate::linux::ENOTEMPTY,
155 Self::NameTooLong { .. } => crate::linux::ENAMETOOLONG,
156 Self::InvalidArgument { .. } => crate::linux::EINVAL,
157 Self::TooManySymlinks { .. } => crate::linux::ELOOP,
158 Self::Closed => crate::linux::EBADF,
159 Self::ConnectFailed { .. } => crate::linux::EIO,
160 Self::NotLeader { .. } => P9_ENOTLEADER as i32,
161 Self::Stale { .. } => crate::linux::ESTALE,
162 Self::Io { errno, .. } => *errno,
163 Self::Protocol { .. } => crate::linux::EIO,
164 }
165 }
166
167 pub(crate) fn from_errno(errno: i32, path: &str) -> Self {
169 let path = path.to_string();
170 match errno {
171 crate::linux::ENOENT => Self::NotFound { path },
172 crate::linux::EACCES => Self::PermissionDenied { path },
173 crate::linux::EPERM => Self::NotPermitted { path },
174 crate::linux::EEXIST => Self::AlreadyExists { path },
175 crate::linux::ENOTDIR => Self::NotADirectory { path },
176 crate::linux::EISDIR => Self::IsADirectory { path },
177 crate::linux::ENOTEMPTY => Self::DirectoryNotEmpty { path },
178 crate::linux::ENAMETOOLONG => Self::NameTooLong { name: path },
179 crate::linux::EINVAL => Self::InvalidArgument {
180 message: format!("{path}: invalid argument"),
181 },
182 crate::linux::ELOOP => Self::TooManySymlinks { path },
183 crate::linux::ESTALE => Self::Stale { path },
184 c if c == P9_ENOTLEADER as i32 => Self::NotLeader { path },
185 errno => Self::Io {
186 message: std::io::Error::from_raw_os_error(errno).to_string(),
187 errno,
188 path,
189 },
190 }
191 }
192
193 pub(crate) fn from_client(e: &ClientError, path: &str) -> Self {
197 match e {
198 ClientError::Errno(code) => Self::from_errno(*code as i32, path),
199 other => Self::Protocol {
200 message: format!("{path}: {other}"),
201 },
202 }
203 }
204}
205
206pub(crate) trait ClientResultExt<T> {
208 fn ctx(self, path: &str) -> Result<T, ZeroFsError>;
209}
210
211impl<T> ClientResultExt<T> for Result<T, ClientError> {
212 fn ctx(self, path: &str) -> Result<T, ZeroFsError> {
213 self.map_err(|e| ZeroFsError::from_client(&e, path))
214 }
215}