瀏覽代碼

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

don't do stat() when not recursing into excluded dir, fixes #3209
TW 7 年之前
父節點
當前提交
926b1c6374
共有 1 個文件被更改,包括 23 次插入14 次删除
  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