Browse Source

Merge pull request #3261 from ThomasWaldmann/stat-after-match-1.1

Stat after match (1.1-maint)
TW 7 years ago
parent
commit
b4267ba4a7
1 changed files with 23 additions and 14 deletions
  1. 23 14
      src/borg/archiver.py

+ 23 - 14
src/borg/archiver.py

@@ -555,20 +555,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,
@@ -2019,10 +2026,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