From 1c666222a746c215227315cd510fe4cb72cf0541 Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Sat, 25 Jun 2016 22:38:47 +0100 Subject: [PATCH 01/14] internals.rst: fix typos --- docs/internals.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 82be188bf..798ce8566 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -48,7 +48,7 @@ Lock files the repository. The locking system is based on creating a directory `lock.exclusive` (for -exclusive locks). Inside the lock directory, there is a file indication +exclusive locks). Inside the lock directory, there is a file indicating hostname, process id and thread id of the lock holder. There is also a json file `lock.roster` that keeps a directory of all shared @@ -338,7 +338,7 @@ more chunks than estimated above, because 1 file is at least 1 chunk). If a remote repository is used the repo index will be allocated on the remote side. -E.g. backing up a total count of 1 Mi (IEC binary prefix e.g. 2^20) files with a total size of 1TiB. +E.g. backing up a total count of 1 Mi (IEC binary prefix i.e. 2^20) files with a total size of 1TiB. a) with ``create --chunker-params 10,23,16,4095`` (custom, like borg < 1.0 or attic): From 273bd57cd821b23614293fac56b9ec7fd57ba0d5 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 21 Aug 2016 18:13:23 +0200 Subject: [PATCH 02/14] re-enable fuse tests for RemoteArchiver at some time they had deadlock issues, but it worked for me now. --- borg/testsuite/archiver.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index 1774f347e..d67cb8792 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -1323,15 +1323,6 @@ class RemoteArchiverTestCase(ArchiverTestCase): with patch.object(RemoteRepository, 'extra_test_args', ['--restrict-to-path', '/foo', '--restrict-to-path', path_prefix]): self.cmd('init', self.repository_location + '_3') - # skip fuse tests here, they deadlock since this change in exec_cmd: - # -output = subprocess.check_output(borg + args, stderr=None) - # +output = subprocess.check_output(borg + args, stderr=subprocess.STDOUT) - # 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(self): - pass - @unittest.skip('only works locally') def test_debug_put_get_delete_obj(self): pass From 7e80f6821db1b29441d540f2d83d919a203d787b Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 21 Aug 2016 20:17:49 +0200 Subject: [PATCH 03/14] use trusty for testing, to have a recent FUSE --- .travis.yml | 3 +++ .travis/install.sh | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0ec266edd..c46fc6f56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,12 +10,15 @@ matrix: include: - python: 3.4 os: linux + dist: trusty env: TOXENV=py34 - python: 3.5 os: linux + dist: trusty env: TOXENV=py35 - python: 3.5 os: linux + dist: trusty env: TOXENV=flake8 - language: generic os: osx diff --git a/.travis/install.sh b/.travis/install.sh index 73e292ddd..6ebbbfed4 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -31,7 +31,6 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then python -m pip install --user 'virtualenv<14.0' else pip install 'virtualenv<14.0' - sudo add-apt-repository -y ppa:gezakovacs/lz4 sudo apt-get update sudo apt-get install -y liblz4-dev sudo apt-get install -y libacl1-dev From 32bd29548bcbb9267546cf93ffba76871b25cca9 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 21 Aug 2016 19:27:23 +0200 Subject: [PATCH 04/14] travis: test fuse-enabled borg --- .travis/install.sh | 5 ++++- tox.ini | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index 6ebbbfed4..64ccd5a24 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -16,6 +16,8 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then brew install lz4 brew outdated pyenv || brew upgrade pyenv + brew install pkg-config + brew install Caskroom/versions/osxfuse-beta case "${TOXENV}" in py34) @@ -34,10 +36,11 @@ else sudo apt-get update sudo apt-get install -y liblz4-dev sudo apt-get install -y libacl1-dev + sudo apt-get install -y libfuse-dev fuse pkg-config # optional, for FUSE support fi python -m virtualenv ~/.venv source ~/.venv/bin/activate pip install -r requirements.d/development.txt pip install codecov -pip install -e . +pip install -e .[fuse] diff --git a/tox.ini b/tox.ini index bc6195d93..699ef251a 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,8 @@ changedir = {toxworkdir} deps = -rrequirements.d/development.txt -rrequirements.d/attic.txt -commands = py.test --cov=borg --cov-config=../.coveragerc --benchmark-skip --pyargs {posargs:borg.testsuite} + -rrequirements.d/fuse.txt +commands = py.test -rs --cov=borg --cov-config=../.coveragerc --benchmark-skip --pyargs {posargs:borg.testsuite} # fakeroot -u needs some env vars: passenv = * From a7c370b5ed7fb6a692685eb94bc271f9b5308ee3 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 21 Aug 2016 23:37:07 +0200 Subject: [PATCH 05/14] add debug-info usage help file --- docs/usage/debug-info.rst.inc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/usage/debug-info.rst.inc diff --git a/docs/usage/debug-info.rst.inc b/docs/usage/debug-info.rst.inc new file mode 100644 index 000000000..2f4f7237f --- /dev/null +++ b/docs/usage/debug-info.rst.inc @@ -0,0 +1,35 @@ +.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit! + +.. _borg_debug-info: + +borg debug-info +--------------- +:: + + usage: borg debug-info [-h] [--critical] [--error] [--warning] [--info] + [--debug] [--lock-wait N] [--show-rc] + [--no-files-cache] [--umask M] [--remote-path PATH] + + display system information for debugging / bug reports + + optional arguments: + -h, --help show this help message and exit + --critical work on log level CRITICAL + --error work on log level ERROR + --warning work on log level WARNING (default) + --info, -v, --verbose + work on log level INFO + --debug work on log level DEBUG + --lock-wait N wait for the lock, but max. N seconds (default: 1). + --show-rc show/log the return code (rc) + --no-files-cache do not load/update the file metadata cache used to + detect unchanged files + --umask M set umask to M (local and remote, default: 0077) + --remote-path PATH set remote path to executable (default: "borg") + +Description +~~~~~~~~~~~ + +This command displays some system information that might be useful for bug +reports and debugging problems. If a traceback happens, this information is +already appended at the end of the traceback. From 484c091c622511d190a155a96bd2b1a8836a8642 Mon Sep 17 00:00:00 2001 From: Martin Hostettler Date: Mon, 22 Aug 2016 19:48:39 +0200 Subject: [PATCH 06/14] =?UTF-8?q?RepositoryServer:=20Don=E2=80=98t=20try?= =?UTF-8?q?=20to=20close=20the=20repository=20if=20it=20was=20not=20yet=20?= =?UTF-8?q?opened.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- borg/remote.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/borg/remote.py b/borg/remote.py index 472d1ac36..5c42b58a1 100644 --- a/borg/remote.py +++ b/borg/remote.py @@ -79,12 +79,14 @@ class RepositoryServer: # pragma: no cover if r: data = os.read(stdin_fd, BUFSIZE) if not data: - self.repository.close() + if self.repository is not None: + self.repository.close() return unpacker.feed(data) for unpacked in unpacker: if not (isinstance(unpacked, tuple) and len(unpacked) == 4): - self.repository.close() + if self.repository is not None: + self.repository.close() raise Exception("Unexpected RPC data format.") type, msgid, method, args = unpacked method = method.decode('ascii') From 248ccf0149ce638a83382299965d58cc7a557e82 Mon Sep 17 00:00:00 2001 From: sven Date: Tue, 23 Aug 2016 15:01:39 +0200 Subject: [PATCH 07/14] Update borg.css --- docs/borg_theme/css/borg.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/borg_theme/css/borg.css b/docs/borg_theme/css/borg.css index 7c5ec811d..612c0aa4a 100644 --- a/docs/borg_theme/css/borg.css +++ b/docs/borg_theme/css/borg.css @@ -9,6 +9,10 @@ background-color: #000000 !important; } +.wy-side-nav-search input[type="text"] { + border-color: #000000; +} + .wy-side-nav-search > a { color: rgba(255, 255, 255, 0.5); } From ddb1c60964209117483e4bc6771b1c612aba5817 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 22 Aug 2016 04:42:42 +0200 Subject: [PATCH 08/14] repo tests: use H(x) instead of byte literals --- borg/testsuite/repository.py | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/borg/testsuite/repository.py b/borg/testsuite/repository.py index 9c5a5a466..56e955785 100644 --- a/borg/testsuite/repository.py +++ b/borg/testsuite/repository.py @@ -61,59 +61,59 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test2(self): """Test multiple sequential transactions """ - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.repository.put(b'00000000000000000000000000000001', b'foo') + self.repository.put(H(0), b'foo') + self.repository.put(H(1), b'foo') self.repository.commit() - self.repository.delete(b'00000000000000000000000000000000') - self.repository.put(b'00000000000000000000000000000001', b'bar') + self.repository.delete(H(0)) + self.repository.put(H(1), b'bar') self.repository.commit() - self.assert_equal(self.repository.get(b'00000000000000000000000000000001'), b'bar') + self.assert_equal(self.repository.get(H(1)), b'bar') def test_consistency(self): """Test cache consistency """ - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'foo') - self.repository.put(b'00000000000000000000000000000000', b'foo2') - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'foo2') - self.repository.put(b'00000000000000000000000000000000', b'bar') - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'bar') - self.repository.delete(b'00000000000000000000000000000000') - self.assert_raises(Repository.ObjectNotFound, lambda: self.repository.get(b'00000000000000000000000000000000')) + self.repository.put(H(0), b'foo') + self.assert_equal(self.repository.get(H(0)), b'foo') + self.repository.put(H(0), b'foo2') + self.assert_equal(self.repository.get(H(0)), b'foo2') + self.repository.put(H(0), b'bar') + self.assert_equal(self.repository.get(H(0)), b'bar') + self.repository.delete(H(0)) + self.assert_raises(Repository.ObjectNotFound, lambda: self.repository.get(H(0))) def test_consistency2(self): """Test cache consistency2 """ - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'foo') + self.repository.put(H(0), b'foo') + self.assert_equal(self.repository.get(H(0)), b'foo') self.repository.commit() - self.repository.put(b'00000000000000000000000000000000', b'foo2') - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'foo2') + self.repository.put(H(0), b'foo2') + self.assert_equal(self.repository.get(H(0)), b'foo2') self.repository.rollback() - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'foo') + self.assert_equal(self.repository.get(H(0)), b'foo') def test_overwrite_in_same_transaction(self): """Test cache consistency2 """ - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.repository.put(b'00000000000000000000000000000000', b'foo2') + self.repository.put(H(0), b'foo') + self.repository.put(H(0), b'foo2') self.repository.commit() - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), b'foo2') + self.assert_equal(self.repository.get(H(0)), b'foo2') def test_single_kind_transactions(self): # put - self.repository.put(b'00000000000000000000000000000000', b'foo') + self.repository.put(H(0), b'foo') self.repository.commit() self.repository.close() # replace self.repository = self.open() with self.repository: - self.repository.put(b'00000000000000000000000000000000', b'bar') + self.repository.put(H(0), b'bar') self.repository.commit() # delete self.repository = self.open() with self.repository: - self.repository.delete(b'00000000000000000000000000000000') + self.repository.delete(H(0)) self.repository.commit() def test_list(self): @@ -131,22 +131,22 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test_max_data_size(self): max_data = b'x' * MAX_DATA_SIZE - self.repository.put(b'00000000000000000000000000000000', max_data) - self.assert_equal(self.repository.get(b'00000000000000000000000000000000'), max_data) + self.repository.put(H(0), max_data) + self.assert_equal(self.repository.get(H(0)), max_data) self.assert_raises(IntegrityError, - lambda: self.repository.put(b'00000000000000000000000000000001', max_data + b'x')) + lambda: self.repository.put(H(1), max_data + b'x')) class RepositoryCommitTestCase(RepositoryTestCaseBase): def add_keys(self): - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.repository.put(b'00000000000000000000000000000001', b'bar') - self.repository.put(b'00000000000000000000000000000003', b'bar') + self.repository.put(H(0), b'foo') + self.repository.put(H(1), b'bar') + self.repository.put(H(3), b'bar') self.repository.commit() - self.repository.put(b'00000000000000000000000000000001', b'bar2') - self.repository.put(b'00000000000000000000000000000002', b'boo') - self.repository.delete(b'00000000000000000000000000000003') + self.repository.put(H(1), b'bar2') + self.repository.put(H(2), b'boo') + self.repository.delete(H(3)) def test_replay_of_missing_index(self): self.add_keys() @@ -264,19 +264,19 @@ class RepositoryAppendOnlyTestCase(RepositoryTestCaseBase): def test_append_only(self): def segments_in_repository(): return len(list(self.repository.io.segment_iterator())) - self.repository.put(b'00000000000000000000000000000000', b'foo') + self.repository.put(H(0), b'foo') self.repository.commit() self.repository.append_only = False assert segments_in_repository() == 1 - self.repository.put(b'00000000000000000000000000000000', b'foo') + self.repository.put(H(0), b'foo') self.repository.commit() # normal: compact squashes the data together, only one segment assert segments_in_repository() == 1 self.repository.append_only = True assert segments_in_repository() == 1 - self.repository.put(b'00000000000000000000000000000000', b'foo') + self.repository.put(H(0), b'foo') self.repository.commit() # append only: does not compact, only new segments written assert segments_in_repository() == 2 From 0da0914955ee51aaa721617775f24813d01a4661 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 22 Aug 2016 04:52:18 +0200 Subject: [PATCH 09/14] repo tests: use H(x) instead of some similar constructs --- borg/testsuite/repository.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/borg/testsuite/repository.py b/borg/testsuite/repository.py index 56e955785..b95093e4a 100644 --- a/borg/testsuite/repository.py +++ b/borg/testsuite/repository.py @@ -44,8 +44,8 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test1(self): for x in range(100): - self.repository.put(('%-32d' % x).encode('ascii'), b'SOMEDATA') - key50 = ('%-32d' % 50).encode('ascii') + self.repository.put(H(x), b'SOMEDATA') + key50 = H(50) self.assert_equal(self.repository.get(key50), b'SOMEDATA') self.repository.delete(key50) self.assert_raises(Repository.ObjectNotFound, lambda: self.repository.get(key50)) @@ -56,7 +56,7 @@ class RepositoryTestCase(RepositoryTestCaseBase): for x in range(100): if x == 50: continue - self.assert_equal(repository2.get(('%-32d' % x).encode('ascii')), b'SOMEDATA') + self.assert_equal(repository2.get(H(x)), b'SOMEDATA') def test2(self): """Test multiple sequential transactions @@ -118,7 +118,7 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test_list(self): for x in range(100): - self.repository.put(('%-32d' % x).encode('ascii'), b'SOMEDATA') + self.repository.put(H(x), b'SOMEDATA') all = self.repository.list() self.assert_equal(len(all), 100) first_half = self.repository.list(limit=50) @@ -222,7 +222,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): self.assert_equal(len(self.repository), 3) def test_ignores_commit_tag_in_data(self): - self.repository.put(b'0' * 32, LoggedIO.COMMIT) + self.repository.put(H(0), LoggedIO.COMMIT) self.reopen() with self.repository: io = self.repository.io @@ -294,12 +294,12 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): def get_objects(self, *ids): for id_ in ids: - self.repository.get(('%032d' % id_).encode('ascii')) + self.repository.get(H(id_)) def add_objects(self, segments): for ids in segments: for id_ in ids: - self.repository.put(('%032d' % id_).encode('ascii'), b'data') + self.repository.put(H(id_), b'data') self.repository.commit() def get_head(self): @@ -310,7 +310,7 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): def corrupt_object(self, id_): idx = self.open_index() - segment, offset = idx[('%032d' % id_).encode('ascii')] + segment, offset = idx[H(id_)] with open(os.path.join(self.tmppath, 'repository', 'data', '0', str(segment)), 'r+b') as fd: fd.seek(offset) fd.write(b'BOOM') @@ -401,8 +401,8 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): self.assert_equal(set([1, 2, 3, 4, 5, 6]), self.list_objects()) def test_crash_before_compact(self): - self.repository.put(bytes(32), b'data') - self.repository.put(bytes(32), b'data2') + self.repository.put(H(0), b'data') + self.repository.put(H(0), b'data2') # Simulate a crash before compact with patch.object(Repository, 'compact_segments') as compact: self.repository.commit() @@ -410,7 +410,7 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): self.reopen() with self.repository: self.check(repair=True) - self.assert_equal(self.repository.get(bytes(32)), b'data2') + self.assert_equal(self.repository.get(H(0)), b'data2') class RemoteRepositoryTestCase(RepositoryTestCase): From 93517ca30ef56f9f5e7d1caccf8d06e12242e64b Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 22 Aug 2016 05:10:48 +0200 Subject: [PATCH 10/14] hashindex tests: use H(x) instead of some similar constructs note: hash values needed updating because H(x) formats differently. --- borg/testsuite/hashindex.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/borg/testsuite/hashindex.py b/borg/testsuite/hashindex.py index fba146952..75cd80227 100644 --- a/borg/testsuite/hashindex.py +++ b/borg/testsuite/hashindex.py @@ -23,19 +23,19 @@ class HashIndexTestCase(BaseTestCase): self.assert_equal(len(idx), 0) # Test set for x in range(100): - idx[bytes('%-32d' % x, 'ascii')] = make_value(x) + idx[H(x)] = make_value(x) self.assert_equal(len(idx), 100) for x in range(100): - self.assert_equal(idx[bytes('%-32d' % x, 'ascii')], make_value(x)) + self.assert_equal(idx[H(x)], make_value(x)) # Test update for x in range(100): - idx[bytes('%-32d' % x, 'ascii')] = make_value(x * 2) + idx[H(x)] = make_value(x * 2) self.assert_equal(len(idx), 100) for x in range(100): - self.assert_equal(idx[bytes('%-32d' % x, 'ascii')], make_value(x * 2)) + self.assert_equal(idx[H(x)], make_value(x * 2)) # Test delete for x in range(50): - del idx[bytes('%-32d' % x, 'ascii')] + del idx[H(x)] self.assert_equal(len(idx), 50) idx_name = tempfile.NamedTemporaryFile() idx.write(idx_name.name) @@ -47,7 +47,7 @@ class HashIndexTestCase(BaseTestCase): idx = cls.read(idx_name.name) self.assert_equal(len(idx), 50) for x in range(50, 100): - self.assert_equal(idx[bytes('%-32d' % x, 'ascii')], make_value(x * 2)) + self.assert_equal(idx[H(x)], make_value(x * 2)) idx.clear() self.assert_equal(len(idx), 0) idx.write(idx_name.name) @@ -56,11 +56,11 @@ class HashIndexTestCase(BaseTestCase): def test_nsindex(self): self._generic_test(NSIndex, lambda x: (x, x), - '80fba5b40f8cf12f1486f1ba33c9d852fb2b41a5b5961d3b9d1228cf2aa9c4c9') + 'b96ec1ddabb4278cc92261ee171f7efc979dc19397cc5e89b778f05fa25bf93f') def test_chunkindex(self): self._generic_test(ChunkIndex, lambda x: (x, x, x), - '1d71865e72e3c3af18d3c7216b6fa7b014695eaa3ed7f14cf9cd02fba75d1c95') + '9d437a1e145beccc790c69e66ba94fc17bd982d83a401c9c6e524609405529d8') def test_resize(self): n = 2000 # Must be >= MIN_BUCKETS @@ -70,11 +70,11 @@ class HashIndexTestCase(BaseTestCase): initial_size = os.path.getsize(idx_name.name) self.assert_equal(len(idx), 0) for x in range(n): - idx[bytes('%-32d' % x, 'ascii')] = x, x + idx[H(x)] = x, x idx.write(idx_name.name) self.assert_true(initial_size < os.path.getsize(idx_name.name)) for x in range(n): - del idx[bytes('%-32d' % x, 'ascii')] + del idx[H(x)] self.assert_equal(len(idx), 0) idx.write(idx_name.name) self.assert_equal(initial_size, os.path.getsize(idx_name.name)) @@ -82,7 +82,7 @@ class HashIndexTestCase(BaseTestCase): def test_iteritems(self): idx = NSIndex() for x in range(100): - idx[bytes('%-0.32d' % x, 'ascii')] = x, x + idx[H(x)] = x, x all = list(idx.iteritems()) self.assert_equal(len(all), 100) second_half = list(idx.iteritems(marker=all[49][0])) From 79de73685bc6b25607833b04aa8f619ebb381945 Mon Sep 17 00:00:00 2001 From: Martin Hostettler Date: Mon, 22 Aug 2016 19:50:53 +0200 Subject: [PATCH 11/14] remote: Change exception message for unexpected RPC data format to indicate dataflow direction don't print stacktraces to clean up error messages when sshing into a forces command to borg serve. --- borg/remote.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/borg/remote.py b/borg/remote.py index 5c42b58a1..debb003fe 100644 --- a/borg/remote.py +++ b/borg/remote.py @@ -37,6 +37,14 @@ class InvalidRPCMethod(Error): """RPC method {} is not valid""" +class UnexpectedRPCDataFormatFromClient(Error): + """Borg {}: Got unexpected RPC data format from client.""" + + +class UnexpectedRPCDataFormatFromServer(Error): + """Got unexpected RPC data format from server.""" + + class RepositoryServer: # pragma: no cover rpc_methods = ( '__len__', @@ -87,7 +95,7 @@ class RepositoryServer: # pragma: no cover if not (isinstance(unpacked, tuple) and len(unpacked) == 4): if self.repository is not None: self.repository.close() - raise Exception("Unexpected RPC data format.") + raise UnexpectedRPCDataFormatFromClient(__version__) type, msgid, method, args = unpacked method = method.decode('ascii') try: @@ -328,7 +336,7 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. self.unpacker.feed(data) for unpacked in self.unpacker: if not (isinstance(unpacked, tuple) and len(unpacked) == 4): - raise Exception("Unexpected RPC data format.") + raise UnexpectedRPCDataFormatFromServer() type, msgid, error, res = unpacked if msgid in self.ignore_responses: self.ignore_responses.remove(msgid) From 549be2129a8ac207df79abda6d0a21cdefc570dc Mon Sep 17 00:00:00 2001 From: Martin Hostettler Date: Mon, 22 Aug 2016 20:22:02 +0200 Subject: [PATCH 12/14] RepositoryServer: Add error message when connection was closed before opening repo. --- borg/remote.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/borg/remote.py b/borg/remote.py index debb003fe..20d0c1857 100644 --- a/borg/remote.py +++ b/borg/remote.py @@ -89,6 +89,9 @@ class RepositoryServer: # pragma: no cover if not data: if self.repository is not None: self.repository.close() + else: + os.write(stderr_fd, "Borg {}: Got connection close before repository was opened.\n" + .format(__version__).encode()) return unpacker.feed(data) for unpacked in unpacker: From c12bcff30fe3428cbca70d310a5adc9d4c807558 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 23 Aug 2016 22:40:01 +0200 Subject: [PATCH 13/14] work around fuse xattr test issue with recent fakeroot fakeroot >= 1.20.2 "supports" xattrs, but this support somehow leads to the fuse tests not seeing the xattrs in fuse, because the file visible in the fuse mount was not created via fakeroot. --- borg/testsuite/archiver.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index d67cb8792..f563ea428 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -276,8 +276,14 @@ class ArchiverTestCase(ArchiverTestCaseBase): os.path.join(self.input_path, 'hardlink')) # Symlink os.symlink('somewhere', os.path.join(self.input_path, 'link1')) - if xattr.is_enabled(self.input_path): - xattr.setxattr(os.path.join(self.input_path, 'file1'), 'user.foo', b'bar') + self.create_regular_file('fusexattr', size=1) + if not xattr.XATTR_FAKEROOT and xattr.is_enabled(self.input_path): + # ironically, due to the way how fakeroot works, comparing fuse file xattrs to orig file xattrs + # will FAIL if fakeroot supports xattrs, thus we only set the xattr if XATTR_FAKEROOT is False. + # This is because fakeroot with xattr-support does not propagate xattrs of the underlying file + # into "fakeroot space". Because the xattrs exposed by borgfs are these of an underlying file + # (from fakeroots point of view) they are invisible to the test process inside the fakeroot. + xattr.setxattr(os.path.join(self.input_path, 'fusexattr'), 'user.foo', b'bar') # XXX this always fails for me # ubuntu 14.04, on a TMP dir filesystem with user_xattr, using fakeroot # same for newer ubuntu and centos. @@ -342,7 +348,7 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.assert_in(name, list_output) self.assert_dirs_equal('input', 'output/input') info_output = self.cmd('info', self.repository_location + '::test') - item_count = 3 if has_lchflags else 4 # one file is UF_NODUMP + item_count = 4 if has_lchflags else 5 # one file is UF_NODUMP self.assert_in('Number of files: %d' % item_count, info_output) shutil.rmtree(self.cache_path) with environment_variable(BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK='yes'): @@ -1048,7 +1054,9 @@ class ArchiverTestCase(ArchiverTestCaseBase): 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): + in_fn = 'input/fusexattr' + out_fn = os.path.join(mountpoint, 'input', 'fusexattr') + if not xattr.XATTR_FAKEROOT and xattr.is_enabled(self.input_path): assert xattr.listxattr(out_fn) == ['user.foo', ] assert xattr.getxattr(out_fn, 'user.foo') == b'bar' else: From 25fa443d2b2215904090596de6c470d65db9e467 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 26 Aug 2016 20:59:22 +0200 Subject: [PATCH 14/14] repo tests: convert some more byte literals to H(x) --- src/borg/testsuite/repository.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/borg/testsuite/repository.py b/src/borg/testsuite/repository.py index 8754ba110..620bcaf3e 100644 --- a/src/borg/testsuite/repository.py +++ b/src/borg/testsuite/repository.py @@ -163,22 +163,22 @@ class LocalRepositoryTestCase(RepositoryTestCaseBase): assert self.repository.compact[0] == 41 + 9 def test_sparse1(self): - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.repository.put(b'00000000000000000000000000000001', b'123456789') + self.repository.put(H(0), b'foo') + self.repository.put(H(1), b'123456789') self.repository.commit() - self.repository.put(b'00000000000000000000000000000001', b'bar') + self.repository.put(H(1), b'bar') self._assert_sparse() def test_sparse2(self): - self.repository.put(b'00000000000000000000000000000000', b'foo') - self.repository.put(b'00000000000000000000000000000001', b'123456789') + self.repository.put(H(0), b'foo') + self.repository.put(H(1), b'123456789') self.repository.commit() - self.repository.delete(b'00000000000000000000000000000001') + self.repository.delete(H(1)) self._assert_sparse() def test_sparse_delete(self): - self.repository.put(b'00000000000000000000000000000000', b'1245') - self.repository.delete(b'00000000000000000000000000000000') + self.repository.put(H(0), b'1245') + self.repository.delete(H(0)) self.repository.io._write_fd.sync() # The on-line tracking works on a per-object basis... @@ -385,7 +385,7 @@ class RepositoryFreeSpaceTestCase(RepositoryTestCaseBase): self.reopen() with self.repository: - self.repository.put(b'00000000000000000000000000000000', b'foobar') + self.repository.put(H(0), b'foobar') with pytest.raises(Repository.InsufficientFreeSpaceError): self.repository.commit() @@ -393,13 +393,13 @@ class RepositoryFreeSpaceTestCase(RepositoryTestCaseBase): class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase): def setUp(self): super().setUp() - self.repository.put(b'00000000000000000000000000000000', b'foo') + self.repository.put(H(0), b'foo') self.repository.commit() self.repository.close() def do_commit(self): with self.repository: - self.repository.put(b'00000000000000000000000000000000', b'fox') + self.repository.put(H(0), b'fox') self.repository.commit() def test_corrupted_hints(self):