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;