pxar: create .pxarexclude-cli file

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-06-09 13:17:55 +02:00
parent ae66873ce9
commit 239e49f927
5 changed files with 60 additions and 21 deletions

View File

@ -37,7 +37,7 @@ pam = "0.7"
pam-sys = "0.5" pam-sys = "0.5"
percent-encoding = "2.1" percent-encoding = "2.1"
pin-utils = "0.1.0" pin-utils = "0.1.0"
pathpatterns = "0.1.0" pathpatterns = "0.1.1"
proxmox = { version = "0.1.39", features = [ "sortable-macro", "api-macro" ] } proxmox = { version = "0.1.39", features = [ "sortable-macro", "api-macro" ] }
#proxmox = { git = "ssh://gitolite3@proxdev.maurer-it.com/rust/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] } #proxmox = { git = "ssh://gitolite3@proxdev.maurer-it.com/rust/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
#proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro" ] } #proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro" ] }

View File

@ -821,10 +821,10 @@ async fn create_backup(
let empty = Vec::new(); let empty = Vec::new();
let exclude_args = param["exclude"].as_array().unwrap_or(&empty); 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 { for entry in exclude_args {
let entry = entry.as_str().ok_or_else(|| format_err!("Invalid pattern string slice"))?; 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) MatchEntry::parse_pattern(entry, PatternFlag::PATH_NAME, MatchType::Exclude)
.map_err(|err| format_err!("invalid exclude pattern entry: {}", err))? .map_err(|err| format_err!("invalid exclude pattern entry: {}", err))?
); );
@ -971,7 +971,7 @@ async fn create_backup(
skip_lost_and_found, skip_lost_and_found,
crypt_config.clone(), crypt_config.clone(),
catalog.clone(), catalog.clone(),
exclude_list.clone(), pattern_list.clone(),
entries_max as usize, entries_max as usize,
).await?; ).await?;
manifest.add_file(target, stats.size, stats.csum)?; manifest.add_file(target, stats.size, stats.csum)?;

View File

