diff --git a/qubes/backup.py b/qubes/backup.py index 27e511e1..e5bf49c4 100644 --- a/qubes/backup.py +++ b/qubes/backup.py @@ -252,12 +252,27 @@ class Backup: # pylint: disable=too-many-instance-attributes class FileToBackup: # pylint: disable=too-few-public-methods - def __init__(self, file_path, subdir=None, name=None, size=None): + def __init__(self, file_path_or_func, subdir=None, name=None, size=None): + """Store a single file to backup + + :param file_path_or_func: path to the file or a function + returning one; in case of function, it can be a coroutine; + if a function is given, *name*, *subdir* and *size* needs to be + given too + :param subdir: directory in a backup archive to place file in + :param name: name of the file in the backup archive + :param size: size + """ + if callable(file_path_or_func): + assert subdir is not None \ + and name is not None \ + and size is not None + if size is None: - size = qubes.storage.file.get_disk_usage(file_path) + size = qubes.storage.file.get_disk_usage(file_path_or_func) if subdir is None: - abs_file_path = os.path.abspath(file_path) + abs_file_path = os.path.abspath(file_path_or_func) abs_base_dir = os.path.abspath( qubes.config.system_path["qubes_base_dir"]) + '/' abs_file_dir = os.path.dirname(abs_file_path) + '/' @@ -269,16 +284,17 @@ class Backup: if subdir and not subdir.endswith('/'): subdir += '/' - #: real path to the file - self.path = file_path + if name is None: + name = os.path.basename(file_path_or_func) + + #: real path to the file (or callable to get one) + self.path = file_path_or_func #: size of the file self.size = size #: directory in backup archive where file should be placed self.subdir = subdir #: use this name in the archive (aka rename) - self.name = os.path.basename(file_path) - if name is not None: - self.name = name + self.name = name class VMToBackup: # pylint: disable=too-few-public-methods @@ -370,7 +386,7 @@ class Backup: if not volume.save_on_stop: continue vm_files.append(self.FileToBackup( - volume.export(), + volume.export, subdir, name + '.img', volume.usage)) @@ -610,18 +626,21 @@ class Backup: # Files will be verified before untaring this. # Prefix the path in archive with filename["subdir"] to have it # verified during untar + path = file_info.path + if callable(path): + path = yield from qubes.utils.coro_maybe(path()) tar_cmdline = (["tar", "-Pc", '--sparse', - '-C', os.path.dirname(file_info.path)] + + '-C', os.path.dirname(path)] + (['--dereference'] if file_info.subdir != "dom0-home/" else []) + ['--xform=s:^%s:%s\\0:' % ( - os.path.basename(file_info.path), + os.path.basename(path), file_info.subdir), - os.path.basename(file_info.path) + os.path.basename(path) ]) - file_stat = os.stat(file_info.path) + file_stat = os.stat(path) if stat.S_ISBLK(file_stat.st_mode) or \ - file_info.name != os.path.basename(file_info.path): + file_info.name != os.path.basename(path): # tar doesn't handle content of block device, use our # writer # also use our tar writer when renaming file @@ -631,7 +650,7 @@ class Backup: '--override-name=%s' % ( os.path.join(file_info.subdir, os.path.basename( file_info.name))), - file_info.path] + path] if self.compressed: tar_cmdline.insert(-2, "--use-compress-program=%s" % self.compression_filter)