From bc3d518faad508a7484d510086dc04f881ac0aa7 Mon Sep 17 00:00:00 2001 From: Raine Makelainen Date: Tue, 27 Nov 2018 13:07:34 +0200 Subject: [PATCH] [nemo-storage] Make block devices without partition/partitiontable visible in model. Fixes JB#42943 This commit adds signleton block devices container that is the main interface that monitor uses for operating block devices. The block devices emits a signal (newBlock) when it sees an acceptable new block device and throws away the ones that cannot / should not be shown in the model. When block device container sees a block device that could be a container for a partition, it'll wait for 3000ms milliseconds before accepting the block. If partition appears during this inverval, then waiting is cancel. --- src/partitionmanager.cpp | 3 +- src/src.pro | 2 + src/udisks2block.cpp | 235 +++++++++++++------------ src/udisks2block_p.h | 42 +++-- src/udisks2blockdevices.cpp | 337 ++++++++++++++++++++++++++++++++++++ src/udisks2blockdevices_p.h | 106 ++++++++++++ src/udisks2defines.h | 15 +- src/udisks2job.cpp | 19 +- src/udisks2job_p.h | 2 + src/udisks2monitor.cpp | 324 ++++++++++++---------------------- src/udisks2monitor_p.h | 14 +- 11 files changed, 733 insertions(+), 366 deletions(-) create mode 100644 src/udisks2blockdevices.cpp create mode 100644 src/udisks2blockdevices_p.h diff --git a/src/partitionmanager.cpp b/src/partitionmanager.cpp index e77493c..2526749 100644 --- a/src/partitionmanager.cpp +++ b/src/partitionmanager.cpp @@ -31,6 +31,7 @@ #include "partitionmanager_p.h" #include "udisks2monitor_p.h" +#include "udisks2blockdevices_p.h" #include "logging_p.h" #include @@ -340,7 +341,7 @@ QString PartitionManagerPrivate::objectPath(const QString &devicePath) const { QString deviceName = devicePath.section(QChar('/'), 2); if (externalMedia.match(deviceName).hasMatch()) { - return m_udisksMonitor->instance()->objectPath(devicePath); + return UDisks2::BlockDevices::instance()->objectPath(devicePath); } else { qCWarning(lcMemoryCardLog) << "Object path existing only for external memory cards:" << devicePath; return QString(); diff --git a/src/src.pro b/src/src.pro index 5b25879..f143e1d 100644 --- a/src/src.pro +++ b/src/src.pro @@ -39,6 +39,7 @@ SOURCES += \ locationsettings.cpp \ timezoneinfo.cpp \ udisks2block.cpp \ + udisks2blockdevices.cpp \ udisks2job.cpp \ udisks2monitor.cpp @@ -78,6 +79,7 @@ HEADERS += \ partition_p.h \ partitionmanager_p.h \ udisks2block_p.h \ + udisks2blockdevices_p.h \ udisks2job_p.h \ udisks2monitor_p.h diff --git a/src/udisks2block.cpp b/src/udisks2block.cpp index 2e29706..c8169a3 100644 --- a/src/udisks2block.cpp +++ b/src/udisks2block.cpp @@ -17,10 +17,6 @@ UDisks2::Block::Block(const QString &path, const UDisks2::InterfacePropertyMap & , m_encrypted(interfacePropertyMap.contains(UDISKS2_ENCRYPTED_INTERFACE)) , m_formatting(false) , m_locking(false) - , m_pendingFileSystem(nullptr) - , m_pendingBlock(nullptr) - , m_pendingEncrypted(nullptr) - , m_pendingDrive(nullptr) { if (!m_connection.connect( UDISKS2_SERVICE, @@ -39,35 +35,61 @@ UDisks2::Block::Block(const QString &path, const UDisks2::InterfacePropertyMap & qCInfo(lcMemoryCardLog) << "Creating a new block. Mountable:" << m_mountable << ", encrypted:" << m_encrypted << "object path:" << m_path << "data is empty:" << m_data.isEmpty(); - if (m_data.isEmpty()) { - getFileSystemInterface(); - getEncryptedInterface(); - QDBusPendingCall pendingCall = dbusPropertyInterface.asyncCall(DBUS_GET_ALL, UDISKS2_BLOCK_INTERFACE); - m_pendingBlock = new QDBusPendingCallWatcher(pendingCall, this); - connect(m_pendingBlock, &QDBusPendingCallWatcher::finished, this, [this, path](QDBusPendingCallWatcher *watcher) { - if (watcher->isValid() && watcher->isFinished()) { - QDBusPendingReply<> reply = *watcher; - QDBusMessage message = reply.reply(); - QVariantMap blockProperties = NemoDBus::demarshallArgument(message.arguments().at(0)); - qCInfo(lcMemoryCardLog) << "Block properties:" << blockProperties; - m_data = blockProperties; - getDriveProperties(); - } else { - QDBusError error = watcher->error(); - qCWarning(lcMemoryCardLog) << "Error reading block properties:" << error.name() << error.message(); - } - m_pendingBlock->deleteLater(); - m_pendingBlock = nullptr; - complete(); + if (m_interfacePropertyMap.isEmpty()) { + // Encrypted interface + getProperties(m_path, UDISKS2_ENCRYPTED_INTERFACE, m_pendingEncrypted, [this](const QVariantMap &encryptedProperties) { + m_encrypted = true; + m_interfacePropertyMap.insert(UDISKS2_ENCRYPTED_INTERFACE, encryptedProperties); + }); + + // File system interface + getProperties(m_path, UDISKS2_FILESYSTEM_INTERFACE, m_pendingFileSystem, [this](const QVariantMap &filesystemProperties) { + updateFileSystemInterface(filesystemProperties); + }); + + // Partition table interface + getProperties(m_path, UDISKS2_PARTITION_TABLE_INTERFACE, m_pendingPartitionTable, [this](const QVariantMap &partitionTableProperties) { + m_interfacePropertyMap.insert(UDISKS2_PARTITION_TABLE_INTERFACE, partitionTableProperties); + }); + + // Partition interface + getProperties(m_path, UDISKS2_PARTITION_INTERFACE, m_pendingPartition, [this](const QVariantMap &partitionProperties) { + m_interfacePropertyMap.insert(UDISKS2_PARTITION_INTERFACE, partitionProperties); }); + + // Block interface + getProperties(m_path, UDISKS2_BLOCK_INTERFACE, m_pendingBlock, [this](const QVariantMap &blockProperties) { + qCInfo(lcMemoryCardLog) << "Block properties:" << blockProperties; + m_data = blockProperties; + m_interfacePropertyMap.insert(UDISKS2_BLOCK_INTERFACE, blockProperties); + + // Drive path is blocks property => doing it the callback. + getProperties(drive(), UDISKS2_DRIVE_INTERFACE, m_pendingDrive, [this](const QVariantMap &driveProperties) { + qCInfo(lcMemoryCardLog) << "Drive properties:" << driveProperties; + m_drive = driveProperties; + }); + + connect(m_pendingDrive.data(), &QObject::destroyed, this, &Block::complete); + }); + + connect(m_pendingEncrypted.data(), &QObject::destroyed, this, &Block::complete); + connect(m_pendingFileSystem.data(), &QObject::destroyed, this, &Block::complete); + connect(m_pendingPartitionTable.data(), &QObject::destroyed, this, &Block::complete); + connect(m_pendingPartition.data(), &QObject::destroyed, this, &Block::complete); + connect(m_pendingBlock.data(), &QObject::destroyed, this, &Block::complete); } else { if (m_mountable) { QVariantMap map = interfacePropertyMap.value(UDISKS2_FILESYSTEM_INTERFACE); - updateMountPoint(map); + updateFileSystemInterface(map); } - getDriveProperties(); - // We have either org.freedesktop.UDisks2.Filesystem or org.freedesktop.UDisks2.Encrypted interface. + getProperties(drive(), UDISKS2_DRIVE_INTERFACE, m_pendingDrive, [this](const QVariantMap &driveProperties) { + qCInfo(lcMemoryCardLog) << "Drive properties:" << driveProperties; + m_drive = driveProperties; + }); + + if (m_pendingDrive) + connect(m_pendingDrive.data(), &QObject::destroyed, this, &Block::complete); complete(); } @@ -78,6 +100,7 @@ UDisks2::Block::Block(const QString &path, const UDisks2::InterfacePropertyMap & UDisks2::Block &UDisks2::Block::operator=(const UDisks2::Block &) { + return *this; } UDisks2::Block::~Block() @@ -125,6 +148,22 @@ QString UDisks2::Block::connectionBus() const return bus; } +QString UDisks2::Block::partitionTable() const +{ + // Partion table that this partition belongs to. + return NemoDBus::demarshallDBusArgument(m_interfacePropertyMap.value(UDISKS2_PARTITION_INTERFACE).value(QStringLiteral("Table"))).toString(); +} + +bool UDisks2::Block::isPartition() const +{ + return !m_interfacePropertyMap.value(UDISKS2_PARTITION_INTERFACE).isEmpty(); +} + +bool UDisks2::Block::isPartitionTable() const +{ + return !m_interfacePropertyMap.value(UDISKS2_PARTITION_TABLE_INTERFACE).isEmpty(); +} + qint64 UDisks2::Block::deviceNumber() const { return value(QStringLiteral("DeviceNumber")).toLongLong(); @@ -277,7 +316,9 @@ void UDisks2::Block::dumpInfo() const qCInfo(lcMemoryCardLog) << "- idversion:" << idVersion() << "idlabel:" << idLabel(); qCInfo(lcMemoryCardLog) << "- iduuid:" << idUUID(); qCInfo(lcMemoryCardLog) << "- ismountable:" << isMountable() << "mount path:" << mountPath(); - qCInfo(lcMemoryCardLog) << "- isencrypted:" << isEncrypted() << "crypto backing device:" << cryptoBackingDevicePath(); + qCInfo(lcMemoryCardLog) << "- isencrypted:" << isEncrypted() << "crypto backing device:" << cryptoBackingDevicePath() << "crypto backing object path:" << cryptoBackingDeviceObjectPath(); + qCInfo(lcMemoryCardLog) << "- isformatting:" << isFormatting(); + qCInfo(lcMemoryCardLog) << "- ispartiontable:" << isPartitionTable() << "ispartition:" << isPartition(); } QString UDisks2::Block::cryptoBackingDevicePath(const QString &objectPath) @@ -309,11 +350,21 @@ void UDisks2::Block::removeInterface(const QString &interface) m_drive.clear(); } else if (interface == UDISKS2_FILESYSTEM_INTERFACE) { setMountable(false); - }else if (interface == UDISKS2_ENCRYPTED_INTERFACE) { + } else if (interface == UDISKS2_ENCRYPTED_INTERFACE) { setEncrypted(false); } } +int UDisks2::Block::interfaceCount() const +{ + return m_interfacePropertyMap.keys().count(); +} + +bool UDisks2::Block::hasInterface(const QString &interface) const +{ + return m_interfacePropertyMap.contains(interface); +} + void UDisks2::Block::morph(const UDisks2::Block &other) { if (&other == this) @@ -341,6 +392,12 @@ void UDisks2::Block::morph(const UDisks2::Block &other) qCWarning(lcMemoryCardLog) << "Failed to connect to Block properties change interface" << m_path << m_connection.lastError().message(); } + qCInfo(lcMemoryCardLog) << "Morphing" << qPrintable(device()) << "that was" << (m_formatting ? "formatting" : "not formatting" ) << "to" << qPrintable(other.device()); + qCInfo(lcMemoryCardLog) << "Old block:"; + dumpInfo(); + qCInfo(lcMemoryCardLog) << "New block:"; + other.dumpInfo(); + m_interfacePropertyMap = other.m_interfacePropertyMap; m_data = other.m_data; m_drive = other.m_drive; @@ -351,6 +408,7 @@ void UDisks2::Block::morph(const UDisks2::Block &other) m_formatting = other.m_formatting; m_locking = other.m_locking; + if (wasFormatting && hasCryptoBackingDevice()) { rescan(cryptoBackingDeviceObjectPath()); } @@ -370,19 +428,21 @@ void UDisks2::Block::updateProperties(const QDBusMessage &message) emit updated(); } } else if (interface == UDISKS2_FILESYSTEM_INTERFACE) { - updateMountPoint(arguments.value(1)); + updateFileSystemInterface(arguments.value(1)); } } bool UDisks2::Block::isCompleted() const { - return !m_pendingFileSystem && !m_pendingBlock && !m_pendingEncrypted && !m_pendingDrive; + return !m_pendingFileSystem && !m_pendingBlock && !m_pendingEncrypted && !m_pendingDrive + && !m_pendingPartition && !m_pendingPartitionTable; } -void UDisks2::Block::updateMountPoint(const QVariant &mountPoints) +void UDisks2::Block::updateFileSystemInterface(const QVariant &filesystemInterface) { - QVariantMap mountPointsMap = NemoDBus::demarshallArgument(mountPoints); - QList mountPointList = NemoDBus::demarshallArgument >(mountPointsMap.value(QStringLiteral("MountPoints"))); + QVariantMap filesystem = NemoDBus::demarshallArgument(filesystemInterface); + m_interfacePropertyMap.insert(UDISKS2_FILESYSTEM_INTERFACE, filesystem); + QList mountPointList = NemoDBus::demarshallArgument >(filesystem.value(QStringLiteral("MountPoints"))); m_mountPath.clear(); for (const QByteArray &bytes : mountPointList) { @@ -402,7 +462,7 @@ void UDisks2::Block::updateMountPoint(const QVariant &mountPoints) emit updated(); } - qCInfo(lcMemoryCardLog) << "New file system mount points:" << mountPoints << "resolved mount path: " << m_mountPath << "trigger update:" << triggerUpdate; + qCInfo(lcMemoryCardLog) << "New file system mount points:" << filesystemInterface << "resolved mount path: " << m_mountPath << "trigger update:" << triggerUpdate; emit mountPathChanged(); } @@ -421,79 +481,6 @@ bool UDisks2::Block::clearFormattingState() return false; } -void UDisks2::Block::getFileSystemInterface() -{ - QDBusInterface dbusPropertyInterface(UDISKS2_SERVICE, - m_path, - DBUS_OBJECT_PROPERTIES_INTERFACE, - m_connection); - QDBusPendingCall pendingCall = dbusPropertyInterface.asyncCall(DBUS_GET_ALL, UDISKS2_FILESYSTEM_INTERFACE); - m_pendingFileSystem = new QDBusPendingCallWatcher(pendingCall, this); - connect(m_pendingFileSystem, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { - if (watcher->isValid() && watcher->isFinished()) { - QDBusPendingReply<> reply = *watcher; - QDBusMessage message = reply.reply(); - updateMountPoint(message.arguments().at(0)); - } else { - QDBusError error = watcher->error(); - qCWarning(lcMemoryCardLog) << "Error reading filesystem properties:" << error.name() << error.message() << m_path; - m_mountable = false; - } - m_pendingFileSystem->deleteLater(); - m_pendingFileSystem = nullptr; - complete(); - }); -} - -void UDisks2::Block::getEncryptedInterface() -{ - QDBusInterface dbusPropertyInterface(UDISKS2_SERVICE, - m_path, - DBUS_OBJECT_PROPERTIES_INTERFACE, - m_connection); - QDBusPendingCall pendingCall = dbusPropertyInterface.asyncCall(DBUS_GET_ALL, UDISKS2_ENCRYPTED_INTERFACE); - m_pendingEncrypted = new QDBusPendingCallWatcher(pendingCall, this); - connect(m_pendingEncrypted, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { - if (watcher->isValid() && watcher->isFinished()) { - m_encrypted = true; - } else { - QDBusError error = watcher->error(); - qCWarning(lcMemoryCardLog) << "Error reading encrypted properties:" << error.name() << error.message() << m_path; - m_encrypted = false; - } - m_pendingEncrypted->deleteLater(); - m_pendingEncrypted = nullptr; - complete(); - }); -} - -void UDisks2::Block::getDriveProperties() -{ - QDBusInterface drivePropertyInterface(UDISKS2_SERVICE, - drive(), - DBUS_OBJECT_PROPERTIES_INTERFACE, - m_connection); - QDBusPendingCall pendingCall = drivePropertyInterface.asyncCall(DBUS_GET_ALL, UDISKS2_DRIVE_INTERFACE); - m_pendingDrive = new QDBusPendingCallWatcher(pendingCall, this); - connect(m_pendingDrive, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { - if (watcher->isValid() && watcher->isFinished()) { - QDBusPendingReply<> reply = *watcher; - QDBusMessage message = reply.reply(); - QVariantMap driveProperties = NemoDBus::demarshallArgument(message.arguments().at(0)); - qCInfo(lcMemoryCardLog) << "Drive properties:" << driveProperties; - m_drive = driveProperties; - } else { - QDBusError error = watcher->error(); - qCWarning(lcMemoryCardLog) << "Error reading drive properties:" << error.name() << error.message(); - m_drive.clear(); - } - - m_pendingDrive->deleteLater(); - m_pendingDrive = nullptr; - complete(); - }); -} - void UDisks2::Block::rescan(const QString &dbusObjectPath) { QVariantList arguments; @@ -516,3 +503,35 @@ void UDisks2::Block::rescan(const QString &dbusObjectPath) watcher->deleteLater(); }); } + +void UDisks2::Block::getProperties(const QString &path, const QString &interface, + QPointer &watcherPointer, + std::function success) +{ + if (path.isEmpty() || path == QLatin1String("/")) { + qCInfo(lcMemoryCardLog) << "Ignoring get properties from path:" << path << "interface:" << interface; + return; + } + + QDBusInterface dbusPropertyInterface(UDISKS2_SERVICE, + path, + DBUS_OBJECT_PROPERTIES_INTERFACE, + m_connection); + QDBusPendingCall pendingCall = dbusPropertyInterface.asyncCall(DBUS_GET_ALL, interface); + watcherPointer = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcherPointer.data(), &QDBusPendingCallWatcher::finished, this, [success, path, interface](QDBusPendingCallWatcher *watcher) { + if (watcher->isValid() && watcher->isFinished()) { + QDBusPendingReply<> reply = *watcher; + QDBusMessage message = reply.reply(); + success(NemoDBus::demarshallArgument(message.arguments().at(0))); + } else { + qCDebug(lcMemoryCardLog) << "Get properties failed" << path << "interface:" << interface << watcher->isValid() << watcher->isFinished() << watcher->isError(); + QDBusPendingReply<> reply = *watcher; + QDBusMessage message = reply.reply(); + QDBusError error = watcher->error(); + qCDebug(lcMemoryCardLog) << "Error reading" << message.interface() << "properties:" << error.name() << error.message(); + } + + watcher->deleteLater(); + }); +} diff --git a/src/udisks2block_p.h b/src/udisks2block_p.h index 797424e..0d62b9e 100644 --- a/src/udisks2block_p.h +++ b/src/udisks2block_p.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "udisks2defines.h" @@ -42,14 +43,13 @@ class QDBusPendingCallWatcher; namespace UDisks2 { + class Block : public QObject { Q_OBJECT Q_PROPERTY(QString connectionBus READ connectionBus NOTIFY updated) public: - Block(const QString &path, const UDisks2::InterfacePropertyMap &interfacePropertyMap, QObject *parent = nullptr); - virtual ~Block(); QString path() const; @@ -58,6 +58,10 @@ class Block : public QObject QString preferredDevice() const; QString drive() const; QString connectionBus() const; + QString partitionTable() const; + + bool isPartition() const; + bool isPartitionTable() const; qint64 deviceNumber() const; QString id() const; @@ -99,34 +103,38 @@ class Block : public QObject static QString cryptoBackingDevicePath(const QString &objectPath); - void addInterface(const QString &interface, QVariantMap propertyMap); - void removeInterface(const QString &interface); - void morph(const Block& other); signals: - void completed(); + void completed(QPrivateSignal); void updated(); void formatted(); void mountPathChanged(); private slots: void updateProperties(const QDBusMessage &message); + void complete(); private: + Block(const QString &path, const UDisks2::InterfacePropertyMap &interfacePropertyMap, QObject *parent = nullptr); Block& operator=(const Block& other); bool setEncrypted(bool encrypted); bool setMountable(bool mountable); + void addInterface(const QString &interface, QVariantMap propertyMap); + void removeInterface(const QString &interface); + int interfaceCount() const; + bool hasInterface(const QString &interface) const; + bool isCompleted() const; - void updateMountPoint(const QVariant &mountPoints); - void complete(); + + void updateFileSystemInterface(const QVariant &mountPoints); bool clearFormattingState(); - void getFileSystemInterface(); - void getEncryptedInterface(); - void getDriveProperties(); + void getProperties(const QString &path, const QString &interface, + QPointer &watcherPointer, + std::function success); void rescan(const QString &dbusObjectPath); @@ -141,10 +149,14 @@ private slots: bool m_formatting; bool m_locking; - QDBusPendingCallWatcher *m_pendingFileSystem; - QDBusPendingCallWatcher *m_pendingBlock; - QDBusPendingCallWatcher *m_pendingEncrypted; - QDBusPendingCallWatcher *m_pendingDrive; + QPointer m_pendingFileSystem; + QPointer m_pendingBlock; + QPointer m_pendingEncrypted; + QPointer m_pendingDrive; + QPointer m_pendingPartition; + QPointer m_pendingPartitionTable; + + friend class BlockDevices; }; } diff --git a/src/udisks2blockdevices.cpp b/src/udisks2blockdevices.cpp new file mode 100644 index 0000000..e114313 --- /dev/null +++ b/src/udisks2blockdevices.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2018 Jolla Ltd. + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +#include "udisks2blockdevices_p.h" +#include "partitionmanager_p.h" +#include "logging_p.h" + +#include +#include + +#include + +#define PARTITION_WAIT_TIMEOUT 3000 + +using namespace UDisks2; + +BlockDevices *BlockDevices::sharedInstance = nullptr; + +BlockDevices *BlockDevices::instance() +{ + return sharedInstance ? sharedInstance : new BlockDevices; +} + +BlockDevices::~BlockDevices() +{ + qDeleteAll(m_blockDevices); + qDeleteAll(m_partitionWaits); + + m_blockDevices.clear(); + m_partitionWaits.clear(); + sharedInstance = nullptr; +} + +bool BlockDevices::contains(const QString &dbusObjectPath) const +{ + return m_blockDevices.contains(dbusObjectPath); +} + +void BlockDevices::remove(const QString &dbusObjectPath) +{ + Block *block = m_blockDevices.take(dbusObjectPath); + block->deleteLater(); +} + +Block *BlockDevices::device(const QString &dbusObjectPath) const +{ + return m_blockDevices.value(dbusObjectPath, nullptr); +} + +Block *BlockDevices::replace(const QString &dbusObjectPath, Block *block) +{ + Block *deviceReplace = device(dbusObjectPath); + + // Clear partition wait before morphing a block. + if (m_partitionWaits.contains(deviceReplace->partitionTable())) { + clearPartitionWait(deviceReplace->partitionTable(), false); + } + + deviceReplace->morph(*block); + m_blockDevices.remove(dbusObjectPath); + insert(deviceReplace->path(), deviceReplace); + block->deleteLater(); + return deviceReplace; +} + +void BlockDevices::insert(const QString &dbusObjectPath, Block *block) +{ + m_blockDevices.insert(dbusObjectPath, block); +} + +Block *BlockDevices::find(std::function condition) +{ + for (QMap::const_iterator i = m_blockDevices.constBegin(); i != m_blockDevices.constEnd(); ++i) { + Block *block = i.value(); + if (condition(block)) { + return block; + } + } + return nullptr; +} + +Block *BlockDevices::find(const QString &devicePath) +{ + return find([devicePath](const Block *block){ + return block->device() == devicePath || block->cryptoBackingDevicePath() == devicePath; + }); +} + +QString BlockDevices::objectPath(const QString &devicePath) const +{ + for (QMap::const_iterator i = m_blockDevices.constBegin(); i != m_blockDevices.constEnd(); ++i) { + Block *block = i.value(); + if (block->device() == devicePath) { + return block->path(); + } else if (block->cryptoBackingDevicePath() == devicePath) { + return block->cryptoBackingDeviceObjectPath(); + } + } + + return QString(); +} + +QStringList BlockDevices::devicePaths(const QStringList &dbusObjectPaths) const +{ + QStringList paths; + for (const QString &objectPath : dbusObjectPaths) { + for (QMap::const_iterator i = m_blockDevices.constBegin(); i != m_blockDevices.constEnd(); ++i) { + Block *block = i.value(); + if (block->path() == objectPath || block->cryptoBackingDeviceObjectPath() == objectPath) { + paths << block->device(); + } + } + } + return paths; +} + +bool BlockDevices::createBlockDevice(const QString &dbusObjectPath, const InterfacePropertyMap &interfacePropertyMap) +{ + if (!BlockDevices::isExternal(dbusObjectPath)) { + return false; + } + + return doCreateBlockDevice(dbusObjectPath, interfacePropertyMap); +} + +void BlockDevices::lock(const QString &dbusObjectPath) +{ + Block *deviceMapped = find([dbusObjectPath](const Block *block) { + return block->cryptoBackingDeviceObjectPath() == dbusObjectPath; + }); + + if (deviceMapped && (deviceMapped->isLocking() || deviceMapped->isFormatting())) { + Block *newBlock = doCreateBlockDevice(dbusObjectPath, InterfacePropertyMap()); + if (newBlock && deviceMapped->isFormatting()) { + newBlock->setFormatting(true); + } + } +} + +void BlockDevices::waitPartition(Block *block) +{ + m_partitionWaits.insert(block->path(), new PartitionWaiter(startTimer(PARTITION_WAIT_TIMEOUT), block)); +} + +void BlockDevices::clearPartitionWait(const QString &dbusObjectPath, bool destroyBlock) +{ + PartitionWaiter *waiter = m_partitionWaits.value(dbusObjectPath, nullptr); + if (waiter) { + killTimer(waiter->timer); + + // Just nullify block and let it live. + if (!destroyBlock) { + waiter->block = nullptr; + } + + delete waiter; + } + + m_partitionWaits.remove(dbusObjectPath); +} + +void BlockDevices::removeInterfaces(const QString &dbusObjectPath, const QStringList &interfaces) +{ + if (contains(dbusObjectPath)) { + UDisks2::Block *block = device(dbusObjectPath); + if (interfaces.contains(UDISKS2_FILESYSTEM_INTERFACE)) { + block->removeInterface(UDISKS2_FILESYSTEM_INTERFACE); + } + if (interfaces.contains(UDISKS2_ENCRYPTED_INTERFACE)) { + block->removeInterface(UDISKS2_ENCRYPTED_INTERFACE); + } + } + clearPartitionWait(dbusObjectPath, true); +} + +bool BlockDevices::isExternal(const QString &dbusObjectPath) +{ + static const QRegularExpression externalBlockDevice(QStringLiteral("^/org/freedesktop/UDisks2/block_devices/%1$").arg(externalDevice)); + return externalBlockDevice.match(dbusObjectPath).hasMatch(); +} + +void BlockDevices::blockCompleted() +{ + Block *completedBlock = qobject_cast(sender()); + if (completedBlock->isPartitionTable() || (completedBlock->hasInterface(UDISKS2_BLOCK_INTERFACE) && completedBlock->interfaceCount() == 1)) { + qCInfo(lcMemoryCardLog) << "Start waiting for block" << completedBlock->device(); + waitPartition(completedBlock); + return; + } + + clearPartitionWait(completedBlock->partitionTable(), true); + complete(completedBlock); +} + +BlockDevices::BlockDevices(QObject *parent) + : QObject(parent) +{ + Q_ASSERT(!sharedInstance); + + sharedInstance = this; +} + +Block *BlockDevices::doCreateBlockDevice(const QString &dbusObjectPath, const InterfacePropertyMap &interfacePropertyMap) +{ + if (contains(dbusObjectPath)) { + Block *block = device(dbusObjectPath); + if (block && interfacePropertyMap.contains(UDISKS2_FILESYSTEM_INTERFACE)) { + block->addInterface(UDISKS2_FILESYSTEM_INTERFACE, interfacePropertyMap.value(UDISKS2_FILESYSTEM_INTERFACE)); + } + if (block && interfacePropertyMap.contains(UDISKS2_ENCRYPTED_INTERFACE)) { + block->addInterface(UDISKS2_ENCRYPTED_INTERFACE, interfacePropertyMap.value(UDISKS2_ENCRYPTED_INTERFACE)); + } + + return block; + } + + Block *block = new Block(dbusObjectPath, interfacePropertyMap); + updateFormattingState(block); + connect(block, &Block::completed, this, &BlockDevices::blockCompleted); + return block; +} + +void BlockDevices::updateFormattingState(Block *block) +{ + Block *cryptoBackingDevice = nullptr; + QString cryptoBackingDevicePath = block->cryptoBackingDeviceObjectPath(); + + // If we have crypto backing device, copy over formatting state. + if (cryptoBackingDevicePath != QLatin1String("/") && (cryptoBackingDevice = m_blockDevices.value(cryptoBackingDevicePath, nullptr))) { + block->blockSignals(true); + block->setFormatting(cryptoBackingDevice->isFormatting()); + block->blockSignals(false); + } +} + +void BlockDevices::dumpBlocks() const +{ + for (QMap::const_iterator i = m_blockDevices.constBegin(); i != m_blockDevices.constEnd(); ++i) { + i.value()->dumpInfo(); + } +} + +void BlockDevices::complete(Block *block, bool forceAccept) +{ + if (!block->isExternal()) { + block->deleteLater(); + return; + } + + // Check if device is already unlocked. + Block *unlocked = nullptr; + if (block->isEncrypted()) { + QString newPath = block->path(); + unlocked = find([newPath](const Block *block) { + return block->cryptoBackingDeviceObjectPath() == newPath; + }); + } + + bool willAccept = !unlocked && (block->isMountable() || block->isEncrypted() || block->isFormatting() || forceAccept); + qCInfo(lcMemoryCardLog) << "Completed block" << qPrintable(block->path()) + << "is" << (willAccept ? "accepted" : "rejected"); + block->dumpInfo(); + + if (willAccept) { + emit newBlock(block); + } else { + // This is garbage block device that should not be exposed + // from the partition model. + block->removeInterface(UDISKS2_BLOCK_INTERFACE); + block->deleteLater(); + } +} + +void BlockDevices::timerEvent(QTimerEvent *e) +{ + for (QMap::iterator i = m_partitionWaits.begin(); i != m_partitionWaits.end(); ++i) { + PartitionWaiter *waiter = i.value(); + int timerId = waiter->timer; + if (e->timerId() == timerId) { + QString path = i.key(); + qCDebug(lcMemoryCardLog) << "Waiting partitions:" << m_partitionWaits.keys() << path; + dumpBlocks(); + + Block *partitionTable = find([path](const Block *block) { + return block->partitionTable() == path; + }); + + // No partition found that would be part of this partion table. Accept this one. + if (!partitionTable) { + complete(waiter->block, true); + } + clearPartitionWait(path, partitionTable); + break; + } + } +} + +BlockDevices::PartitionWaiter::PartitionWaiter(int timer, Block *block) + : timer(timer) + , block(block) +{ +} + +BlockDevices::PartitionWaiter::~PartitionWaiter() +{ + delete block; + block = nullptr; + timer = 0; +} diff --git a/src/udisks2blockdevices_p.h b/src/udisks2blockdevices_p.h new file mode 100644 index 0000000..09a3dd2 --- /dev/null +++ b/src/udisks2blockdevices_p.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 Jolla Ltd. + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +#ifndef UDISKS2_BLOCK_DEVICES_H +#define UDISKS2_BLOCK_DEVICES_H + +#include +#include "udisks2block_p.h" + +class QTimerEvent; + +namespace UDisks2 { + +class BlockDevices : public QObject +{ + Q_OBJECT +public: + static BlockDevices* instance(); + virtual ~BlockDevices() override; + + bool contains(const QString &dbusObjectPath) const; + void remove(const QString &dbusObjectPath); + + Block *device(const QString &dbusObjectPath) const; + Block *replace(const QString &dbusObjectPath, Block *block); + + void insert(const QString &dbusObjectPath, Block *block); + Block *find(std::function condition); + Block *find(const QString &devicePath); + + QString objectPath(const QString &devicePath) const; + QStringList devicePaths(const QStringList &dbusObjectPaths) const; + + bool createBlockDevice(const QString &dbusObjectPath, const InterfacePropertyMap &interfacePropertyMap); + void lock(const QString &dbusObjectPath); + + void waitPartition(Block *block); + void clearPartitionWait(const QString &dbusObjectPath, bool destroyBlock); + + void removeInterfaces(const QString &dbusObjectPath, const QStringList &interfaces); + + void dumpBlocks() const; + + static bool isExternal(const QString &dbusObjectPath); + +signals: + void newBlock(Block *block); + +private slots: + void blockCompleted(); + +private: + + struct PartitionWaiter { + PartitionWaiter(int timer, Block *block); + ~PartitionWaiter(); + + int timer; + Block *block; + }; + + BlockDevices(QObject *parent = nullptr); + Block *doCreateBlockDevice(const QString &dbusObjectPath, const InterfacePropertyMap &interfacePropertyMap); + void updateFormattingState(Block *block); + + void complete(Block *block, bool forceAccept = false); + + void timerEvent(QTimerEvent *e) override; + + QMap m_blockDevices; + QMap m_partitionWaits; + + static BlockDevices *sharedInstance; +}; + +} + +#endif diff --git a/src/udisks2defines.h b/src/udisks2defines.h index 995c437..b1f66bb 100644 --- a/src/udisks2defines.h +++ b/src/udisks2defines.h @@ -54,13 +54,14 @@ Q_DECLARE_METATYPE(UDisks2::InterfacePropertyMap) #define UDISKS2_MANAGER_PATH QLatin1String("/org/freedesktop/UDisks2/Manager") // Interfaces -#define UDISKS2_MANAGER_INTERFACE QLatin1String("org.freedesktop.UDisks2.Manager") -#define UDISKS2_ENCRYPTED_INTERFACE QLatin1String("org.freedesktop.UDisks2.Encrypted") -#define UDISKS2_DRIVE_INTERFACE QLatin1String("org.freedesktop.UDisks2.Drive") -#define UDISKS2_BLOCK_INTERFACE QLatin1String("org.freedesktop.UDisks2.Block") -#define UDISKS2_FILESYSTEM_INTERFACE QLatin1String("org.freedesktop.UDisks2.Filesystem") -#define UDISKS2_PARTITION_INTERFACE QLatin1String("org.freedesktop.UDisks2.Partition") -#define UDISKS2_JOB_INTERFACE QLatin1String("org.freedesktop.UDisks2.Job") +#define UDISKS2_MANAGER_INTERFACE QLatin1String("org.freedesktop.UDisks2.Manager") +#define UDISKS2_ENCRYPTED_INTERFACE QLatin1String("org.freedesktop.UDisks2.Encrypted") +#define UDISKS2_DRIVE_INTERFACE QLatin1String("org.freedesktop.UDisks2.Drive") +#define UDISKS2_BLOCK_INTERFACE QLatin1String("org.freedesktop.UDisks2.Block") +#define UDISKS2_FILESYSTEM_INTERFACE QLatin1String("org.freedesktop.UDisks2.Filesystem") +#define UDISKS2_PARTITION_INTERFACE QLatin1String("org.freedesktop.UDisks2.Partition") +#define UDISKS2_PARTITION_TABLE_INTERFACE QLatin1String("org.freedesktop.UDisks2.PartitionTable") +#define UDISKS2_JOB_INTERFACE QLatin1String("org.freedesktop.UDisks2.Job") // Jobs #define UDISKS2_JOB_OP_ENC_LOCK QLatin1String("encrypted-lock") diff --git a/src/udisks2job.cpp b/src/udisks2job.cpp index 3284a87..4f28829 100644 --- a/src/udisks2job.cpp +++ b/src/udisks2job.cpp @@ -137,17 +137,16 @@ UDisks2::Job::Operation UDisks2::Job::operation() const } } +void UDisks2::Job::dumpInfo() const +{ + qCInfo(lcMemoryCardLog) << "Job" << path() << ((status() == Added) ? "added" : "completed"); + for (const QString &key : m_data.keys()) { + qCInfo(lcMemoryCardLog) << "- " << qPrintable(key) << value(key); + } +} + void UDisks2::Job::updateCompleted(bool success, const QString &message) { - complete(success); m_message = message; - - QDBusConnection systemBus = QDBusConnection::systemBus(); - systemBus.disconnect( - UDISKS2_SERVICE, - m_path, - UDISKS2_JOB_INTERFACE, - QStringLiteral("Completed"), - this, - SLOT(updateJobStatus(bool, QString))); + complete(success); } diff --git a/src/udisks2job_p.h b/src/udisks2job_p.h index a69e983..c8dbd26 100644 --- a/src/udisks2job_p.h +++ b/src/udisks2job_p.h @@ -76,6 +76,8 @@ class Job : public QObject Status status() const; Operation operation() const; + void dumpInfo() const; + signals: void completed(bool success); diff --git a/src/udisks2monitor.cpp b/src/udisks2monitor.cpp index d677c11..2967e5f 100644 --- a/src/udisks2monitor.cpp +++ b/src/udisks2monitor.cpp @@ -31,6 +31,7 @@ #include "udisks2monitor_p.h" #include "udisks2block_p.h" +#include "udisks2blockdevices_p.h" #include "udisks2job_p.h" #include "udisks2defines.h" #include "nemo-dbus/dbus.h" @@ -81,6 +82,7 @@ UDisks2::Monitor *UDisks2::Monitor::instance() UDisks2::Monitor::Monitor(PartitionManagerPrivate *manager, QObject *parent) : QObject(parent) , m_manager(manager) + , m_blockDevices(BlockDevices::instance()) { Q_ASSERT(!sharedInstance); sharedInstance = this; @@ -121,6 +123,8 @@ UDisks2::Monitor::Monitor(PartitionManagerPrivate *manager, QObject *parent) } getBlockDevices(); + + connect(m_blockDevices, &BlockDevices::newBlock, this, &Monitor::handleNewBlock); } UDisks2::Monitor::~Monitor() @@ -138,7 +142,7 @@ void UDisks2::Monitor::lock(const QString &devicePath) QVariantMap options; arguments << options; - if (Block *block = findBlock(devicePath)) { + if (Block *block = m_blockDevices->find(devicePath)) { block->dumpInfo(); block->setLocking(); @@ -147,7 +151,7 @@ void UDisks2::Monitor::lock(const QString &devicePath) m_operationQueue.enqueue(Operation(UDISKS2_ENCRYPTED_LOCK, devicePath)); unmount(block->device()); } else { - startLuksOperation(devicePath, UDISKS2_ENCRYPTED_LOCK, objectPath(devicePath), arguments); + startLuksOperation(devicePath, UDISKS2_ENCRYPTED_LOCK, m_blockDevices->objectPath(devicePath), arguments); } } else { qCWarning(lcMemoryCardLog) << "Block device" << devicePath << "not found"; @@ -160,7 +164,7 @@ void UDisks2::Monitor::unlock(const QString &devicePath, const QString &passphra arguments << passphrase; QVariantMap options; arguments << options; - startLuksOperation(devicePath, UDISKS2_ENCRYPTED_UNLOCK, objectPath(devicePath), arguments); + startLuksOperation(devicePath, UDISKS2_ENCRYPTED_UNLOCK, m_blockDevices->objectPath(devicePath), arguments); } void UDisks2::Monitor::mount(const QString &devicePath) @@ -169,7 +173,7 @@ void UDisks2::Monitor::mount(const QString &devicePath) QVariantMap options; options.insert(QStringLiteral("fstype"), QString()); arguments << options; - startMountOperation(devicePath, UDISKS2_FILESYSTEM_MOUNT, objectPath(devicePath), arguments); + startMountOperation(devicePath, UDISKS2_FILESYSTEM_MOUNT, m_blockDevices->objectPath(devicePath), arguments); } void UDisks2::Monitor::unmount(const QString &devicePath) @@ -177,7 +181,7 @@ void UDisks2::Monitor::unmount(const QString &devicePath) QVariantList arguments; QVariantMap options; arguments << options; - startMountOperation(devicePath, UDISKS2_FILESYSTEM_UNMOUNT, objectPath(devicePath), arguments); + startMountOperation(devicePath, UDISKS2_FILESYSTEM_UNMOUNT, m_blockDevices->objectPath(devicePath), arguments); } void UDisks2::Monitor::format(const QString &devicePath, const QString &filesystemType, const QVariantMap &arguments) @@ -193,13 +197,13 @@ void UDisks2::Monitor::format(const QString &devicePath, const QString &filesyst return; } - const QString objectPath = this->objectPath(devicePath); + const QString objectPath = m_blockDevices->objectPath(devicePath); PartitionManagerPrivate::Partitions affectedPartitions; lookupPartitions(affectedPartitions, QStringList() << objectPath); for (auto partition : affectedPartitions) { // Mark block to formatting state. - if (Block *block = findBlock(devicePath)) { + if (Block *block = m_blockDevices->find(devicePath)) { block->setFormatting(true); } @@ -221,24 +225,12 @@ void UDisks2::Monitor::format(const QString &devicePath, const QString &filesyst void UDisks2::Monitor::interfacesAdded(const QDBusObjectPath &objectPath, const UDisks2::InterfacePropertyMap &interfaces) { QString path = objectPath.path(); - qCDebug(lcMemoryCardLog) << "UDisks interface added:" << path << externalBlockDevice(path); + qCDebug(lcMemoryCardLog) << "UDisks interface added:" << path << BlockDevices::isExternal(path); qCInfo(lcMemoryCardLog) << "UDisks dump interface:" << interfaces; // External device must have file system or partition so that it can added to the model. // Devices without partition table have filesystem interface. - - if (path.startsWith(QStringLiteral("/org/freedesktop/UDisks2/block_devices/")) && externalBlockDevice(path)) { - if (m_blockDevices.contains(path)) { - UDisks2::Block *block = m_blockDevices.value(path); - if (interfaces.contains(UDISKS2_FILESYSTEM_INTERFACE)) { - block->addInterface(UDISKS2_FILESYSTEM_INTERFACE, interfaces.value(UDISKS2_FILESYSTEM_INTERFACE)); - } - if (interfaces.contains(UDISKS2_ENCRYPTED_INTERFACE)) { - block->addInterface(UDISKS2_ENCRYPTED_INTERFACE, interfaces.value(UDISKS2_ENCRYPTED_INTERFACE)); - } - } else { - UDisks2::Block *block = createBlockDevice(path, interfaces); - updateFormattingState(block); - } + if (path.startsWith(QStringLiteral("/org/freedesktop/UDisks2/block_devices/")) && BlockDevices::isExternal(path)) { + m_blockDevices->createBlockDevice(path, interfaces); } else if (path.startsWith(QStringLiteral("/org/freedesktop/UDisks2/jobs"))) { QVariantMap dict = interfaces.value(UDISKS2_JOB_INTERFACE); QString operation = dict.value(UDISKS2_JOB_KEY_OPERATION, QString()).toString(); @@ -253,12 +245,20 @@ void UDisks2::Monitor::interfacesAdded(const QDBusObjectPath &objectPath, const connect(job, &UDisks2::Job::completed, this, [this](bool success) { UDisks2::Job *job = qobject_cast(sender()); - updatePartitionStatus(job, success); + job->dumpInfo(); + + if (job->operation() == Job::Lock) { + for (const QString &dbusObjectPath : job->objects()) { + m_blockDevices->lock(dbusObjectPath); + } + } else { + updatePartitionStatus(job, success); + } }); if (job->operation() == Job::Format) { for (const QString &objectPath : job->objects()) { - if (UDisks2::Block *block = m_blockDevices.value(objectPath, nullptr)) { + if (UDisks2::Block *block = m_blockDevices->device(objectPath)) { block->blockSignals(true); block->setFormatting(true); block->blockSignals(false); @@ -267,6 +267,7 @@ void UDisks2::Monitor::interfacesAdded(const QDBusObjectPath &objectPath, const } m_jobsToWait.insert(path, job); + job->dumpInfo(); } } } @@ -280,36 +281,18 @@ void UDisks2::Monitor::interfacesRemoved(const QDBusObjectPath &objectPath, cons if (m_jobsToWait.contains(path)) { UDisks2::Job *job = m_jobsToWait.take(path); job->deleteLater(); - } else if (m_blockDevices.contains(path) && interfaces.contains(UDISKS2_BLOCK_INTERFACE)) { + } else if (m_blockDevices->contains(path) && interfaces.contains(UDISKS2_BLOCK_INTERFACE)) { // Cleanup partitions first. - if (externalBlockDevice(path)) { + if (BlockDevices::isExternal(path)) { PartitionManagerPrivate::Partitions removedPartitions; QStringList blockDevPaths = { path }; lookupPartitions(removedPartitions, blockDevPaths); m_manager->remove(removedPartitions); } - UDisks2::Block *block = m_blockDevices.take(path); - - // Crypto device got removed - bool deviceMapped = path.startsWith(QStringLiteral("/org/freedesktop/UDisks2/block_devices/dm_")); - if (deviceMapped && (block->isLocking() || block->isFormatting())) { - UDisks2::Block *newBlock = createBlockDevice(block->cryptoBackingDeviceObjectPath(), UDisks2::InterfacePropertyMap()); - if (newBlock && block->isFormatting()) { - newBlock->setFormatting(true); - } - } - - block->deleteLater(); - - } else if (m_blockDevices.contains(path)) { - UDisks2::Block *block = m_blockDevices.value(path); - if (interfaces.contains(UDISKS2_FILESYSTEM_INTERFACE)) { - block->removeInterface(UDISKS2_FILESYSTEM_INTERFACE); - } - if (interfaces.contains(UDISKS2_ENCRYPTED_INTERFACE)) { - block->removeInterface(UDISKS2_ENCRYPTED_INTERFACE); - } + m_blockDevices->remove(path); + } else { + m_blockDevices->removeInterfaces(path, interfaces); } } @@ -453,13 +436,6 @@ void UDisks2::Monitor::updatePartitionStatus(const UDisks2::Job *job, bool succe } } -// Used in UDisks2 InterfacesAdded / InterfacesRemoved signals. -bool UDisks2::Monitor::externalBlockDevice(const QString &deviceName) const -{ - static const QRegularExpression externalBlockDevice(QStringLiteral("^/org/freedesktop/UDisks2/block_devices/%1$").arg(externalDevice)); - return externalBlockDevice.match(deviceName).hasMatch(); -} - void UDisks2::Monitor::startLuksOperation(const QString &devicePath, const QString &dbusMethod, const QString &dbusObjectPath, const QVariantList &arguments) { Q_ASSERT(dbusMethod == UDISKS2_ENCRYPTED_LOCK || dbusMethod == UDISKS2_ENCRYPTED_UNLOCK); @@ -541,7 +517,7 @@ void UDisks2::Monitor::startMountOperation(const QString &devicePath, const QStr connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, devicePath, dbusMethod](QDBusPendingCallWatcher *watcher) { if (watcher->isValid() && watcher->isFinished()) { - Block *block = findBlock(devicePath); + Block *block = m_blockDevices->find(devicePath); if (block && block->isFormatting()) { // Do nothing } else if (dbusMethod == UDISKS2_FILESYSTEM_MOUNT) { @@ -584,7 +560,7 @@ void UDisks2::Monitor::startMountOperation(const QString &devicePath, const QStr watcher->deleteLater(); }); - Block *block = findBlock(devicePath); + Block *block = m_blockDevices->find(devicePath); if (block && block->isFormatting()) { emit status(devicePath, Partition::Formatting); } else if (dbusMethod == UDISKS2_FILESYSTEM_MOUNT) { @@ -596,16 +572,7 @@ void UDisks2::Monitor::startMountOperation(const QString &devicePath, const QStr void UDisks2::Monitor::lookupPartitions(PartitionManagerPrivate::Partitions &affectedPartitions, const QStringList &objects) { - QStringList blockDevs; - for (const QString &objectPath : objects) { - for (QMap::const_iterator i = m_blockDevices.constBegin(); i != m_blockDevices.constEnd(); ++i) { - Block *block = i.value(); - if (block->path() == objectPath || block->cryptoBackingDeviceObjectPath() == objectPath) { - blockDevs << block->device(); - } - } - } - + QStringList blockDevs = m_blockDevices->devicePaths(objects); for (const QString &dev : blockDevs) { for (auto partition : m_manager->m_partitions) { if (partition->devicePath == dev) { @@ -626,123 +593,6 @@ void UDisks2::Monitor::createPartition(const UDisks2::Block *block) m_manager->add(partition); } -UDisks2::Block *UDisks2::Monitor::createBlockDevice(const QString &dbusObjectPath, const UDisks2::InterfacePropertyMap &interfacePropertyMap) -{ - if (m_blockDevices.contains(dbusObjectPath)) { - return m_blockDevices.value(dbusObjectPath, nullptr); - } - - // First guards that we don't create extensively block devices that cannot be - // external block devices. - if (externalBlockDevice(dbusObjectPath)) { - UDisks2::Block *block = new UDisks2::Block(dbusObjectPath, interfacePropertyMap); - - // Upon creation. - connect(block, &UDisks2::Block::completed, this, [this]() { - UDisks2::Block *completedBlock = qobject_cast(sender()); - bool unlocked = false; - - // Check if device is already unlocked. - if (completedBlock->isEncrypted()) { - for (const Block *b : m_blockDevices.values()) { - if (b->cryptoBackingDeviceObjectPath() == completedBlock->path()) { - unlocked = true; - break; - } - } - } - - if (!unlocked && completedBlock->isExternal() && (completedBlock->isMountable() || completedBlock->isEncrypted())) { - const QString cryptoBackingDeviceObjectPath = completedBlock->cryptoBackingDeviceObjectPath(); - if (completedBlock->hasCryptoBackingDevice() && m_blockDevices.contains(cryptoBackingDeviceObjectPath)) { - // Update crypto backing device to file system device. - UDisks2::Block *cryptoBackingDev = m_blockDevices.value(cryptoBackingDeviceObjectPath); - cryptoBackingDev->morph(*completedBlock); - m_blockDevices.remove(cryptoBackingDeviceObjectPath); - m_blockDevices.insert(cryptoBackingDev->path(), cryptoBackingDev); - updatePartitionProperties(cryptoBackingDev); - completedBlock->deleteLater(); - } else if (!m_blockDevices.contains(completedBlock->path())) { - m_blockDevices.insert(completedBlock->path(), completedBlock); - createPartition(completedBlock); - - if (completedBlock->isFormatting()) { - if (!m_operationQueue.isEmpty()) { - Operation op = m_operationQueue.head(); - if (op.command == UDISKS2_BLOCK_FORMAT) { - m_operationQueue.dequeue(); - QMetaObject::invokeMethod(this, "doFormat", Qt::QueuedConnection, - Q_ARG(QString, op.devicePath), Q_ARG(QString, op.dbusObjectPath), - Q_ARG(QString, op.filesystemType), Q_ARG(QVariantMap, op.arguments)); - } - } else { - qCDebug(lcMemoryCardLog) << "Formatting cannot be executed. Is block mounted:" << !completedBlock->mountPath().isEmpty(); - } - } - } - } else { - // This is garbage block device that should not be exposed - // from the partition model. - completedBlock->removeInterface(UDISKS2_BLOCK_INTERFACE); - completedBlock->deleteLater(); - } - }); - - connect(block, &UDisks2::Block::formatted, this, [this]() { - UDisks2::Block *block = qobject_cast(sender()); - if (m_blockDevices.contains(block->path())) { - for (auto partition : m_manager->m_partitions) { - if (partition->devicePath == block->device()) { - partition->status = Partition::Formatted; - partition->activeState = QStringLiteral("inactive"); - partition->valid = true; - m_manager->refresh(partition.data()); - } - } - } - }); - - // When block info updated - connect(block, &UDisks2::Block::updated, this, [this]() { - UDisks2::Block *block = qobject_cast(sender()); - if (m_blockDevices.contains(block->path())) { - updatePartitionProperties(block); - } - }); - - connect(block, &UDisks2::Block::mountPathChanged, this, [this]() { - UDisks2::Block *block = qobject_cast(sender()); - // Both updatePartitionStatus and updatePartitionProperties - // emits partition refresh => latter one is enough. - - m_manager->blockSignals(true); - QVariantMap data; - data.insert(UDISKS2_JOB_KEY_OPERATION, block->mountPath().isEmpty() ? UDISKS2_JOB_OP_FS_UNMOUNT : UDISKS2_JOB_OP_FS_MOUNT); - data.insert(UDISKS2_JOB_KEY_OBJECTS, QStringList() << block->path()); - qCDebug(lcMemoryCardLog) << "New partition status:" << data; - UDisks2::Job tmpJob(QString(), data); - tmpJob.complete(true); - updatePartitionStatus(&tmpJob, true); - m_manager->blockSignals(false); - - updatePartitionProperties(block); - - if (!m_operationQueue.isEmpty()) { - Operation op = m_operationQueue.head(); - if (op.command == UDISKS2_BLOCK_FORMAT && block->mountPath().isEmpty()) { - m_operationQueue.dequeue(); - doFormat(op.devicePath, op.dbusObjectPath, op.filesystemType, op.arguments); - } else if (op.command == UDISKS2_ENCRYPTED_LOCK && block->mountPath().isEmpty()) { - m_operationQueue.dequeue(); - lock(op.devicePath); - } - } - }); - return block; - } - return nullptr; -} - void UDisks2::Monitor::doFormat(const QString &devicePath, const QString &dbusObjectPath, const QString &filesystemType, const QVariantMap &arguments) { QDBusInterface blockDeviceInterface(UDISKS2_SERVICE, @@ -757,7 +607,7 @@ void UDisks2::Monitor::doFormat(const QString &devicePath, const QString &dbusOb if (watcher->isValid() && watcher->isFinished()) { emit status(devicePath, Partition::Formatted); } else if (watcher->isError()) { - Block *block = findBlock(devicePath); + Block *block = m_blockDevices->find(devicePath); if (block) { block->setFormatting(false); } @@ -792,10 +642,7 @@ void UDisks2::Monitor::getBlockDevices() QDBusPendingReply > reply = *watcher; const QList blockDevicePaths = reply.argumentAt<0>(); for (const QDBusObjectPath &dbusObjectPath : blockDevicePaths) { - QString path = dbusObjectPath.path(); - if (externalBlockDevice(path)) { - createBlockDevice(path, UDisks2::InterfacePropertyMap()); - } + m_blockDevices->createBlockDevice(dbusObjectPath.path(), UDisks2::InterfacePropertyMap()); } } else if (watcher->isError()) { QDBusError error = watcher->error(); @@ -804,41 +651,86 @@ void UDisks2::Monitor::getBlockDevices() }); } -UDisks2::Block *UDisks2::Monitor::findBlock(const QString &devicePath) const +void UDisks2::Monitor::connectSignals(UDisks2::Block *block) { - for (QMap::const_iterator i = m_blockDevices.constBegin(); i != m_blockDevices.constEnd(); ++i) { - Block *block = i.value(); - if (block->device() == devicePath || block->cryptoBackingDevicePath() == devicePath) { - return block; + connect(block, &UDisks2::Block::formatted, this, [this]() { + UDisks2::Block *block = qobject_cast(sender()); + if (m_blockDevices->contains(block->path())) { + for (auto partition : m_manager->m_partitions) { + if (partition->devicePath == block->device()) { + partition->status = Partition::Formatted; + partition->activeState = QStringLiteral("inactive"); + partition->valid = true; + m_manager->refresh(partition.data()); + } + } } - } - - return nullptr; -} + }, Qt::UniqueConnection); -void UDisks2::Monitor::updateFormattingState(UDisks2::Block *block) -{ - UDisks2::Block *cryptoBackingDevice = nullptr; - QString cryptoBackingDevicePath = block->cryptoBackingDeviceObjectPath(); - - // If we have crypto backing device, copy over formatting state. - if (cryptoBackingDevicePath != QLatin1String("/") && (cryptoBackingDevice = m_blockDevices.value(cryptoBackingDevicePath, nullptr))) { - block->blockSignals(true); - block->setFormatting(cryptoBackingDevice->isFormatting()); - block->blockSignals(false); - } + // When block info updated + connect(block, &UDisks2::Block::updated, this, [this]() { + UDisks2::Block *block = qobject_cast(sender()); + if (m_blockDevices->contains(block->path())) { + updatePartitionProperties(block); + } + }, Qt::UniqueConnection); + + connect(block, &UDisks2::Block::mountPathChanged, this, [this]() { + UDisks2::Block *block = qobject_cast(sender()); + // Both updatePartitionStatus and updatePartitionProperties + // emits partition refresh => latter one is enough. + + m_manager->blockSignals(true); + QVariantMap data; + data.insert(UDISKS2_JOB_KEY_OPERATION, block->mountPath().isEmpty() ? UDISKS2_JOB_OP_FS_UNMOUNT : UDISKS2_JOB_OP_FS_MOUNT); + data.insert(UDISKS2_JOB_KEY_OBJECTS, QStringList() << block->path()); + qCDebug(lcMemoryCardLog) << "New partition status:" << data; + UDisks2::Job tmpJob(QString(), data); + tmpJob.complete(true); + updatePartitionStatus(&tmpJob, true); + m_manager->blockSignals(false); + + updatePartitionProperties(block); + + if (!m_operationQueue.isEmpty()) { + Operation op = m_operationQueue.head(); + if (op.command == UDISKS2_BLOCK_FORMAT && block->mountPath().isEmpty()) { + m_operationQueue.dequeue(); + doFormat(op.devicePath, op.dbusObjectPath, op.filesystemType, op.arguments); + } else if (op.command == UDISKS2_ENCRYPTED_LOCK && block->mountPath().isEmpty()) { + m_operationQueue.dequeue(); + lock(op.devicePath); + } + } + }, Qt::UniqueConnection); } -QString UDisks2::Monitor::objectPath(const QString &devicePath) const +void UDisks2::Monitor::handleNewBlock(UDisks2::Block *block) { - for (QMap::const_iterator i = m_blockDevices.begin(); i != m_blockDevices.end(); ++i) { - Block *block = i.value(); - if (block->device() == devicePath) { - return block->path(); - } else if (block->cryptoBackingDevicePath() == devicePath) { - return block->cryptoBackingDeviceObjectPath(); + const QString cryptoBackingDeviceObjectPath = block->cryptoBackingDeviceObjectPath(); + if (block->hasCryptoBackingDevice() && m_blockDevices->contains(cryptoBackingDeviceObjectPath)) { + // Update crypto backing device to file system device. + UDisks2::Block *fsBlock = m_blockDevices->replace(cryptoBackingDeviceObjectPath, block); + updatePartitionProperties(fsBlock); + } else if (!m_blockDevices->contains(block->path())) { + m_blockDevices->insert(block->path(), block); + createPartition(block); + + if (block->isFormatting()) { + if (!m_operationQueue.isEmpty()) { + Operation op = m_operationQueue.head(); + if (op.command == UDISKS2_BLOCK_FORMAT) { + m_operationQueue.dequeue(); + QMetaObject::invokeMethod(this, "doFormat", Qt::QueuedConnection, + Q_ARG(QString, op.devicePath), Q_ARG(QString, op.dbusObjectPath), + Q_ARG(QString, op.filesystemType), Q_ARG(QVariantMap, op.arguments)); + } + } else { + qCDebug(lcMemoryCardLog) << "Formatting cannot be executed. Is block mounted:" << !block->mountPath().isEmpty(); + } } } - return QString(); + connectSignals(block); + } diff --git a/src/udisks2monitor_p.h b/src/udisks2monitor_p.h index 0174feb..c2af9cd 100644 --- a/src/udisks2monitor_p.h +++ b/src/udisks2monitor_p.h @@ -50,6 +50,7 @@ static const QRegularExpression deviceRoot(QStringLiteral("^mmcblk\\d+$")); namespace UDisks2 { class Block; +class BlockDevices; class Job; struct Operation @@ -86,8 +87,6 @@ class Monitor : public QObject void format(const QString &devicePath, const QString &filesystemType, const QVariantMap &arguments); - QString objectPath(const QString &devicePath) const; - signals: void status(const QString &devicePath, Partition::Status); void errorMessage(const QString &objectPath, const QString &errorName); @@ -101,33 +100,30 @@ private slots: void interfacesAdded(const QDBusObjectPath &objectPath, const UDisks2::InterfacePropertyMap &interfaces); void interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces); void doFormat(const QString &devicePath, const QString &dbusObjectPath, const QString &filesystemType, const QVariantMap &arguments); + void handleNewBlock(UDisks2::Block *block); private: void setPartitionProperties(QExplicitlySharedDataPointer &partition, const Block *blockDevice); void updatePartitionProperties(const Block *blockDevice); void updatePartitionStatus(const Job *job, bool success); - bool externalBlockDevice(const QString &objectPathStr) const; void startLuksOperation(const QString &devicePath, const QString &dbusMethod, const QString &dbusObjectPath, const QVariantList &arguments); void startMountOperation(const QString &devicePath, const QString &dbusMethod, const QString &dbusObjectPath, const QVariantList &arguments); void lookupPartitions(PartitionManagerPrivate::Partitions &affectedPartitions, const QStringList &objects); void createPartition(const Block *block); - Block *createBlockDevice(const QString &dbusObjectPath, const UDisks2::InterfacePropertyMap &interfacePropertyMap); - void getBlockDevices(); - - Block *findBlock(const QString &devicePath) const; - void updateFormattingState(UDisks2::Block *block); + void connectSignals(UDisks2::Block *block); private: static Monitor *sharedInstance; QExplicitlySharedDataPointer m_manager; QMap m_jobsToWait; - QMap m_blockDevices; QQueue m_operationQueue; + + BlockDevices *m_blockDevices; }; }