mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-09 08:51:54 -04:00
Handle permission and similar errors on the index
This commit is contained in:
parent
252c1b9802
commit
d979a84f37
3 changed files with 41 additions and 14 deletions
|
|
@ -27,6 +27,14 @@ cdef extern from "_hashindex.c":
|
|||
uint32_t _le32toh(uint32_t v)
|
||||
|
||||
|
||||
cdef extern from "errno.h":
|
||||
int errno
|
||||
|
||||
|
||||
cdef extern from "string.h":
|
||||
char *strerror(int errnum)
|
||||
|
||||
|
||||
cdef _NoDefault = object()
|
||||
|
||||
"""
|
||||
|
|
@ -63,6 +71,8 @@ cdef class IndexBase:
|
|||
path = os.fsencode(path)
|
||||
self.index = hashindex_read(path)
|
||||
if not self.index:
|
||||
if errno:
|
||||
raise OSError(errno, strerror(errno), path)
|
||||
raise RuntimeError('hashindex_read failed')
|
||||
else:
|
||||
self.index = hashindex_init(capacity, self.key_size, self.value_size)
|
||||
|
|
|
|||
|
|
@ -238,20 +238,24 @@ class Repository:
|
|||
def open_index(self, transaction_id, auto_recover=True):
|
||||
if transaction_id is None:
|
||||
return NSIndex()
|
||||
index_path = (os.path.join(self.path, 'index.%d') % transaction_id).encode('utf-8')
|
||||
index_path = os.path.join(self.path, 'index.%d' % transaction_id).encode('utf-8')
|
||||
try:
|
||||
return NSIndex.read(index_path)
|
||||
except RuntimeError as re:
|
||||
assert str(re) == 'hashindex_read failed' # everything else means we're in *deep* trouble
|
||||
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
|
||||
os.unlink(os.path.join(self.path, 'hints.%d' % transaction_id))
|
||||
os.unlink(os.path.join(self.path, 'index.%d' % transaction_id))
|
||||
try:
|
||||
os.unlink(index_path)
|
||||
except OSError as e:
|
||||
raise InternalOSError from e
|
||||
if not auto_recover:
|
||||
raise
|
||||
self.prepare_txn(self.get_transaction_id())
|
||||
# don't leave an open transaction around
|
||||
self.commit()
|
||||
return self.open_index(self.get_transaction_id())
|
||||
except OSError as e:
|
||||
raise InternalOSError from e
|
||||
|
||||
def prepare_txn(self, transaction_id, do_cleanup=True):
|
||||
self._active_txn = True
|
||||
|
|
@ -275,15 +279,17 @@ class Repository:
|
|||
else:
|
||||
if do_cleanup:
|
||||
self.io.cleanup(transaction_id)
|
||||
hints_path = os.path.join(self.path, 'hints.%d' % transaction_id)
|
||||
index_path = os.path.join(self.path, 'index.%d' % transaction_id)
|
||||
try:
|
||||
with open(os.path.join(self.path, 'hints.%d' % transaction_id), 'rb') as fd:
|
||||
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
|
||||
if not isinstance(e, FileNotFoundError):
|
||||
os.unlink(os.path.join(self.path, 'hints.%d' % transaction_id))
|
||||
os.unlink(hints_path)
|
||||
# index must exist at this point
|
||||
os.unlink(os.path.join(self.path, 'index.%d' % transaction_id))
|
||||
os.unlink(index_path)
|
||||
self.check_transaction()
|
||||
self.prepare_txn(transaction_id)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -283,14 +283,18 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase):
|
|||
self.repository.commit()
|
||||
|
||||
def test_corrupted_hints(self):
|
||||
with open(os.path.join(self.repository.path, 'hints.0'), 'ab') as fp:
|
||||
fp.write(b'123456789')
|
||||
with open(os.path.join(self.repository.path, 'hints.0'), 'ab') as fd:
|
||||
fd.write(b'123456789')
|
||||
self.do_commit()
|
||||
|
||||
def test_deleted_hints(self):
|
||||
os.unlink(os.path.join(self.repository.path, 'hints.0'))
|
||||
self.do_commit()
|
||||
|
||||
def test_deleted_index(self):
|
||||
os.unlink(os.path.join(self.repository.path, 'index.0'))
|
||||
self.do_commit()
|
||||
|
||||
def test_unreadable_hints(self):
|
||||
hints = os.path.join(self.repository.path, 'hints.0')
|
||||
os.unlink(hints)
|
||||
|
|
@ -299,16 +303,23 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase):
|
|||
self.do_commit()
|
||||
|
||||
def test_index(self):
|
||||
with open(os.path.join(self.repository.path, 'index.0'), 'wb') as fp:
|
||||
fp.write(b'123456789')
|
||||
with open(os.path.join(self.repository.path, 'index.0'), 'wb') as fd:
|
||||
fd.write(b'123456789')
|
||||
self.do_commit()
|
||||
|
||||
def test_index_outside_transaction(self):
|
||||
with open(os.path.join(self.repository.path, 'index.0'), 'wb') as fp:
|
||||
fp.write(b'123456789')
|
||||
with open(os.path.join(self.repository.path, 'index.0'), 'wb') as fd:
|
||||
fd.write(b'123456789')
|
||||
with self.repository:
|
||||
assert len(self.repository) == 1
|
||||
|
||||
def test_unreadable_index(self):
|
||||
index = os.path.join(self.repository.path, 'index.0')
|
||||
os.unlink(index)
|
||||
os.mkdir(index)
|
||||
with self.assert_raises(InternalOSError):
|
||||
self.do_commit()
|
||||
|
||||
|
||||
class RepositoryCheckTestCase(RepositoryTestCaseBase):
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue