From b4227f758175562e6382c30498f82fabf465d80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 3 Apr 2016 03:09:15 +0200 Subject: [PATCH] backup: use simple classes instead of hashes, use events for appmenus This makes typo errors much easier to find (also using pylint or so). While at it, also removed explicit appmenus backup, as it should be provided by appmenus extension. --- qubes/backup.py | 140 +++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 78 deletions(-) diff --git a/qubes/backup.py b/qubes/backup.py index d70186d4..1adc90b7 100644 --- a/qubes/backup.py +++ b/qubes/backup.py @@ -212,6 +212,36 @@ class SendWorker(Process): class Backup(object): + class FileToBackup(object): + def __init__(self, file_path, subdir=None): + sz = qubes.storage.get_disk_usage(file_path) + + if subdir is None: + abs_file_path = os.path.abspath(file_path) + abs_base_dir = os.path.abspath( + qubes.config.system_path["qubes_base_dir"]) + '/' + abs_file_dir = os.path.dirname(abs_file_path) + '/' + (nothing, directory, subdir) = abs_file_dir.partition(abs_base_dir) + assert nothing == "" + assert directory == abs_base_dir + else: + if len(subdir) > 0 and not subdir.endswith('/'): + subdir += '/' + + self.path = file_path + self.size = sz + self.subdir = subdir + + class VMToBackup(object): + def __init__(self, vm, files, subdir): + self.vm = vm + self.files = files + self.subdir = subdir + + @property + def size(self): + return reduce(lambda x, y: x + y.size, self.files, 0) + def __init__(self, app, vms_list=None, exclude_list=None, **kwargs): """ If vms = None, include all (sensible) VMs; @@ -307,27 +337,9 @@ class Backup(object): except OSError: pass - @staticmethod - def _file_to_backup(file_path, subdir=None): - sz = qubes.storage.get_disk_usage(file_path) - - if subdir is None: - abs_file_path = os.path.abspath(file_path) - abs_base_dir = os.path.abspath( - qubes.config.system_path["qubes_base_dir"]) + '/' - abs_file_dir = os.path.dirname(abs_file_path) + '/' - (nothing, directory, subdir) = abs_file_dir.partition(abs_base_dir) - assert nothing == "" - assert directory == abs_base_dir - else: - if len(subdir) > 0 and not subdir.endswith('/'): - subdir += '/' - return [{"path": file_path, "size": sz, "subdir": subdir}] - def get_files_to_backup(self): files_to_backup = {} - total_backup_sz = 0 for vm in self.vms_for_backup: if vm.qid == 0: # handle dom0 later @@ -340,43 +352,20 @@ class Backup(object): vm_files = [] if vm.private_img is not None: - vm_files += self._file_to_backup(vm.private_img, subdir) + vm_files.append(self.FileToBackup(vm.private_img, subdir)) - vm_files += self._file_to_backup(vm.icon_path, subdir) - if vm.updateable: - if os.path.exists(vm.dir_path + "/apps.templates"): - # template - vm_files += self._file_to_backup( - vm.dir_path + "/apps.templates", subdir) - else: - # standaloneVM - vm_files += self._file_to_backup( - vm.dir_path + "/apps", subdir) + vm_files.append(self.FileToBackup(vm.icon_path, subdir)) + vm_files.extend(self.FileToBackup(i, subdir) + for i in vm.fire_event('backup-get-files')) # TODO: drop after merging firewall.xml into qubes.xml firewall_conf = os.path.join(vm.dir_path, vm.firewall_conf) if os.path.exists(firewall_conf): - vm_files += self._file_to_backup( - firewall_conf, subdir) - if 'appmenus_whitelist' in qubes.config.vm_files and \ - os.path.exists(os.path.join(vm.dir_path, - qubes.config.vm_files[ - 'appmenus_whitelist'])): - vm_files += self._file_to_backup( - os.path.join(vm.dir_path, qubes.config.vm_files[ - 'appmenus_whitelist']), - subdir) + vm_files.append(self.FileToBackup(firewall_conf, subdir)) if vm.updateable: - vm_files += self._file_to_backup(vm.root_img, subdir) - vm_size = reduce(lambda x, y: x + y['size'], vm_files, 0) - files_to_backup[vm.qid] = { - 'vm': vm, - 'files': vm_files, - 'subdir': subdir, - 'size': vm_size, - } - total_backup_sz += vm_size + vm_files.append(self.FileToBackup(vm.root_img, subdir)) + files_to_backup[vm.qid] = self.VMToBackup(vm, vm_files, subdir) # Dom0 user home if 0 in [vm.qid for vm in self.vms_for_backup]: @@ -387,20 +376,16 @@ class Backup(object): # left after 'sudo bash' and similar commands subprocess.check_call(['sudo', 'chown', '-R', local_user, home_dir]) - home_sz = qubes.storage.get_disk_usage(home_dir) home_to_backup = [ - {"path": home_dir, "size": home_sz, "subdir": 'dom0-home/'}] + self.FileToBackup(home_dir, 'dom0-home/')] vm_files = home_to_backup - files_to_backup[0] = { - 'vm': self.app.domains[0], - 'files': vm_files, - 'subdir': os.path.join('dom0-home', os.path.basename(home_dir)), - 'size': home_sz, - } - total_backup_sz += home_sz + files_to_backup[0] = self.VMToBackup(self.app.domains[0], + vm_files, + os.path.join('dom0-home', os.path.basename(home_dir))) - self.total_backup_bytes = total_backup_sz + self.total_backup_bytes = reduce( + lambda x, y: x + y.size, files_to_backup.values(), 0) return files_to_backup @@ -541,12 +526,12 @@ class Backup(object): vm.backup_content = qubes.property.DEFAULT for qid, vm_info in files_to_backup.iteritems(): - if qid != 0 and vm_info['vm'].is_running(): - raise qubes.exc.QubesVMNotHaltedError(vm_info['vm']) + if qid != 0 and vm_info.vm.is_running(): + raise qubes.exc.QubesVMNotHaltedError(vm_info.vm) # VM is included in the backup - backup_app.domains[qid].backup_content = True - backup_app.domains[qid].backup_path = vm_info['subdir'] - backup_app.domains[qid].backup_size = vm_info['size'] + backup_app.domains[qid].backup-content = True + backup_app.domains[qid].backup-path = vm_info.subdir + backup_app.domains[qid].backup-size = vm_info.size backup_app.save() passphrase = self.passphrase.encode('utf-8') @@ -603,21 +588,20 @@ class Backup(object): to_send.put(f) vm_files_to_backup = self.get_files_to_backup() - qubes_xml_info = { - 'files': self._file_to_backup(qubes_xml, ''), - 'vm': None, - 'size': 0, - 'subdir': '', - } + qubes_xml_info = self.VMToBackup( + None, + [self.FileToBackup(qubes_xml, '')], + '' + ) for vm_info in itertools.chain([qubes_xml_info], vm_files_to_backup.itervalues()): - for file_info in vm_info['files']: + for file_info in vm_info.files: self.log.debug("Backing up {}".format(file_info)) backup_tempfile = os.path.join( - self.tmpdir, file_info["subdir"], - os.path.basename(file_info["path"])) + self.tmpdir, file_info.subdir, + os.path.basename(file_info.path)) self.log.debug("Using temporary location: {}".format( backup_tempfile)) @@ -631,13 +615,13 @@ class Backup(object): # verified during untar tar_cmdline = (["tar", "-Pc", '--sparse', "-f", backup_pipe, - '-C', os.path.dirname(file_info["path"])] + + '-C', os.path.dirname(file_info.path)] + (['--dereference'] if - file_info["subdir"] != "dom0-home/" else []) + + file_info.subdir != "dom0-home/" else []) + ['--xform', 's:^%s:%s\\0:' % ( - os.path.basename(file_info["path"]), - file_info["subdir"]), - os.path.basename(file_info["path"]) + os.path.basename(file_info.path), + file_info.subdir), + os.path.basename(file_info.path) ]) if self.compressed: tar_cmdline.insert(-1, @@ -760,7 +744,7 @@ class Backup(object): pipe.close() # This VM done, update progress - self._done_vms_bytes += vm_info['size'] + self._done_vms_bytes += vm_info.size self._current_vm_bytes = 0 self._send_progress_update()