From 41348a76efe5078dd9594c71b617df2a9ca62f4c Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 8 Jul 2016 19:18:03 +0200 Subject: [PATCH 1/3] deduplicate fuse (u)mount code --- borg/testsuite/__init__.py | 15 +++++++++++++++ borg/testsuite/archiver.py | 30 ++++-------------------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/borg/testsuite/__init__.py b/borg/testsuite/__init__.py index 5c1a0a6fd..b6f64b19c 100644 --- a/borg/testsuite/__init__.py +++ b/borg/testsuite/__init__.py @@ -93,6 +93,21 @@ def _assert_dirs_equal_cmp(self, diff): for sub_diff in diff.subdirs.values(): self._assert_dirs_equal_cmp(sub_diff) + @contextmanager + def fuse_mount(self, location, mountpoint): + os.mkdir(mountpoint) + self.cmd('mount', location, mountpoint, fork=True) + self.wait_for_mount(mountpoint) + yield + if sys.platform.startswith('linux'): + cmd = 'fusermount -u %s' % mountpoint + else: + cmd = 'umount %s' % mountpoint + os.system(cmd) + os.rmdir(mountpoint) + # Give the daemon some time to exit + time.sleep(.2) + def wait_for_mount(self, path, timeout=5): """Wait until a filesystem is mounted on `path` """ diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index c4020809d..84a29cde3 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -975,45 +975,23 @@ def test_help(self): @unittest.skipUnless(has_llfuse, 'llfuse not installed') def test_fuse_mount_repository(self): - mountpoint = os.path.join(self.tmpdir, 'mountpoint') - os.mkdir(mountpoint) self.cmd('init', self.repository_location) self.create_test_files() self.cmd('create', self.repository_location + '::archive', 'input') self.cmd('create', self.repository_location + '::archive2', 'input') - try: - self.cmd('mount', self.repository_location, mountpoint, fork=True) - self.wait_for_mount(mountpoint) + mountpoint = os.path.join(self.tmpdir, 'mountpoint') + with self.fuse_mount(self.repository_location, mountpoint): self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'archive', 'input')) self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'archive2', 'input')) - finally: - if sys.platform.startswith('linux'): - os.system('fusermount -u ' + mountpoint) - else: - os.system('umount ' + mountpoint) - os.rmdir(mountpoint) - # Give the daemon some time to exit - time.sleep(.2) @unittest.skipUnless(has_llfuse, 'llfuse not installed') def test_fuse_mount_archive(self): - mountpoint = os.path.join(self.tmpdir, 'mountpoint') - os.mkdir(mountpoint) self.cmd('init', self.repository_location) self.create_test_files() self.cmd('create', self.repository_location + '::archive', 'input') - try: - self.cmd('mount', self.repository_location + '::archive', mountpoint, fork=True) - self.wait_for_mount(mountpoint) + mountpoint = os.path.join(self.tmpdir, 'mountpoint') + with self.fuse_mount(self.repository_location + '::archive', mountpoint): self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'input')) - finally: - if sys.platform.startswith('linux'): - os.system('fusermount -u ' + mountpoint) - else: - os.system('umount ' + mountpoint) - os.rmdir(mountpoint) - # Give the daemon some time to exit - time.sleep(.2) def verify_aes_counter_uniqueness(self, method): seen = set() # Chunks already seen From 846febb079450a7bba211f37423c2fcf5b8cef0c Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 8 Jul 2016 19:53:14 +0200 Subject: [PATCH 2/3] merge both fuse mounting tests --- borg/testsuite/archiver.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index 84a29cde3..7f371ed77 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -974,22 +974,17 @@ def test_help(self): assert 'This command initializes' not in self.cmd('help', 'init', '--usage-only') @unittest.skipUnless(has_llfuse, 'llfuse not installed') - def test_fuse_mount_repository(self): + def test_fuse_mount(self): self.cmd('init', self.repository_location) self.create_test_files() self.cmd('create', self.repository_location + '::archive', 'input') self.cmd('create', self.repository_location + '::archive2', 'input') mountpoint = os.path.join(self.tmpdir, 'mountpoint') + # mount the whole repository, archive contents shall show up in archivename subdirs of mountpoint: with self.fuse_mount(self.repository_location, mountpoint): self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'archive', 'input')) self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'archive2', 'input')) - - @unittest.skipUnless(has_llfuse, 'llfuse not installed') - def test_fuse_mount_archive(self): - self.cmd('init', self.repository_location) - self.create_test_files() - self.cmd('create', self.repository_location + '::archive', 'input') - mountpoint = os.path.join(self.tmpdir, 'mountpoint') + # mount only 1 archive, its contents shall show up directly in mountpoint: with self.fuse_mount(self.repository_location + '::archive', mountpoint): self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'input')) @@ -1165,11 +1160,7 @@ def test_remote_repo_restrict_to_path(self): # this was introduced because some tests expect stderr contents to show up # in "output" also. Also, the non-forking exec_cmd catches both, too. @unittest.skip('deadlock issues') - def test_fuse_mount_repository(self): - pass - - @unittest.skip('deadlock issues') - def test_fuse_mount_archive(self): + def test_fuse_mount(self): pass @unittest.skip('only works locally') From edb70513eb38f140198cf65f77d924a88d2773e8 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 8 Jul 2016 21:54:25 +0200 Subject: [PATCH 3/3] add more FUSE tests, fixes #1284 --- borg/testsuite/archiver.py | 53 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index 7f371ed77..d52391239 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -974,7 +974,7 @@ def test_help(self): assert 'This command initializes' not in self.cmd('help', 'init', '--usage-only') @unittest.skipUnless(has_llfuse, 'llfuse not installed') - def test_fuse_mount(self): + def test_fuse(self): self.cmd('init', self.repository_location) self.create_test_files() self.cmd('create', self.repository_location + '::archive', 'input') @@ -987,6 +987,55 @@ def test_fuse_mount(self): # mount only 1 archive, its contents shall show up directly in mountpoint: with self.fuse_mount(self.repository_location + '::archive', mountpoint): self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'input')) + # regular file + in_fn = 'input/file1' + out_fn = os.path.join(mountpoint, 'input', 'file1') + # stat + sti1 = os.stat(in_fn) + sto1 = os.stat(out_fn) + assert sti1.st_mode == sto1.st_mode + assert sti1.st_uid == sto1.st_uid + assert sti1.st_gid == sto1.st_gid + assert sti1.st_size == sto1.st_size + assert sti1.st_atime == sto1.st_atime + assert sti1.st_ctime == sto1.st_ctime + assert sti1.st_mtime == sto1.st_mtime + # note: there is another hardlink to this, see below + assert sti1.st_nlink == sto1.st_nlink == 2 + # read + with open(in_fn, 'rb') as in_f, open(out_fn, 'rb') as out_f: + assert in_f.read() == out_f.read() + # list/read xattrs + if xattr.is_enabled(self.input_path): + assert xattr.listxattr(out_fn) == ['user.foo', ] + assert xattr.getxattr(out_fn, 'user.foo') == b'bar' + else: + assert xattr.listxattr(out_fn) == [] + try: + xattr.getxattr(out_fn, 'user.foo') + except OSError as e: + assert e.errno == llfuse.ENOATTR + else: + assert False, "expected OSError(ENOATTR), but no error was raised" + # hardlink (to 'input/file1') + in_fn = 'input/hardlink' + out_fn = os.path.join(mountpoint, 'input', 'hardlink') + sti2 = os.stat(in_fn) + sto2 = os.stat(out_fn) + assert sti2.st_nlink == sto2.st_nlink == 2 + assert sto1.st_ino == sto2.st_ino + # symlink + in_fn = 'input/link1' + out_fn = os.path.join(mountpoint, 'input', 'link1') + sti = os.stat(in_fn, follow_symlinks=False) + sto = os.stat(out_fn, follow_symlinks=False) + assert stat.S_ISLNK(sti.st_mode) + assert stat.S_ISLNK(sto.st_mode) + assert os.readlink(in_fn) == os.readlink(out_fn) + # FIFO + out_fn = os.path.join(mountpoint, 'input', 'fifo1') + sto = os.stat(out_fn) + assert stat.S_ISFIFO(sto.st_mode) def verify_aes_counter_uniqueness(self, method): seen = set() # Chunks already seen @@ -1160,7 +1209,7 @@ def test_remote_repo_restrict_to_path(self): # this was introduced because some tests expect stderr contents to show up # in "output" also. Also, the non-forking exec_cmd catches both, too. @unittest.skip('deadlock issues') - def test_fuse_mount(self): + def test_fuse(self): pass @unittest.skip('only works locally')