1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2024-12-29 11:16:43 +00:00

Merge pull request #2964 from ThomasWaldmann/detect-attic-repos-1.1

Detect non-upgraded Attic repositories
This commit is contained in:
TW 2017-08-27 14:52:53 +02:00 committed by GitHub
commit 1b7b58e712
6 changed files with 52 additions and 5 deletions

View file

@ -500,6 +500,8 @@ Errors
Insufficient free space to complete transaction (required: {}, available: {}).
Repository.InvalidRepository
{} is not a valid repository. Check repo config.
Repository.AtticRepository
Attic repository detected. Please run "borg upgrade {}".
Repository.ObjectNotFound
Object with key {} not found in repository {}.

View file

@ -733,6 +733,11 @@ def handle_error(unpacked):
raise IntegrityError('(not available)')
else:
raise IntegrityError(args[0].decode())
elif error == 'AtticRepository':
if old_server:
raise Repository.AtticRepository('(not available)')
else:
raise Repository.AtticRepository(args[0].decode())
elif error == 'PathNotAllowed':
if old_server:
raise PathNotAllowed('(unknown)')

View file

@ -30,6 +30,8 @@
MAGIC = b'BORG_SEG'
MAGIC_LEN = len(MAGIC)
ATTIC_MAGIC = b'ATTICSEG'
assert len(ATTIC_MAGIC) == MAGIC_LEN
TAG_PUT = 0
TAG_DELETE = 1
TAG_COMMIT = 2
@ -116,6 +118,9 @@ class AlreadyExists(Error):
class InvalidRepository(Error):
"""{} is not a valid repository. Check repo config."""
class AtticRepository(Error):
"""Attic repository detected. Please run "borg upgrade {}"."""
class CheckNeeded(ErrorWithTraceback):
"""Inconsistency detected. Please run "borg check {}"."""
@ -134,7 +139,7 @@ class StorageQuotaExceeded(Error):
"""The storage quota ({}) has been exceeded ({}). Try deleting some archives."""
def __init__(self, path, create=False, exclusive=False, lock_wait=None, lock=True,
append_only=False, storage_quota=None):
append_only=False, storage_quota=None, check_segment_magic=True):
self.path = os.path.abspath(path)
self._location = Location('file://%s' % self.path)
self.io = None # type: LoggedIO
@ -154,6 +159,7 @@ def __init__(self, path, create=False, exclusive=False, lock_wait=None, lock=Tru
self.storage_quota = storage_quota
self.storage_quota_use = 0
self.transaction_doomed = None
self.check_segment_magic = check_segment_magic
def __del__(self):
if self.lock:
@ -375,6 +381,12 @@ def open(self, path, exclusive, lock_wait=None, lock=True):
self.storage_quota = self.config.getint('repository', 'storage_quota', fallback=0)
self.id = unhexlify(self.config.get('repository', 'id').strip())
self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
if self.check_segment_magic:
# read a segment and check whether we are dealing with a non-upgraded Attic repository
segment = self.io.get_latest_segment()
if segment is not None and self.io.get_segment_magic(segment) == ATTIC_MAGIC:
self.close()
raise self.AtticRepository(path)
def close(self):
if self.lock:
@ -1250,6 +1262,11 @@ def segment_exists(self, segment):
def segment_size(self, segment):
return os.path.getsize(self.segment_filename(segment))
def get_segment_magic(self, segment):
fd = self.get_fd(segment)
fd.seek(0)
return fd.read(MAGIC_LEN)
def iter_objects(self, segment, offset=0, include_data=False, read_data=True):
"""
Return object iterator for *segment*.

View file

@ -55,6 +55,7 @@
from . import BaseTestCase, changedir, environment_variable, no_selinux
from . import are_symlinks_supported, are_hardlinks_supported, are_fifos_supported, is_utime_fully_supported
from .platform import fakeroot_detected
from .upgrader import attic_repo
from . import key
@ -2733,6 +2734,27 @@ def test_extract_hardlinks(self):
assert os.stat('input/dir1/aaaa').st_nlink == 2
assert os.stat('input/dir1/source2').st_nlink == 2
def test_detect_attic_repo(self):
path = attic_repo(self.repository_path)
cmds = [
['create', path + '::test', self.tmpdir],
['extract', path + '::test'],
['check', path],
['rename', path + '::test', 'newname'],
['list', path],
['delete', path],
['prune', path],
['info', path + '::test'],
['mount', path, self.tmpdir],
['key', 'export', path, 'exported'],
['key', 'import', path, 'import'],
['change-passphrase', path],
['break-lock', path],
]
for args in cmds:
output = self.cmd(*args, fork=True, exit_code=2)
assert 'Attic repository detected.' in output
@unittest.skipUnless('binary' in BORG_EXES, 'no borg.exe available')
class ArchiverTestCaseBinary(ArchiverTestCase):

View file

@ -85,8 +85,8 @@ def test_convert_segments(attic_repo, inplace):
:param attic_repo: a populated attic repository (fixture)
"""
repo_path = attic_repo
# check should fail because of magic number
assert not repo_valid(repo_path)
with pytest.raises(Repository.AtticRepository):
repo_valid(repo_path)
repository = AtticRepositoryUpgrader(repo_path, create=False)
with repository:
segments = [filename for i, filename in repository.io.segment_iterator()]
@ -149,8 +149,8 @@ def test_convert_all(attic_repo, attic_key_file, inplace):
"""
repo_path = attic_repo
# check should fail because of magic number
assert not repo_valid(repo_path)
with pytest.raises(Repository.AtticRepository):
repo_valid(repo_path)
def stat_segment(path):
return os.stat(os.path.join(path, 'data', '0', '0'))

View file

@ -19,6 +19,7 @@
class AtticRepositoryUpgrader(Repository):
def __init__(self, *args, **kw):
kw['lock'] = False # do not create borg lock files (now) in attic repo
kw['check_segment_magic'] = False # skip the Attic check when upgrading
super().__init__(*args, **kw)
def upgrade(self, dryrun=True, inplace=False, progress=False):