From 5772467e04150535b97f6a5796bce9d204a46f3b Mon Sep 17 00:00:00 2001 From: Raine Makelainen Date: Wed, 22 Aug 2018 12:53:21 +0300 Subject: [PATCH] [systemsettings] Cleanup unwanted partitions. Contributes to JB#40936 External block device are now read through udisks2 upon PartitionModel creation. This guarantees that all block devices will have also file system interface. For instance now memory sticks that do not have partition table but formatted directly will be shown as well. In future, similar approach should be take for encrypted devices. --- src/partitionmanager.cpp | 115 +++++++-------------- src/partitionmanager_p.h | 5 +- src/udisks2block.cpp | 36 +++++-- src/udisks2block_p.h | 12 ++- src/udisks2defines.h | 8 +- src/udisks2job.cpp | 4 +- src/udisks2monitor.cpp | 213 ++++++++++++++++++++++++++------------- src/udisks2monitor_p.h | 10 +- 8 files changed, 237 insertions(+), 166 deletions(-) diff --git a/src/partitionmanager.cpp b/src/partitionmanager.cpp index 1b309d7..b39bbaf 100644 --- a/src/partitionmanager.cpp +++ b/src/partitionmanager.cpp @@ -71,8 +71,7 @@ PartitionManagerPrivate::PartitionManagerPrivate() home->mountPath = QStringLiteral("/home"); m_partitions.append(home); - - refresh(); + refresh(m_partitions, m_partitions); // Remove any prospective internal partitions that aren't mounted. int internalPartitionCount = 0; @@ -105,8 +104,6 @@ PartitionManagerPrivate::PartitionManagerPrivate() if (root->status == Partition::Mounted) { m_root = Partition(QExplicitlySharedDataPointer(root)); } - - m_udisksMonitor->getBlockDevices(); } PartitionManagerPrivate::~PartitionManagerPrivate() @@ -147,85 +144,41 @@ QVector PartitionManagerPrivate::partitions(const Partition::StorageT return partitions; } -void PartitionManagerPrivate::refresh() +void PartitionManagerPrivate::add(Partitions &partitions) { - int index; - for (index = 0; index < m_partitions.count(); ++index) { - if (m_partitions.at(index)->storageType == Partition::External) { - break; - } - } - - Partitions addedPartitions; - Partitions changedPartitions; - - QFile partitionFile(QStringLiteral("/proc/partitions")); - if (partitionFile.open(QIODevice::ReadOnly)) { - // Read headers. - partitionFile.readLine(); - partitionFile.readLine(); - - static const QRegularExpression whitespace(QStringLiteral("\\s+")); - static const QRegularExpression deviceRoot(QStringLiteral("^mmcblk\\d+$")); + m_partitions.append(partitions); + refresh(partitions, partitions); - while (!partitionFile.atEnd()) { - QStringList line = QString::fromUtf8(partitionFile.readLine()).split(whitespace, QString::SkipEmptyParts); - - if (line.count() != 4) { - continue; - } - - const QString deviceName = line.at(3); + for (const auto partition : partitions) { + emit partitionAdded(Partition(partition)); + } +} - if (!externalMedia.match(deviceName).hasMatch()) { - continue; +void PartitionManagerPrivate::remove(const Partitions &partitions) +{ + for (const auto removedPartition : partitions) { + for (int i = m_partitions.count() - 1; i >= 0 && m_partitions.at(i)->storageType == Partition::External; --i) { + const auto partition = m_partitions.at(i); + if (removedPartition->devicePath == partition->devicePath) { + m_partitions.removeAt(i); } - - const auto partition = [&]() { - for (int i = index; i < m_partitions.count(); ++i) { - const auto partition = m_partitions.at(i); - if (partition->deviceName == deviceName) { - if (index != i) { - m_partitions.removeAt(i); - m_partitions.insert(index, partition); - } - - changedPartitions.append(partition); - - return partition; - } - } - QExplicitlySharedDataPointer partition(new PartitionPrivate(this)); - partition->storageType = Partition::External; - partition->deviceName = deviceName; - partition->devicePath = QStringLiteral("/dev/") + deviceName; - partition->deviceRoot = deviceRoot.match(deviceName).hasMatch(); - - m_partitions.insert(index, partition); - addedPartitions.append(partition); - - return partition; - }(); - - partition->bytesTotal = line.at(2).toInt() * 1024; - - ++index; } - } - - const auto removedPartitions = m_partitions.mid(index); - m_partitions.resize(index); - refresh(m_partitions, changedPartitions); - - for (const auto partition : removedPartitions) { - emit partitionRemoved(Partition(partition)); + emit partitionRemoved(Partition(removedPartition)); } +} - for (const auto partition : addedPartitions) { - emit partitionAdded(Partition(partition)); +void PartitionManagerPrivate::refresh() +{ + Partitions changedPartitions; + for (int index = 0; index < m_partitions.count(); ++index) { + const auto partition = m_partitions.at(index); + if (partition->storageType == Partition::External) { + changedPartitions.append(partition); + } } + refresh(m_partitions, changedPartitions); for (const auto partition : changedPartitions) { emit partitionChanged(Partition(partition)); } @@ -246,9 +199,11 @@ void PartitionManagerPrivate::refresh(const Partitions &partitions, Partitions & partition->bytesFree = 0; partition->bytesAvailable = 0; if (!partition->valid) { - partition->status = partition->activeState == QStringLiteral("activating") - ? Partition::Mounting - : Partition::Unmounted; + if (partition->status != Partition::Formatting) { + partition->status = partition->activeState == QStringLiteral("activating") + ? Partition::Mounting + : Partition::Unmounted; + } partition->canMount = false; partition->readOnly = true; partition->filesystemType.clear(); @@ -266,7 +221,7 @@ void PartitionManagerPrivate::refresh(const Partitions &partitions, Partitions & const QString mountPath = QString::fromUtf8(mountEntry.mnt_dir); const QString devicePath = QString::fromUtf8(mountEntry.mnt_fsname); - + const QString deviceName = devicePath.section(QChar('/'), 2); for (auto partition : partitions) { if (partition->valid || ((partition->status == Partition::Mounted || partition->status == Partition::Mounting) && @@ -282,6 +237,10 @@ void PartitionManagerPrivate::refresh(const Partitions &partitions, Partitions & && partition->devicePath == devicePath)) { partition->mountPath = mountPath; partition->devicePath = devicePath; + // There two values wrong for system partitions as devicePath will not start with mmcblk. + // Currently deviceName and deviceRoot are merely informative data fields. + partition->deviceName = deviceName; + partition->deviceRoot = deviceRoot.match(deviceName).hasMatch(); partition->filesystemType = QString::fromUtf8(mountEntry.mnt_type); partition->status = partition->activeState == QStringLiteral("deactivating") ? Partition::Unmounting @@ -292,7 +251,7 @@ void PartitionManagerPrivate::refresh(const Partitions &partitions, Partitions & } endmntent(mtab); - + for (auto partition : partitions) { if (partition->status == Partition::Mounted) { struct statvfs64 stat; diff --git a/src/partitionmanager_p.h b/src/partitionmanager_p.h index 2b6d2fa..80c7e0d 100644 --- a/src/partitionmanager_p.h +++ b/src/partitionmanager_p.h @@ -43,7 +43,7 @@ namespace UDisks2 { class Monitor; } -static const auto externalDevice = QStringLiteral("mmcblk(?!0)\\d+(?:p\\d+$)?|(sd[a-z]\\d+)"); +static const auto externalDevice = QStringLiteral("mmcblk(?!0)\\d+(?:p\\d+$)?|(sd[a-z]\\d*)"); class PartitionManagerPrivate : public QObject, public QSharedData { @@ -59,6 +59,9 @@ class PartitionManagerPrivate : public QObject, public QSharedData Partition root() const; QVector partitions(Partition::StorageTypes types) const; + void add(Partitions &partitions); + void remove(const Partitions &partitions); + void refresh(); void refresh(PartitionPrivate *partition); void refresh(const Partitions &partitions, Partitions &changedPartitions); diff --git a/src/udisks2block.cpp b/src/udisks2block.cpp index a9fe6ba..27a837c 100644 --- a/src/udisks2block.cpp +++ b/src/udisks2block.cpp @@ -12,6 +12,9 @@ UDisks2::Block::Block(const QString &path, const QVariantMap &data, QObject *par , m_path(path) , m_data(data) , m_connection(QDBusConnection::systemBus()) + , m_mountable(false) + , m_pendingFileSystem(nullptr) + , m_pendingBlock(nullptr) { if (!m_connection.connect( UDISKS2_SERVICE, @@ -28,36 +31,42 @@ UDisks2::Block::Block(const QString &path, const QVariantMap &data, QObject *par DBUS_OBJECT_PROPERTIES_INTERFACE, m_connection); QDBusPendingCall pendingCall = dbusPropertyInterface.asyncCall(DBUS_GET_ALL, UDISKS2_FILESYSTEM_INTERFACE); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); - connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, path](QDBusPendingCallWatcher *watcher) { + m_pendingFileSystem = new QDBusPendingCallWatcher(pendingCall, this); + connect(m_pendingFileSystem, &QDBusPendingCallWatcher::finished, this, [this, path](QDBusPendingCallWatcher *watcher) { if (watcher->isValid() && watcher->isFinished()) { QDBusPendingReply<> reply = *watcher; QDBusMessage message = reply.reply(); + m_mountable = true; updateMountPoint(message.arguments().at(0)); } else { QDBusError error = watcher->error(); - qCWarning(lcMemoryCardLog) << "Error reading filesystem properties:" << error.name() << error.message(); + qCWarning(lcMemoryCardLog) << "Error reading filesystem properties:" << error.name() << error.message() << path; } watcher->deleteLater(); + m_pendingFileSystem = nullptr; + complete(); }); if (data.isEmpty()) { pendingCall = dbusPropertyInterface.asyncCall(DBUS_GET_ALL, UDISKS2_BLOCK_INTERFACE); - watcher = new QDBusPendingCallWatcher(pendingCall, this); - connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, path](QDBusPendingCallWatcher *watcher) { + 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; - emit blockUpdated(); } else { QDBusError error = watcher->error(); qCWarning(lcMemoryCardLog) << "Error reading block properties:" << error.name() << error.message(); } watcher->deleteLater(); + m_pendingBlock = nullptr; + complete(); }); + } else { + complete(); } } @@ -102,6 +111,11 @@ qint64 UDisks2::Block::size() const return value(QStringLiteral("Size")).toLongLong(); } +bool UDisks2::Block::isMountable() const +{ + return m_mountable; +} + bool UDisks2::Block::isReadOnly() const { return value(QStringLiteral("ReadOnly")).toBool(); @@ -152,8 +166,9 @@ void UDisks2::Block::updateProperties(const QDBusMessage &message) for (QMap::const_iterator i = changedProperties.begin(); i != changedProperties.end(); ++i) { m_data.insert(i.key(), i.value()); } - emit blockUpdated(); + emit updated(); } else if (interface == UDISKS2_FILESYSTEM_INTERFACE) { + m_mountable = true; updateMountPoint(arguments.value(1)); } } @@ -174,3 +189,10 @@ void UDisks2::Block::updateMountPoint(const QVariant &mountPoints) qCInfo(lcMemoryCardLog) << "New file system mount points:" << mountPoints << "resolved mount path: " << m_mountPath; emit mountPathChanged(); } + +void UDisks2::Block::complete() +{ + if (!m_pendingFileSystem && !m_pendingBlock) { + QMetaObject::invokeMethod(this, "completed", Qt::QueuedConnection); + } +} diff --git a/src/udisks2block_p.h b/src/udisks2block_p.h index 3899eec..f1cf777 100644 --- a/src/udisks2block_p.h +++ b/src/udisks2block_p.h @@ -36,6 +36,8 @@ #include #include +class QDBusPendingCallWatcher; + namespace UDisks2 { class Block : public QObject @@ -57,6 +59,8 @@ class Block : public QObject qint64 size() const; + bool isMountable() const; + bool isReadOnly() const; QString idType() const; @@ -71,7 +75,8 @@ class Block : public QObject bool hasData() const; signals: - void blockUpdated(); + void completed(); + void updated(); void mountPathChanged(); private slots: @@ -79,11 +84,16 @@ private slots: private: void updateMountPoint(const QVariant &mountPoints); + void complete(); QString m_path; QVariantMap m_data; QDBusConnection m_connection; QString m_mountPath; + bool m_mountable; + + QDBusPendingCallWatcher *m_pendingFileSystem; + QDBusPendingCallWatcher *m_pendingBlock; }; } diff --git a/src/udisks2defines.h b/src/udisks2defines.h index 3e4c524..7a8b348 100644 --- a/src/udisks2defines.h +++ b/src/udisks2defines.h @@ -36,10 +36,12 @@ #define DBUS_OBJECT_PROPERTIES_INTERFACE QLatin1String("org.freedesktop.DBus.Properties") #define DBUS_GET_ALL QLatin1String("GetAll") -#define UDISKS2_SERVICE QLatin1String("org.freedesktop.UDisks2") -#define UDISKS2_PATH QLatin1String("/org/freedesktop/UDisks2") +#define UDISKS2_SERVICE QLatin1String("org.freedesktop.UDisks2") +#define UDISKS2_PATH QLatin1String("/org/freedesktop/UDisks2") +#define UDISKS2_MANAGER_PATH QLatin1String("/org/freedesktop/UDisks2/Manager") // Interfaces +#define UDISKS2_MANAGER_INTERFACE QLatin1String("org.freedesktop.UDisks2.Manager") #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") @@ -56,7 +58,7 @@ #define UDISKS2_JOB_KEY_OBJECTS QLatin1String("Objects") // Mount, Unmount, Format -#define UDISKS2_BLOCK_DEVICE_PATH QString("/org/freedesktop/UDisks2/block_devices/%1") +#define UDISKS2_BLOCK_DEVICE_PATH QString(QLatin1String("/org/freedesktop/UDisks2/block_devices/%1")) #define UDISKS2_BLOCK_FORMAT QLatin1String("Format") #define UDISKS2_FILESYSTEM_MOUNT QLatin1String("Mount") #define UDISKS2_FILESYSTEM_UNMOUNT QLatin1String("Unmount") diff --git a/src/udisks2job.cpp b/src/udisks2job.cpp index 9c7a280..e673e83 100644 --- a/src/udisks2job.cpp +++ b/src/udisks2job.cpp @@ -32,9 +32,9 @@ #include "udisks2job_p.h" #include "udisks2monitor_p.h" #include "udisks2defines.h" +#include "logging_p.h" #include -#include #include @@ -54,7 +54,7 @@ UDisks2::Job::Job(const QString &path, const QVariantMap &data, QObject *parent) QStringLiteral("Completed"), this, SLOT(updateCompleted(bool, QString)))) { - qWarning("Failed to connect to Job's at path %p completed signal: %s: ", qPrintable(m_path), qPrintable(m_connection.lastError().message())); + qCWarning(lcMemoryCardLog) << "Failed to connect to Job's at path" << qPrintable(m_path) << "completed signal" << qPrintable(m_connection.lastError().message()); } connect(Monitor::instance(), &Monitor::errorMessage, this, [this](const QString &objectPath, const QString &errorName) { diff --git a/src/udisks2monitor.cpp b/src/udisks2monitor.cpp index a7cd9fd..716f8de 100644 --- a/src/udisks2monitor.cpp +++ b/src/udisks2monitor.cpp @@ -43,7 +43,6 @@ #include #include #include -#include struct ErrorEntry { Partition::Error errorCode; @@ -120,6 +119,8 @@ UDisks2::Monitor::Monitor(PartitionManagerPrivate *manager, QObject *parent) SLOT(interfacesRemoved(QDBusObjectPath, QStringList)))) { qCWarning(lcMemoryCardLog) << "Failed to connect to interfaces added signal:" << qPrintable(systemBus.lastError().message()); } + + getBlockDevices(); } UDisks2::Monitor::~Monitor() @@ -179,10 +180,11 @@ void UDisks2::Monitor::interfacesAdded(const QDBusObjectPath &objectPath, const { qCInfo(lcMemoryCardLog) << "Interface added:" << objectPath.path() << interfaces; QString path = objectPath.path(); + // External device must have file system or partition so that it can added to the model. + // Devices without partition table have filesystem interface. if ((interfaces.contains(UDISKS2_PARTITION_INTERFACE) || interfaces.contains(UDISKS2_FILESYSTEM_INTERFACE)) && externalBlockDevice(path)) { - m_manager->refresh(); QVariantMap dict = interfaces.value(UDISKS2_BLOCK_INTERFACE); - addBlockDevice(path, dict); + createBlockDevice(path, dict); } 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(); @@ -190,7 +192,7 @@ void UDisks2::Monitor::interfacesAdded(const QDBusObjectPath &objectPath, const operation == UDISKS2_JOB_OP_FS_UNMOUNT || operation == UDISKS2_JOB_OP_CLEANUP || operation == UDISKS2_JOB_OF_FS_FORMAT) { - UDisks2::Job *job = new UDisks2::Job(objectPath.path(), dict); + UDisks2::Job *job = new UDisks2::Job(path, dict); updatePartitionStatus(job, true); connect(job, &UDisks2::Job::completed, this, [this](bool success) { @@ -221,41 +223,48 @@ 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)) { + } else if (m_blockDevices.contains(path) && interfaces.contains(UDISKS2_BLOCK_INTERFACE)) { UDisks2::Block *block = m_blockDevices.take(path); block->deleteLater(); if (externalBlockDevice(path)) { - m_manager->refresh(); + PartitionManagerPrivate::Partitions removedPartitions; + QStringList blockDevPaths = { path }; + lookupPartitions(removedPartitions, blockDevPaths); + m_manager->remove(removedPartitions); } } } +void UDisks2::Monitor::setPartitionProperties(QExplicitlySharedDataPointer &partition, const UDisks2::Block *blockDevice) +{ + QString label = blockDevice->idLabel(); + if (label.isEmpty()) { + label = blockDevice->idUUID(); + } + + qCInfo(lcMemoryCardLog) << "Set block:" << blockDevice->device() << "pref:" << blockDevice->preferredDevice(); + qCInfo(lcMemoryCardLog) << "- drive:" << blockDevice->drive() << "dNumber:" << blockDevice->deviceNumber(); + qCInfo(lcMemoryCardLog) << "- id:" << blockDevice->id() << "size:" << blockDevice->size(); + qCInfo(lcMemoryCardLog) << "- isreadonly:" << blockDevice->isReadOnly() << "idtype:" << blockDevice->idType(); + qCInfo(lcMemoryCardLog) << "- idversion" << blockDevice->idVersion() << "idlabel" << blockDevice->idLabel(); + qCInfo(lcMemoryCardLog) << "- iduuid" << blockDevice->idUUID(); + + partition->devicePath = blockDevice->device(); + partition->mountPath = blockDevice->mountPath(); + partition->deviceLabel = label; + partition->filesystemType = blockDevice->idType(); + partition->readOnly = blockDevice->isReadOnly(); + partition->canMount = blockDevice->value(QStringLiteral("HintAuto")).toBool() + && !partition->filesystemType.isEmpty() + && m_manager->supportedFileSystems().contains(partition->filesystemType); +} + void UDisks2::Monitor::updatePartitionProperties(const UDisks2::Block *blockDevice) { for (auto partition : m_manager->m_partitions) { if (partition->devicePath == blockDevice->device()) { - QString label = blockDevice->idLabel(); - if (label.isEmpty()) { - label = blockDevice->idUUID(); - } - - qCInfo(lcMemoryCardLog) << "Update block:" << blockDevice->device() << "pref:" << blockDevice->preferredDevice(); - qCInfo(lcMemoryCardLog) << "- drive:" << blockDevice->drive() << "dNumber:" << blockDevice->deviceNumber(); - qCInfo(lcMemoryCardLog) << "- id:" << blockDevice->id() << "size:" << blockDevice->size(); - qCInfo(lcMemoryCardLog) << "- isreadonly:" << blockDevice->isReadOnly() << "idtype:" << blockDevice->idType(); - qCInfo(lcMemoryCardLog) << "- idversion" << blockDevice->idVersion() << "idlabel" << blockDevice->idLabel(); - qCInfo(lcMemoryCardLog) << "- iduuid" << blockDevice->idUUID(); - - partition->devicePath = blockDevice->device(); - partition->mountPath = blockDevice->mountPath(); - partition->deviceLabel = label; - partition->filesystemType = blockDevice->idType(); - partition->readOnly = blockDevice->isReadOnly(); - partition->canMount = blockDevice->value(QStringLiteral("HintAuto")).toBool() - && !partition->filesystemType.isEmpty() - && m_manager->supportedFileSystems().contains(partition->filesystemType); + setPartitionProperties(partition, blockDevice); partition->valid = true; - m_manager->refresh(partition.data()); } } @@ -264,7 +273,6 @@ void UDisks2::Monitor::updatePartitionProperties(const UDisks2::Block *blockDevi void UDisks2::Monitor::updatePartitionStatus(const UDisks2::Job *job, bool success) { UDisks2::Job::Operation operation = job->operation(); - PartitionManagerPrivate::Partitions affectedPartions; lookupPartitions(affectedPartions, job->value(UDISKS2_JOB_KEY_OBJECTS).toStringList()); @@ -302,15 +310,23 @@ void UDisks2::Monitor::updatePartitionStatus(const UDisks2::Job *job, bool succe if (job->status() == UDisks2::Job::Added) { partition->activeState = QStringLiteral("inactive"); partition->status = Partition::Formatting; + partition->bytesAvailable = 0; + partition->bytesTotal = 0; + partition->bytesFree = 0; + partition->filesystemType.clear(); + partition->canMount = false; + partition->valid = false; } else { partition->activeState = QStringLiteral("inactive"); partition->status = Partition::Formatted; + partition->valid = true; } } else { partition->activeState = QStringLiteral("failed"); partition->status = Partition::Unmounted; + partition->valid = false; } - partition->valid = true; + if (oldStatus != partition->status) { m_manager->refresh(partition.data()); } @@ -404,49 +420,90 @@ void UDisks2::Monitor::lookupPartitions(PartitionManagerPrivate::Partitions &aff } } -void UDisks2::Monitor::addBlockDevice(const QString &path, const QVariantMap &dict) +void UDisks2::Monitor::createPartition(const UDisks2::Block *block) +{ + QExplicitlySharedDataPointer partition(new PartitionPrivate(m_manager.data())); + partition->storageType = Partition::External; + partition->devicePath = block->device(); + + QString deviceName = partition->devicePath.section(QChar('/'), 2); + partition->deviceName = deviceName; + partition->deviceRoot = deviceRoot.match(deviceName).hasMatch(); + partition->bytesTotal = block->size(); + partition->status = block->mountPath().isEmpty() ? Partition::Unmounted : Partition::Mounted; + + setPartitionProperties(partition, block); + partition->valid = true; + PartitionManagerPrivate::Partitions addedPartitions = { partition }; + m_manager->add(addedPartitions); +} + +void UDisks2::Monitor::createBlockDevice(const QString &path, const QVariantMap &dict) { if (m_blockDevices.contains(path)) { return; } - UDisks2::Block *block = new UDisks2::Block(path, dict); - m_blockDevices.insert(path, block); - if (block->hasData()) { - updatePartitionProperties(block); - } - // When e.g. partition formatted, update partition info. - connect(block, &UDisks2::Block::blockUpdated, this, [this]() { - UDisks2::Block *block = qobject_cast(sender()); - updatePartitionProperties(block); - }); + QString deviceName = path.section(QChar('/'), 5); - 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()); - qCInfo(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 == QStringLiteral("format") && block->mountPath().isEmpty()) { - m_operationQueue.dequeue(); - doFormat(op.deviceName, op.type, op.arguments); - } + if (externalBlockDevice(deviceName)) { + UDisks2::Block *block = new UDisks2::Block(path, dict); + if (block->hasData()) { + m_blockDevices.insert(path, block); + createPartition(block); } - }); + + // Upon creation. + connect(block, &UDisks2::Block::completed, this, [this]() { + UDisks2::Block *block = qobject_cast(sender()); + // TODO: The block could implement also isEncrypted(). + if (block->isMountable()) { + if (!m_blockDevices.contains(block->path())) { + m_blockDevices.insert(block->path(), block); + createPartition(block); + } + } else { + // This is garbage block device that should not be exposed + // from the partition model. + block->deleteLater(); + } + }); + + // When e.g. partition formatted, partition info updated + connect(block, &UDisks2::Block::updated, this, [this]() { + UDisks2::Block *block = qobject_cast(sender()); + QString blockPath = block->path(); + if (m_blockDevices.contains(blockPath)) { + 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()); + qCInfo(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 == QStringLiteral("format") && block->mountPath().isEmpty()) { + m_operationQueue.dequeue(); + doFormat(op.deviceName, op.type, op.arguments); + } + } + }); + } } void UDisks2::Monitor::doFormat(const QString &deviceName, const QString &type, const QVariantHash &arguments) @@ -481,10 +538,24 @@ void UDisks2::Monitor::doFormat(const QString &deviceName, const QString &type, void UDisks2::Monitor::getBlockDevices() { - QVector partitions = m_manager->partitions(Partition::External | Partition::ExcludeParents); - for (const Partition &partition : partitions) { - QString path = UDISKS2_BLOCK_DEVICE_PATH.arg(partition.deviceName()); - QVariantMap data; - addBlockDevice(path, data); - } + QDBusInterface managerInterface(UDISKS2_SERVICE, + UDISKS2_MANAGER_PATH, + UDISKS2_MANAGER_INTERFACE, + QDBusConnection::systemBus()); + QDBusPendingCall pendingCall = managerInterface.asyncCallWithArgumentList( + QStringLiteral("GetBlockDevices"), + QVariantList() << QVariantMap()); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { + if (watcher->isValid() && watcher->isFinished()) { + QDBusPendingReply > reply = *watcher; + const QList blockDevicePaths = reply.argumentAt<0>(); + for (const QDBusObjectPath &dbusObjectPath : blockDevicePaths) { + createBlockDevice(dbusObjectPath.path(), QVariantMap()); + } + } else if (watcher->isError()) { + QDBusError error = watcher->error(); + qCWarning(lcMemoryCardLog) << "Unable to enumerate block devices:" << error.name() << error.message(); + } + }); } diff --git a/src/udisks2monitor_p.h b/src/udisks2monitor_p.h index 135631c..f8e2123 100644 --- a/src/udisks2monitor_p.h +++ b/src/udisks2monitor_p.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "partitionmodel.h" @@ -44,6 +45,8 @@ class PartitionManagerPrivate; typedef QMap InterfaceAndPropertyMap; +static const QRegularExpression deviceRoot(QStringLiteral("^mmcblk\\d+$")); + Q_DECLARE_METATYPE(InterfaceAndPropertyMap) namespace UDisks2 { @@ -80,8 +83,6 @@ class Monitor : public QObject void format(const QString &deviceName, const QString &type, const QString &label); - void getBlockDevices(); - signals: void status(const QString &deviceName, Partition::Status); void errorMessage(const QString &objectPath, const QString &errorName); @@ -94,6 +95,7 @@ private slots: void interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces); private: + void setPartitionProperties(QExplicitlySharedDataPointer &partition, const UDisks2::Block *blockDevice); void updatePartitionProperties(const UDisks2::Block *blockDevice); void updatePartitionStatus(const UDisks2::Job *job, bool success); bool externalBlockDevice(const QString &objectPathStr) const; @@ -101,9 +103,11 @@ private slots: void startMountOperation(const QString &dbusMethod, const QString &deviceName, QVariantHash arguments); void lookupPartitions(PartitionManagerPrivate::Partitions &affectedPartions, const QStringList &objects); - void addBlockDevice(const QString &path, const QVariantMap &dict); + void createPartition(const Block *block); + void createBlockDevice(const QString &path, const QVariantMap &dict); void doFormat(const QString &deviceName, const QString &type, const QVariantHash &arguments); + void getBlockDevices(); private: static Monitor *sharedInstance;