diff --git a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp index dd8f76f..acdd6e6 100644 --- a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp +++ b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp @@ -1,394 +1,383 @@ /* Copyright 2009 Pino Toscano Copyright 2009-2012 Lukáš Tinkl This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "udisksstorageaccess.h" #include "udisks2.h" #include #include #include #include #include using namespace Solid::Backends::UDisks2; StorageAccess::StorageAccess(Device *device) : DeviceInterface(device), m_setupInProgress(false), m_teardownInProgress(false), m_passphraseRequested(false) { connect(device, SIGNAL(changed()), this, SLOT(checkAccessibility())); updateCache(); // Delay connecting to DBus signals to avoid the related time penalty // in hot paths such as predicate matching QTimer::singleShot(0, this, SLOT(connectDBusSignals())); } StorageAccess::~StorageAccess() { } void StorageAccess::connectDBusSignals() { m_device->registerAction("setup", this, SLOT(slotSetupRequested()), SLOT(slotSetupDone(int,QString))); m_device->registerAction("teardown", this, SLOT(slotTeardownRequested()), SLOT(slotTeardownDone(int,QString))); } bool StorageAccess::isLuksDevice() const { return m_device->isEncryptedContainer(); // encrypted device } bool StorageAccess::isAccessible() const { if (isLuksDevice()) { // check if the cleartext slave is mounted const QString path = clearTextPath(); //qDebug() << Q_FUNC_INFO << "CLEARTEXT device path: " << path; if (path.isEmpty() || path == "/") { return false; } Device holderDevice(path); return holderDevice.isMounted(); } return m_device->isMounted(); } QString StorageAccess::filePath() const { - QByteArrayList mntPoints; - - if (isLuksDevice()) { // encrypted (and unlocked) device - const QString path = clearTextPath(); - if (path.isEmpty() || path == "/") { - return QString(); - } - Device holderDevice(path); - mntPoints = qdbus_cast(holderDevice.prop("MountPoints")); - if (!mntPoints.isEmpty()) { - return QFile::decodeName(mntPoints.first()); // FIXME Solid doesn't support multiple mount points - } else { - return QString(); - } - } - - mntPoints = qdbus_cast(m_device->prop("MountPoints")); - - if (!mntPoints.isEmpty()) { - return QFile::decodeName(mntPoints.first()); // FIXME Solid doesn't support multiple mount points - } else { - return QString(); - } + return m_mountPoint; } bool StorageAccess::isIgnored() const { if (m_device->prop("HintIgnore").toBool()) { return true; } const QString path = filePath(); return !path.isEmpty() && !path.startsWith(QLatin1String("/media/")) && !path.startsWith(QLatin1String("/run/media/")) && !path.startsWith(QDir::homePath()); } bool StorageAccess::setup() { if (m_teardownInProgress || m_setupInProgress) { return false; } m_setupInProgress = true; m_device->broadcastActionRequested("setup"); if (m_device->isEncryptedContainer() && clearTextPath().isEmpty()) { return requestPassphrase(); } else { return mount(); } } bool StorageAccess::teardown() { if (m_teardownInProgress || m_setupInProgress) { return false; } m_teardownInProgress = true; m_device->broadcastActionRequested("teardown"); return unmount(); } void StorageAccess::updateCache() { m_isAccessible = isAccessible(); } void StorageAccess::checkAccessibility() { const bool old_isAccessible = m_isAccessible; updateCache(); if (old_isAccessible != m_isAccessible) { emit accessibilityChanged(m_isAccessible, m_device->udi()); } } -void StorageAccess::slotDBusReply(const QDBusMessage & /*reply*/) +void StorageAccess::slotDBusReply(const QDBusMessage &reply) { const QString ctPath = clearTextPath(); if (m_setupInProgress) { - if (isLuksDevice() && !isAccessible()) { // unlocked device, now mount it - mount(); + if (isLuksDevice()) { + if(!isAccessible()) { // unlocked device, now mount it + mount(); + } else { + const QString path = clearTextPath(); + if (path.isEmpty() || path == "/") { + return; + } + Device holderDevice(path); + QByteArrayList mntPoints = qdbus_cast(holderDevice.prop("MountPoints")); + m_mountPoint = QFile::decodeName(mntPoints.first()); // FIXME Solid doesn't support multiple mount points + } } else { // Don't broadcast setupDone unless the setup is really done. (Fix kde#271156) m_setupInProgress = false; + m_mountPoint = reply.arguments().first().toString(); // FIXME Solid doesn't support multiple mount points m_device->broadcastActionDone("setup"); checkAccessibility(); } } else if (m_teardownInProgress) { // FIXME if (isLuksDevice() && !ctPath.isEmpty() && ctPath != "/") { // unlocked device, lock it callCryptoTeardown(); } else if (!ctPath.isEmpty() && ctPath != "/") { callCryptoTeardown(true); // Lock crypted parent } else { // try to "eject" (aka safely remove) from the (parent) drive, e.g. SD card from a reader QString drivePath = m_device->drivePath(); if (!drivePath.isEmpty() || drivePath != "/") { Device drive(drivePath); if (drive.prop("Ejectable").toBool() && drive.prop("MediaAvailable").toBool() && !m_device->isOpticalDisc()) { // optical drives have their Eject method QDBusConnection c = QDBusConnection::systemBus(); QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, drivePath, UD2_DBUS_INTERFACE_DRIVE, "Eject"); msg << QVariantMap(); // options, unused now c.call(msg, QDBus::NoBlock); } } m_teardownInProgress = false; m_device->broadcastActionDone("teardown"); checkAccessibility(); } } } void StorageAccess::slotDBusError(const QDBusError &error) { //qDebug() << Q_FUNC_INFO << "DBUS ERROR:" << error.name() << error.message(); if (m_setupInProgress) { m_setupInProgress = false; m_device->broadcastActionDone("setup", m_device->errorToSolidError(error.name()), m_device->errorToString(error.name()) + ": " + error.message()); checkAccessibility(); } else if (m_teardownInProgress) { m_teardownInProgress = false; m_device->broadcastActionDone("teardown", m_device->errorToSolidError(error.name()), m_device->errorToString(error.name()) + ": " + error.message()); checkAccessibility(); } } void StorageAccess::slotSetupRequested() { m_setupInProgress = true; //qDebug() << "SETUP REQUESTED:" << m_device->udi(); emit setupRequested(m_device->udi()); } void StorageAccess::slotSetupDone(int error, const QString &errorString) { m_setupInProgress = false; //qDebug() << "SETUP DONE:" << m_device->udi(); emit setupDone(static_cast(error), errorString, m_device->udi()); checkAccessibility(); } void StorageAccess::slotTeardownRequested() { m_teardownInProgress = true; emit teardownRequested(m_device->udi()); } void StorageAccess::slotTeardownDone(int error, const QString &errorString) { m_teardownInProgress = false; emit teardownDone(static_cast(error), errorString, m_device->udi()); checkAccessibility(); } bool StorageAccess::mount() { QString path = m_device->udi(); const QString ctPath = clearTextPath(); if (isLuksDevice() && !ctPath.isEmpty()) { // mount options for the cleartext volume path = ctPath; } QDBusConnection c = QDBusConnection::systemBus(); QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, path, UD2_DBUS_INTERFACE_FILESYSTEM, "Mount"); QVariantMap options; if (m_device->prop("IdType").toString() == "vfat") { options.insert("options", "flush"); } msg << options; return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError))); } bool StorageAccess::unmount() { QString path = m_device->udi(); const QString ctPath = clearTextPath(); if (isLuksDevice() && !ctPath.isEmpty()) { // unmount options for the cleartext volume path = ctPath; } QDBusConnection c = QDBusConnection::systemBus(); QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, path, UD2_DBUS_INTERFACE_FILESYSTEM, "Unmount"); msg << QVariantMap(); // options, unused now return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)), s_unmountTimeout); } QString StorageAccess::generateReturnObjectPath() { static int number = 1; return "/org/kde/solid/UDisks2StorageAccess_" + QString::number(number++); } QString StorageAccess::clearTextPath() const { const QString prefix = "/org/freedesktop/UDisks2/block_devices"; QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, prefix, DBUS_INTERFACE_INTROSPECT, "Introspect"); QDBusPendingReply reply = QDBusConnection::systemBus().asyncCall(call); reply.waitForFinished(); if (reply.isValid()) { QDomDocument dom; dom.setContent(reply.value()); QDomNodeList nodeList = dom.documentElement().elementsByTagName("node"); for (int i = 0; i < nodeList.count(); i++) { QDomElement nodeElem = nodeList.item(i).toElement(); if (!nodeElem.isNull() && nodeElem.hasAttribute("name")) { const QString udi = prefix + "/" + nodeElem.attribute("name"); Device holderDevice(udi); if (m_device->udi() == holderDevice.prop("CryptoBackingDevice").value().path()) { //qDebug() << Q_FUNC_INFO << "CLEARTEXT device path: " << udi; return udi; } } } } return QString(); } bool StorageAccess::requestPassphrase() { QString udi = m_device->udi(); QString returnService = QDBusConnection::sessionBus().baseService(); m_lastReturnObject = generateReturnObjectPath(); QDBusConnection::sessionBus().registerObject(m_lastReturnObject, this, QDBusConnection::ExportScriptableSlots); QWidget *activeWindow = QApplication::activeWindow(); uint wId = 0; if (activeWindow != nullptr) { wId = (uint)activeWindow->winId(); } QString appId = QCoreApplication::applicationName(); QDBusInterface soliduiserver("org.kde.kded5", "/modules/soliduiserver", "org.kde.SolidUiServer"); QDBusReply reply = soliduiserver.call("showPassphraseDialog", udi, returnService, m_lastReturnObject, wId, appId); m_passphraseRequested = reply.isValid(); if (!m_passphraseRequested) { qWarning() << "Failed to call the SolidUiServer, D-Bus said:" << reply.error(); } return m_passphraseRequested; } void StorageAccess::passphraseReply(const QString &passphrase) { if (m_passphraseRequested) { QDBusConnection::sessionBus().unregisterObject(m_lastReturnObject); m_passphraseRequested = false; if (!passphrase.isEmpty()) { callCryptoSetup(passphrase); } else { m_setupInProgress = false; m_device->broadcastActionDone("setup", Solid::UserCanceled); } } } void StorageAccess::callCryptoSetup(const QString &passphrase) { QDBusConnection c = QDBusConnection::systemBus(); QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, m_device->udi(), UD2_DBUS_INTERFACE_ENCRYPTED, "Unlock"); msg << passphrase; msg << QVariantMap(); // options, unused now c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError))); } bool StorageAccess::callCryptoTeardown(bool actOnParent) { QDBusConnection c = QDBusConnection::systemBus(); QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, actOnParent ? (m_device->prop("CryptoBackingDevice").value().path()) : m_device->udi(), UD2_DBUS_INTERFACE_ENCRYPTED, "Lock"); msg << QVariantMap(); // options, unused now return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError))); } diff --git a/src/solid/devices/backends/udisks2/udisksstorageaccess.h b/src/solid/devices/backends/udisks2/udisksstorageaccess.h index a18261b..0a4cfde 100644 --- a/src/solid/devices/backends/udisks2/udisksstorageaccess.h +++ b/src/solid/devices/backends/udisks2/udisksstorageaccess.h @@ -1,104 +1,105 @@ /* Copyright 2009 Pino Toscano Copyright 2009-2012 Lukáš Tinkl This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef UDISKS2STORAGEACCESS_H #define UDISKS2STORAGEACCESS_H #include #include "udisksdeviceinterface.h" #include #include namespace Solid { namespace Backends { namespace UDisks2 { class StorageAccess : public DeviceInterface, virtual public Solid::Ifaces::StorageAccess { Q_OBJECT Q_INTERFACES(Solid::Ifaces::StorageAccess) public: StorageAccess(Device *device); virtual ~StorageAccess(); bool isAccessible() const Q_DECL_OVERRIDE; QString filePath() const Q_DECL_OVERRIDE; bool isIgnored() const Q_DECL_OVERRIDE; bool setup() Q_DECL_OVERRIDE; bool teardown() Q_DECL_OVERRIDE; Q_SIGNALS: void accessibilityChanged(bool accessible, const QString &udi) Q_DECL_OVERRIDE; void setupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) Q_DECL_OVERRIDE; void teardownDone(Solid::ErrorType error, QVariant errorData, const QString &udi) Q_DECL_OVERRIDE; void setupRequested(const QString &udi) Q_DECL_OVERRIDE; void teardownRequested(const QString &udi) Q_DECL_OVERRIDE; public Q_SLOTS: Q_SCRIPTABLE Q_NOREPLY void passphraseReply(const QString &passphrase); private Q_SLOTS: void slotDBusReply(const QDBusMessage &reply); void slotDBusError(const QDBusError &error); void connectDBusSignals(); void slotSetupRequested(); void slotSetupDone(int error, const QString &errorString); void slotTeardownRequested(); void slotTeardownDone(int error, const QString &errorString); void checkAccessibility(); private: /// @return true if this device is luks and unlocked bool isLuksDevice() const; void updateCache(); bool mount(); bool unmount(); bool requestPassphrase(); void callCryptoSetup(const QString &passphrase); bool callCryptoTeardown(bool actOnParent = false); QString generateReturnObjectPath(); QString clearTextPath() const; private: bool m_isAccessible; bool m_setupInProgress; bool m_teardownInProgress; bool m_passphraseRequested; QString m_lastReturnObject; + QString m_mountPoint; static const int s_unmountTimeout = 0x7fffffff; }; } } } #endif // UDISKS2STORAGEACCESS_H