[neon/kf6/kf6-solid/Neon/unstable] debian/patches: add kai_udisks.diff for testing in unstable only

Carlos De Maine null at kde.org
Thu Jul 17 07:24:59 BST 2025


Git commit 43824b49743d7b13a719db894bc0a4c74f040253 by Carlos De Maine.
Committed on 17/07/2025 at 06:24.
Pushed by carlosdem into branch 'Neon/unstable'.

add kai_udisks.diff for testing in unstable only

A  +1138 -0    debian/patches/kai_udisks.diff
A  +1    -0    debian/patches/series

https://invent.kde.org/neon/kf6/kf6-solid/-/commit/43824b49743d7b13a719db894bc0a4c74f040253

diff --git a/debian/patches/kai_udisks.diff b/debian/patches/kai_udisks.diff
new file mode 100644
index 0000000..d26d381
--- /dev/null
+++ b/debian/patches/kai_udisks.diff
@@ -0,0 +1,1138 @@
+diff --git a/src/solid/devices/backends/udisks2/CMakeLists.txt b/src/solid/devices/backends/udisks2/CMakeLists.txt
+index 1e8f0143eee681919b87f04ea013089bc7bc1290..9be3732657de9e368382ba13183fc12d67d2119c 100644
+--- a/src/solid/devices/backends/udisks2/CMakeLists.txt
++++ b/src/solid/devices/backends/udisks2/CMakeLists.txt
+@@ -1,7 +1,6 @@
+ set(backend_sources
+     udisksmanager.cpp
+     udisksdevice.cpp
+-    udisksdevicebackend.cpp
+     udisksblock.cpp
+     udisksstoragevolume.cpp
+     udisksdeviceinterface.cpp
+@@ -10,6 +9,7 @@ set(backend_sources
+     udisksstoragedrive.cpp
+     udisksstorageaccess.cpp
+     udisksgenericinterface.cpp
++    udisksutils.cpp
+     dbus/manager.cpp
+ )
+ 
+diff --git a/src/solid/devices/backends/udisks2/udisks2.h b/src/solid/devices/backends/udisks2/udisks2.h
+index 04d90128a99b502ab518291dcb3dc80b16ee2259..c92a42dae9eec6f89be9d6058baf94a8e892cbc0 100644
+--- a/src/solid/devices/backends/udisks2/udisks2.h
++++ b/src/solid/devices/backends/udisks2/udisks2.h
+@@ -32,6 +32,7 @@ Q_DECLARE_METATYPE(DBUSManagerStruct)
+ #define UD2_DBUS_PATH                    "/org/freedesktop/UDisks2"
+ #define UD2_UDI_DISKS_PREFIX             "/org/freedesktop/UDisks2"
+ #define UD2_DBUS_PATH_MANAGER            "/org/freedesktop/UDisks2/Manager"
++#define UD2_DBUS_PATH_BLOCK_DEVICES      "/org/freedesktop/UDisks2/block_devices"
+ #define UD2_DBUS_PATH_DRIVES             "/org/freedesktop/UDisks2/drives"
+ #define UD2_DBUS_PATH_JOBS               "/org/freedesktop/UDisks2/jobs/"
+ #define UD2_DBUS_PATH_BLOCKDEVICES       "/org/freedesktop/UDisks2/block_devices"
+diff --git a/src/solid/devices/backends/udisks2/udisksblock.cpp b/src/solid/devices/backends/udisks2/udisksblock.cpp
+index d57e6523b790a4a8ddf7a4662c06a0b858de1ce7..45890af06ada7465be2a7aa2ceef769d71728140 100644
+--- a/src/solid/devices/backends/udisks2/udisksblock.cpp
++++ b/src/solid/devices/backends/udisks2/udisksblock.cpp
+@@ -48,7 +48,7 @@ Block::Block(Device *dev)
+                 if (!nodeElem.isNull() && nodeElem.hasAttribute(QStringLiteral("name"))) {
+                     const QString udi = QStringLiteral(UD2_DBUS_PATH_BLOCKDEVICES) + QLatin1Char('/') + nodeElem.attribute(QStringLiteral("name"));
+ 
+-                    Device device(udi);
++                    Device device(dev->manager(), udi);
+                     if (device.drivePath() == dev->udi()) {
+                         m_devNum = device.prop(QStringLiteral("DeviceNumber")).toULongLong();
+                         m_devFile = QFile::decodeName(device.prop(QStringLiteral("Device")).toByteArray());
+diff --git a/src/solid/devices/backends/udisks2/udisksdevice.cpp b/src/solid/devices/backends/udisks2/udisksdevice.cpp
+index 10f7c55d7f53483911b9852808878cd5b1fa3911..c50f00c7916dc935466ffcfb5c5a83e9eec1027c 100644
+--- a/src/solid/devices/backends/udisks2/udisksdevice.cpp
++++ b/src/solid/devices/backends/udisks2/udisksdevice.cpp
+@@ -8,9 +8,9 @@
+ #include "udisksdevice.h"
+ #include "udisks_debug.h"
+ #include "udisksblock.h"
+-#include "udisksdevicebackend.h"
+ #include "udisksdeviceinterface.h"
+ #include "udisksgenericinterface.h"
++#include "udisksmanager.h"
+ #include "udisksopticaldisc.h"
+ #include "udisksopticaldrive.h"
+ #include "udisksstorageaccess.h"
+@@ -87,16 +87,18 @@ static QString concatBlockDeviceDescription(const QString &name, qulonglong size
+     return description;
+ }
+ 
+-Device::Device(const QString &udi)
++Device::Device(Manager *manager, const QString &udi)
+     : Solid::Ifaces::Device()
+-    , m_backend(DeviceBackend::backendForUDI(udi))
++    , m_manager(manager)
++    , m_udi(udi)
+ {
+-    if (m_backend) {
+-        connect(m_backend, &DeviceBackend::changed, this, &Device::changed);
+-        connect(m_backend, &DeviceBackend::propertyChanged, this, &Device::propertyChanged);
+-    } else {
+-        qCDebug(UDISKS2) << "Created invalid Device for udi" << udi;
+-    }
++    connect(m_manager, &Manager::propertyChanged, this, [this](const QString &udi, const QMap<QString, int> &changes) {
++        if (udi == m_udi) {
++            Q_EMIT propertyChanged(changes);
++        }
++    });
++
++    connect(this, &Device::propertyChanged, this, &Device::changed);
+ }
+ 
+ Device::~Device()
+@@ -105,63 +107,44 @@ Device::~Device()
+ 
+ QString Device::udi() const
+ {
+-    if (m_backend) {
+-        return m_backend->udi();
+-    }
+-
+-    return QString();
++    return m_udi;
+ }
+ 
+ QVariant Device::prop(const QString &key) const
+ {
+-    if (m_backend) {
+-        return m_backend->prop(key);
+-    }
+-
+-    return QVariant();
++    return m_manager->deviceProperty(m_udi, key);
+ }
+ 
+ bool Device::propertyExists(const QString &key) const
+ {
+-    if (m_backend) {
+-        return m_backend->propertyExists(key);
+-    }
+-
+-    return false;
++    return m_manager->deviceProperty(m_udi, key, Manager::CachedOnly).isValid();
+ }
+ 
+ QVariantMap Device::allProperties() const
+ {
+-    if (m_backend) {
+-        return m_backend->allProperties();
+-    }
++    QVariantMap flattened;
+ 
+-    return QVariantMap();
+-}
++    const auto interfaces = m_manager->deviceProperties(m_udi);
+ 
+-bool Device::hasInterface(const QString &name) const
+-{
+-    if (m_backend) {
+-        return m_backend->interfaces().contains(name);
++    // Flatten per-interface properties into a single map.
++    // We iterate the interfaces in reverse since Manager::prop() returns the *first*
++    // property found, so we'll override any other properties that way.
++    // FIXME actually reverse it lol, no rbegin on QMap :(
++    for (const auto &props : interfaces) {
++        flattened.insert(props);
+     }
+ 
+-    return false;
++    return flattened;
+ }
+ 
+-QStringList Device::interfaces() const
++bool Device::hasInterface(const QString &name) const
+ {
+-    if (m_backend) {
+-        return m_backend->interfaces();
+-    }
+-
+-    return QStringList();
++    return m_manager->hasInterface(m_udi, name);
+ }
+ 
+-void Device::invalidateCache()
++Manager *Device::manager() const
+ {
+-    if (m_backend) {
+-        return m_backend->invalidateProperties();
+-    }
++    return m_manager;
+ }
+ 
+ QObject *Device::createDeviceInterface(const Solid::DeviceInterface::Type &type)
+@@ -419,7 +402,7 @@ QString Device::volumeDescription() const
+         return volume_label;
+     }
+ 
+-    UDisks2::Device storageDevice(drivePath());
++    UDisks2::Device storageDevice(manager(), drivePath());
+     const UDisks2::StorageDrive storageDrive(&storageDevice);
+     Solid::StorageDrive::DriveType drive_type = storageDrive.driveType();
+ 
+@@ -629,7 +612,7 @@ QString Device::icon() const
+             return QStringLiteral("drive-harddisk"); // stuff like loop devices or swap which don't have the Drive prop set
+         }
+ 
+-        Device drive(drv);
++        Device drive(manager(), drv);
+ 
+         // handle media
+         const QString media = drive.prop(QStringLiteral("Media")).toString();
+@@ -696,7 +679,7 @@ QString Device::icon() const
+ QString Device::product() const
+ {
+     if (!isDrive()) {
+-        Device drive(drivePath());
++        Device drive(manager(), drivePath());
+         return drive.prop(QStringLiteral("Model")).toString();
+     }
+ 
+@@ -706,7 +689,7 @@ QString Device::product() const
+ QString Device::vendor() const
+ {
+     if (!isDrive()) {
+-        Device drive(drivePath());
++        Device drive(manager(), drivePath());
+         return drive.prop(QStringLiteral("Vendor")).toString();
+     }
+ 
+@@ -825,7 +808,7 @@ bool Device::isOpticalDisc() const
+         return false;
+     }
+ 
+-    Device drive(drv);
++    Device drive(manager(), drv);
+     return drive.prop(QStringLiteral("Optical")).toBool();
+ }
+ 
+@@ -836,7 +819,7 @@ bool Device::mightBeOpticalDisc() const
+         return false;
+     }
+ 
+-    Device drive(drv);
++    Device drive(manager(), drv);
+     return drive.isOpticalDrive();
+ }
+ 
+diff --git a/src/solid/devices/backends/udisks2/udisksdevice.h b/src/solid/devices/backends/udisks2/udisksdevice.h
+index 6a5f2ece84e01791d34e73877302b0159e602927..4f024a5c365b964c0f48396108cee9b5799c6568 100644
+--- a/src/solid/devices/backends/udisks2/udisksdevice.h
++++ b/src/solid/devices/backends/udisks2/udisksdevice.h
+@@ -24,14 +24,17 @@ namespace Backends
+ namespace UDisks2
+ {
+ class DeviceBackend;
++class Manager;
+ 
+ class Device : public Solid::Ifaces::Device
+ {
+     Q_OBJECT
+ public:
+-    Device(const QString &udi);
++    Device(Manager *manager, const QString &udi);
+     ~Device() override;
+ 
++    Manager *manager() const;
++
+     QObject *createDeviceInterface(const Solid::DeviceInterface::Type &type) override;
+     bool queryDeviceInterface(const Solid::DeviceInterface::Type &type) const override;
+     QString description() const override;
+@@ -45,10 +48,8 @@ public:
+     QVariant prop(const QString &key) const;
+     bool propertyExists(const QString &key) const;
+     QVariantMap allProperties() const;
+-    void invalidateCache();
+ 
+     bool hasInterface(const QString &name) const;
+-    QStringList interfaces() const;
+ 
+     QString errorToString(const QString &error) const;
+     Solid::ErrorType errorToSolidError(const QString &error) const;
+@@ -75,10 +76,10 @@ Q_SIGNALS:
+     void changed();
+     void propertyChanged(const QMap<QString, int> &changes);
+ 
+-protected:
+-    QPointer<DeviceBackend> m_backend;
+-
+ private:
++    Manager *m_manager;
++    QString m_udi;
++
+     QString loopDescription() const;
+     QString storageDescription() const;
+     QString volumeDescription() const;
+diff --git a/src/solid/devices/backends/udisks2/udisksdevicebackend.h b/src/solid/devices/backends/udisks2/udisksdevicebackend.h
+deleted file mode 100644
+index ac2c0266b5fe5a75f7712e32f93c763d02b51d76..0000000000000000000000000000000000000000
+--- a/src/solid/devices/backends/udisks2/udisksdevicebackend.h
++++ /dev/null
+@@ -1,71 +0,0 @@
+-/*
+-    SPDX-FileCopyrightText: 2010 Michael Zanetti <mzanetti at kde.org>
+-    SPDX-FileCopyrightText: 2010-2012 Lukáš Tinkl <ltinkl at redhat.com>
+-    SPDX-FileCopyrightText: 2012 Dan Vrátil <dvratil at redhat.com>
+-
+-    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+-*/
+-
+-#ifndef UDISKSDEVICEBACKEND_H
+-#define UDISKSDEVICEBACKEND_H
+-
+-#include <QDBusObjectPath>
+-#include <QObject>
+-#include <QStringList>
+-#include <QThreadStorage>
+-
+-#include "udisks2.h"
+-
+-namespace Solid
+-{
+-namespace Backends
+-{
+-namespace UDisks2
+-{
+-class DeviceBackend : public QObject
+-{
+-    Q_OBJECT
+-
+-public:
+-    static DeviceBackend *backendForUDI(const QString &udi, bool create = true);
+-    static void destroyBackend(const QString &udi);
+-
+-    DeviceBackend(const QString &udi);
+-    ~DeviceBackend() override;
+-
+-    QVariant prop(const QString &key) const;
+-    bool propertyExists(const QString &key) const;
+-    QVariantMap allProperties() const;
+-
+-    QStringList interfaces() const;
+-    const QString &udi() const;
+-
+-    void invalidateProperties();
+-Q_SIGNALS:
+-    void propertyChanged(const QMap<QString, int> &changeMap);
+-    void changed();
+-
+-private Q_SLOTS:
+-    void slotInterfacesAdded(const QDBusObjectPath &object_path, const VariantMapMap &interfaces_and_properties);
+-    void slotInterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces);
+-    void slotPropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps);
+-
+-private:
+-    void initInterfaces();
+-    QString introspect() const;
+-    void checkCache(const QString &key) const;
+-    void cacheProperty(const QString &key, const QVariant &value) const;
+-
+-    // NOTE: make sure to insert items only through cacheProperty
+-    mutable QVariantMap m_propertyCache;
+-    QStringList m_interfaces;
+-    QString m_udi;
+-
+-    static QThreadStorage<QMap<QString, DeviceBackend *>> s_backends;
+-};
+-
+-} /* namespace UDisks2 */
+-} /* namespace Backends */
+-} /* namespace Solid */
+-
+-#endif /* UDISKSDEVICEBACKEND_H */
+diff --git a/src/solid/devices/backends/udisks2/udisksgenericinterface.cpp b/src/solid/devices/backends/udisks2/udisksgenericinterface.cpp
+index 288503225b71bf3b54b5afd2b1142f0ee8abb412..1fa6a1e21999deebf79b0b1a1ba5bcfc87cea254 100644
+--- a/src/solid/devices/backends/udisks2/udisksgenericinterface.cpp
++++ b/src/solid/devices/backends/udisks2/udisksgenericinterface.cpp
+@@ -14,7 +14,7 @@ using namespace Solid::Backends::UDisks2;
+ GenericInterface::GenericInterface(Device *device)
+     : DeviceInterface(device)
+ {
+-    connect(device, SIGNAL(propertyChanged(QMap<QString, int>)), this, SIGNAL(propertyChanged(QMap<QString, int>)));
++    connect(device, &Device::propertyChanged, this, &GenericInterface::propertyChanged);
+ }
+ 
+ GenericInterface::~GenericInterface()
+diff --git a/src/solid/devices/backends/udisks2/udisksmanager.cpp b/src/solid/devices/backends/udisks2/udisksmanager.cpp
+index 799face79eb057d188388d5745792b591fb9a422..71f453c76a150744d8cbb4c4a1dcf0a1519749fc 100644
+--- a/src/solid/devices/backends/udisks2/udisksmanager.cpp
++++ b/src/solid/devices/backends/udisks2/udisksmanager.cpp
+@@ -5,8 +5,10 @@
+ */
+ 
+ #include "udisksmanager.h"
++#include "dbus/manager.h"
+ #include "udisks_debug.h"
+-#include "udisksdevicebackend.h"
++#include "udisksopticaldisc.h"
++#include "udisksutils.h"
+ 
+ #include <QDBusConnection>
+ #include <QDBusConnectionInterface>
+@@ -14,14 +16,17 @@
+ #include <QDBusObjectPath>
+ #include <QDomDocument>
+ 
++#include <algorithm>
++
+ #include "../shared/rootdevice.h"
++#include "solid/genericinterface.h"
+ 
+ using namespace Solid::Backends::UDisks2;
+ using namespace Solid::Backends::Shared;
+ 
+ Manager::Manager(QObject *parent)
+     : Solid::Ifaces::DeviceManager(parent)
+-    , m_manager(QStringLiteral(UD2_DBUS_SERVICE), QStringLiteral(UD2_DBUS_PATH), QDBusConnection::systemBus())
++    , m_manager(org::freedesktop::DBus::ObjectManager(QStringLiteral(UD2_DBUS_SERVICE), QStringLiteral(UD2_DBUS_PATH), QDBusConnection::systemBus()))
+ {
+     m_supportedInterfaces = {
+         Solid::DeviceInterface::GenericInterface,
+@@ -38,33 +43,59 @@ Manager::Manager(QObject *parent)
+     qDBusRegisterMetaType<VariantMapMap>();
+     qDBusRegisterMetaType<DBUSManagerStruct>();
+ 
+-    bool serviceFound = m_manager.isValid();
+-    if (!serviceFound) {
+-        // find out whether it will be activated automatically
+-        QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), //
+-                                                              QStringLiteral("/org/freedesktop/DBus"),
+-                                                              QStringLiteral("org.freedesktop.DBus"),
+-                                                              QStringLiteral("ListActivatableNames"));
+-
+-        QDBusReply<QStringList> reply = QDBusConnection::systemBus().call(message);
+-        if (reply.isValid() && reply.value().contains(QStringLiteral(UD2_DBUS_SERVICE))) {
+-            QDBusConnection::systemBus().interface()->startService(QStringLiteral(UD2_DBUS_SERVICE));
+-            serviceFound = true;
+-        }
+-    }
++    connect(&m_manager, &org::freedesktop::DBus::ObjectManager::InterfacesAdded, this, &Manager::slotInterfacesAdded);
++    connect(&m_manager, &org::freedesktop::DBus::ObjectManager::InterfacesRemoved, this, &Manager::slotInterfacesRemoved);
+ 
+-    if (serviceFound) {
+-        connect(&m_manager, SIGNAL(InterfacesAdded(QDBusObjectPath, VariantMapMap)), this, SLOT(slotInterfacesAdded(QDBusObjectPath, VariantMapMap)));
+-        connect(&m_manager, SIGNAL(InterfacesRemoved(QDBusObjectPath, QStringList)), this, SLOT(slotInterfacesRemoved(QDBusObjectPath, QStringList)));
+-    }
++    QDBusConnection::systemBus()
++        .connect(QStringLiteral(UD2_DBUS_SERVICE), QString() /*any path*/, QStringLiteral(DBUS_INTERFACE_PROPS), QStringLiteral("PropertiesChanged"), this, SLOT(slotPropertiesChanged(QDBusMessage)));
+ }
+ 
+ Manager::~Manager()
+ {
+-    while (!m_deviceCache.isEmpty()) {
+-        QString udi = m_deviceCache.takeFirst();
+-        DeviceBackend::destroyBackend(udi);
++}
++
++bool Manager::hasInterface(const QString &udi, const QString &interface)
++{
++    return deviceCache().value(udi).contains(interface);
++}
++
++QMap<QString, PropertyMap> Manager::allProperties()
++{
++    return deviceCache();
++}
++
++PropertyMap Manager::deviceProperties(const QString &udi)
++{
++    return deviceCache().value(udi);
++}
++
++QVariant Manager::deviceProperty(const QString &udi, const QString &name, Manager::FetchMode fetchMode)
++{
++    const auto &props = m_cache.value(udi);
++
++    // Loop through all interfaces looking for a property.
++    for (auto it = props.begin(), end = props.end(); it != end; ++it) {
++        const QString iface = it.key();
++        const auto valueIt = it->constFind(name);
++        if (valueIt != it->constEnd()) {
++            if (!valueIt->isValid() && fetchMode == FetchIfNeeded) {
++                QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), udi, QStringLiteral(DBUS_INTERFACE_PROPS), QStringLiteral("Get"));
++                call.setArguments({iface, name});
++                QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(call);
++
++                /* We don't check for error here and store the item in the cache anyway so next time we don't have to
++                 * do the DBus call to find out it does not exist but just check whether
++                 * prop(key).isValid() */
++                const QVariant value = Utils::sanitizeValue(reply.value());
++                m_cache[udi][iface][name] = value;
++                return value;
++            }
++
++            return *valueIt;
++        }
+     }
++
++    return QVariant();
+ }
+ 
+ QObject *Manager::createDevice(const QString &udi)
+@@ -78,7 +109,7 @@ QObject *Manager::createDevice(const QString &udi)
+ 
+         return root;
+     } else if (deviceCache().contains(udi)) {
+-        return new Device(udi);
++        return new Device(this, udi);
+     } else {
+         return nullptr;
+     }
+@@ -87,79 +118,71 @@ QObject *Manager::createDevice(const QString &udi)
+ QStringList Manager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type)
+ {
+     QStringList result;
+-    const QStringList deviceList = deviceCache();
++
++    const auto devices = deviceCache();
+ 
+     if (!parentUdi.isEmpty()) {
+-        for (const QString &udi : deviceList) {
+-            Device device(udi);
++        for (auto it = devices.keyBegin(), end = devices.keyEnd(); it != end; ++it) {
++            Device device(this, *it);
+             if (device.queryDeviceInterface(type) && device.parentUdi() == parentUdi) {
+-                result << udi;
++                result << *it;
+             }
+         }
+ 
+         return result;
+     } else if (type != Solid::DeviceInterface::Unknown) {
+-        for (const QString &udi : deviceList) {
+-            Device device(udi);
++        for (auto it = devices.keyBegin(), end = devices.keyEnd(); it != end; ++it) {
++            Device device(this, *it);
+             if (device.queryDeviceInterface(type)) {
+-                result << udi;
++                result << *it;
+             }
+         }
+ 
+         return result;
+     }
+ 
+-    return deviceCache();
++    return devices.keys();
+ }
+ 
+ QStringList Manager::allDevices()
+ {
+-    m_deviceCache.clear();
+-
+-    introspect(QStringLiteral(UD2_DBUS_PATH_BLOCKDEVICES), true /*checkOptical*/);
+-    introspect(QStringLiteral(UD2_DBUS_PATH_DRIVES));
++    m_devices.clear();
++    m_cache.clear();
++
++    org::freedesktop::DBus::ObjectManager manager(QStringLiteral(UD2_DBUS_SERVICE), QStringLiteral(UD2_DBUS_PATH), QDBusConnection::systemBus());
++    QDBusPendingReply<DBUSManagerStruct> reply = manager.GetManagedObjects();
++    reply.waitForFinished();
++    if (reply.isError()) {
++        qCWarning(UDISKS2) << "Failed to fetch all devices" << reply.error().name() << reply.error().message();
++        return m_devices;
++    }
+ 
+-    return m_deviceCache;
+-}
++    const auto items = reply.value();
++    for (auto it = items.begin(), end = items.end(); it != end; ++it) {
++        const QString udi = it.key().path();
+ 
+-void Manager::introspect(const QString &path, bool checkOptical)
+-{
+-    QDBusMessage call =
+-        QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(DBUS_INTERFACE_INTROSPECT), QStringLiteral("Introspect"));
+-    QDBusPendingReply<QString> reply = QDBusConnection::systemBus().call(call);
+-
+-    if (reply.isValid()) {
+-        QDomDocument dom;
+-        dom.setContent(reply.value());
+-        QDomNodeList nodeList = dom.documentElement().elementsByTagName(QStringLiteral("node"));
+-        for (int i = 0; i < nodeList.count(); i++) {
+-            QDomElement nodeElem = nodeList.item(i).toElement();
+-            if (!nodeElem.isNull() && nodeElem.hasAttribute(QStringLiteral("name"))) {
+-                const QString name = nodeElem.attribute(QStringLiteral("name"));
+-                const QString udi = path + QStringLiteral("/") + name;
+-
+-                // Optimization, a loop device cannot really have a physical drive associated with it
+-                if (checkOptical && !name.startsWith(QLatin1String("loop"))) {
+-                    Device device(udi);
+-                    if (device.mightBeOpticalDisc()) {
+-                        QDBusConnection::systemBus().connect(QStringLiteral(UD2_DBUS_SERVICE), //
+-                                                             udi,
+-                                                             QStringLiteral(DBUS_INTERFACE_PROPS),
+-                                                             QStringLiteral("PropertiesChanged"),
+-                                                             this,
+-                                                             SLOT(slotMediaChanged(QDBusMessage)));
+-                        if (!device.isOpticalDisc()) { // skip empty CD disc
+-                            continue;
+-                        }
+-                    }
+-                }
++        if (!udi.startsWith(QLatin1String(UD2_DBUS_PATH_BLOCK_DEVICES)) && !udi.startsWith(QLatin1String(UD2_DBUS_PATH_DRIVES))) {
++            continue;
++        }
+ 
+-                m_deviceCache.append(udi);
+-            }
++        VariantMapMap mapMap = it.value();
++        for (QVariantMap &map : mapMap) {
++            map = Utils::sanitizeValue(map);
+         }
+-    } else {
+-        qCWarning(UDISKS2) << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message();
++        m_devices.append(udi);
++        m_cache.insert(udi, mapMap);
+     }
++
++    // Filter out empty optical drives.
++    m_devices.erase(std::remove_if(m_devices.begin(),
++                                   m_devices.end(),
++                                   [this](const QString &udi) {
++                                       Device device(this, udi);
++                                       return device.mightBeOpticalDisc() && !device.isOpticalDisc();
++                                   }),
++                    m_devices.end());
++
++    return m_devices;
+ }
+ 
+ QSet<Solid::DeviceInterface::Type> Manager::supportedInterfaces() const
+@@ -183,33 +206,56 @@ void Manager::slotInterfacesAdded(const QDBusObjectPath &object_path, const Vari
+ 
+     qCDebug(UDISKS2) << udi << "has new interfaces:" << interfaces_and_properties.keys();
+ 
+-    // If device gained an org.freedesktop.UDisks2.Block interface, we
+-    // should check if it is an optical drive, in order to properly
+-    // register mediaChanged event handler with newly-plugged external
+-    // drives
+-    if (interfaces_and_properties.contains(QStringLiteral("org.freedesktop.UDisks2.Block"))) {
+-        Device device(udi);
+-        if (device.mightBeOpticalDisc()) {
+-            QDBusConnection::systemBus().connect(QStringLiteral(UD2_DBUS_SERVICE), //
+-                                                 udi,
+-                                                 QStringLiteral(DBUS_INTERFACE_PROPS),
+-                                                 QStringLiteral("PropertiesChanged"),
+-                                                 this,
+-                                                 SLOT(slotMediaChanged(QDBusMessage)));
++    auto cachedIt = m_cache.find(udi);
++    if (cachedIt == m_cache.end()) {
++        cachedIt = m_cache.insert(udi, VariantMapMap{});
++    }
++
++    // We need to re-fetch all existing interfaces to ensure by the time we emit "add" for FileSystem
++    // the rest is up to date (e.g. if Loop gets updated after we gained FileSystem) some propertes aren't updated yet.
++    // We'll skip Block as every device we are interested in will be a Block device.
++    QStringList oldInterfaces = cachedIt->keys();
++    oldInterfaces.removeOne(QStringLiteral(UD2_DBUS_INTERFACE_BLOCK));
++
++    for (auto it = interfaces_and_properties.begin(), end = interfaces_and_properties.end(); it != end; ++it) {
++        // Filters generic DBus interfaces.
++        if (!it.key().startsWith(QLatin1String(UD2_DBUS_SERVICE))) {
++            continue;
+         }
++        cachedIt->insert(it.key(), Utils::sanitizeValue(it.value()));
+     }
+ 
+-    updateBackend(udi);
++    for (const QString &interface : oldInterfaces) {
++        QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), udi, QStringLiteral(DBUS_INTERFACE_PROPS), QStringLiteral("GetAll"));
++        call.setArguments({interface});
++        QDBusReply<QVariantMap> reply = QDBusConnection::systemBus().call(call);
++        if (reply.isValid()) {
++            cachedIt->insert(interface, Utils::sanitizeValue(reply.value()));
++        }
++    }
+ 
+-    // new device, we don't know it yet
+-    if (!m_deviceCache.contains(udi)) {
+-        m_deviceCache.append(udi);
+-        Q_EMIT deviceAdded(udi);
++    bool isNewDevice = !m_devices.contains(udi);
++    if (isNewDevice) {
++        // Check if it is an empty optical drive, and if so, ignore it.
++        Device device(this, udi);
++        if (device.mightBeOpticalDisc() && !device.isOpticalDisc()) {
++            qCDebug(UDISKS2) << "\tIt's a new empty optical drive, ignoring";
++            isNewDevice = false;
++        } else {
++            qCDebug(UDISKS2) << "\tIt's a new device, emitting added";
++            m_devices.append(udi);
++        }
+     }
+-    // re-emit in case of 2-stage devices like N9 or some Android phones
+-    else if (m_deviceCache.contains(udi) && interfaces_and_properties.keys().contains(QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM))) {
++
++    if (isNewDevice) {
++        Q_EMIT deviceAdded(udi);
++    } else if (interfaces_and_properties.contains(QLatin1String(UD2_DBUS_INTERFACE_FILESYSTEM))) {
++        // re-emit in case of 2-stage devices like N9 or some Android phones
++        Q_EMIT deviceRemoved(udi);
+         Q_EMIT deviceAdded(udi);
+     }
++
++    // TODO invalidate drive? updateBackend did that
+ }
+ 
+ void Manager::slotInterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces)
+@@ -224,23 +270,27 @@ void Manager::slotInterfacesRemoved(const QDBusObjectPath &object_path, const QS
+         return;
+     }
+ 
++    auto cachedIt = m_cache.find(udi);
++    if (cachedIt == m_cache.end()) {
++        return;
++    }
++
+     qCDebug(UDISKS2) << udi << "lost interfaces:" << interfaces;
+ 
++    for (const QString &iface : interfaces) {
++        cachedIt->remove(iface);
++    }
++
+     /*
+      * Determine left interfaces. The device backend may have processed the
+      * InterfacesRemoved signal already, but the result set is the same
+      * independent if the backend or the manager processes the signal first.
+      */
+-    Device device(udi);
+-    const QStringList ifaceList = device.interfaces();
+-    QSet<QString> leftInterfaces(ifaceList.begin(), ifaceList.end());
+-    leftInterfaces.subtract(QSet<QString>(interfaces.begin(), interfaces.end()));
+-
+-    if (leftInterfaces.isEmpty()) {
+-        // remove the device if the last interface is removed
++    if (cachedIt->isEmpty()) {
++        qCDebug(UDISKS2) << "\tThere are no more interface, emitting device removal";
+         Q_EMIT deviceRemoved(udi);
+-        m_deviceCache.removeAll(udi);
+-        DeviceBackend::destroyBackend(udi);
++        m_cache.remove(udi);
++        m_devices.removeOne(udi);
+     } else {
+         /*
+          * Changes in the interface composition may change if a device
+@@ -252,65 +302,87 @@ void Manager::slotInterfacesRemoved(const QDBusObjectPath &object_path, const QS
+     }
+ }
+ 
+-void Manager::slotMediaChanged(const QDBusMessage &msg)
++void Manager::slotPropertiesChanged(const QDBusMessage &msg)
+ {
+-    const QVariantMap properties = qdbus_cast<QVariantMap>(msg.arguments().at(1));
++    const QString udi = msg.path();
+ 
+-    if (!properties.contains(QStringLiteral("Size"))) { // react only on Size changes
++    if (udi.isEmpty() || !udi.startsWith(QLatin1String(UD2_UDI_DISKS_PREFIX)) || udi.startsWith(QLatin1String(UD2_DBUS_PATH_JOBS))) {
+         return;
+     }
+ 
+-    const QString udi = msg.path();
+-    updateBackend(udi);
+-    qulonglong size = properties.value(QStringLiteral("Size")).toULongLong();
+-    qCDebug(UDISKS2) << "MEDIA CHANGED in" << udi << "; size is:" << size;
+-
+-    Device device(udi);
+-    if (!device.interfaces().contains(u"org.freedesktop.UDisks2.Filesystem")) {
+-        if (!m_deviceCache.contains(udi) && size > 0) { // we don't know the optdisc, got inserted
+-            m_deviceCache.append(udi);
+-            Q_EMIT deviceAdded(udi);
+-        }
+-
+-        if (m_deviceCache.contains(udi) && size == 0) { // we know the optdisc, got removed
+-            Q_EMIT deviceRemoved(udi);
+-            m_deviceCache.removeAll(udi);
+-            DeviceBackend::destroyBackend(udi);
+-        }
++    const auto args = msg.arguments();
++    if (Q_UNLIKELY(args.size() != 3)) {
++        return;
+     }
+-}
+ 
+-const QStringList &Manager::deviceCache()
+-{
+-    if (m_deviceCache.isEmpty()) {
+-        allDevices();
++    const QString iface = qdbus_cast<QString>(args.at(0));
++    const QVariantMap changed = qdbus_cast<QVariantMap>(args.at(1));
++    const QStringList invalidated = qdbus_cast<QStringList>(args.at(2));
++
++    auto cachedIt = m_cache.find(udi);
++    if (cachedIt == m_cache.end()) {
++        return;
+     }
+ 
+-    return m_deviceCache;
+-}
++    const bool knownDevice = m_devices.contains(udi);
+ 
+-void Manager::updateBackend(const QString &udi)
+-{
+-    DeviceBackend *backend = DeviceBackend::backendForUDI(udi);
+-    if (!backend) {
+-        return;
++    // Update cache of internal devices even if we don't advertise them at this time.
++    QMap<QString, int> changeMap;
++
++    for (const QString &prop : invalidated) {
++        // Invalid QVariant() marks property that exists but needs to be fetched first.
++        (*cachedIt)[iface].insert(prop, QVariant());
++        changeMap.insert(prop, Solid::GenericInterface::PropertyModified);
+     }
+ 
+-    // This doesn't emit "changed" signals. Signals are emitted later by DeviceBackend's slots
+-    backend->allProperties();
++    for (auto it = changed.begin(), end = changed.end(); it != end; ++it) {
++        (*cachedIt)[iface].insert(it.key(), Utils::sanitizeValue(it.value()));
++        changeMap.insert(it.key(), Solid::GenericInterface::PropertyModified);
++    }
+ 
+-    QVariant driveProp = backend->prop(QStringLiteral("Drive"));
+-    if (!driveProp.isValid()) {
+-        return;
++    // Only announce the change if the device is advertised.
++    if (knownDevice && !changeMap.isEmpty()) {
++        Q_EMIT propertyChanged(udi, changeMap);
+     }
+ 
+-    QDBusObjectPath drivePath = qdbus_cast<QDBusObjectPath>(driveProp);
+-    DeviceBackend *driveBackend = DeviceBackend::backendForUDI(drivePath.path(), false);
+-    if (!driveBackend) {
+-        return;
++    // Special handling for optical media insertion/removal.
++    if (iface == QLatin1String(UD2_DBUS_INTERFACE_BLOCK) && (changed.contains(QStringLiteral("Size")) || invalidated.contains(QStringLiteral("Size")))) {
++        qulonglong size = deviceProperty(udi, QStringLiteral("Size")).toULongLong();
++
++        const bool mediaInserted = !knownDevice && size > 0;
++        const bool mediaRemoved = knownDevice && size == 0;
++
++        if (mediaInserted || mediaRemoved) {
++            Device device(this, udi);
++            if (device.mightBeOpticalDisc()) {
++                if (!knownDevice && size > 0) { // we don't know the optical disc, got inserted.
++                    const OpticalDisc disc(&device);
++                    // If it is a data disc, wait for its FileSystem interface to be announced instead,
++                    // otherwise we'll add this disc twice. But if we don't add it here, we will miss
++                    // Audio CDs that will never have a FileSystem interface.
++                    if (!disc.availableContent().testFlag(Solid::OpticalDisc::Data)) {
++                        m_devices.append(udi);
++                        Q_EMIT deviceAdded(udi);
++                    }
++                } else if (knownDevice && size == 0) { // we know the optical disc, got removed.
++                    Q_EMIT deviceRemoved(udi);
++                    // Keeping the cache as we never fetch all device properties again after the initial query.
++                    m_devices.removeOne(udi);
++                }
++            }
++        }
++    }
++
++    // TODO invalidate drive? updateBackend did that
++}
++
++QMap<QString, PropertyMap> Manager::deviceCache()
++{
++    if (m_cache.isEmpty()) {
++        allDevices();
+     }
+ 
+-    driveBackend->invalidateProperties();
++    return m_cache;
+ }
+ 
+ #include "moc_udisksmanager.cpp"
+diff --git a/src/solid/devices/backends/udisks2/udisksmanager.h b/src/solid/devices/backends/udisks2/udisksmanager.h
+index e73cfa8628b27b408674d51d0fa573f1d3def177..5b52fd0fe89729d87feeb2ed4e68b0cbd1a85856 100644
+--- a/src/solid/devices/backends/udisks2/udisksmanager.h
++++ b/src/solid/devices/backends/udisks2/udisksmanager.h
+@@ -16,6 +16,8 @@
+ 
+ #include <QSet>
+ 
++using PropertyMap = QMap<QString, QVariantMap>;
++
+ namespace Solid
+ {
+ namespace Backends
+@@ -27,6 +29,11 @@ class Manager : public Solid::Ifaces::DeviceManager
+     Q_OBJECT
+ 
+ public:
++    enum FetchMode {
++        FetchIfNeeded,
++        CachedOnly,
++    };
++
+     Manager(QObject *parent);
+     QObject *createDevice(const QString &udi) override;
+     QStringList devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type) override;
+@@ -35,18 +42,29 @@ public:
+     QString udiPrefix() const override;
+     ~Manager() override;
+ 
++    bool hasInterface(const QString &udi, const QString &interface);
++    QMap<QString, PropertyMap> allProperties();
++    PropertyMap deviceProperties(const QString &udi);
++    QVariant deviceProperty(const QString &udi, const QString &name, FetchMode fetchMode = FetchIfNeeded);
++
++Q_SIGNALS:
++    void propertyChanged(const QString &udi, const QMap<QString, int> &changes);
++
+ private Q_SLOTS:
+     void slotInterfacesAdded(const QDBusObjectPath &object_path, const VariantMapMap &interfaces_and_properties);
+     void slotInterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces);
+-    void slotMediaChanged(const QDBusMessage &msg);
++    void slotPropertiesChanged(const QDBusMessage &msg);
+ 
+ private:
+-    const QStringList &deviceCache();
+-    void introspect(const QString &path, bool checkOptical = false);
++    QMap<QString, PropertyMap> deviceCache();
+     void updateBackend(const QString &udi);
+     QSet<Solid::DeviceInterface::Type> m_supportedInterfaces;
+     org::freedesktop::DBus::ObjectManager m_manager;
+-    QStringList m_deviceCache;
++
++    // Optical media can be removed from the list of "known" devices
++    // but we still want to cache their properties for when the media is inserted back.
++    QStringList m_devices;
++    QMap<QString, PropertyMap> m_cache;
+ };
+ 
+ }
+diff --git a/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp b/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp
+index f88f7c7ebf1a56929039f69668e35ddcfee07121..7204a23055a4487ac87762545d763c4920e65626 100644
+--- a/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp
++++ b/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp
+@@ -331,7 +331,7 @@ OpticalDisc::OpticalDisc(Device *dev)
+     /*qDebug() << "\tProperties:" << */ m_udevDevice.deviceProperties(); // initialize the properties DB so that it doesn't crash further down, #298416
+ #endif
+ 
+-    m_drive = new Device(m_device->drivePath());
++    m_drive = new Device(m_device->manager(), m_device->drivePath());
+ }
+ 
+ OpticalDisc::~OpticalDisc()
+diff --git a/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp b/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp
+index 18bc4328ff1d324b2f83c659b54291460fc208a2..15a7e1f45870d426e19f02195fd8280bef8c66c0 100644
+--- a/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp
++++ b/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp
+@@ -34,7 +34,7 @@ OpticalDrive::OpticalDrive(Device *device)
+ {
+     m_device->registerAction(QStringLiteral("eject"), this, SLOT(slotEjectRequested()), SLOT(slotEjectDone(int, QString)));
+ 
+-    connect(m_device, SIGNAL(changed()), this, SLOT(slotChanged()));
++    connect(m_device, &Device::changed, this, &OpticalDrive::slotChanged);
+ }
+ 
+ OpticalDrive::~OpticalDrive()
+@@ -70,7 +70,7 @@ bool OpticalDrive::eject()
+                 continue;
+             }
+ 
+-            Device device(udi);
++            Device device(m_device->manager(), udi);
+             if (device.drivePath() == path && device.isMounted()) {
+                 // qDebug() << "Got mounted block device:" << udi;
+                 blockPath = udi;
+diff --git a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp
+index 16d49406c8a0337c7343735c1831f551d17058d9..579452df9438c5050287cfb50e8e019fcac418ab 100644
+--- a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp
++++ b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp
+@@ -8,12 +8,14 @@
+ #include "udisksstorageaccess.h"
+ #include "udisks2.h"
+ #include "udisks_debug.h"
++#include "udisksutils.h"
+ 
+ #include <QDBusConnection>
+ #include <QDBusInterface>
+ #include <QDBusMetaType>
+ #include <QDir>
+ #include <QGuiApplication>
++#include <QMetaObject>
+ #include <QWindow>
+ 
+ #include <config-solid.h>
+@@ -53,14 +55,12 @@ StorageAccess::StorageAccess(Device *device)
+     , m_repairInProgress(false)
+     , m_passphraseRequested(false)
+ {
+-    qDBusRegisterMetaType<AvailableAnswer>();
+-
+     connect(device, SIGNAL(changed()), this, SLOT(checkAccessibility()));
+     updateCache();
+ 
+     // Delay connecting to DBus signals to avoid the related time penalty
+     // in hot paths such as predicate matching
+-    QTimer::singleShot(0, this, SLOT(connectDBusSignals()));
++    QMetaObject::invokeMethod(this, &StorageAccess::connectDBusSignals, Qt::QueuedConnection);
+ }
+ 
+ StorageAccess::~StorageAccess()
+@@ -91,7 +91,7 @@ bool StorageAccess::isAccessible() const
+         if (path.isEmpty() || path == QLatin1String("/")) {
+             return false;
+         }
+-        Device holderDevice(path);
++        Device holderDevice(m_device->manager(), path);
+         return holderDevice.isMounted();
+     }
+ 
+@@ -193,7 +193,7 @@ static QString baseMountPoint(const QByteArray &dev)
+             struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
+             struct libmnt_fs *fs;
+ 
+-            const QByteArray devicePath = dev.endsWith('\x00') ? dev.chopped(1) : dev;
++            const QByteArray devicePath = Utils::sanitizeValue(dev);
+ 
+             while (mnt_table_next_fs(table, itr, &fs) == 0) {
+                 if (mnt_fs_get_srcpath(fs) == devicePath //
+@@ -223,14 +223,10 @@ QString StorageAccess::filePath() const
+         if (path.isEmpty() || path == QLatin1String("/")) {
+             return QString();
+         }
+-        Device holderDevice(path);
++        Device holderDevice(m_device->manager(), path);
+         const auto mntPoints = qdbus_cast<QByteArrayList>(holderDevice.prop(QStringLiteral("MountPoints")));
+         if (!mntPoints.isEmpty()) {
+-            QByteArray first = mntPoints.first();
+-            if (first.endsWith('\x00')) {
+-                first.chop(1);
+-            }
+-            return QFile::decodeName(first); // FIXME Solid doesn't support multiple mount points
++            return QFile::decodeName(Utils::sanitizeValue(mntPoints.first())); // FIXME Solid doesn't support multiple mount points
+         } else {
+             return QString();
+         }
+@@ -241,11 +237,7 @@ QString StorageAccess::filePath() const
+         return {};
+     }
+ 
+-    QByteArray first = mntPoints.first();
+-    if (first.endsWith('\x00')) {
+-        first.chop(1);
+-    }
+-    const QString potentialMountPoint = QFile::decodeName(first);
++    const QString potentialMountPoint = QFile::decodeName(Utils::sanitizeValue(mntPoints.first()));
+ 
+     if (mntPoints.size() == 1) {
+         return potentialMountPoint;
+@@ -324,7 +316,6 @@ void StorageAccess::slotDBusReply(const QDBusMessage &reply)
+             mount();
+         } else { // Don't broadcast setupDone unless the setup is really done. (Fix kde#271156)
+             m_setupInProgress = false;
+-            m_device->invalidateCache();
+             m_device->broadcastActionDone(QStringLiteral("setup"));
+ 
+             checkAccessibility();
+@@ -340,7 +331,7 @@ void StorageAccess::slotDBusReply(const QDBusMessage &reply)
+             // try to "eject" (aka safely remove) from the (parent) drive, e.g. SD card from a reader
+             QString drivePath = m_device->drivePath();
+             if (!drivePath.isEmpty() || drivePath != QStringLiteral("/")) {
+-                Device drive(drivePath);
++                Device drive(m_device->manager(), drivePath);
+                 QDBusConnection c = QDBusConnection::systemBus();
+ 
+                 if (drive.prop(QStringLiteral("MediaRemovable")).toBool() //
+@@ -365,7 +356,6 @@ void StorageAccess::slotDBusReply(const QDBusMessage &reply)
+             }
+ 
+             m_teardownInProgress = false;
+-            m_device->invalidateCache();
+             m_device->broadcastActionDone(QStringLiteral("teardown"));
+ 
+             checkAccessibility();
+diff --git a/src/solid/devices/backends/udisks2/udisksutils.cpp b/src/solid/devices/backends/udisks2/udisksutils.cpp
+new file mode 100644
+index 0000000000000000000000000000000000000000..b6d6d0ef963f60dfa9e23c3d812ff356d216f32b
+--- /dev/null
++++ b/src/solid/devices/backends/udisks2/udisksutils.cpp
+@@ -0,0 +1,41 @@
++/*
++    SPDX-FileCopyrightText: 2012 Lukáš Tinkl <ltinkl at redhat.com>
++
++    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
++*/
++
++#include "udisksutils.h"
++
++#include <QDebug>
++
++using namespace Solid::Backends::UDisks2;
++
++QByteArray Utils::sanitizeValue(const QByteArray &value)
++{
++    QByteArray blob = value;
++    // UDisks2 sends us null terminated strings, make sure to strip the extranous \0 in favor of the implicit \0.
++    // Otherwise comparision becomes unnecessarily complicated because 'foo\0' != 'foo'. QByteArrays are implicitly
++    // terminated already.
++    while (blob.endsWith('\0')) {
++        blob.chop(1);
++    }
++    return blob;
++}
++
++QVariant Utils::sanitizeValue(const QVariant &value)
++{
++    if (value.userType() == QMetaType::QByteArray) {
++        return sanitizeValue(value.toByteArray());
++    } else {
++        return value;
++    }
++}
++
++QVariantMap Utils::sanitizeValue(const QVariantMap &map)
++{
++    QVariantMap ret = map;
++    for (QVariant &value : ret) {
++        value = sanitizeValue(value);
++    }
++    return ret;
++}
+diff --git a/src/solid/devices/backends/udisks2/udisksutils.h b/src/solid/devices/backends/udisks2/udisksutils.h
+new file mode 100644
+index 0000000000000000000000000000000000000000..751055cdbd22dac8cfb8abb8536237f408d57050
+--- /dev/null
++++ b/src/solid/devices/backends/udisks2/udisksutils.h
+@@ -0,0 +1,34 @@
++/*
++    SPDX-FileCopyrightText: 2024 Kai Uwe Broulik <kde at broulik.de>
++
++    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
++*/
++
++#ifndef UDISKSUTILS_H
++#define UDISKSUTILS_H
++
++#include <QByteArray>
++#include <QVariant>
++#include <QVariantMap>
++
++namespace Solid
++{
++namespace Backends
++{
++namespace UDisks2
++{
++
++namespace Utils
++{
++
++QByteArray sanitizeValue(const QByteArray &value);
++QVariant sanitizeValue(const QVariant &value);
++QVariantMap sanitizeValue(const QVariantMap &map);
++
++} // Utils
++
++} // UDisks2
++} // Backends
++} // Solid
++
++#endif // UDISKSUTILS_H
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..ae2c1e3
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+kai_udisks.diff


More information about the Neon-commits mailing list