use mountpoint_s3_client::error::{GetObjectError, ObjectClientError};
use tracing::Level;
use crate::fs::error_metadata::ErrorMetadata;
use crate::metablock::InodeError;
use crate::prefetch::PrefetchReadError;
use crate::upload::UploadError;
#[macro_export]
macro_rules! err {
($errno:expr, __source:$source:expr, $level:expr, $metadata:expr, $message:literal, $($args:tt)*) => {
Error {
errno: $errno,
message: format!($message, $($args)*),
source: $source,
level: $level,
metadata: $metadata,
}
};
($errno:expr, source:$source:expr, $level:expr, metadata:$metadata:expr, $message:literal) => {
err!($errno, __source:Some(::anyhow::Error::new($source)), $level, $metadata, $message, )
};
($errno:expr, source:$source:expr, $level:expr, $message:literal, $($args:tt)*) => {
err!($errno, __source:Some(::anyhow::Error::new($source)), $level, Default::default(), $message, $($args)*)
};
($errno:expr, source:$source:expr, $level:expr, $message:literal) => {
err!($errno, __source:Some(::anyhow::Error::new($source)), $level, Default::default(), $message,)
};
($errno:expr, source:$source:expr, $message:literal, $($args:tt)*) => {
err!($errno, __source:Some(::anyhow::Error::new($source)), ::tracing::Level::WARN, Default::default(), $message, $($args)*)
};
($errno:expr, source:$source:expr, $message:literal) => {
err!($errno, __source:Some(::anyhow::Error::new($source)), ::tracing::Level::WARN, Default::default(), $message,)
};
($errno:expr, $message:literal, $($args:tt)*) => {
err!($errno, __source:None, ::tracing::Level::WARN, Default::default(), $message, $($args)*)
};
($errno:expr, $message:literal) => {
err!($errno, __source:None, ::tracing::Level::WARN, Default::default(), $message,)
};
}
#[derive(Debug, thiserror::Error)]
pub struct Error {
pub(crate) errno: libc::c_int,
pub(crate) message: String,
pub(crate) source: Option<anyhow::Error>,
pub(crate) level: Level,
pub(crate) metadata: ErrorMetadata,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(source) = self.source.as_ref() {
write!(f, "{}: {:#}", self.message, source)
} else {
write!(f, "{}", self.message)
}
}
}
impl From<InodeError> for Error {
fn from(err: InodeError) -> Self {
let errno = err.to_errno();
let metadata = err.meta().clone();
Error {
errno,
message: String::from("inode error"),
source: Some(anyhow::anyhow!(err)),
level: Level::WARN,
metadata,
}
}
}
impl<E: std::error::Error + Send + Sync + 'static> From<UploadError<E>> for Error {
fn from(err: UploadError<E>) -> Self {
let errno = err.to_errno();
Error {
errno,
message: String::from("upload error"),
source: Some(anyhow::anyhow!(err)),
level: Level::WARN,
metadata: Default::default(), }
}
}
impl<E: std::error::Error + Send + Sync + 'static> From<PrefetchReadError<E>> for Error {
fn from(err: PrefetchReadError<E>) -> Self {
match err {
PrefetchReadError::GetRequestFailed {
source: ObjectClientError::ServiceError(GetObjectError::PreconditionFailed(_)),
metadata,
} => err!(libc::ESTALE, __source:None, Level::WARN, (*metadata).clone(), "object was mutated remotely",),
PrefetchReadError::GetRequestFailed { source, metadata } => {
err!(libc::EIO, source:source, Level::WARN, metadata:(*metadata).clone(), "get request failed")
}
PrefetchReadError::Integrity(e) => err!(libc::EIO, source:e, "integrity error"),
PrefetchReadError::PartReadFailed(e) => err!(libc::EIO, source:e, "part read failed"),
PrefetchReadError::GetRequestTerminatedUnexpectedly
| PrefetchReadError::GetRequestReturnedWrongOffset { .. }
| PrefetchReadError::BackpressurePreconditionFailed
| PrefetchReadError::ReadWindowIncrement => {
err!(libc::EIO, source:err, "get request failed")
}
}
}
}
pub trait ToErrno {
fn to_errno(&self) -> libc::c_int;
}
impl ToErrno for Error {
fn to_errno(&self) -> libc::c_int {
self.errno
}
}
impl ToErrno for InodeError {
fn to_errno(&self) -> libc::c_int {
match self {
InodeError::ClientError { .. } => libc::EIO,
InodeError::FileDoesNotExist(_, _) => libc::ENOENT,
InodeError::InodeDoesNotExist(_) => libc::ENOENT,
InodeError::InvalidFileName(_) => libc::EINVAL,
InodeError::NotADirectory(_) => libc::ENOTDIR,
InodeError::IsDirectory(_) => libc::EISDIR,
InodeError::FileAlreadyExists(_) => libc::EEXIST,
InodeError::InodeNotWritable(_) => libc::EPERM,
InodeError::InodeInvalidWriteStatus(_) => libc::EPERM,
InodeError::InodeAlreadyWriting(_) => libc::EPERM,
InodeError::InodeNotReadableWhileWriting(_) => libc::EPERM,
InodeError::InodeNotWritableWhileReading(_) => libc::EPERM,
InodeError::CannotRemoveRemoteDirectory(_) => libc::EPERM,
InodeError::DirectoryNotEmpty(_) => libc::ENOTEMPTY,
InodeError::UnlinkNotPermittedWhileWriting(_) => libc::EPERM,
InodeError::CorruptedMetadata(_) => libc::EIO,
InodeError::SetAttrNotPermittedOnRemoteInode(_) => libc::EPERM,
InodeError::StaleInode { .. } => libc::ESTALE,
InodeError::CannotRenameDirectory(_) => libc::EPERM,
InodeError::RenameDestinationExists { .. } => libc::EEXIST,
InodeError::RenameNotPermittedWhileWriting(_) => libc::EPERM,
InodeError::RenameNotSupported() => libc::ENOSYS,
InodeError::NameTooLong(_) => libc::ENAMETOOLONG,
#[cfg(feature = "manifest")]
InodeError::ManifestError { .. } => libc::EIO,
InodeError::OperationNotSupportedOnSyntheticInode { .. } => libc::EIO,
InodeError::OutOfOrderReadDir { .. } => libc::EBADF,
InodeError::NoSuchDirHandle { .. } => libc::EINVAL,
InodeError::FlexibleRetrievalObjectNotAccessible(_) => libc::EACCES,
}
}
}
impl<E: std::error::Error> ToErrno for UploadError<E> {
fn to_errno(&self) -> libc::c_int {
match self {
UploadError::PutRequestFailed(_) => libc::EIO,
UploadError::UploadAlreadyTerminated => libc::EIO,
UploadError::SseCorruptedError(_) => libc::EIO,
UploadError::ChecksumComputationFailed(_) => libc::EIO,
UploadError::HeadObjectFailed(_) => libc::EIO,
UploadError::OutOfOrderWrite { .. } => libc::EINVAL,
UploadError::ObjectTooBig { .. } => libc::EFBIG,
}
}
}
impl Error {
pub fn meta(&self) -> &ErrorMetadata {
&self.metadata
}
}