From edea587f3586974dfd50fa56899d7afef6c50375 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 28 Jul 2016 22:10:29 +0200 Subject: [PATCH 1/3] catch unpacker exceptions, resync, fixes #1351 --- borg/archive.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/borg/archive.py b/borg/archive.py index 9964f5f27..ac7d77565 100644 --- a/borg/archive.py +++ b/borg/archive.py @@ -754,6 +754,9 @@ def valid_msgpacked_dict(d, keys_serialized): class RobustUnpacker: """A restartable/robust version of the streaming msgpack unpacker """ + class UnpackerCrashed(Exception): + """raise if unpacker crashed""" + def __init__(self, validator, item_keys): super().__init__() self.item_keys = [msgpack.packb(name) for name in item_keys] @@ -798,7 +801,10 @@ class RobustUnpacker: pass data = data[1:] else: - return next(self._unpacker) + try: + return next(self._unpacker) + except (TypeError, ValueError) as err: + raise self.UnpackerCrashed(str(err)) class ArchiveChecker: @@ -1017,6 +1023,9 @@ class ArchiveChecker: yield item else: report('Did not get expected metadata dict when unpacking item metadata', chunk_id, i) + except RobustUnpacker.UnpackerCrashed as err: + report('Unpacker crashed while unpacking item metadata, trying to resync...', chunk_id, i) + unpacker.resync() except Exception: report('Exception while unpacking item metadata', chunk_id, i) raise From 852c583076efa8fbc29d576b2cd3c932b7f9c726 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 28 Jul 2016 22:12:34 +0200 Subject: [PATCH 2/3] handle unpacker exception with tighter scope --- borg/archive.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/borg/archive.py b/borg/archive.py index ac7d77565..854544779 100644 --- a/borg/archive.py +++ b/borg/archive.py @@ -792,13 +792,14 @@ class RobustUnpacker: self._unpacker.feed(data) try: item = next(self._unpacker) + except (TypeError, ValueError, StopIteration): + # Ignore exceptions that might be raised when feeding + # msgpack with invalid data + pass + else: if self.validator(item): self._resync = False return item - # Ignore exceptions that might be raised when feeding - # msgpack with invalid data - except (TypeError, ValueError, StopIteration): - pass data = data[1:] else: try: From 97383e9e6051dd174b588a18e67aff65acc7b5b6 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 28 Jul 2016 22:23:38 +0200 Subject: [PATCH 3/3] transform unpacker exception only at 1 place --- borg/archive.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/borg/archive.py b/borg/archive.py index 854544779..653e2dd1c 100644 --- a/borg/archive.py +++ b/borg/archive.py @@ -779,6 +779,14 @@ class RobustUnpacker: return self def __next__(self): + def unpack_next(): + try: + return next(self._unpacker) + except (TypeError, ValueError) as err: + # transform exceptions that might be raised when feeding + # msgpack with invalid data to a more specific exception + raise self.UnpackerCrashed(str(err)) + if self._resync: data = b''.join(self._buffered_data) while self._resync: @@ -791,10 +799,9 @@ class RobustUnpacker: self._unpacker = msgpack.Unpacker(object_hook=StableDict) self._unpacker.feed(data) try: - item = next(self._unpacker) - except (TypeError, ValueError, StopIteration): - # Ignore exceptions that might be raised when feeding - # msgpack with invalid data + item = unpack_next() + except (self.UnpackerCrashed, StopIteration): + # as long as we are resyncing, we also ignore StopIteration pass else: if self.validator(item): @@ -802,10 +809,7 @@ class RobustUnpacker: return item data = data[1:] else: - try: - return next(self._unpacker) - except (TypeError, ValueError) as err: - raise self.UnpackerCrashed(str(err)) + return unpack_next() class ArchiveChecker: