From 7d178e09b0f41fd73c90b6d984db2c8565e4ebff Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 9 Nov 2015 04:08:49 +0100 Subject: [PATCH] Implement --keep-tag-files to preserve directory roots/tag-files We also add --keep-tag-files to keep in the archive the root directory and the tag/exclusion file in the archive. This is taken from a attic PR (and adapted for borg): commit f61e22cacc90e76e6c8f4b23677eee62c09e97ac Author: Yuri D'Elia Date: Mon Dec 15 12:27:43 2014 +0100 --- borg/archiver.py | 19 ++++++++++++++----- borg/helpers.py | 10 ++++++---- borg/testsuite/archiver.py | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/borg/archiver.py b/borg/archiver.py index 4e4682059..5512bb258 100644 --- a/borg/archiver.py +++ b/borg/archiver.py @@ -167,7 +167,8 @@ def do_create(self, args): else: restrict_dev = None self._process(archive, cache, args.excludes, args.exclude_caches, args.exclude_if_present, - skip_inodes, path, restrict_dev, read_special=args.read_special, dry_run=dry_run) + args.keep_tag_files, skip_inodes, path, restrict_dev, + read_special=args.read_special, dry_run=dry_run) if not dry_run: archive.save(timestamp=args.timestamp) if args.progress: @@ -183,7 +184,8 @@ def do_create(self, args): return self.exit_code def _process(self, archive, cache, excludes, exclude_caches, exclude_if_present, - skip_inodes, path, restrict_dev, read_special=False, dry_run=False): + keep_tag_files, skip_inodes, path, restrict_dev, + read_special=False, dry_run=False): if exclude_path(path, excludes): return try: @@ -209,7 +211,11 @@ def _process(self, archive, cache, excludes, exclude_caches, exclude_if_present, status = 'E' self.print_warning('%s: %s', path, e) elif stat.S_ISDIR(st.st_mode): - if dir_is_tagged(path, exclude_caches, exclude_if_present): + tag_path = dir_is_tagged(path, exclude_caches, exclude_if_present) + if tag_path: + if keep_tag_files: + archive.process_dir(path, st) + archive.process_file(tag_path, st, cache) return if not dry_run: status = archive.process_dir(path, st) @@ -222,8 +228,8 @@ def _process(self, archive, cache, excludes, exclude_caches, exclude_if_present, for filename in sorted(entries): entry_path = os.path.normpath(os.path.join(path, filename)) self._process(archive, cache, excludes, exclude_caches, exclude_if_present, - skip_inodes, entry_path, restrict_dev, read_special=read_special, - dry_run=dry_run) + keep_tag_files, skip_inodes, entry_path, restrict_dev, + read_special=read_special, dry_run=dry_run) elif stat.S_ISLNK(st.st_mode): if not dry_run: status = archive.process_symlink(path, st) @@ -788,6 +794,9 @@ def build_parser(self, args=None, prog=None): subparser.add_argument('--exclude-if-present', dest='exclude_if_present', metavar='FILENAME', action='append', type=str, help='exclude directories that contain the specified file') + subparser.add_argument('--keep-tag-files', dest='keep_tag_files', + action='store_true', default=False, + help='keep tag files of excluded caches/directories') subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval', type=int, default=300, metavar='SECONDS', help='write checkpoint every SECONDS seconds (Default: 300)') diff --git a/borg/helpers.py b/borg/helpers.py index 9fc5c7ba4..84379e75a 100644 --- a/borg/helpers.py +++ b/borg/helpers.py @@ -450,16 +450,18 @@ def dir_is_cachedir(path): def dir_is_tagged(path, exclude_caches, exclude_if_present): """Determines whether the specified path is excluded by being a cache - directory or containing the user-specified tag file. + directory or containing the user-specified tag file. Returns the + path of the tag file (either CACHEDIR.TAG or the matching + user-specified file) """ if exclude_caches and dir_is_cachedir(path): - return True + return os.path.join(path, 'CACHEDIR.TAG') if exclude_if_present is not None: for tag in exclude_if_present: tag_path = os.path.join(path, tag) if os.path.isfile(tag_path): - return True - return False + return tag_path + return None def format_time(t): diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index 02dc5d0a5..aaa758338 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -510,6 +510,20 @@ def test_exclude_tagged(self): self.cmd('extract', self.repository_location + '::test') self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'tagged3']) + def test_exclude_keep_tagged(self): + self.cmd('init', self.repository_location) + self.create_regular_file('file1', size=1024 * 80) + self.create_regular_file('tagged1/.NOBACKUP') + self.create_regular_file('tagged1/file2', size=1024 * 80) + self.create_regular_file('tagged2/CACHEDIR.TAG', contents = b'Signature: 8a477f597d28d172789f06886806bc55 extra stuff') + self.create_regular_file('tagged2/file3', size=1024 * 80) + self.cmd('create', '--exclude-if-present', '.NOBACKUP', '--exclude-caches', '--keep-tag-files', self.repository_location + '::test', 'input') + with changedir('output'): + self.cmd('extract', self.repository_location + '::test') + self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'tagged1', 'tagged2']) + self.assert_equal(sorted(os.listdir('output/input/tagged1')), ['.NOBACKUP']) + self.assert_equal(sorted(os.listdir('output/input/tagged2')), ['CACHEDIR.TAG']) + def test_path_normalization(self): self.cmd('init', self.repository_location) self.create_regular_file('dir1/dir2/file', size=1024 * 80)