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 CD6106A57A for ; Tue, 16 Feb 2021 18:08:05 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id DCF2018E9E for ; Tue, 16 Feb 2021 18:07:34 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (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 id 6798018DE2 for ; Tue, 16 Feb 2021 18:07:30 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 2114F4622F for ; Tue, 16 Feb 2021 18:07:30 +0100 (CET) From: Stefan Reiter To: pbs-devel@lists.proxmox.com Date: Tue, 16 Feb 2021 18:06:55 +0100 Message-Id: <20210216170710.31767-8-s.reiter@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210216170710.31767-1-s.reiter@proxmox.com> References: <20210216170710.31767-1-s.reiter@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.029 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [extract.rs, mod.rs] Subject: [pbs-devel] [PATCH proxmox-backup 07/22] pxar/extract: add extract_sub_dir 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: Tue, 16 Feb 2021 17:08:05 -0000 From: Dominik Csapak to extract some subdirectory of a pxar into a given target this will be used in the client Signed-off-by: Dominik Csapak Signed-off-by: Stefan Reiter --- src/pxar/extract.rs | 122 ++++++++++++++++++++++++++++++++++++++++++++ src/pxar/mod.rs | 2 +- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/pxar/extract.rs b/src/pxar/extract.rs index d246e7ec..b673b4b8 100644 --- a/src/pxar/extract.rs +++ b/src/pxar/extract.rs @@ -583,3 +583,125 @@ where }) } + +pub async fn extract_sub_dir( + destination: DEST, + mut decoder: Accessor, + path: PATH, + verbose: bool, +) -> Result<(), Error> +where + T: Clone + pxar::accessor::ReadAt + Unpin + Send + Sync + 'static, + DEST: AsRef, + PATH: AsRef, +{ + let root = decoder.open_root().await?; + + create_path( + &destination, + None, + Some(CreateOptions::new().perm(Mode::from_bits_truncate(0o700))), + ) + .map_err(|err| format_err!("error creating directory {:?}: {}", destination.as_ref(), err))?; + + let dir = Dir::open( + destination.as_ref(), + OFlag::O_DIRECTORY | OFlag::O_CLOEXEC, + Mode::empty(), + ) + .map_err(|err| format_err!("unable to open target directory {:?}: {}", destination.as_ref(), err,))?; + + let mut extractor = Extractor::new( + dir, + root.lookup_self().await?.entry().metadata().clone(), + false, + Flags::DEFAULT, + ); + + let file = root + .lookup(&path).await? + .ok_or(format_err!("error opening '{:?}'", path.as_ref()))?; + + recurse_files_extractor(&mut extractor, &mut decoder, file, verbose).await +} + +fn recurse_files_extractor<'a, T>( + extractor: &'a mut Extractor, + decoder: &'a mut Accessor, + file: FileEntry, + verbose: bool, +) -> Pin> + Send + 'a>> +where + T: Clone + pxar::accessor::ReadAt + Unpin + Send + Sync + 'static, +{ + use pxar::EntryKind; + Box::pin(async move { + let metadata = file.entry().metadata(); + let file_name_os = file.file_name(); + + // safety check: a file entry in an archive must never contain slashes: + if file_name_os.as_bytes().contains(&b'/') { + bail!("archive file entry contains slashes, which is invalid and a security concern"); + } + + let file_name = CString::new(file_name_os.as_bytes()) + .map_err(|_| format_err!("encountered file name with null-bytes"))?; + + if verbose { + eprintln!("extracting: {}", file.path().display()); + } + + match file.kind() { + EntryKind::Directory => { + extractor + .enter_directory(file_name_os.to_owned(), metadata.clone(), true) + .map_err(|err| format_err!("error at entry {:?}: {}", file_name_os, err))?; + + let dir = file.enter_directory().await?; + let mut readdir = dir.read_dir(); + while let Some(entry) = readdir.next().await { + let entry = entry?.decode_entry().await?; + let filename = entry.path().to_path_buf(); + + // log errors and continue + if let Err(err) = recurse_files_extractor(extractor, decoder, entry, verbose).await { + eprintln!("error extracting {:?}: {}", filename.display(), err); + } + } + extractor.leave_directory()?; + } + EntryKind::Symlink(link) => { + extractor.extract_symlink(&file_name, metadata, link.as_ref())?; + } + EntryKind::Hardlink(link) => { + extractor.extract_hardlink(&file_name, link.as_os_str())?; + } + EntryKind::Device(dev) => { + if extractor.contains_flags(Flags::WITH_DEVICE_NODES) { + extractor.extract_device(&file_name, metadata, dev)?; + } + } + EntryKind::Fifo => { + if extractor.contains_flags(Flags::WITH_FIFOS) { + extractor.extract_special(&file_name, metadata, 0)?; + } + } + EntryKind::Socket => { + if extractor.contains_flags(Flags::WITH_SOCKETS) { + extractor.extract_special(&file_name, metadata, 0)?; + } + } + EntryKind::File { size, .. } => extractor.async_extract_file( + &file_name, + metadata, + *size, + &mut file.contents().await.map_err(|_| { + format_err!("found regular file entry without contents in archive") + })?, + ).await?, + EntryKind::GoodbyeTable => {}, // ignore + } + Ok(()) + }) +} + diff --git a/src/pxar/mod.rs b/src/pxar/mod.rs index e2632653..d1302962 100644 --- a/src/pxar/mod.rs +++ b/src/pxar/mod.rs @@ -59,7 +59,7 @@ mod flags; pub use flags::Flags; pub use create::{create_archive, PxarCreateOptions}; -pub use extract::{create_zip, extract_archive, ErrorHandler, PxarExtractOptions}; +pub use extract::{create_zip, extract_archive, extract_sub_dir, ErrorHandler, PxarExtractOptions}; /// The format requires to build sorted directory lookup tables in /// memory, so we restrict the number of allowed entries to limit -- 2.20.1