|
@@ -56,7 +56,8 @@ def argument(args, str_or_bool):
|
|
|
return str_or_bool
|
|
|
|
|
|
|
|
|
-def with_repository(fake=False, invert_fake=False, create=False, lock=True, exclusive=False, manifest=True, cache=False):
|
|
|
+def with_repository(fake=False, invert_fake=False, create=False, lock=True,
|
|
|
+ exclusive=False, manifest=True, cache=False, compatibility=None):
|
|
|
"""
|
|
|
Method decorator for subcommand-handling methods: do_XYZ(self, args, repository, …)
|
|
|
|
|
@@ -67,7 +68,20 @@ def with_repository(fake=False, invert_fake=False, create=False, lock=True, excl
|
|
|
:param exclusive: (str or bool) lock repository exclusively (for writing)
|
|
|
:param manifest: load manifest and key, pass them as keyword arguments
|
|
|
:param cache: open cache, pass it as keyword argument (implies manifest)
|
|
|
+ :param compatibility: mandatory if not create and (manifest or cache), specifies mandatory feature categories to check
|
|
|
"""
|
|
|
+
|
|
|
+ if not create and (manifest or cache):
|
|
|
+ if compatibility is None:
|
|
|
+ raise AssertionError("with_repository decorator used without compatibility argument")
|
|
|
+ if type(compatibility) is not tuple:
|
|
|
+ raise AssertionError("with_repository decorator compatibility argument must be of type tuple")
|
|
|
+ else:
|
|
|
+ if compatibility is not None:
|
|
|
+ raise AssertionError("with_repository called with compatibility argument but would not check" + repr(compatibility))
|
|
|
+ if create:
|
|
|
+ compatibility = Manifest.NO_OPERATION_CHECK
|
|
|
+
|
|
|
def decorator(method):
|
|
|
@functools.wraps(method)
|
|
|
def wrapper(self, args, **kwargs):
|
|
@@ -84,7 +98,7 @@ def with_repository(fake=False, invert_fake=False, create=False, lock=True, excl
|
|
|
append_only=append_only)
|
|
|
with repository:
|
|
|
if manifest or cache:
|
|
|
- kwargs['manifest'], kwargs['key'] = Manifest.load(repository)
|
|
|
+ kwargs['manifest'], kwargs['key'] = Manifest.load(repository, compatibility)
|
|
|
if cache:
|
|
|
with Cache(repository, kwargs['key'], kwargs['manifest'],
|
|
|
do_files=getattr(args, 'cache_files', False), lock_wait=self.lock_wait) as cache_:
|
|
@@ -176,7 +190,7 @@ class Archiver:
|
|
|
return EXIT_WARNING
|
|
|
return EXIT_SUCCESS
|
|
|
|
|
|
- @with_repository()
|
|
|
+ @with_repository(compatibility=(Manifest.Operation.CHECK,))
|
|
|
def do_change_passphrase(self, args, repository, manifest, key):
|
|
|
"""Change repository key file passphrase"""
|
|
|
if not hasattr(key, 'change_passphrase'):
|
|
@@ -241,7 +255,7 @@ class Archiver:
|
|
|
logger.info('Key updated')
|
|
|
return EXIT_SUCCESS
|
|
|
|
|
|
- @with_repository(fake='dry_run', exclusive=True)
|
|
|
+ @with_repository(fake='dry_run', exclusive=True, compatibility=(Manifest.Operation.WRITE,))
|
|
|
def do_create(self, args, repository, manifest=None, key=None):
|
|
|
"""Create new archive"""
|
|
|
matcher = PatternMatcher(fallback=True)
|
|
@@ -424,7 +438,7 @@ class Archiver:
|
|
|
return matcher.match(item[b'path'])
|
|
|
return item_filter
|
|
|
|
|
|
- @with_repository()
|
|
|
+ @with_repository(compatibility=(Manifest.Operation.READ,))
|
|
|
@with_archive
|
|
|
def do_extract(self, args, repository, manifest, key, archive):
|
|
|
"""Extract archive contents"""
|
|
@@ -490,7 +504,7 @@ class Archiver:
|
|
|
self.print_warning("Include pattern '%s' never matched.", pattern)
|
|
|
return self.exit_code
|
|
|
|
|
|
- @with_repository(exclusive=True, cache=True)
|
|
|
+ @with_repository(exclusive=True, cache=True, compatibility=(Manifest.Operation.CHECK,))
|
|
|
@with_archive
|
|
|
def do_rename(self, args, repository, manifest, key, cache, archive):
|
|
|
"""Rename an existing archive"""
|
|
@@ -506,7 +520,7 @@ class Archiver:
|
|
|
"""Delete an existing repository or archive"""
|
|
|
if args.location.archive:
|
|
|
archive_name = args.location.archive
|
|
|
- manifest, key = Manifest.load(repository)
|
|
|
+ manifest, key = Manifest.load(repository, (Manifest.Operation.DELETE,))
|
|
|
|
|
|
if args.forced == 2:
|
|
|
try:
|
|
@@ -537,7 +551,7 @@ class Archiver:
|
|
|
if not args.cache_only:
|
|
|
msg = []
|
|
|
try:
|
|
|
- manifest, key = Manifest.load(repository)
|
|
|
+ manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK)
|
|
|
except NoManifestError:
|
|
|
msg.append("You requested to completely DELETE the repository *including* all archives it may contain.")
|
|
|
msg.append("This repository seems to have no manifest, so we can't tell anything about its contents.")
|
|
@@ -573,7 +587,7 @@ class Archiver:
|
|
|
|
|
|
return self._do_mount(args)
|
|
|
|
|
|
- @with_repository()
|
|
|
+ @with_repository(compatibility=(Manifest.Operation.READ,))
|
|
|
def _do_mount(self, args, repository, manifest, key):
|
|
|
from .fuse import FuseOperations
|
|
|
|
|
@@ -595,7 +609,7 @@ class Archiver:
|
|
|
"""un-mount the FUSE filesystem"""
|
|
|
return umount(args.mountpoint)
|
|
|
|
|
|
- @with_repository()
|
|
|
+ @with_repository(compatibility=(Manifest.Operation.READ,))
|
|
|
def do_list(self, args, repository, manifest, key):
|
|
|
"""List archive or repository contents"""
|
|
|
if args.location.archive:
|
|
@@ -681,7 +695,7 @@ class Archiver:
|
|
|
print(format_archive(archive_info))
|
|
|
return self.exit_code
|
|
|
|
|
|
- @with_repository(cache=True)
|
|
|
+ @with_repository(cache=True, compatibility=(Manifest.Operation.READ,))
|
|
|
@with_archive
|
|
|
def do_info(self, args, repository, manifest, key, archive, cache):
|
|
|
"""Show archive details such as disk space used"""
|
|
@@ -699,7 +713,7 @@ class Archiver:
|
|
|
print(str(cache))
|
|
|
return self.exit_code
|
|
|
|
|
|
- @with_repository(exclusive=True)
|
|
|
+ @with_repository(exclusive=True, compatibility=(Manifest.Operation.DELETE,))
|
|
|
def do_prune(self, args, repository, manifest, key):
|
|
|
"""Prune repository archives according to specified rules"""
|
|
|
if not any((args.hourly, args.daily,
|
|
@@ -759,7 +773,7 @@ class Archiver:
|
|
|
def do_upgrade(self, args, repository, manifest=None, key=None):
|
|
|
"""upgrade a repository from a previous version"""
|
|
|
if args.tam:
|
|
|
- manifest, key = Manifest.load(repository, force_tam_not_required=args.force)
|
|
|
+ manifest, key = Manifest.load(repository, (Manifest.Operation.CHECK,), force_tam_not_required=args.force)
|
|
|
|
|
|
if not hasattr(key, 'change_passphrase'):
|
|
|
print('This repository is not encrypted, cannot enable TAM.')
|
|
@@ -784,7 +798,7 @@ class Archiver:
|
|
|
open(tam_file, 'w').close()
|
|
|
print('Updated security database')
|
|
|
elif args.disable_tam:
|
|
|
- manifest, key = Manifest.load(repository, force_tam_not_required=True)
|
|
|
+ manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK, force_tam_not_required=True)
|
|
|
if tam_required(repository):
|
|
|
os.unlink(tam_required_file(repository))
|
|
|
if key.tam_required:
|
|
@@ -817,7 +831,7 @@ class Archiver:
|
|
|
print(sysinfo())
|
|
|
return EXIT_SUCCESS
|
|
|
|
|
|
- @with_repository()
|
|
|
+ @with_repository(compatibility=Manifest.NO_OPERATION_CHECK)
|
|
|
def do_debug_dump_archive_items(self, args, repository, manifest, key):
|
|
|
"""dump (decrypted, decompressed) archive items metadata (not: data)"""
|
|
|
archive = Archive(repository, key, manifest, args.location.archive)
|
|
@@ -830,7 +844,7 @@ class Archiver:
|
|
|
print('Done.')
|
|
|
return EXIT_SUCCESS
|
|
|
|
|
|
- @with_repository()
|
|
|
+ @with_repository(compatibility=Manifest.NO_OPERATION_CHECK)
|
|
|
def do_debug_dump_repo_objs(self, args, repository, manifest, key):
|
|
|
"""dump (decrypted, decompressed) repo objects"""
|
|
|
marker = None
|
|
@@ -904,7 +918,7 @@ class Archiver:
|
|
|
print('Done.')
|
|
|
return EXIT_SUCCESS
|
|
|
|
|
|
- @with_repository(manifest=False, exclusive=True, cache=True)
|
|
|
+ @with_repository(manifest=False, exclusive=True, cache=True, compatibility=Manifest.NO_OPERATION_CHECK)
|
|
|
def do_debug_refcount_obj(self, args, repository, manifest, key, cache):
|
|
|
"""display refcounts for the objects with the given IDs"""
|
|
|
for hex_id in args.ids:
|