Bladeren bron

Merge pull request #3212 from ThomasWaldmann/stat-after-match

don't do stat() when not recursing into excluded dir, fixes #3209
TW 7 jaren geleden
bovenliggende
commit
926b1c6374
1 gewijzigde bestanden met toevoegingen van 23 en 14 verwijderingen
  1. 23 14
      src/borg/archiver.py

+ 23 - 14
src/borg/archiver.py

@@ -537,20 +537,27 @@ class Archiver:
 
 
         This should only raise on critical errors. Per-item errors must be handled within this method.
         This should only raise on critical errors. Per-item errors must be handled within this method.
         """
         """
-        if st is None:
-            with backup_io('stat'):
-                st = os.stat(path, follow_symlinks=False)
-
-        recurse_excluded_dir = False
-        if not matcher.match(path):
-            self.print_file_status('x', path)
-
-            if stat.S_ISDIR(st.st_mode) and matcher.recurse_dir:
-                recurse_excluded_dir = True
+        try:
+            recurse_excluded_dir = False
+            if matcher.match(path):
+                if st is None:
+                    with backup_io('stat'):
+                        st = os.stat(path, follow_symlinks=False)
             else:
             else:
-                return
+                self.print_file_status('x', path)
+                # get out here as quickly as possible:
+                # we only need to continue if we shall recurse into an excluded directory.
+                # if we shall not recurse, then do not even touch (stat()) the item, it
+                # could trigger an error, e.g. if access is forbidden, see #3209.
+                if not matcher.recurse_dir:
+                    return
+                if st is None:
+                    with backup_io('stat'):
+                        st = os.stat(path, follow_symlinks=False)
+                recurse_excluded_dir = stat.S_ISDIR(st.st_mode)
+                if not recurse_excluded_dir:
+                    return
 
 
-        try:
             if (st.st_ino, st.st_dev) in skip_inodes:
             if (st.st_ino, st.st_dev) in skip_inodes:
                 return
                 return
             # if restrict_dev is given, we do not want to recurse into a new filesystem,
             # if restrict_dev is given, we do not want to recurse into a new filesystem,
@@ -1823,10 +1830,12 @@ class Archiver:
             may specify the backup roots (starting points) and patterns for inclusion/exclusion.
             may specify the backup roots (starting points) and patterns for inclusion/exclusion.
             A root path starts with the prefix `R`, followed by a path (a plain path, not a
             A root path starts with the prefix `R`, followed by a path (a plain path, not a
             file pattern). An include rule starts with the prefix +, an exclude rule starts
             file pattern). An include rule starts with the prefix +, an exclude rule starts
-            with the prefix -, both followed by a pattern.
+            with the prefix -, an exclude-norecurse rule starts with !, all followed by a pattern.
             Inclusion patterns are useful to include paths that are contained in an excluded
             Inclusion patterns are useful to include paths that are contained in an excluded
             path. The first matching pattern is used so if an include pattern matches before
             path. The first matching pattern is used so if an include pattern matches before
-            an exclude pattern, the file is backed up.
+            an exclude pattern, the file is backed up. If an exclude-norecurse pattern matches
+            a directory, it won't recurse into it and won't discover any potential matches for
+            include rules below that directory.
 
 
             Note that the default pattern style for ``--pattern`` and ``--patterns-from`` is
             Note that the default pattern style for ``--pattern`` and ``--patterns-from`` is
             shell style (`sh:`), so those patterns behave similar to rsync include/exclude
             shell style (`sh:`), so those patterns behave similar to rsync include/exclude