[pbs-devel] [PATCH proxmox-backup] fix #3613: catalog_shell: include matched dir's contents on restore

Dylan Whyte d.whyte at proxmox.com
Mon Apr 4 18:19:20 CEST 2022


Prior to this, during an interactive restore, if a directory was matched
via a pattern match or selection, only the empty directory would be
restored, and not its contents. Now the entire contents is restored on
directory match

Signed-off-by: Dylan Whyte <d.whyte at proxmox.com>
---
 pbs-client/src/catalog_shell.rs | 56 ++++++++++++++++++++++-----------
 1 file changed, 37 insertions(+), 19 deletions(-)

diff --git a/pbs-client/src/catalog_shell.rs b/pbs-client/src/catalog_shell.rs
index c9c9da67..d0658def 100644
--- a/pbs-client/src/catalog_shell.rs
+++ b/pbs-client/src/catalog_shell.rs
@@ -997,6 +997,9 @@ impl Shell {
             &self.accessor,
         )?;
 
+        // Clear match list following restore
+        self.selected.clear();
+
         extractor.extract().await
     }
 }
@@ -1007,9 +1010,7 @@ struct ExtractorState<'a> {
     path_len_stack: Vec<usize>,
 
     dir_stack: Vec<PathStackEntry>,
-
-    matches: bool,
-    matches_stack: Vec<bool>,
+    match_pos: Option<usize>,
 
     read_dir: <Vec<catalog::DirEntry> as IntoIterator>::IntoIter,
     read_dir_stack: Vec<<Vec<catalog::DirEntry> as IntoIterator>::IntoIter>,
@@ -1029,6 +1030,7 @@ impl<'a> ExtractorState<'a> {
         match_list: &'a [MatchEntry],
         accessor: &'a Accessor,
     ) -> Result<Self, Error> {
+        let match_pos = None;
         let read_dir = catalog
             .read_dir(&dir_stack.last().unwrap().catalog)?
             .into_iter();
@@ -1038,9 +1040,7 @@ impl<'a> ExtractorState<'a> {
             path_len_stack: Vec::new(),
 
             dir_stack,
-
-            matches: match_list.is_empty(),
-            matches_stack: Vec::new(),
+            match_pos,
 
             read_dir,
             read_dir_stack: Vec::new(),
@@ -1084,11 +1084,6 @@ impl<'a> ExtractorState<'a> {
             None => return Ok(ControlFlow::Break(())), // out of root directory
         };
 
-        self.matches = self
-            .matches_stack
-            .pop()
-            .ok_or_else(|| format_err!("internal iterator error (matches_stack)"))?;
-
         self.dir_stack
             .pop()
             .ok_or_else(|| format_err!("internal iterator error (dir_stack)"))?;
@@ -1106,14 +1101,14 @@ impl<'a> ExtractorState<'a> {
     async fn handle_new_directory(
         &mut self,
         entry: catalog::DirEntry,
-        match_result: Option<MatchType>,
+        create_dir: bool,
     ) -> Result<(), Error> {
         // enter a new directory:
         self.read_dir_stack.push(mem::replace(
             &mut self.read_dir,
             self.catalog.read_dir(&entry)?.into_iter(),
         ));
-        self.matches_stack.push(self.matches);
+
         self.dir_stack.push(PathStackEntry::new(entry));
         self.path_len_stack.push(self.path_len);
         self.path_len = self.path.len();
@@ -1121,23 +1116,46 @@ impl<'a> ExtractorState<'a> {
         Shell::walk_pxar_archive(self.accessor, &mut self.dir_stack).await?;
         let dir_pxar = self.dir_stack.last().unwrap().pxar.as_ref().unwrap();
         let dir_meta = dir_pxar.entry().metadata().clone();
-        let create = self.matches && match_result != Some(MatchType::Exclude);
-        self.extractor.enter_directory(dir_pxar.file_name().to_os_string(), dir_meta, create)?;
+        self.extractor.enter_directory(dir_pxar.file_name().to_os_string(), dir_meta, create_dir)?;
 
         Ok(())
     }
 
     pub async fn handle_entry(&mut self, entry: catalog::DirEntry) -> Result<(), Error> {
         let match_result = self.match_list.matches(&self.path, entry.get_file_mode());
+
+        let current_pos = self.dir_stack.len();
+
         let did_match = match match_result {
-            Some(MatchType::Include) => true,
-            Some(MatchType::Exclude) => false,
-            None => self.matches,
+            Some(MatchType::Include) => {
+                // Only update match position for items lower in the directory stack
+                if let Some(match_pos) = self.match_pos {
+                    self.match_pos = Some(usize::min(current_pos, match_pos));
+                } else {
+                    self.match_pos = Some(current_pos)
+                }
+                true
+            },
+            Some(MatchType::Exclude) => return Ok(()), // no need to continue for excluded item
+            None => {
+                // Restore items contained within a matched directory
+                if let Some(match_pos) = self.match_pos {
+                    if current_pos > match_pos {
+                        true
+                    } else {
+                        self.match_pos = None;
+                        false
+                    }
+                } else {
+                    // Restore everything if no matches were specified
+                    self.match_list.is_empty()
+                }
+            }
         };
 
         match (did_match, &entry.attr) {
             (_, DirEntryAttribute::Directory { .. }) => {
-                self.handle_new_directory(entry, match_result).await?;
+                self.handle_new_directory(entry, did_match).await?;
             }
             (true, DirEntryAttribute::File { .. }) => {
                 self.dir_stack.push(PathStackEntry::new(entry));
-- 
2.30.2






More information about the pbs-devel mailing list