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;
use proxmox_backup::pxar;
Ok(Value::Null)
}
-fn extract_archive_from_reader<R: std::io::Read>(reader: &mut R, target: &str, feature_flags: u64, verbose: bool) -> Result<(), Error> {
+fn extract_archive_from_reader<R: std::io::Read>(
+ reader: &mut R,
+ target: &str,
+ feature_flags: u64,
+ verbose: bool,
+ pattern: Option<Vec<pxar::PxarExcludePattern>>
+) -> Result<(), Error> {
let mut decoder = pxar::SequentialDecoder::new(reader, feature_flags, |path| {
if verbose {
println!("{:?}", path);
Ok(())
});
- decoder.restore(Path::new(target))?;
+ let pattern = pattern.unwrap_or(Vec::new());
+ decoder.restore(Path::new(target), &pattern)?;
Ok(())
}
) -> Result<Value, Error> {
let archive = tools::required_string_param(¶m, "archive")?;
- let target = tools::required_string_param(¶m, "target")?;
+ let target = param["target"].as_str().unwrap_or(".");
let verbose = param["verbose"].as_bool().unwrap_or(false);
let no_xattrs = param["no-xattrs"].as_bool().unwrap_or(false);
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 {
feature_flags ^= pxar::CA_FORMAT_WITH_ACL;
}
+ 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);
+ }
+ }
+
+ 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 == "-" {
let stdin = std::io::stdin();
let mut reader = stdin.lock();
- extract_archive_from_reader(&mut reader, target, feature_flags, verbose)?;
+ extract_archive_from_reader(&mut reader, target, feature_flags, verbose, pattern)?;
} else {
- println!("PXAR dump: {}", archive);
+ if verbose { println!("PXAR extract: {}", archive); }
let file = std::fs::File::open(archive)?;
let mut reader = std::io::BufReader::new(file);
- extract_archive_from_reader(&mut reader, target, feature_flags, verbose)?;
+ extract_archive_from_reader(&mut reader, target, feature_flags, verbose, pattern)?;
}
Ok(Value::Null)
.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."))
- .required("target", StringSchema::new("Target directory."))
+ .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))
- ))
- .arg_param(vec!["archive", "target"])
+ .optional("files-from", StringSchema::new("Match pattern for files to restore."))
+ ))
+ .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)
.into()
)
.insert("list", CliCommand::new(