From 7ba9e48da005dcebc4b195b9e4c0983320d5a2d5 Mon Sep 17 00:00:00 2001 From: Ben Grande Date: Mon, 13 Oct 2025 23:39:27 +0200 Subject: [PATCH] Ignore preload as a dependency There doesn't seem to be a sane setup that would set a preloaded disposable as a property of another qube or a system property. The core already ignores preloaded disposables in certain cases, do the same on the client to not have derived applications such as Qube Manager having to deal with it. Fixes: https://github.com/QubesOS/qubes-issues/issues/10227 For: https://github.com/QubesOS/qubes-issues/issues/1512 --- qubesadmin/tests/utils.py | 42 ++++++++++++++++++++++++++++++++++----- qubesadmin/utils.py | 14 ++++++++++++- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/qubesadmin/tests/utils.py b/qubesadmin/tests/utils.py index e35bdf362..36f7434c1 100644 --- a/qubesadmin/tests/utils.py +++ b/qubesadmin/tests/utils.py @@ -35,7 +35,10 @@ def setUp(self): b'template2 class=TemplateVM state=Running\n' \ b'vm2 class=AppVM state=Running\n' \ b'sys-net class=AppVM state=Running\n' \ - b'sys-firewall class=AppVM state=Running\n' + b'sys-firewall class=AppVM state=Running\n' \ + b'test-dvm class=AppVM state=Running\n' \ + b'disp1 class=DispVM state=Running\n' \ + b'disp2 class=DispVM state=Running\n' self.global_properties = ['default_dispvm', 'default_netvm', 'default_guivm', 'default_audiovm', @@ -47,13 +50,20 @@ def setUp(self): ('dom0', 'admin.property.Get', prop, None)] = \ b'0\x00default=True type=vm vm2' - self.vms = ['vm1', 'vm2', 'sys-net', 'sys-firewall', - 'template1', 'template2'] + self.vms = [ + 'vm1', 'vm2', 'sys-net', 'sys-firewall', 'template1', 'template2', + 'test-dvm', 'disp1', 'disp2', + ] - self.vm_properties = ['template', 'netvm', 'guivm', 'audiovm', - 'default_dispvm', 'management_dispvm'] + self.vm_properties = [ + 'template', 'netvm', 'guivm', 'audiovm', 'default_dispvm', + 'management_dispvm' + ] for vm in self.vms: + self.app.expected_calls[ + (vm, 'admin.vm.property.Get', 'is_preload', None) + ] = b'2\0QubesNoSuchPropertyError\0\0invalid property\0' for prop in self.vm_properties: if not prop.startswith('template') or \ not vm.startswith('template'): @@ -65,6 +75,7 @@ def setUp(self): (vm, 'admin.vm.property.Get', prop, None)] = \ b'2\0QubesNoSuchPropertyError\0\0invalid property\0' + def test_00_only_global(self): result = qubesadmin.utils.vm_dependencies(self.app, self.app.domains['vm2']) @@ -114,6 +125,27 @@ def test_04_defaults(self): self.assertListEqual(result, [(self.app.domains['vm1'], 'netvm')]) + def test_05_preloaded_disposables(self): + self.app.expected_calls[ + ('disp1', 'admin.vm.property.Get', 'template', None) + ] = b'0\x00default=False type=vm test-dvm' + self.app.expected_calls[ + ('disp2', 'admin.vm.property.Get', 'template', None) + ] = b'0\x00default=False type=vm test-dvm' + self.app.expected_calls[ + ('disp1', 'admin.vm.property.Get', 'is_preload', None) + ] = b'0\x00default=False type=bool False' + self.app.expected_calls[ + ('disp2', 'admin.vm.property.Get', 'is_preload', None) + ] = b'0\x00default=False type=bool True' + + result = qubesadmin.utils.vm_dependencies( + self.app, self.app.domains['test-dvm'] + ) + self.assertListEqual( + result, [(self.app.domains['disp1'], 'template')] + ) + class TestVMExecEncode(qubesadmin.tests.QubesTestCase): def test_00_encode(self): diff --git a/qubesadmin/utils.py b/qubesadmin/utils.py index 9614f0f36..128c5a02c 100644 --- a/qubesadmin/utils.py +++ b/qubesadmin/utils.py @@ -143,6 +143,7 @@ def vm_dependencies(app, reference_vm): for vm in app.domains: if vm == reference_vm: continue + is_preload = getattr(vm, "is_preload", False) for prop in vm_properties: if not hasattr(vm, prop): continue @@ -150,7 +151,18 @@ def vm_dependencies(app, reference_vm): is_prop_default = vm.property_is_default(prop) except qubesadmin.exc.QubesPropertyAccessError: is_prop_default = False - if reference_vm == getattr(vm, prop, None) and not is_prop_default: + if ( + reference_vm == getattr(vm, prop, None) + and not is_prop_default + and not ( + is_preload + and prop == "template" + or ( + prop == "default_dispvm" + and getattr(vm, "template", None) == vm + ) + ) + ): result.append((vm, prop)) return result