Parcourir la source

Repository: better error reporting for index/hints failures

Marian Beermann il y a 9 ans
Parent
commit
1f33861fd6
3 fichiers modifiés avec 22 ajouts et 8 suppressions
  1. 7 1
      borg/hashindex.pyx
  2. 10 2
      borg/helpers.py
  3. 5 5
      borg/repository.py

+ 7 - 1
borg/hashindex.pyx

@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 from collections import namedtuple
+import locale
 import os
 
 cimport cython
@@ -60,6 +61,11 @@ MAX_VALUE = _MAX_VALUE
 
 assert _MAX_VALUE % 2 == 1
 
+
+def decoded_strerror(errno):
+    return strerror(errno).decode(locale.getpreferredencoding(), 'surrogateescape')
+
+
 @cython.internal
 cdef class IndexBase:
     cdef HashIndex *index
@@ -72,7 +78,7 @@ cdef class IndexBase:
             self.index = hashindex_read(path)
             if not self.index:
                 if errno:
-                    raise OSError(errno, strerror(errno), path)
+                    raise OSError(errno, decoded_strerror(errno), os.fsdecode(path))
                 raise RuntimeError('hashindex_read failed')
         else:
             self.index = hashindex_init(capacity, self.key_size, self.value_size)

+ 10 - 2
borg/helpers.py

@@ -65,8 +65,16 @@ class ErrorWithTraceback(Error):
     traceback = True
 
 
-class InternalOSError(ErrorWithTraceback):
-    """Error while accessing repository / cache files"""
+class InternalOSError(Error):
+    """Error while accessing repository: [Errno {}] {}: {}"""
+
+    def __init__(self, os_error):
+        self.errno = os_error.errno
+        self.strerror = os_error.strerror
+        self.filename = os_error.filename
+
+    def get_message(self):
+        return self.__doc__.format(self.errno, self.strerror, self.filename)
 
 
 class IntegrityError(ErrorWithTraceback):

+ 5 - 5
borg/repository.py

@@ -243,11 +243,11 @@ class Repository:
             return NSIndex.read(index_path)
         except RuntimeError as error:
             assert str(error) == 'hashindex_read failed'  # everything else means we're in *deep* trouble
-            # corrupted index file, need to replay segments
+            logger.warning('Repository index missing or corrupted, trying to recover')
             try:
                 os.unlink(index_path)
             except OSError as e:
-                raise InternalOSError from e
+                raise InternalOSError(e) from None
             if not auto_recover:
                 raise
             self.prepare_txn(self.get_transaction_id())
@@ -255,7 +255,7 @@ class Repository:
             self.commit()
             return self.open_index(self.get_transaction_id())
         except OSError as e:
-            raise InternalOSError from e
+            raise InternalOSError(e) from None
 
     def prepare_txn(self, transaction_id, do_cleanup=True):
         self._active_txn = True
@@ -285,7 +285,7 @@ class Repository:
                 with open(hints_path, 'rb') as fd:
                     hints = msgpack.unpack(fd)
             except (msgpack.UnpackException, msgpack.ExtraData, FileNotFoundError) as e:
-                # corrupted or deleted hints file, need to replay segments
+                logger.warning('Repository hints file missing or corrupted, trying to recover')
                 if not isinstance(e, FileNotFoundError):
                     os.unlink(hints_path)
                 # index must exist at this point
@@ -294,7 +294,7 @@ class Repository:
                 self.prepare_txn(transaction_id)
                 return
             except OSError as os_error:
-                raise InternalOSError from os_error
+                raise InternalOSError(os_error) from None
             if hints[b'version'] == 1:
                 logger.debug('Upgrading from v1 hints.%d', transaction_id)
                 self.segments = hints[b'segments']