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