|
@@ -16,6 +16,10 @@ ATTIC_MAGIC = b'ATTICSEG'
|
|
|
|
|
|
|
|
|
|
class AtticRepositoryUpgrader(Repository):
|
|
class AtticRepositoryUpgrader(Repository):
|
|
|
|
+ def __init__(self, *args, **kw):
|
|
|
|
+ kw['lock'] = False # do not create borg lock files (now) in attic repo
|
|
|
|
+ super().__init__(*args, **kw)
|
|
|
|
+
|
|
def upgrade(self, dryrun=True, inplace=False):
|
|
def upgrade(self, dryrun=True, inplace=False):
|
|
"""convert an attic repository to a borg repository
|
|
"""convert an attic repository to a borg repository
|
|
|
|
|
|
@@ -33,9 +37,19 @@ class AtticRepositoryUpgrader(Repository):
|
|
logger.info('making a hardlink copy in %s', backup)
|
|
logger.info('making a hardlink copy in %s', backup)
|
|
if not dryrun:
|
|
if not dryrun:
|
|
shutil.copytree(self.path, backup, copy_function=os.link)
|
|
shutil.copytree(self.path, backup, copy_function=os.link)
|
|
|
|
+ # we need to create a real copy (not hardlink copy) of index.* and hints.*
|
|
|
|
+ # otherwise the backup copy will get modified.
|
|
|
|
+ transaction_id = self.get_index_transaction_id()
|
|
|
|
+ for name in ['index', 'hints']:
|
|
|
|
+ fname = "%s.%d" % (name, transaction_id)
|
|
|
|
+ fname_orig = os.path.join(self.path, fname)
|
|
|
|
+ fname_backup = os.path.join(backup, fname)
|
|
|
|
+ os.remove(fname_backup)
|
|
|
|
+ shutil.copy(fname_orig, fname_backup)
|
|
|
|
+
|
|
logger.info("opening attic repository with borg and converting")
|
|
logger.info("opening attic repository with borg and converting")
|
|
- # we need to open the repo to load configuration, keyfiles and segments
|
|
|
|
- self.open(self.path, exclusive=False)
|
|
|
|
|
|
+ # now lock the repo, after we have made the copy
|
|
|
|
+ self.lock = UpgradableLock(os.path.join(self.path, 'lock'), exclusive=True, timeout=1.0).acquire()
|
|
segments = [filename for i, filename in self.io.segment_iterator()]
|
|
segments = [filename for i, filename in self.io.segment_iterator()]
|
|
try:
|
|
try:
|
|
keyfile = self.find_attic_keyfile()
|
|
keyfile = self.find_attic_keyfile()
|
|
@@ -48,13 +62,21 @@ class AtticRepositoryUpgrader(Repository):
|
|
self.lock = UpgradableLock(os.path.join(self.path, 'lock'),
|
|
self.lock = UpgradableLock(os.path.join(self.path, 'lock'),
|
|
exclusive=True).acquire()
|
|
exclusive=True).acquire()
|
|
try:
|
|
try:
|
|
|
|
+ self.convert_repo_index(dryrun)
|
|
self.convert_cache(dryrun)
|
|
self.convert_cache(dryrun)
|
|
self.convert_segments(segments, dryrun=dryrun, inplace=inplace)
|
|
self.convert_segments(segments, dryrun=dryrun, inplace=inplace)
|
|
|
|
+ self.borg_readme()
|
|
finally:
|
|
finally:
|
|
self.lock.release()
|
|
self.lock.release()
|
|
self.lock = None
|
|
self.lock = None
|
|
return backup
|
|
return backup
|
|
|
|
|
|
|
|
+ def borg_readme(self):
|
|
|
|
+ readme = os.path.join(self.path, 'README')
|
|
|
|
+ os.remove(readme)
|
|
|
|
+ with open(readme, 'w') as fd:
|
|
|
|
+ fd.write('This is a Borg repository\n')
|
|
|
|
+
|
|
@staticmethod
|
|
@staticmethod
|
|
def convert_segments(segments, dryrun=True, inplace=False):
|
|
def convert_segments(segments, dryrun=True, inplace=False):
|
|
"""convert repository segments from attic to borg
|
|
"""convert repository segments from attic to borg
|
|
@@ -139,8 +161,8 @@ class AtticRepositoryUpgrader(Repository):
|
|
with open(keyfile, 'w') as f:
|
|
with open(keyfile, 'w') as f:
|
|
f.write(data)
|
|
f.write(data)
|
|
|
|
|
|
- def convert_cache(self, dryrun):
|
|
|
|
- """convert caches from attic to borg
|
|
|
|
|
|
+ def convert_repo_index(self, dryrun):
|
|
|
|
+ """convert some repo files
|
|
|
|
|
|
those are all hash indexes, so we need to
|
|
those are all hash indexes, so we need to
|
|
`s/ATTICIDX/BORG_IDX/` in a few locations:
|
|
`s/ATTICIDX/BORG_IDX/` in a few locations:
|
|
@@ -150,22 +172,28 @@ class AtticRepositoryUpgrader(Repository):
|
|
should probably update, with a lock, see
|
|
should probably update, with a lock, see
|
|
`Repository.open()`, which i'm not sure we should use
|
|
`Repository.open()`, which i'm not sure we should use
|
|
because it may write data on `Repository.close()`...
|
|
because it may write data on `Repository.close()`...
|
|
-
|
|
|
|
- * the `files` and `chunks` cache (in `$ATTIC_CACHE_DIR` or
|
|
|
|
- `$HOME/.cache/attic/<repoid>/`), which we could just drop,
|
|
|
|
- but if we'd want to convert, we could open it with the
|
|
|
|
- `Cache.open()`, edit in place and then `Cache.close()` to
|
|
|
|
- make sure we have locking right
|
|
|
|
"""
|
|
"""
|
|
transaction_id = self.get_index_transaction_id()
|
|
transaction_id = self.get_index_transaction_id()
|
|
if transaction_id is None:
|
|
if transaction_id is None:
|
|
logger.warning('no index file found for repository %s' % self.path)
|
|
logger.warning('no index file found for repository %s' % self.path)
|
|
else:
|
|
else:
|
|
index = os.path.join(self.path, 'index.%d' % transaction_id).encode('utf-8')
|
|
index = os.path.join(self.path, 'index.%d' % transaction_id).encode('utf-8')
|
|
- logger.info("converting index index %s" % index)
|
|
|
|
|
|
+ logger.info("converting repo index %s" % index)
|
|
if not dryrun:
|
|
if not dryrun:
|
|
AtticRepositoryUpgrader.header_replace(index, b'ATTICIDX', b'BORG_IDX')
|
|
AtticRepositoryUpgrader.header_replace(index, b'ATTICIDX', b'BORG_IDX')
|
|
|
|
|
|
|
|
+ def convert_cache(self, dryrun):
|
|
|
|
+ """convert caches from attic to borg
|
|
|
|
+
|
|
|
|
+ those are all hash indexes, so we need to
|
|
|
|
+ `s/ATTICIDX/BORG_IDX/` in a few locations:
|
|
|
|
+
|
|
|
|
+ * the `files` and `chunks` cache (in `$ATTIC_CACHE_DIR` or
|
|
|
|
+ `$HOME/.cache/attic/<repoid>/`), which we could just drop,
|
|
|
|
+ but if we'd want to convert, we could open it with the
|
|
|
|
+ `Cache.open()`, edit in place and then `Cache.close()` to
|
|
|
|
+ make sure we have locking right
|
|
|
|
+ """
|
|
# copy of attic's get_cache_dir()
|
|
# copy of attic's get_cache_dir()
|
|
attic_cache_dir = os.environ.get('ATTIC_CACHE_DIR',
|
|
attic_cache_dir = os.environ.get('ATTIC_CACHE_DIR',
|
|
os.path.join(os.path.expanduser('~'),
|
|
os.path.join(os.path.expanduser('~'),
|