Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions qubes/device_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,18 +858,23 @@ def _load_classes(cls, bus: str):
subclass_id = None
for line in pciids.readlines():
line = line.rstrip()
if not line:
continue
first_char = line[0]
if (
line.startswith("\t\t")
first_char == "\t"
and len(line) >= 2
and line[1] == "\t"
and class_id is not None
and subclass_id is not None
):
progif_id, _, progif_name = line[2:].split(" ", 2)
result[class_id + subclass_id + progif_id] = progif_name
elif line.startswith("\t") and class_id:
elif first_char == "\t" and class_id:
subclass_id, _, subclass_name = line[1:].split(" ", 2)
# store both prog-if specific entry and generic one
result[class_id + subclass_id + "**"] = subclass_name
elif line.startswith("C "):
elif first_char == "C" and len(line) >= 2 and line[1] == " ":
_, class_id, _, class_name = line.split(" ", 3)
result[class_id + "****"] = class_name
subclass_id = None
Expand Down
2 changes: 1 addition & 1 deletion qubes/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def _fire_event(self, event, kwargs, pre_event=False):
h_func
for h_name, h_func_set in handlers_dict.items()
for h_func in h_func_set
if fnmatch.fnmatch(event, h_name)
if fnmatch.fnmatchcase(event, h_name)
]
for func in sorted(
handlers,
Expand Down
64 changes: 18 additions & 46 deletions qubes/ext/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,8 @@ def attachment(self) -> Optional[QubesVM]:
def _is_attached_to(self, vm):
xml_desc = lxml.etree.fromstring(vm.libvirt_domain.XMLDesc())

for disk in xml_desc.findall("devices/disk"):
info = _try_get_block_device_info(vm.app, disk)
if not info:
continue
backend_domain, port_id = info
for disk in xml_desc.findall("devices/disk[source][@type='block']"):
backend_domain, port_id = _try_get_block_device_info(vm.app, disk)

if backend_domain.name != self.backend_domain.name:
continue
Expand Down Expand Up @@ -288,18 +285,13 @@ def _sanitize(


def _try_get_block_device_info(app, disk):
if disk.get("type") != "block":
return None
dev_path_node = disk.find("source")
if dev_path_node is None:
return None

backend_domain_node = disk.find("backenddomain")
if backend_domain_node is not None:
backend_domain = app.domains[backend_domain_node.get("name")]
else:
backend_domain = app.domains[0]

dev_path_node = disk.find("source")
dev_path = dev_path_node.get("dev")

if dev_path.startswith("/dev/"):
Expand Down Expand Up @@ -357,11 +349,10 @@ def get_device_attachments(vm_):

xml_desc = lxml.etree.fromstring(vm.libvirt_domain.XMLDesc())

for disk in xml_desc.findall("devices/disk"):
info = _try_get_block_device_info(vm.app, disk)
if not info:
continue
backend_domain, port_id = info
for disk in xml_desc.findall("devices/disk[source][@type='block']"):
backend_domain, port_id = _try_get_block_device_info(
vm.app, disk
)
if backend_domain != vm_:
continue

Expand Down Expand Up @@ -434,29 +425,16 @@ def on_device_list_attached(self, vm, event, **kwargs):
system_disks = SYSTEM_DISKS_DOM0_KERNEL
xml_desc = lxml.etree.fromstring(vm.libvirt_domain.XMLDesc())

for disk in xml_desc.findall("devices/disk"):
if disk.get("type") != "block":
continue
dev_path_node = disk.find("source")
if dev_path_node is None:
continue
dev_path = dev_path_node.get("dev")

for disk in xml_desc.findall(
"devices/disk[source][target][@type='block']"
):
backend_domain, port_id = _try_get_block_device_info(vm.app, disk)
target_node = disk.find("target")
if target_node is not None:
frontend_dev = target_node.get("dev")
if not frontend_dev:
continue
if frontend_dev in system_disks:
continue
else:
frontend_dev = target_node.get("dev")
if not frontend_dev:
continue
if frontend_dev in system_disks:
continue

backend_domain_node = disk.find("backenddomain")
if backend_domain_node is not None:
backend_domain = vm.app.domains[backend_domain_node.get("name")]
else:
backend_domain = vm.app.domains[0]

options = {}
read_only_node = disk.find("readonly")
Expand All @@ -465,15 +443,9 @@ def on_device_list_attached(self, vm, event, **kwargs):
else:
options["read-only"] = "no"
options["frontend-dev"] = frontend_dev
if disk.get("device") != "disk":
options["devtype"] = disk.get("device")

if dev_path.startswith("/dev/"):
port_id = dev_path[len("/dev/") :]
else:
port_id = dev_path

port_id = port_id.replace("/", "_")
devtype = disk.get("device")
if devtype != "disk":
options["devtype"] = devtype

yield BlockDevice(Port(backend_domain, port_id, "block")), options

Expand Down
39 changes: 21 additions & 18 deletions qubes/ext/pci.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,24 @@ def load_pci_classes():
subclass_id = None
for line in pciids.readlines():
line = line.rstrip()
if line.startswith("\t\t") and class_id and subclass_id:
if not line:
continue
first_char = line[0]
if (
first_char == "\t"
and len(line) >= 2
and line[1] == "\t"
and class_id is not None
and subclass_id is not None
):
progif_id, _, class_name = line[2:].split(" ", 2)
result[class_id + subclass_id + progif_id] = class_name
elif line.startswith("\t") and class_id:
elif first_char == "\t" and class_id:
subclass_id, _, class_name = line[1:].split(" ", 2)
# store both prog-if specific entry and generic one
result[class_id + subclass_id + "00"] = class_name
result[class_id + subclass_id] = class_name
elif line.startswith("C "):
elif first_char == "C" and len(line) >= 2 and line[1] == " ":
_, class_id, _, class_name = line.split(" ", 3)
result[class_id + "0000"] = class_name
result[class_id + "00"] = class_name
Expand Down Expand Up @@ -355,16 +364,11 @@ def __init__(self):
@qubes.ext.handler("device-list:pci")
def on_device_list_pci(self, vm, event):
# pylint: disable=unused-argument
# only dom0 expose PCI devices
# only dom0 exposes PCI devices
if vm.qid != 0:
return

for dev in vm.app.vmm.libvirt_conn.listAllDevices():
if "pci" not in dev.listCaps():
continue

xml_desc = lxml.etree.fromstring(dev.XMLDesc())
libvirt_name = xml_desc.findtext("name")
for libvirt_name in vm.app.vmm.libvirt_conn.listDevices("pci"):
try:
yield PCIDevice(
Port(backend_domain=vm, port_id=None, devclass="pci"),
Expand All @@ -388,14 +392,13 @@ def on_device_list_attached(self, vm, event, **kwargs):
return
xml_desc = lxml.etree.fromstring(vm.libvirt_domain.XMLDesc())

for hostdev in xml_desc.findall("devices/hostdev"):
if hostdev.get("type") != "pci":
continue
address = hostdev.find("source/address")
segment = address.get("domain")[2:]
bus = address.get("bus")[2:]
device = address.get("slot")[2:]
function = address.get("function")[2:]
for hostdev in xml_desc.findall(
"devices/hostdev[@type='pci']/source/address"
):
segment = hostdev.get("domain")[2:]
bus = hostdev.get("bus")[2:]
device = hostdev.get("slot")[2:]
function = hostdev.get("function")[2:]

libvirt_name = "pci_{segment}_{bus}_{device}_{function}".format(
segment=segment,
Expand Down
31 changes: 18 additions & 13 deletions qubes/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,24 +657,28 @@ def attach(self, volume, rw=False):

def _is_already_attached(self, volume):
"""Checks if the given volume is already attached"""
# TODO: ben: cache
parsed_xml = lxml.etree.fromstring(self.vm.libvirt_domain.XMLDesc())
disk_sources = parsed_xml.xpath("//domain/devices/disk/source")
for source in disk_sources:
if source.get("dev") == "/dev/%s" % volume.vid:
return True
disks = parsed_xml.xpath(
"//domain/devices/disk/source[@dev='/dev/$vid']", vid=volume.vid
)
if disks:
return True
return False

def detach(self, volume):
"""Detach a volume from domain"""
# TODO: ben: cache
parsed_xml = lxml.etree.fromstring(self.vm.libvirt_domain.XMLDesc())
disks = parsed_xml.xpath("//domain/devices/disk")
for disk in disks:
source = disk.xpath("source")[0]
if source.get("dev") == "/dev/%s" % volume.vid:
disk_xml = lxml.etree.tostring(disk, encoding="utf-8")
self.vm.libvirt_domain.detachDevice(disk_xml)
return
raise StoragePoolException("Volume {!r} is not attached".format(volume))
disks = parsed_xml.xpath(
"//domain/devices/disk/source[@dev='/dev/$vid']", vid=volume.vid
)
if not disks:
raise StoragePoolException(
"Volume {!r} is not attached".format(volume)
)
disk_xml = lxml.etree.tostring(disks[0], encoding="utf-8")
self.vm.libvirt_domain.detachDevice(disk_xml)

@property
def kernels_dir(self):
Expand Down Expand Up @@ -878,11 +882,12 @@ def unused_frontend(self):
@property
def used_frontends(self):
"""Used device names"""
# TODO: ben: cache
xml = self.vm.libvirt_domain.XMLDesc()
parsed_xml = lxml.etree.fromstring(xml)
return {
target.get("dev", None)
for target in parsed_xml.xpath("//domain/devices/disk/target")
for target in parsed_xml.xpath("//domain/devices/disk/target[@dev]")
}

async def export(self, volume):
Expand Down
26 changes: 6 additions & 20 deletions qubes/tests/devices_pci.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def __hash__(self):


PCI_XML = """<device>
<name>pci_{}_00_14_0</name>
<path>/sys/devices/pci{}:00/{}:00:14.0</path>
<name>pci_{address}_00_14_0</name>
<path>/sys/devices/pci{address}:00/{address}:00:14.0</path>
<parent>computer</parent>
<driver>
<name>pciback</name>
Expand Down Expand Up @@ -196,25 +196,11 @@ def test_000_unsupported_device(self):
**{
"vmm.offline_mode": False,
"vmm.libvirt_conn.nodeDeviceLookupByName.return_value": mock.Mock(
**{"XMLDesc.return_value": PCI_XML.format(*["0000"] * 3)}
**{"XMLDesc.return_value": PCI_XML.format(address="0000")}
),
"vmm.libvirt_conn.listAllDevices.return_value": [
mock.Mock(
**{
"XMLDesc.return_value": PCI_XML.format(
*["0000"] * 3
),
"listCaps.return_value": ["pci"],
}
),
mock.Mock(
**{
"XMLDesc.return_value": PCI_XML.format(
*["10000"] * 3
),
"listCaps.return_value": ["pci"],
}
),
"vmm.libvirt_conn.listDevices.return_value": [
"pci_0000_00_14_0",
"pci_10000_00_14_0",
],
}
)
Expand Down
2 changes: 2 additions & 0 deletions qubes/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#

import asyncio
import functools
import hashlib
import logging
import re
Expand Down Expand Up @@ -177,6 +178,7 @@ def urandom(size):
return hashlib.sha512(rand).digest()


@functools.cache
def get_entry_point_one(group, name):
epoints = tuple(importlib.metadata.entry_points(group=group, name=name))
if not epoints:
Expand Down
17 changes: 8 additions & 9 deletions qubes/vm/qubesvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,15 +1030,14 @@ def attached_volumes(self):
result = []
xml_desc = self.libvirt_domain.XMLDesc()
xml = lxml.etree.fromstring(xml_desc)
for disk in xml.xpath("//domain/devices/disk"):
if disk.find("backenddomain") is not None:
pool_name = "p_%s" % disk.find("backenddomain").get("name")
pool = self.app.pools[pool_name]
vid = disk.find("source").get("dev").split("/dev/")[1]
for volume in pool.volumes:
if volume.vid == vid:
result += [volume]
break
for disk in xml.xpath("//domain/devices/disk[source][backenddomain]"):
pool_name = "p_%s" % disk.find("backenddomain").get("name")
pool = self.app.pools[pool_name]
vid = disk.find("source").get("dev").split("/dev/")[1]
for volume in pool.volumes:
if volume.vid == vid:
result += [volume]
break

return result + list(self.volumes.values())

Expand Down