let empty = Vec::new();
let exclude_args = param["exclude"].as_array().unwrap_or(&empty);
- let mut exclude_list = Vec::with_capacity(exclude_args.len());
+ let mut pattern_list = Vec::with_capacity(exclude_args.len());
for entry in exclude_args {
let entry = entry.as_str().ok_or_else(|| format_err!("Invalid pattern string slice"))?;
- exclude_list.push(
+ pattern_list.push(
MatchEntry::parse_pattern(entry, PatternFlag::PATH_NAME, MatchType::Exclude)
.map_err(|err| format_err!("invalid exclude pattern entry: {}", err))?
);
skip_lost_and_found,
crypt_config.clone(),
catalog.clone(),
- exclude_list.clone(),
+ pattern_list.clone(),
entries_max as usize,
).await?;
manifest.add_file(target, stats.size, stats.csum)?;
exclude: Option<Vec<String>>,
entries_max: isize,
) -> Result<(), Error> {
- let exclude_list = {
+ let pattern_list = {
let input = exclude.unwrap_or_else(Vec::new);
- let mut exclude = Vec::with_capacity(input.len());
+ let mut pattern_list = Vec::with_capacity(input.len());
for entry in input {
- exclude.push(
+ pattern_list.push(
MatchEntry::parse_pattern(entry, PatternFlag::PATH_NAME, MatchType::Exclude)
.map_err(|err| format_err!("error in exclude pattern: {}", err))?,
);
}
- exclude
+ pattern_list
};
let device_set = if all_file_systems {
proxmox_backup::pxar::create_archive(
dir,
writer,
- exclude_list,
+ pattern_list,
feature_flags,
device_set,
true,
_verbose: bool,
skip_lost_and_found: bool,
catalog: Arc<Mutex<CatalogWriter<W>>>,
- exclude_pattern: Vec<MatchEntry>,
+ patterns: Vec<MatchEntry>,
entries_max: usize,
) -> Result<Self, Error> {
let (tx, rx) = std::sync::mpsc::sync_channel(10);
if let Err(err) = crate::pxar::create_archive(
dir,
writer,
- exclude_pattern,
+ patterns,
crate::pxar::flags::DEFAULT,
device_set,
skip_lost_and_found,
verbose: bool,
skip_lost_and_found: bool,
catalog: Arc<Mutex<CatalogWriter<W>>>,
- exclude_pattern: Vec<MatchEntry>,
+ patterns: Vec<MatchEntry>,
entries_max: usize,
) -> Result<Self, Error> {
let dir = nix::dir::Dir::open(dirname, OFlag::O_DIRECTORY, Mode::empty())?;
verbose,
skip_lost_and_found,
catalog,
- exclude_pattern,
+ patterns,
entries_max,
)
}
feature_flags: u64,
fs_feature_flags: u64,
fs_magic: i64,
- excludes: &'a [MatchEntry],
+ patterns: &'a [MatchEntry],
callback: &'a mut dyn FnMut(&Path) -> Result<(), Error>,
catalog: Option<&'b mut dyn BackupCatalogWriter>,
path: PathBuf,
pub fn create_archive<T, F>(
source_dir: Dir,
mut writer: T,
- mut excludes: Vec<MatchEntry>,
+ mut patterns: Vec<MatchEntry>,
feature_flags: u64,
mut device_set: Option<HashSet<u64>>,
skip_lost_and_found: bool,
let mut encoder = Encoder::new(writer, &metadata)?;
if skip_lost_and_found {
- excludes.push(MatchEntry::parse_pattern(
+ patterns.push(MatchEntry::parse_pattern(
"**/lost+found",
PatternFlag::PATH_NAME,
MatchType::Exclude,
fs_feature_flags,
fs_magic,
callback: &mut callback,
- excludes: &excludes,
+ patterns: &patterns,
catalog,
path: PathBuf::new(),
entry_counter: 0,
hardlinks: HashMap::new(),
};
+ if !patterns.is_empty() {
+ let content = generate_pxar_excludes_cli(&patterns);
+ let mut file = encoder.create_file(
+ &Metadata::default(),
+ ".pxarexclude-cli",
+ content.len() as u64,
+ )?;
+
+ use std::io::Write;
+ file.write_all(&content)?;
+ }
+
archiver.archive_dir_contents(&mut encoder, source_dir)?;
encoder.finish()?;
Ok(())
continue;
}
- // FIXME: deal with `.pxarexclude-cli`
-
if file_name_bytes == b".pxarexclude" {
// FIXME: handle this file!
continue;
};
if self
- .excludes
+ .patterns
.matches(full_path.as_os_str().as_bytes(), Some(stat.st_mode as u32))
== Some(MatchType::Exclude)
{
let metadata = get_metadata(fd.as_raw_fd(), &stat, self.flags(), self.fs_magic)?;
if self
- .excludes
+ .patterns
.matches(self.path.as_os_str().as_bytes(), Some(stat.st_mode as u32))
== Some(MatchType::Exclude)
{
Ok(())
}
+
+/// Note that our pattern lists are "positive". `MatchType::Include` means the file is included.
+/// Since we are generating an *exclude* list, we need to invert this, so includes get a `'!'`
+/// prefix.
+fn generate_pxar_excludes_cli(patterns: &[MatchEntry]) -> Vec<u8> {
+ use pathpatterns::{MatchFlag, MatchPattern};
+
+ let mut content = Vec::new();
+
+ for pattern in patterns {
+ match pattern.match_type() {
+ MatchType::Include => content.push(b'!'),
+ MatchType::Exclude => (),
+ }
+
+ match pattern.pattern() {
+ MatchPattern::Literal(lit) => content.extend(lit),
+ MatchPattern::Pattern(pat) => content.extend(pat.pattern().to_bytes()),
+ }
+
+ if pattern.match_flags() == MatchFlag::MATCH_DIRECTORIES && content.last() != Some(&b'/') {
+ content.push(b'/');
+ }
+
+ content.push(b'\n');
+ }
+
+ content
+}