pxar: add functionality to pass exclude MatchPatterns on create
This exposes the option to pass a list of exclude MatchPattern via the '--exclude' option. The list is encoded as file '.pxarexclude-cli' in the archives root directory. If such a file is present in the filesystem, it is skipped and not included in the archive in order to avoid conflicting information. Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
parent
ba8165c607
commit
62d123e50a
@ -174,6 +174,8 @@ fn create_archive(
|
||||
let no_device_nodes = param["no-device-nodes"].as_bool().unwrap_or(false);
|
||||
let no_fifos = param["no-fifos"].as_bool().unwrap_or(false);
|
||||
let no_sockets = param["no-sockets"].as_bool().unwrap_or(false);
|
||||
let empty = Vec::new();
|
||||
let exclude_pattern = param["exclude"].as_array().unwrap_or(&empty);
|
||||
|
||||
let devices = if all_file_systems { None } else { Some(HashSet::new()) };
|
||||
|
||||
@ -209,8 +211,26 @@ fn create_archive(
|
||||
feature_flags ^= pxar::flags::WITH_SOCKETS;
|
||||
}
|
||||
|
||||
let mut pattern_list = Vec::new();
|
||||
for s in exclude_pattern {
|
||||
let l = s.as_str().ok_or_else(|| format_err!("Invalid pattern string slice"))?;
|
||||
let p = pxar::MatchPattern::from_line(l.as_bytes())?
|
||||
.ok_or_else(|| format_err!("Invalid match pattern in arguments"))?;
|
||||
pattern_list.push(p);
|
||||
}
|
||||
|
||||
let catalog = None::<&mut pxar::catalog::DummyCatalogWriter>;
|
||||
pxar::Encoder::encode(source, &mut dir, &mut writer, catalog, devices, verbose, false, feature_flags)?;
|
||||
pxar::Encoder::encode(
|
||||
source,
|
||||
&mut dir,
|
||||
&mut writer,
|
||||
catalog,
|
||||
devices,
|
||||
verbose,
|
||||
false,
|
||||
feature_flags,
|
||||
pattern_list,
|
||||
)?;
|
||||
|
||||
writer.flush()?;
|
||||
|
||||
@ -257,8 +277,14 @@ fn main() {
|
||||
.optional("no-device-nodes", BooleanSchema::new("Ignore device nodes.").default(false))
|
||||
.optional("no-fifos", BooleanSchema::new("Ignore fifos.").default(false))
|
||||
.optional("no-sockets", BooleanSchema::new("Ignore sockets.").default(false))
|
||||
.optional("exclude", Arc::new(
|
||||
ArraySchema::new(
|
||||
"List of paths or pattern matching files to exclude.",
|
||||
Arc::new(StringSchema::new("Path or pattern matching files to restore.").into())
|
||||
).into()
|
||||
))
|
||||
))
|
||||
.arg_param(vec!["archive", "source"])
|
||||
.arg_param(vec!["archive", "source", "exclude"])
|
||||
.completion_cb("archive", tools::complete_file_name)
|
||||
.completion_cb("source", tools::complete_file_name)
|
||||
.into()
|
||||
|
@ -59,10 +59,21 @@ impl PxarBackupStream {
|
||||
let error2 = error.clone();
|
||||
|
||||
let catalog = catalog.clone();
|
||||
let exclude_pattern = Vec::new();
|
||||
let child = thread::spawn(move || {
|
||||
let mut guard = catalog.lock().unwrap();
|
||||
let mut writer = unsafe { std::fs::File::from_raw_fd(tx) };
|
||||
if let Err(err) = pxar::Encoder::encode(path, &mut dir, &mut writer, Some(&mut *guard), device_set, verbose, skip_lost_and_found, pxar::flags::DEFAULT) {
|
||||
if let Err(err) = pxar::Encoder::encode(
|
||||
path,
|
||||
&mut dir,
|
||||
&mut writer,
|
||||
Some(&mut *guard),
|
||||
device_set,
|
||||
verbose,
|
||||
skip_lost_and_found,
|
||||
pxar::flags::DEFAULT,
|
||||
exclude_pattern,
|
||||
) {
|
||||
let mut error = error2.lock().unwrap();
|
||||
*error = Some(err.to_string());
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! This module contain the code to generate *pxar* archive files.
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::Write;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
@ -81,6 +81,7 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||
verbose: bool,
|
||||
skip_lost_and_found: bool, // fixme: should be a feature flag ??
|
||||
feature_flags: u64,
|
||||
mut excludes: Vec<MatchPattern>,
|
||||
) -> Result<(), Error> {
|
||||
const FILE_COPY_BUFFER_SIZE: usize = 1024 * 1024;
|
||||
|
||||
@ -131,10 +132,10 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||
println!("{:?}", me.full_path());
|
||||
}
|
||||
|
||||
let mut excludes = Vec::new();
|
||||
if skip_lost_and_found {
|
||||
excludes.push(MatchPattern::from_line(b"**/lost+found").unwrap().unwrap());
|
||||
}
|
||||
|
||||
me.encode_dir(dir, &stat, magic, excludes)?;
|
||||
|
||||
Ok(())
|
||||
@ -631,6 +632,8 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||
|
||||
let dir_start_pos = self.writer_pos;
|
||||
|
||||
let is_root = dir_start_pos == 0;
|
||||
|
||||
let mut dir_entry = self.create_entry(&dir_stat)?;
|
||||
|
||||
self.read_chattr(rawfd, &mut dir_entry)?;
|
||||
@ -706,6 +709,13 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||
if name == b".\0" || name == b"..\0" {
|
||||
continue;
|
||||
}
|
||||
// Do not store a ".pxarexclude-cli" file found in the archive root,
|
||||
// as this would confilict with new cli passed exclude patterns,
|
||||
// if present.
|
||||
if is_root && name == b".pxarexclude-cli\0" {
|
||||
eprintln!("skip existing '.pxarexclude-cli' in archive root.");
|
||||
continue;
|
||||
}
|
||||
|
||||
let stat = match nix::sys::stat::fstatat(
|
||||
rawfd,
|
||||
@ -740,6 +750,20 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude patterns passed via the CLI are stored as '.pxarexclude-cli'
|
||||
// in the root directory of the archive.
|
||||
if is_root && match_pattern.len() > 0 {
|
||||
let filename = CString::new(".pxarexclude-cli")?;
|
||||
name_list.push((filename, dir_stat.clone(), match_pattern.clone()));
|
||||
if name_list.len() > MAX_DIRECTORY_ENTRIES {
|
||||
bail!(
|
||||
"too many directory items in {:?} (> {})",
|
||||
self.full_path(),
|
||||
MAX_DIRECTORY_ENTRIES
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("skip mount point: {:?}", self.full_path());
|
||||
}
|
||||
@ -789,6 +813,18 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||
}
|
||||
}
|
||||
|
||||
if is_root && filename.as_bytes() == b".pxarexclude-cli" {
|
||||
// '.pxarexclude-cli' is used to store the exclude MatchPatterns
|
||||
// passed via the cli in the root directory of the archive.
|
||||
self.write_filename(&filename)?;
|
||||
let content = MatchPattern::to_bytes(&exclude_list);
|
||||
if let Some(ref mut catalog) = self.catalog {
|
||||
catalog.add_file(&filename, content.len() as u64, 0)?;
|
||||
}
|
||||
self.encode_pxar_exclude_cli(stat.st_uid, stat.st_gid, 0, &content)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
self.relative_path
|
||||
.push(std::ffi::OsStr::from_bytes(filename.as_bytes()));
|
||||
|
||||
|
@ -27,7 +27,17 @@ fn run_test(dir_name: &str) -> Result<(), Error> {
|
||||
let path = std::path::PathBuf::from(dir_name);
|
||||
|
||||
let catalog = None::<&mut catalog::DummyCatalogWriter>;
|
||||
Encoder::encode(path, &mut dir, &mut writer, catalog, None, false, false, flags::DEFAULT)?;
|
||||
Encoder::encode(
|
||||
path,
|
||||
&mut dir,
|
||||
&mut writer,
|
||||
catalog,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
flags::DEFAULT,
|
||||
Vec::new(),
|
||||
)?;
|
||||
|
||||
Command::new("cmp")
|
||||
.arg("--verbose")
|
||||
|
Loading…
Reference in New Issue
Block a user