From a0ec687cd76379bc35b3eb8997f6c46ca397bbf6 Mon Sep 17 00:00:00 2001 From: Christian Ebner Date: Thu, 18 Jul 2019 11:35:51 +0200 Subject: [PATCH] src/bin/pxar.rs: allow to pass paths and match patterns as args to pxar extract To improve usability it is now possible to directly pass paths or match patterns as arguments to pxar extract to partially restore an archive. The patterns provided via CLI are appended to the ones read from file by the --files-from option in order to have priority over those. Signed-off-by: Christian Ebner --- src/bin/pxar.rs | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/bin/pxar.rs b/src/bin/pxar.rs index 492f5603..a6025fd6 100644 --- a/src/bin/pxar.rs +++ b/src/bin/pxar.rs @@ -12,6 +12,7 @@ use serde_json::{Value}; use std::io::Write; use std::path::{Path, PathBuf}; use std::fs::OpenOptions; +use std::sync::Arc; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::AsRawFd; @@ -91,6 +92,8 @@ fn extract_archive( let no_fcaps = param["no-fcaps"].as_bool().unwrap_or(false); let no_acls = param["no-acls"].as_bool().unwrap_or(false); let files_from = param["files-from"].as_str(); + let empty = Vec::new(); + let arg_pattern = param["pattern"].as_array().unwrap_or(&empty); let mut feature_flags = pxar::CA_FORMAT_DEFAULT; if no_xattrs { @@ -103,15 +106,25 @@ fn extract_archive( feature_flags ^= pxar::CA_FORMAT_WITH_ACL; } - let pattern = match files_from { - Some(filename) => { - let dir = nix::dir::Dir::open("./", nix::fcntl::OFlag::O_RDONLY, nix::sys::stat::Mode::empty())?; - let fd = dir.as_raw_fd(); + let mut pattern_list = Vec::new(); + if let Some(filename) = files_from { + let dir = nix::dir::Dir::open("./", nix::fcntl::OFlag::O_RDONLY, nix::sys::stat::Mode::empty())?; + if let Some((mut pattern, _, _)) = pxar::PxarExcludePattern::from_file(dir.as_raw_fd(), filename)? { + pattern_list.append(&mut pattern); + } + } - pxar::PxarExcludePattern::from_file(fd, filename)? - .and_then(|(pattern, _, _)| Some(pattern)) - }, - None => None, + for s in arg_pattern { + let l = s.as_str().ok_or_else(|| format_err!("Invalid pattern string slice"))?; + let p = pxar::PxarExcludePattern::from_line(l.as_bytes())? + .ok_or_else(|| format_err!("Invalid match pattern in arguments"))?; + pattern_list.push(p); + } + + let pattern = if pattern_list.len() > 0 { + Some(pattern_list) + } else { + None }; if archive == "-" { @@ -186,25 +199,31 @@ fn main() { .optional("no-fcaps", BooleanSchema::new("Ignore file capabilities.").default(false)) .optional("no-acls", BooleanSchema::new("Ignore access control list entries.").default(false)) .optional("all-file-systems", BooleanSchema::new("Include mounted sudirs.").default(false)) - )) + )) .arg_param(vec!["archive", "source"]) .completion_cb("archive", tools::complete_file_name) .completion_cb("source", tools::complete_file_name) - .into() + .into() ) .insert("extract", CliCommand::new( ApiMethod::new( extract_archive, ObjectSchema::new("Extract an archive.") .required("archive", StringSchema::new("Archive name.")) + .optional("pattern", Arc::new( + ArraySchema::new( + "List of paths or pattern matching files to restore", + Arc::new(StringSchema::new("Path or pattern matching files to restore.").into()) + ).into() + )) .optional("target", StringSchema::new("Target directory.")) .optional("verbose", BooleanSchema::new("Verbose output.").default(false)) .optional("no-xattrs", BooleanSchema::new("Ignore extended file attributes.").default(false)) .optional("no-fcaps", BooleanSchema::new("Ignore file capabilities.").default(false)) .optional("no-acls", BooleanSchema::new("Ignore access control list entries.").default(false)) .optional("files-from", StringSchema::new("Match pattern for files to restore.")) - )) - .arg_param(vec!["archive"]) + )) + .arg_param(vec!["archive", "pattern"]) .completion_cb("archive", tools::complete_file_name) .completion_cb("target", tools::complete_file_name) .completion_cb("files-from", tools::complete_file_name)