[pbs-devel] [PATCH v2 proxmox-backup 3/5] pbs-client: pxar: add PxarExtractContext
Max Carrara
m.carrara at proxmox.com
Wed Jul 12 15:00:42 CEST 2023
This enum's purpose is to provide context to errors that occur during
the extraction of a pxar archive, making it possible to handle
extraction errors in a more granular manner.
For now, it's only implemented in `ExtractorIter::next()`, but may be
used in other places if necessary or desired.
Signed-off-by: Max Carrara <m.carrara at proxmox.com>
---
Changes v1 --> v2:
* None
pbs-client/src/pxar/extract.rs | 98 ++++++++++++++++++++++++++++++----
pbs-client/src/pxar/mod.rs | 2 +-
2 files changed, 90 insertions(+), 10 deletions(-)
diff --git a/pbs-client/src/pxar/extract.rs b/pbs-client/src/pxar/extract.rs
index bc51d4e8..4dbaf52d 100644
--- a/pbs-client/src/pxar/extract.rs
+++ b/pbs-client/src/pxar/extract.rs
@@ -185,6 +185,8 @@ where
/// [`ErrorHandler`] provided by the [`PxarExtractOptions`] used to
/// initialize the iterator.
///
+ /// Extraction errors will have a corresponding [`PxarExtractContext`] attached.
+ ///
/// [E]: pxar::Entry
/// [D]: pxar::decoder::Decoder
fn next(&mut self) -> Option<Self::Item> {
@@ -252,11 +254,10 @@ where
self.callback(entry.path());
let create = self.state.current_match && match_result != Some(MatchType::Exclude);
- let res = self.extractor.enter_directory(
- file_name_os.to_owned(),
- metadata.clone(),
- create,
- );
+ let res = self
+ .extractor
+ .enter_directory(file_name_os.to_owned(), metadata.clone(), create)
+ .context(PxarExtractContext::EnterDirectory);
if res.is_ok() {
// We're starting a new directory, push our old matching state and replace it with
@@ -281,7 +282,8 @@ where
.pop()
.context("unexpected end of directory")
.map(|path| self.extractor.set_path(path))
- .and(self.extractor.leave_directory());
+ .and(self.extractor.leave_directory())
+ .context(PxarExtractContext::LeaveDirectory);
if res.is_ok() {
// We left a directory, also get back our previous matching state. This is in sync
@@ -296,16 +298,20 @@ where
self.callback(entry.path());
self.extractor
.extract_symlink(&file_name, metadata, link.as_ref())
+ .context(PxarExtractContext::ExtractSymlink)
}
(true, EntryKind::Hardlink(link)) => {
self.callback(entry.path());
self.extractor
.extract_hardlink(&file_name, link.as_os_str())
+ .context(PxarExtractContext::ExtractHardlink)
}
(true, EntryKind::Device(dev)) => {
if self.extractor.contains_flags(Flags::WITH_DEVICE_NODES) {
self.callback(entry.path());
- self.extractor.extract_device(&file_name, metadata, dev)
+ self.extractor
+ .extract_device(&file_name, metadata, dev)
+ .context(PxarExtractContext::ExtractDevice)
} else {
Ok(())
}
@@ -313,7 +319,9 @@ where
(true, EntryKind::Fifo) => {
if self.extractor.contains_flags(Flags::WITH_FIFOS) {
self.callback(entry.path());
- self.extractor.extract_special(&file_name, metadata, 0)
+ self.extractor
+ .extract_special(&file_name, metadata, 0)
+ .context(PxarExtractContext::ExtractFifo)
} else {
Ok(())
}
@@ -321,7 +329,9 @@ where
(true, EntryKind::Socket) => {
if self.extractor.contains_flags(Flags::WITH_SOCKETS) {
self.callback(entry.path());
- self.extractor.extract_special(&file_name, metadata, 0)
+ self.extractor
+ .extract_special(&file_name, metadata, 0)
+ .context(PxarExtractContext::ExtractSocket)
} else {
Ok(())
}
@@ -342,6 +352,7 @@ where
"found regular file entry without contents in archive"
))
}
+ .context(PxarExtractContext::ExtractFile)
}
(false, _) => Ok(()), // skip this
};
@@ -354,6 +365,75 @@ where
}
}
+/// Provides additional [context][C] for [`anyhow::Error`]s that are returned
+/// while traversing an [`ExtractorIter`]. The [`PxarExtractContext`] can then
+/// be accessed [via `anyhow`'s facilities][A] and may aid during error handling.
+///
+///
+/// # Example
+///
+/// ```
+/// # use anyhow::{anyhow, Error};
+/// # use std::io;
+/// # use pbs_client::pxar::PxarExtractContext;
+///
+/// let err = anyhow!("oh noes!").context(PxarExtractContext::ExtractFile);
+///
+/// if let Some(ctx) = err.downcast_ref::<PxarExtractContext>() {
+/// match ctx {
+/// PxarExtractContext::ExtractFile => {
+/// // Conditionally handle the underlying error by type
+/// if let Some(io_err) = err.downcast_ref::<io::Error>() {
+/// // ...
+/// };
+/// },
+/// PxarExtractContext::ExtractSocket => {
+/// // ...
+/// },
+/// // ...
+/// # _ => (),
+/// }
+/// }
+/// ```
+///
+/// [A]: anyhow::Error
+/// [C]: anyhow::Context
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum PxarExtractContext {
+ EnterDirectory,
+ LeaveDirectory,
+ ExtractSymlink,
+ ExtractHardlink,
+ ExtractDevice,
+ ExtractFifo,
+ ExtractSocket,
+ ExtractFile,
+}
+
+impl PxarExtractContext {
+ #[inline]
+ pub fn as_str(&self) -> &'static str {
+ use PxarExtractContext::*;
+
+ match *self {
+ EnterDirectory => "failed to enter directory",
+ LeaveDirectory => "failed to leave directory",
+ ExtractSymlink => "failed to extract symlink",
+ ExtractHardlink => "failed to extract hardlink",
+ ExtractDevice => "failed to extract device",
+ ExtractFifo => "failed to extract named pipe",
+ ExtractSocket => "failed to extract unix socket",
+ ExtractFile => "failed to extract file",
+ }
+ }
+}
+
+impl std::fmt::Display for PxarExtractContext {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
/// Common state for file extraction.
pub struct Extractor {
feature_flags: Flags,
diff --git a/pbs-client/src/pxar/mod.rs b/pbs-client/src/pxar/mod.rs
index a158101d..b042717d 100644
--- a/pbs-client/src/pxar/mod.rs
+++ b/pbs-client/src/pxar/mod.rs
@@ -59,7 +59,7 @@ pub use flags::Flags;
pub use create::{create_archive, PxarCreateOptions};
pub use extract::{
create_tar, create_zip, extract_archive, extract_sub_dir, extract_sub_dir_seq, ErrorHandler,
- PxarExtractOptions,
+ PxarExtractContext, PxarExtractOptions,
};
/// The format requires to build sorted directory lookup tables in
--
2.39.2
More information about the pbs-devel
mailing list