@ -275,16 +275,16 @@ fn create_archive(
exclude: Option<Vec<String>>, exclude: Option<Vec<String>>,
entries_max: isize, entries_max: isize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let exclude_list = { let pattern_list = {
let input = exclude.unwrap_or_else(Vec::new); 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 { for entry in input {
exclude.push( pattern_list.push(
MatchEntry::parse_pattern(entry, PatternFlag::PATH_NAME, MatchType::Exclude) MatchEntry::parse_pattern(entry, PatternFlag::PATH_NAME, MatchType::Exclude)
.map_err(|err| format_err!("error in exclude pattern: {}", err))?, .map_err(|err| format_err!("error in exclude pattern: {}", err))?,
); );
} }
exclude pattern_list
}; };
let device_set = if all_file_systems { let device_set = if all_file_systems {
@ -332,7 +332,7 @@ fn create_archive(
proxmox_backup::pxar::create_archive( proxmox_backup::pxar::create_archive(
dir, dir,
writer, writer,
exclude_list, pattern_list,
feature_flags, feature_flags,
device_set, device_set,
true, true,

View File

@ -43,7 +43,7 @@ impl PxarBackupStream {
_verbose: bool, _verbose: bool,
skip_lost_and_found: bool, skip_lost_and_found: bool,
catalog: Arc<Mutex<CatalogWriter<W>>>, catalog: Arc<Mutex<CatalogWriter<W>>>,
exclude_pattern: Vec<MatchEntry>, patterns: Vec<MatchEntry>,
entries_max: usize, entries_max: usize,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let (tx, rx) = std::sync::mpsc::sync_channel(10); let (tx, rx) = std::sync::mpsc::sync_channel(10);
@ -66,7 +66,7 @@ impl PxarBackupStream {
if let Err(err) = crate::pxar::create_archive( if let Err(err) = crate::pxar::create_archive(
dir, dir,
writer, writer,
exclude_pattern, patterns,
crate::pxar::flags::DEFAULT, crate::pxar::flags::DEFAULT,
device_set, device_set,
skip_lost_and_found, skip_lost_and_found,
@ -93,7 +93,7 @@ impl PxarBackupStream {
verbose: bool, verbose: bool,
skip_lost_and_found: bool, skip_lost_and_found: bool,
catalog: Arc<Mutex<CatalogWriter<W>>>, catalog: Arc<Mutex<CatalogWriter<W>>>,
exclude_pattern: Vec<MatchEntry>, patterns: Vec<MatchEntry>,
entries_max: usize, entries_max: usize,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let dir = nix::dir::Dir::open(dirname, OFlag::O_DIRECTORY, Mode::empty())?; let dir = nix::dir::Dir::open(dirname, OFlag::O_DIRECTORY, Mode::empty())?;
@ -106,7 +106,7 @@ impl PxarBackupStream {
verbose, verbose,
skip_lost_and_found, skip_lost_and_found,
catalog, catalog,
exclude_pattern, patterns,
entries_max, entries_max,
) )
} }

View File

@ -90,7 +90,7 @@ struct Archiver<'a, 'b> {
feature_flags: u64, feature_flags: u64,
fs_feature_flags: u64, fs_feature_flags: u64,
fs_magic: i64, fs_magic: i64,
excludes: &'a [MatchEntry], patterns: &'a [MatchEntry],
callback: &'a mut dyn FnMut(&Path) -> Result<(), Error>, callback: &'a mut dyn FnMut(&Path) -> Result<(), Error>,
catalog: Option<&'b mut dyn BackupCatalogWriter>, catalog: Option<&'b mut dyn BackupCatalogWriter>,
path: PathBuf, path: PathBuf,
@ -106,7 +106,7 @@ type Encoder<'a, 'b> = pxar::encoder::Encoder<'a, &'b mut dyn pxar::encoder::Seq
pub fn create_archive<T, F>( pub fn create_archive<T, F>(
source_dir: Dir, source_dir: Dir,
mut writer: T, mut writer: T,
mut excludes: Vec<MatchEntry>, mut patterns: Vec<MatchEntry>,
feature_flags: u64, feature_flags: u64,
mut device_set: Option<HashSet<u64>>, mut device_set: Option<HashSet<u64>>,
skip_lost_and_found: bool, skip_lost_and_found: bool,
@ -142,7 +142,7 @@ where
let mut encoder = Encoder::new(writer, &metadata)?; let mut encoder = Encoder::new(writer, &metadata)?;
if skip_lost_and_found { if skip_lost_and_found {
excludes.push(MatchEntry::parse_pattern( patterns.push(MatchEntry::parse_pattern(
"**/lost+found", "**/lost+found",
PatternFlag::PATH_NAME, PatternFlag::PATH_NAME,
MatchType::Exclude, MatchType::Exclude,
@ -154,7 +154,7 @@ where
fs_feature_flags, fs_feature_flags,
fs_magic, fs_magic,
callback: &mut callback, callback: &mut callback,
excludes: &excludes, patterns: &patterns,
catalog, catalog,
path: PathBuf::new(), path: PathBuf::new(),
entry_counter: 0, entry_counter: 0,
@ -164,6 +164,18 @@ where
hardlinks: HashMap::new(), 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)?; archiver.archive_dir_contents(&mut encoder, source_dir)?;
encoder.finish()?; encoder.finish()?;
Ok(()) Ok(())
@ -222,8 +234,6 @@ impl<'a, 'b> Archiver<'a, 'b> {
continue; continue;
} }
// FIXME: deal with `.pxarexclude-cli`
if file_name_bytes == b".pxarexclude" { if file_name_bytes == b".pxarexclude" {
// FIXME: handle this file! // FIXME: handle this file!
continue; continue;
@ -244,7 +254,7 @@ impl<'a, 'b> Archiver<'a, 'b> {
}; };
if self if self
.excludes .patterns
.matches(full_path.as_os_str().as_bytes(), Some(stat.st_mode as u32)) .matches(full_path.as_os_str().as_bytes(), Some(stat.st_mode as u32))
== Some(MatchType::Exclude) == Some(MatchType::Exclude)
{ {
@ -294,7 +304,7 @@ impl<'a, 'b> Archiver<'a, 'b> {
let metadata = get_metadata(fd.as_raw_fd(), &stat, self.flags(), self.fs_magic)?; let metadata = get_metadata(fd.as_raw_fd(), &stat, self.flags(), self.fs_magic)?;
if self if self
.excludes .patterns
.matches(self.path.as_os_str().as_bytes(), Some(stat.st_mode as u32)) .matches(self.path.as_os_str().as_bytes(), Some(stat.st_mode as u32))
== Some(MatchType::Exclude) == Some(MatchType::Exclude)
{ {
@ -767,3 +777,32 @@ fn process_acl(
Ok(()) 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
}