[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