Skip to content

Commit 93713ed

Browse files
committed
q-dev: device_protocol
1 parent e09010e commit 93713ed

File tree

3 files changed

+30
-26
lines changed

3 files changed

+30
-26
lines changed

qubes/api/admin.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
import qubes.vm
4646
import qubes.vm.adminvm
4747
import qubes.vm.qubesvm
48-
from qubes.device_protocol import Port, VirtualDevice, DeviceInfo
48+
from qubes.device_protocol import (Port, VirtualDevice, UnknownDevice,
49+
DeviceAssignment)
4950

5051

5152
class QubesMgmtEventsDispatcher:
@@ -1324,10 +1325,15 @@ async def vm_device_assign(self, endpoint, untrusted_payload):
13241325
def load_device_info(self, devclass) -> VirtualDevice:
13251326
# qrexec already verified that no strange characters are in self.arg
13261327
_dev = VirtualDevice.from_qarg(self.arg, devclass, self.app.domains)
1328+
if _dev.port_id == '*' or _dev.device_id == '*':
1329+
return _dev
13271330
# load all info, may raise KeyError, either on domain or port_id
13281331
try:
1329-
return self.app.domains[
1332+
dev = self.app.domains[
13301333
_dev.backend_domain].devices[devclass][_dev.port_id]
1334+
if isinstance(dev, UnknownDevice):
1335+
return _dev
1336+
return dev
13311337
except KeyError:
13321338
return _dev
13331339

@@ -1338,14 +1344,15 @@ def load_device_info(self, devclass) -> VirtualDevice:
13381344
endpoints=(
13391345
ep.name
13401346
for ep in importlib.metadata.entry_points(group='qubes.devices')),
1341-
no_payload=True, scope='local', write=True)
1342-
async def vm_device_unassign(self, endpoint):
1347+
scope='local', write=True)
1348+
async def vm_device_unassign(self, endpoint, untrusted_payload):
13431349
devclass = endpoint
13441350
dev = self.load_device_info(devclass)
1351+
assignment = DeviceAssignment.deserialize(
1352+
untrusted_payload, expected_device=dev)
13451353

13461354
self.fire_event_for_permission(device=dev, devclass=devclass)
13471355

1348-
assignment = qubes.device_protocol.DeviceAssignment(dev)
13491356
await self.dest.devices[devclass].unassign(assignment)
13501357
self.app.save()
13511358

@@ -1356,14 +1363,12 @@ async def vm_device_unassign(self, endpoint):
13561363
endpoints=(
13571364
ep.name
13581365
for ep in importlib.metadata.entry_points(group='qubes.devices')),
1359-
scope='local', execute=True)
1360-
async def vm_device_attach(self, endpoint, untrusted_payload):
1366+
no_payload=True, scope='local', execute=True)
1367+
async def vm_device_attach(self, endpoint):
13611368
devclass = endpoint
13621369
dev = self.load_device_info(devclass)
13631370

1364-
assignment = qubes.device_protocol.DeviceAssignment.deserialize(
1365-
untrusted_payload, expected_device=dev
1366-
)
1371+
assignment = DeviceAssignment(dev)
13671372

13681373
self.fire_event_for_permission(
13691374
device=dev, devclass=devclass,

qubes/device_protocol.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def parse_basic_device_properties(
145145
the expected values.
146146
"""
147147
expected = expected_device.port
148-
exp_vm_name = expected.backend_domain.name
148+
exp_vm_name = expected.backend_name
149149
if properties.get('backend_domain', exp_vm_name) != exp_vm_name:
150150
raise UnexpectedDeviceProperty(
151151
f"Got device exposed by {properties['backend_domain']}"
@@ -264,7 +264,7 @@ def __str__(self):
264264

265265
@property
266266
def backend_name(self) -> str:
267-
if self.backend_domain is not None:
267+
if self.backend_domain not in (None, "*"):
268268
return self.backend_domain.name
269269
return "*"
270270

@@ -307,7 +307,9 @@ def port_id(self) -> str:
307307
308308
Unique for given domain and devclass.
309309
"""
310-
return self.__port_id
310+
if self.__port_id is not None:
311+
return self.__port_id
312+
return '*'
311313

312314
@property
313315
def backend_domain(self) -> Optional[QubesVM]:
@@ -338,7 +340,7 @@ def __init__(
338340
port: Optional[Port] = None,
339341
device_id: Optional[str] = None,
340342
):
341-
# TODO! one of them cannot be None
343+
assert port is not None or device_id is not None
342344
self.port: Optional[Port] = port
343345
self._device_id = device_id
344346

@@ -379,7 +381,6 @@ def backend_name(self):
379381
return self.port.backend_name
380382
return '*'
381383

382-
383384
@property
384385
def port_id(self):
385386
if self.port != '*' and self.port.port_id is not None:
@@ -496,8 +497,8 @@ def _parse(
496497
else:
497498
identity = representation
498499
port_id, _, devid = identity.partition(':')
499-
if devid in ('', '*'):
500-
devid = '*'
500+
if devid == '':
501+
devid = None
501502
return cls(
502503
Port(backend_domain=backend, port_id=port_id, devclass=devclass),
503504
device_id=devid
@@ -515,7 +516,7 @@ def serialize(self) -> bytes:
515516
('devclass', self.devclass)))
516517

517518
properties += b' ' + DeviceSerializer.pack_property(
518-
'backend_domain', self.backend_domain.name)
519+
'backend_domain', self.backend_name)
519520

520521
return properties
521522

@@ -1143,7 +1144,7 @@ def attached(self) -> bool:
11431144
Returns False if device is attached to different domain
11441145
"""
11451146
for device in self.devices:
1146-
if device.attachment == self.frontend_domain:
1147+
if device.attachment and device.attachment == self.frontend_domain:
11471148
return True
11481149
return False
11491150

@@ -1233,11 +1234,11 @@ def _deserialize(
12331234
return cls(**properties)
12341235

12351236
def matches(self, device: VirtualDevice) -> bool:
1236-
if self.backend_domain != '*' and self.backend_domain != device.backend_domain:
1237+
if self.devclass != device.devclass:
12371238
return False
1238-
if self.port_id != '*' and self.port_id != device.port_id:
1239+
if self.backend_domain != device.backend_domain:
12391240
return False
1240-
if self.devclass != '*' and self.devclass != device.devclass:
1241+
if self.port_id != '*' and self.port_id != device.port_id:
12411242
return False
12421243
if self.device_id != '*' and self.device_id != device.device_id:
12431244
return False

qubes/devices.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -319,16 +319,14 @@ async def unassign(self, assignment: DeviceAssignment):
319319
"""
320320
all_ass = []
321321
for assign in self.get_assigned_devices():
322-
all_ass.append(assign.devclass)
322+
all_ass.append(assign)
323323
if assignment == assign:
324324
# load all options
325325
assignment = assign
326326
break
327327
else:
328328
raise DeviceNotAssigned(
329-
f'{self._bus} device at port {assignment}'
330-
f'not assigned to {self._vm!s} '
331-
f'| {all_ass} vs {assignment.devclass}')
329+
f'{self._bus} device {assignment} not assigned to {self._vm!s}')
332330

333331
self._set.discard(assignment)
334332

0 commit comments

Comments
 (0)