diff --git a/qubes/storage/file.py b/qubes/storage/file.py index f598f406..ead07f4f 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -267,17 +267,29 @@ class FileVolume(qubes.storage.Volume): return self.path def import_volume(self, src_volume): - msg = "Can not import snapshot volume {!s} in to pool {!s} " - msg = msg.format(src_volume, self) - assert not src_volume.snap_on_start, msg + if src_volume.snap_on_start: + raise qubes.storage.StoragePoolException( + "Can not import snapshot volume {!s} in to pool {!s} ".format( + src_volume, self)) if self.save_on_stop: _remove_if_exists(self.path) copy_file(src_volume.export(), self.path) return self - def import_data(self): - return self.path + if not self.save_on_stop: + raise qubes.storage.StoragePoolException( + "Can not import into save_on_stop=False volume {!s}".format( + self)) + create_sparse_file(self.path_import, self.size) + return self.path_import + + def import_data_end(self, success): + if success: + os.rename(self.path_import, self.path) + else: + os.unlink(self.path_import) + return self def reset(self): ''' Remove and recreate a volatile volume ''' @@ -327,6 +339,11 @@ class FileVolume(qubes.storage.Volume): img_name = self.vid + '-cow.img' return os.path.join(self.dir_path, img_name) + @property + def path_import(self): + img_name = self.vid + '-import.img' + return os.path.join(self.dir_path, img_name) + def verify(self): ''' Verifies the volume. ''' if not os.path.exists(self.path) and \ diff --git a/qubes/storage/lvm.py b/qubes/storage/lvm.py index 2d35f2fe..1ff08811 100644 --- a/qubes/storage/lvm.py +++ b/qubes/storage/lvm.py @@ -99,7 +99,15 @@ class ThinPool(qubes.storage.Pool): return volume def setup(self): - pass # TODO Should we create a non existing pool? + reset_cache() + cache_key = self.volume_group + '/' + self.thin_pool + if cache_key not in size_cache: + raise qubes.storage.StoragePoolException( + 'Thin pool {} does not exist'.format(cache_key)) + if size_cache[cache_key]['attr'][0] != 't': + raise qubes.storage.StoragePoolException( + 'Volume {} is not a thin pool'.format(cache_key)) + # TODO Should we create a non existing pool? def get_volume(self, vid): ''' Return a volume with given vid''' diff --git a/qubes/tests/api_admin.py b/qubes/tests/api_admin.py index d40746b1..2e3cbad9 100644 --- a/qubes/tests/api_admin.py +++ b/qubes/tests/api_admin.py @@ -1645,7 +1645,7 @@ class TC_00_VMs(AdminAPITestCase): value = self.call_mgmt_func(b'admin.vm.volume.Import', b'test-vm1', b'private') self.assertEqual(value, '{} {}'.format( - 2*2**30, '/tmp/qubes-test-dir/appvms/test-vm1/private.img')) + 2*2**30, '/tmp/qubes-test-dir/appvms/test-vm1/private-import.img')) self.assertFalse(self.app.save.called) def test_511_vm_volume_import_running(self): diff --git a/qubes/tests/storage_file.py b/qubes/tests/storage_file.py index c65093ec..2e8c1361 100644 --- a/qubes/tests/storage_file.py +++ b/qubes/tests/storage_file.py @@ -312,6 +312,48 @@ class TC_01_FileVolumes(qubes.tests.QubesTestCase): volume.revisions_to_keep = 2 self.assertEqual(volume.revisions_to_keep, 1) + def test_020_import_data(self): + config = { + 'name': 'root', + 'pool': self.POOL_NAME, + 'save_on_stop': True, + 'rw': True, + 'size': 1024 * 1024, + } + vm = qubes.tests.storage.TestVM(self) + volume = self.app.get_pool(self.POOL_NAME).init_volume(vm, config) + volume.create() + import_path = volume.import_data() + self.assertNotEqual(volume.path, import_path) + with open(import_path, 'w+') as import_file: + import_file.write('test') + volume.import_data_end(True) + self.assertFalse(os.path.exists(import_path), import_path) + with open(volume.path) as volume_file: + volume_data = volume_file.read().strip('\0') + self.assertEqual(volume_data, 'test') + + def test_021_import_data_fail(self): + config = { + 'name': 'root', + 'pool': self.POOL_NAME, + 'save_on_stop': True, + 'rw': True, + 'size': 1024 * 1024, + } + vm = qubes.tests.storage.TestVM(self) + volume = self.app.get_pool(self.POOL_NAME).init_volume(vm, config) + volume.create() + import_path = volume.import_data() + self.assertNotEqual(volume.path, import_path) + with open(import_path, 'w+') as import_file: + import_file.write('test') + volume.import_data_end(False) + self.assertFalse(os.path.exists(import_path), import_path) + with open(volume.path) as volume_file: + volume_data = volume_file.read().strip('\0') + self.assertNotEqual(volume_data, 'test') + def assertVolumePath(self, vm, dev_name, expected, rw=True): # :pylint: disable=invalid-name volumes = vm.volumes