From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 51BE2D90D for ; Mon, 17 Jul 2023 10:04:47 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 32FC5AA51 for ; Mon, 17 Jul 2023 10:04:17 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Mon, 17 Jul 2023 10:04:16 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 20818425E3 for ; Mon, 17 Jul 2023 10:04:16 +0200 (CEST) From: Max Carrara To: pbs-devel@lists.proxmox.com Date: Mon, 17 Jul 2023 10:04:08 +0200 Message-Id: <20230717080410.429362-4-m.carrara@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230717080410.429362-1-m.carrara@proxmox.com> References: <20230717080410.429362-1-m.carrara@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.011 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pbs-devel] [PATCH v3 proxmox-backup 3/5] pbs-client: pxar: add PxarExtractContext X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 17 Jul 2023 08:04:47 -0000 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 --- Changes v1 --> v2: * None Changes v2 --> v3: * 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 { @@ -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::() { +/// match ctx { +/// PxarExtractContext::ExtractFile => { +/// // Conditionally handle the underlying error by type +/// if let Some(io_err) = err.downcast_ref::() { +/// // ... +/// }; +/// }, +/// 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