diff --git a/doc/qubes-storage.rst b/doc/qubes-storage.rst index 3d9fafa6..d2abdeab 100644 --- a/doc/qubes-storage.rst +++ b/doc/qubes-storage.rst @@ -99,6 +99,10 @@ Methods and properties required to be implemented by the volume class: - :py:meth:`~qubes.storage.Volume.export` - return a path to be read to extract volume data; for complex formats, this can be a pipe (connected to some data-extracting process) + - :py:meth:`~qubes.storage.Volume.export_end` - cleanup after exporting the + data; this function is called when the path returned by + :py:meth:`~qubes.storage.Volume.export` is not used anymore. This method + optional - some storage drivers may not implement it if not needed. - :py:meth:`~qubes.storage.Volume.import_data` - return a path the data should be written to, to import volume data; for complex formats, this can be pipe (connected to some data-importing process) diff --git a/qubes/backup.py b/qubes/backup.py index 661af44a..5b81b2d0 100644 --- a/qubes/backup.py +++ b/qubes/backup.py @@ -394,7 +394,8 @@ class Backup: volume.export, subdir, name + '.img', - volume.usage)) + volume.usage, + cleanup_func=volume.export_end)) vm_files.extend(self.FileToBackup(i, subdir) for i in vm.fire_event('backup-get-files')) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 519c8bf3..f778c41b 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -206,6 +206,18 @@ class Volume: ''' raise self._not_implemented("export") + def export_end(self, path): + """ Cleanup after exporting data. + + This method is called after exporting the volume data (using + :py:meth:`export`), when the *path* is not needed anymore. + + This can be implemented as a coroutine. + + :param path: path to cleanup, returned by :py:meth:`export` + """ + # do nothing by default (optional method) + def import_data(self, size): ''' Returns a path to overwrite volume data. @@ -643,6 +655,19 @@ class Storage: return self.vm.volumes[volume].export() + @asyncio.coroutine + def export_end(self, volume, export_path): + """ Cleanup after exporting data from the volume + + :param volume: volume that was exported + :param export_path: path returned by the export() call + """ + assert isinstance(volume, (Volume, str)), \ + "You need to pass a Volume or pool name as str" + if not isinstance(volume, Volume): + volume = self.vm.volumes[volume] + yield from qubes.utils.coro_maybe(volume.export_end(export_path)) + @asyncio.coroutine def import_data(self, volume, size): '''