From 0fb6cb8417e3f84afc03fbfaef3cb7ca6212e61b Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 11 Jul 2016 20:20:10 +0200 Subject: [PATCH] more compatible sparse file testing, fixes #1310 removed the pointless platform check. just first test the input file with the same checks we expect succeeding on the extracted file. skip sparse archiving / extraction testing if the input file checks fail - likely we have a problem with the OS or the FS then. --- borg/testsuite/archiver.py | 71 ++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index dbe21b0f3..ab69770e0 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -393,11 +393,30 @@ class ArchiverTestCase(ArchiverTestCaseBase): return repository.id def test_sparse_file(self): - # no sparse file support on Mac OS X - sparse_support = sys.platform != 'darwin' + def is_sparse(fn, total_size, hole_size): + st = os.stat(fn) + assert st.st_size == total_size + sparse = True + if sparse and hasattr(st, 'st_blocks') and st.st_blocks * 512 >= st.st_size: + sparse = False + if sparse and hasattr(os, 'SEEK_HOLE') and hasattr(os, 'SEEK_DATA'): + with open(fn, 'rb') as fd: + # only check if the first hole is as expected, because the 2nd hole check + # is problematic on xfs due to its "dynamic speculative EOF preallocation + try: + if fd.seek(0, os.SEEK_HOLE) != 0: + sparse = False + if fd.seek(0, os.SEEK_DATA) != hole_size: + sparse = False + except OSError: + # OS/FS does not really support SEEK_HOLE/SEEK_DATA + sparse = False + return sparse + filename = os.path.join(self.input_path, 'sparse') content = b'foobar' hole_size = 5 * (1 << CHUNK_MAX_EXP) # 5 full chunker buffers + total_size = hole_size + len(content) + hole_size with open(filename, 'wb') as fd: # create a file that has a hole at the beginning and end (if the # OS and filesystem supports sparse files) @@ -406,39 +425,23 @@ class ArchiverTestCase(ArchiverTestCaseBase): fd.seek(hole_size, 1) pos = fd.tell() fd.truncate(pos) - total_len = hole_size + len(content) + hole_size - st = os.stat(filename) - self.assert_equal(st.st_size, total_len) - if sparse_support and hasattr(st, 'st_blocks'): - self.assert_true(st.st_blocks * 512 < total_len) # is input sparse? - self.cmd('init', self.repository_location) - self.cmd('create', self.repository_location + '::test', 'input') - with changedir('output'): - self.cmd('extract', '--sparse', self.repository_location + '::test') - self.assert_dirs_equal('input', 'output/input') - filename = os.path.join(self.output_path, 'input', 'sparse') - with open(filename, 'rb') as fd: - # check if file contents are as expected - self.assert_equal(fd.read(hole_size), b'\0' * hole_size) - self.assert_equal(fd.read(len(content)), content) - self.assert_equal(fd.read(hole_size), b'\0' * hole_size) - st = os.stat(filename) - self.assert_equal(st.st_size, total_len) + # we first check if we could create a sparse input file: + sparse_support = is_sparse(filename, total_size, hole_size) if sparse_support: - if hasattr(st, 'st_blocks'): - # do only check if it is less, do NOT check if it is much less - # as that causes troubles on xfs, zfs, ntfs: - self.assert_true(st.st_blocks * 512 < total_len) - if hasattr(os, 'SEEK_HOLE') and hasattr(os, 'SEEK_DATA'): - with open(filename, 'rb') as fd: - # only check if the first hole is as expected, because the 2nd hole check - # is problematic on xfs due to its "dynamic speculative EOF preallocation - try: - self.assert_equal(fd.seek(0, os.SEEK_HOLE), 0) - self.assert_equal(fd.seek(0, os.SEEK_DATA), hole_size) - except OSError: - # does not really support SEEK_HOLE/SEEK_DATA - pass + # we could create a sparse input file, so creating a backup of it and + # extracting it again (as sparse) should also work: + self.cmd('init', self.repository_location) + self.cmd('create', self.repository_location + '::test', 'input') + with changedir(self.output_path): + self.cmd('extract', '--sparse', self.repository_location + '::test') + self.assert_dirs_equal('input', 'output/input') + filename = os.path.join(self.output_path, 'input', 'sparse') + with open(filename, 'rb') as fd: + # check if file contents are as expected + self.assert_equal(fd.read(hole_size), b'\0' * hole_size) + self.assert_equal(fd.read(len(content)), content) + self.assert_equal(fd.read(hole_size), b'\0' * hole_size) + self.assert_true(is_sparse(filename, total_size, hole_size)) def test_unusual_filenames(self): filenames = ['normal', 'with some blanks', '(with_parens)', ]