From 338f56b97d2844fe5d57c2869453681bdf2df2a5 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 24 Oct 2017 01:25:53 +0200 Subject: [PATCH 1/2] don't do stat() when not recursing into excluded dir, fixes #3209 also: fix exception handling for the stat() calls just moving all these lines into the "try"-block below (like it was in 1.0). (cherry picked from commit 0c410e84fe9d86b24a6022c13df24005283e70c8) --- src/borg/archiver.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 84c913a5e..2e7856b04 100644 --- a/src/borg/archiver.py +++ b/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. """ - 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 - else: - return - 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: + 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 + if (st.st_ino, st.st_dev) in skip_inodes: return # if restrict_dev is given, we do not want to recurse into a new filesystem, From 0943f2235e66726e714df9407ddd1991ee144065 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 24 Oct 2017 02:38:03 +0200 Subject: [PATCH 2/2] mention "!" (exclude-norecurse) type in patterns help (cherry picked from commit 36f2a4e1dcac69f58e5cfc8141a65b5006f32cf3) --- src/borg/archiver.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 2e7856b04..d978ec107 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -2026,10 +2026,12 @@ class Archiver: 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 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 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 shell style (`sh:`), so those patterns behave similar to rsync include/exclude