diff --git a/qubes/storage/file.py b/qubes/storage/file.py index 22c2d8a4..e5dadb18 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -176,11 +176,14 @@ class FilePool(qubes.storage.Pool): class FileVolume(qubes.storage.Volume): ''' Parent class for the xen volumes implementation which expects a `target_dir` param on initialization. ''' + _marker_running = object() + _marker_exported = object() def __init__(self, dir_path, **kwargs): self.dir_path = dir_path assert self.dir_path, "dir_path not specified" self._revisions_to_keep = 0 + self._locked = None super().__init__(**kwargs) if self.snap_on_start: @@ -265,10 +268,18 @@ class FileVolume(qubes.storage.Volume): return self def export(self): - if self.is_dirty(): + if self._locked is not None: + assert self._locked is FileVolume._marker_running, \ + 'nested calls to export()' self._not_implemented('exporting a dirty volume') + self._locked = FileVolume._marker_exported return self.path + def export_end(self, path): + assert self._locked is FileVolume._marker_exported, \ + 'cannot end an export that never began' + self._locked = None + @asyncio.coroutine def import_volume(self, src_volume): if src_volume.snap_on_start: @@ -311,6 +322,11 @@ class FileVolume(qubes.storage.Volume): return self def start(self): + if self._locked is not None: + assert self._locked is FileVolume._marker_exported, \ + 'nested calls to start()' + self._not_implemented('starting a VM with an exported volume') + self._locked = FileVolume._marker_running if not self.save_on_stop and not self.snap_on_start: self.reset() else: @@ -328,12 +344,15 @@ class FileVolume(qubes.storage.Volume): return self def stop(self): + assert self._locked is FileVolume._marker_running, \ + 'cannot stop a volume that has not been started' if self.save_on_stop: self.commit() elif self.snap_on_start: _remove_if_exists(self.path_cow) else: _remove_if_exists(self.path) + self._locked = None return self @property