storage/reflink: add _path_import (don't reuse _path_dirty)

Import volume data to a new _path_import (instead of _path_dirty) before
committing to _path_clean. In case the computer crashes while an import
operation is running, the partially written file should not be attached
to Xen on the next volume startup.

Use <name>-import.img as the filename like 'file' does, to be compatible
with qubes.tests.api_admin/TC_00_VMs/test_510_vm_volume_import.
This commit is contained in:
Rusty Bird 2018-09-09 20:01:18 +00:00
parent d301aa2e50
commit 60bf68a748
No known key found for this signature in database
GPG Key ID: 469D78F47AAF2ADF

View File

@ -121,6 +121,7 @@ class ReflinkVolume(qubes.storage.Volume):
self._path_vid = os.path.join(self.pool.dir_path, self.vid) self._path_vid = os.path.join(self.pool.dir_path, self.vid)
self._path_clean = self._path_vid + '.img' self._path_clean = self._path_vid + '.img'
self._path_dirty = self._path_vid + '-dirty.img' self._path_dirty = self._path_vid + '-dirty.img'
self._path_import = self._path_vid + '-import.img'
self.path = self._path_dirty self.path = self._path_dirty
def create(self): def create(self):
@ -156,6 +157,7 @@ class ReflinkVolume(qubes.storage.Volume):
def _cleanup(self): def _cleanup(self):
for tmp in glob.iglob(glob.escape(self._path_vid) + '*.img*~*'): for tmp in glob.iglob(glob.escape(self._path_vid) + '*.img*~*'):
_remove_file(tmp) _remove_file(tmp)
_remove_file(self._path_import)
def is_outdated(self): def is_outdated(self):
if self.snap_on_start: if self.snap_on_start:
@ -183,16 +185,16 @@ class ReflinkVolume(qubes.storage.Volume):
def stop(self): def stop(self):
if self.save_on_stop: if self.save_on_stop:
self._commit() self._commit(self._path_dirty)
else: else:
_remove_file(self._path_dirty) _remove_file(self._path_dirty)
_remove_file(self._path_clean) _remove_file(self._path_clean)
return self return self
def _commit(self): def _commit(self, path_from):
self._add_revision() self._add_revision()
self._prune_revisions() self._prune_revisions()
_rename_file(self._path_dirty, self._path_clean) _rename_file(path_from, self._path_clean)
def _add_revision(self): def _add_revision(self):
if self.revisions_to_keep == 0: if self.revisions_to_keep == 0:
@ -263,20 +265,20 @@ class ReflinkVolume(qubes.storage.Volume):
def import_data(self): def import_data(self):
self._require_save_on_stop('import_data') self._require_save_on_stop('import_data')
_create_sparse_file(self._path_dirty, self.size) _create_sparse_file(self._path_import, self.size)
return self._path_dirty return self._path_import
def import_data_end(self, success): def import_data_end(self, success):
if success: if success:
self._commit() self._commit(self._path_import)
else: else:
_remove_file(self._path_dirty) _remove_file(self._path_import)
return self return self
def import_volume(self, src_volume): def import_volume(self, src_volume):
self._require_save_on_stop('import_volume') self._require_save_on_stop('import_volume')
try: try:
_copy_file(src_volume.export(), self._path_dirty) _copy_file(src_volume.export(), self._path_import)
except: except:
self.import_data_end(False) self.import_data_end(False)
raise raise