]> git.proxmox.com Git - proxmox-backup.git/commitdiff
pxar: create .pxarexclude-cli file
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Tue, 9 Jun 2020 11:17:55 +0000 (13:17 +0200)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Tue, 9 Jun 2020 11:17:59 +0000 (13:17 +0200)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Cargo.toml
src/bin/proxmox-backup-client.rs
src/bin/pxar.rs
src/client/pxar_backup_stream.rs
src/pxar/create.rs

index 99b5b3302daf83f37efebb838d0b81052e26fb6b..d63da939620b728d95a14b109378d2922d2fae2c 100644 (file)
@@ -37,7 +37,7 @@ pam = "0.7"
 pam-sys = "0.5"
 percent-encoding = "2.1"
 pin-utils = "0.1.0"
-pathpatterns = "0.1.0"
+pathpatterns = "0.1.1"
 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 = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro" ] }
index e9ae1d95869ef2af6ce8b8501e24667706b44b5d..29ee562bd0895df775710d89599a5377de3742bb 100644 (file)
@@ -821,10 +821,10 @@ async fn create_backup(
     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))?
         );
@@ -971,7 +971,7 @@ async fn create_backup(
                     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)?;
index e99550f76e63a53492f532ada12096e6bd423ec9..fc5b8046b96694af5f4d939b2b9db053721f8aaa 100644 (file)
@@ -275,16 +275,16 @@ fn create_archive(
     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 {
@@ -332,7 +332,7 @@ fn create_archive(
     proxmox_backup::pxar::create_archive(
         dir,
         writer,
-        exclude_list,
+        pattern_list,
         feature_flags,
         device_set,
         true,
index c651cc7935e6fd41f17cd511ab1363bc681e40ac..11f5d924e1fee1bd62e8f7d7f7ec619837d1ef26 100644 (file)
@@ -43,7 +43,7 @@ impl PxarBackupStream {
         _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);
@@ -66,7 +66,7 @@ impl PxarBackupStream {
                     if let Err(err) = crate::pxar::create_archive(
                         dir,
                         writer,
-                        exclude_pattern,
+                        patterns,
                         crate::pxar::flags::DEFAULT,
                         device_set,
                         skip_lost_and_found,
@@ -93,7 +93,7 @@ impl PxarBackupStream {
         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())?;
@@ -106,7 +106,7 @@ impl PxarBackupStream {
             verbose,
             skip_lost_and_found,
             catalog,
-            exclude_pattern,
+            patterns,
             entries_max,
         )
     }
index 7fbcae40e6761801649e3c10e3a4de36869a8d98..9146cedeb3941d575919894ffaf60bb83d334dcc 100644 (file)
@@ -90,7 +90,7 @@ struct Archiver<'a, 'b> {
     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,
@@ -106,7 +106,7 @@ type Encoder<'a, 'b> = pxar::encoder::Encoder<'a, &'b mut dyn pxar::encoder::Seq
 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,
@@ -142,7 +142,7 @@ where
     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,
@@ -154,7 +154,7 @@ where
         fs_feature_flags,
         fs_magic,
         callback: &mut callback,
-        excludes: &excludes,
+        patterns: &patterns,
         catalog,
         path: PathBuf::new(),
         entry_counter: 0,
@@ -164,6 +164,18 @@ where
         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(())
@@ -222,8 +234,6 @@ impl<'a, 'b> Archiver<'a, 'b> {
                 continue;
             }
 
-            // FIXME: deal with `.pxarexclude-cli`
-
             if file_name_bytes == b".pxarexclude" {
                 // FIXME: handle this file!
                 continue;
@@ -244,7 +254,7 @@ impl<'a, 'b> Archiver<'a, 'b> {
             };
 
             if self
-                .excludes
+                .patterns
                 .matches(full_path.as_os_str().as_bytes(), Some(stat.st_mode as u32))
                 == 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)?;
 
         if self
-            .excludes
+            .patterns
             .matches(self.path.as_os_str().as_bytes(), Some(stat.st_mode as u32))
             == Some(MatchType::Exclude)
         {
@@ -767,3 +777,32 @@ fn process_acl(
 
     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
+}