diff --git a/src/solid/devices/backends/udisks2/udisksdevice.cpp b/src/solid/devices/backends/udisks2/udisksdevice.cpp index f3fdfff..0df32be 100644 --- a/src/solid/devices/backends/udisks2/udisksdevice.cpp +++ b/src/solid/devices/backends/udisks2/udisksdevice.cpp @@ -1,872 +1,879 @@ /* Copyright 2010 Michael Zanetti Copyright 2010-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 "udisksdevice.h" #include "udisksdevicebackend.h" #include "udisksblock.h" #include "udisksdeviceinterface.h" #include "udisksstoragevolume.h" #include "udisksopticaldisc.h" #include "udisksopticaldrive.h" #include "udisksstorageaccess.h" #include "udisksgenericinterface.h" #include #include #include #include #include #include #include #include #include #include using namespace Solid::Backends::UDisks2; // Adapted from KLocale as Solid needs to be Qt-only static QString formatByteSize(double size) { // Per IEC 60027-2 // Binary prefixes //Tebi-byte TiB 2^40 1,099,511,627,776 bytes //Gibi-byte GiB 2^30 1,073,741,824 bytes //Mebi-byte MiB 2^20 1,048,576 bytes //Kibi-byte KiB 2^10 1,024 bytes QString s; // Gibi-byte if (size >= 1073741824.0) { size /= 1073741824.0; if (size > 1024) { // Tebi-byte s = QCoreApplication::translate("udisksdevice", "%1 TiB").arg(QLocale().toString(size / 1024.0, 'f', 1)); } else { s = QCoreApplication::translate("udisksdevice", "%1 GiB").arg(QLocale().toString(size, 'f', 1)); } } // Mebi-byte else if (size >= 1048576.0) { size /= 1048576.0; s = QCoreApplication::translate("udisksdevice", "%1 MiB").arg(QLocale().toString(size, 'f', 1)); } // Kibi-byte else if (size >= 1024.0) { size /= 1024.0; s = QCoreApplication::translate("udisksdevice", "%1 KiB").arg(QLocale().toString(size, 'f', 1)); } // Just byte else if (size > 0) { s = QCoreApplication::translate("udisksdevice", "%1 B").arg(QLocale().toString(size, 'f', 1)); } // Nothing else { s = QCoreApplication::translate("udisksdevice", "0 B"); } return s; } Device::Device(const QString &udi) : Solid::Ifaces::Device() , m_backend(DeviceBackend::backendForUDI(udi)) { if (m_backend) { connect(m_backend, SIGNAL(changed()), this, SIGNAL(changed())); connect(m_backend, SIGNAL(propertyChanged(QMap)), this, SIGNAL(propertyChanged(QMap))); } else { qDebug() << "Created invalid Device for udi" << udi; } } Device::~Device() { } QString Device::udi() const { if (m_backend) { return m_backend->udi(); } return QString(); } QVariant Device::prop(const QString &key) const { if (m_backend) { return m_backend->prop(key); } return QVariant(); } bool Device::propertyExists(const QString &key) const { if (m_backend) { return m_backend->propertyExists(key); } return false; } QVariantMap Device::allProperties() const { if (m_backend) { return m_backend->allProperties(); } return QVariantMap(); } bool Device::hasInterface(const QString &name) const { if (m_backend) { return m_backend->interfaces().contains(name); } return false; } QStringList Device::interfaces() const { if (m_backend) { return m_backend->interfaces(); } return QStringList(); } +void Device::invalidateCache() +{ + if (m_backend) { + return m_backend->invalidateProperties(); + } +} + QObject *Device::createDeviceInterface(const Solid::DeviceInterface::Type &type) { if (!queryDeviceInterface(type)) { return nullptr; } DeviceInterface *iface = nullptr; switch (type) { case Solid::DeviceInterface::GenericInterface: iface = new GenericInterface(this); break; case Solid::DeviceInterface::Block: iface = new Block(this); break; case Solid::DeviceInterface::StorageAccess: iface = new StorageAccess(this); break; case Solid::DeviceInterface::StorageDrive: iface = new StorageDrive(this); break; case Solid::DeviceInterface::OpticalDrive: iface = new OpticalDrive(this); break; case Solid::DeviceInterface::StorageVolume: iface = new StorageVolume(this); break; case Solid::DeviceInterface::OpticalDisc: iface = new OpticalDisc(this); break; default: break; } return iface; } bool Device::queryDeviceInterface(const Solid::DeviceInterface::Type &type) const { switch (type) { case Solid::DeviceInterface::GenericInterface: return true; case Solid::DeviceInterface::Block: return isBlock() || isDrive(); case Solid::DeviceInterface::StorageVolume: return isStorageVolume(); case Solid::DeviceInterface::StorageAccess: return isStorageAccess(); case Solid::DeviceInterface::StorageDrive: return isDrive(); case Solid::DeviceInterface::OpticalDrive: return isOpticalDrive(); case Solid::DeviceInterface::OpticalDisc: return isOpticalDisc(); default: return false; } } QStringList Device::emblems() const { QStringList res; if (queryDeviceInterface(Solid::DeviceInterface::StorageAccess)) { const UDisks2::StorageAccess accessIface(const_cast(this)); if (accessIface.isAccessible()) { if (isEncryptedContainer()) { res << "emblem-encrypted-unlocked"; } else { res << "emblem-mounted"; } } else { if (isEncryptedContainer()) { res << "emblem-encrypted-locked"; } else { res << "emblem-unmounted"; } } } return res; } QString Device::description() const { const QString hintName = property("HintName").toString(); // non-cached if (!hintName.isEmpty()) { return hintName; } if (isLoop()) { return loopDescription(); } else if (isSwap()) { return tr("Swap Space"); } else if (queryDeviceInterface(Solid::DeviceInterface::StorageDrive)) { return storageDescription(); } else if (queryDeviceInterface(Solid::DeviceInterface::StorageVolume)) { return volumeDescription(); } else { return product(); } } QString Device::loopDescription() const { const QString backingFile = prop("BackingFile").toString(); if (!backingFile.isEmpty()) { return backingFile.section(QLatin1Char('/'), -1); } return tr("Loop Device"); } QString Device::storageDescription() const { QString description; const UDisks2::StorageDrive storageDrive(const_cast(this)); Solid::StorageDrive::DriveType drive_type = storageDrive.driveType(); const bool drive_is_hotpluggable = storageDrive.isHotpluggable(); if (drive_type == Solid::StorageDrive::CdromDrive) { const UDisks2::OpticalDrive opticalDrive(const_cast(this)); Solid::OpticalDrive::MediumTypes mediumTypes = opticalDrive.supportedMedia(); QString first; QString second; first = tr("CD-ROM", "First item of %1%2 Drive sentence"); if (mediumTypes & Solid::OpticalDrive::Cdr) { first = tr("CD-R", "First item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Cdrw) { first = tr("CD-RW", "First item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Dvd) { second = tr("/DVD-ROM", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Dvdplusr) { second = tr("/DVD+R", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Dvdplusrw) { second = tr("/DVD+RW", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Dvdr) { second = tr("/DVD-R", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Dvdrw) { second = tr("/DVD-RW", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Dvdram) { second = tr("/DVD-RAM", "Second item of %1%2 Drive sentence"); } if ((mediumTypes & Solid::OpticalDrive::Dvdr) && (mediumTypes & Solid::OpticalDrive::Dvdplusr)) { if (mediumTypes & Solid::OpticalDrive::Dvdplusdl) { second = trUtf8("/DVD±R DL", "Second item of %1%2 Drive sentence"); } else { second = trUtf8("/DVD±R", "Second item of %1%2 Drive sentence"); } } if ((mediumTypes & Solid::OpticalDrive::Dvdrw) && (mediumTypes & Solid::OpticalDrive::Dvdplusrw)) { if ((mediumTypes & Solid::OpticalDrive::Dvdplusdl) || (mediumTypes & Solid::OpticalDrive::Dvdplusdlrw)) { second = trUtf8("/DVD±RW DL", "Second item of %1%2 Drive sentence"); } else { second = trUtf8("/DVD±RW", "Second item of %1%2 Drive sentence"); } } if (mediumTypes & Solid::OpticalDrive::Bd) { second = tr("/BD-ROM", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Bdr) { second = tr("/BD-R", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::Bdre) { second = tr("/BD-RE", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::HdDvd) { second = tr("/HD DVD-ROM", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::HdDvdr) { second = tr("/HD DVD-R", "Second item of %1%2 Drive sentence"); } if (mediumTypes & Solid::OpticalDrive::HdDvdrw) { second = tr("/HD DVD-RW", "Second item of %1%2 Drive sentence"); } if (drive_is_hotpluggable) { description = tr("External %1%2 Drive", "%1 is CD-ROM/CD-R/etc; %2 is '/DVD-ROM'/'/DVD-R'/etc (with leading slash)").arg(first).arg(second); } else { description = tr("%1%2 Drive", "%1 is CD-ROM/CD-R/etc; %2 is '/DVD-ROM'/'/DVD-R'/etc (with leading slash)").arg(first).arg(second); } return description; } if (drive_type == Solid::StorageDrive::Floppy) { if (drive_is_hotpluggable) { description = tr("External Floppy Drive"); } else { description = tr("Floppy Drive"); } return description; } const bool drive_is_removable = storageDrive.isRemovable(); if (drive_type == Solid::StorageDrive::HardDisk && !drive_is_removable) { QString size_str = formatByteSize(storageDrive.size()); if (!size_str.isEmpty()) { if (drive_is_hotpluggable) { description = tr("%1 External Hard Drive", "%1 is the size").arg(size_str); } else { description = tr("%1 Hard Drive", "%1 is the size").arg(size_str); } } else { if (drive_is_hotpluggable) { description = tr("External Hard Drive"); } else { description = tr("Hard Drive"); } } return description; } QString vendormodel_str; QString model = product(); QString vendor_str = vendor(); if (vendor_str.isEmpty()) { if (!model.isEmpty()) { vendormodel_str = model; } } else { if (model.isEmpty()) { vendormodel_str = vendor_str; } else { if (model.startsWith(vendor_str)) { // e.g. vendor is "Nokia" and model is "Nokia N950" we do not want "Nokia Nokia N950" as description vendormodel_str = model; } else { vendormodel_str = tr("%1 %2", "%1 is the vendor, %2 is the model of the device").arg(vendor_str).arg(model); } } } if (vendormodel_str.isEmpty()) { description = tr("Drive"); } else { description = vendormodel_str; } return description; } QString Device::volumeDescription() const { QString description; const UDisks2::StorageVolume storageVolume(const_cast(this)); QString volume_label = prop("IdLabel").toString(); if (volume_label.isEmpty()) { volume_label = prop("Name").toString(); } if (!volume_label.isEmpty()) { return volume_label; } UDisks2::Device storageDevice(drivePath()); const UDisks2::StorageDrive storageDrive(&storageDevice); Solid::StorageDrive::DriveType drive_type = storageDrive.driveType(); // Handle media in optical drives if (drive_type == Solid::StorageDrive::CdromDrive) { const UDisks2::OpticalDisc disc(const_cast(this)); switch (disc.discType()) { case Solid::OpticalDisc::UnknownDiscType: case Solid::OpticalDisc::CdRom: description = tr("CD-ROM"); break; case Solid::OpticalDisc::CdRecordable: if (disc.isBlank()) { description = tr("Blank CD-R"); } else { description = tr("CD-R"); } break; case Solid::OpticalDisc::CdRewritable: if (disc.isBlank()) { description = tr("Blank CD-RW"); } else { description = tr("CD-RW"); } break; case Solid::OpticalDisc::DvdRom: description = tr("DVD-ROM"); break; case Solid::OpticalDisc::DvdRam: if (disc.isBlank()) { description = tr("Blank DVD-RAM"); } else { description = tr("DVD-RAM"); } break; case Solid::OpticalDisc::DvdRecordable: if (disc.isBlank()) { description = tr("Blank DVD-R"); } else { description = tr("DVD-R"); } break; case Solid::OpticalDisc::DvdPlusRecordableDuallayer: if (disc.isBlank()) { description = tr("Blank DVD+R Dual-Layer"); } else { description = tr("DVD+R Dual-Layer"); } break; case Solid::OpticalDisc::DvdRewritable: if (disc.isBlank()) { description = tr("Blank DVD-RW"); } else { description = tr("DVD-RW"); } break; case Solid::OpticalDisc::DvdPlusRecordable: if (disc.isBlank()) { description = tr("Blank DVD+R"); } else { description = tr("DVD+R"); } break; case Solid::OpticalDisc::DvdPlusRewritable: if (disc.isBlank()) { description = tr("Blank DVD+RW"); } else { description = tr("DVD+RW"); } break; case Solid::OpticalDisc::DvdPlusRewritableDuallayer: if (disc.isBlank()) { description = tr("Blank DVD+RW Dual-Layer"); } else { description = tr("DVD+RW Dual-Layer"); } break; case Solid::OpticalDisc::BluRayRom: description = tr("BD-ROM"); break; case Solid::OpticalDisc::BluRayRecordable: if (disc.isBlank()) { description = tr("Blank BD-R"); } else { description = tr("BD-R"); } break; case Solid::OpticalDisc::BluRayRewritable: if (disc.isBlank()) { description = tr("Blank BD-RE"); } else { description = tr("BD-RE"); } break; case Solid::OpticalDisc::HdDvdRom: description = tr("HD DVD-ROM"); break; case Solid::OpticalDisc::HdDvdRecordable: if (disc.isBlank()) { description = tr("Blank HD DVD-R"); } else { description = tr("HD DVD-R"); } break; case Solid::OpticalDisc::HdDvdRewritable: if (disc.isBlank()) { description = tr("Blank HD DVD-RW"); } else { description = tr("HD DVD-RW"); } break; } // Special case for pure audio disc if (disc.availableContent() == Solid::OpticalDisc::Audio) { description = tr("Audio CD"); } return description; } const bool drive_is_removable = storageDrive.isRemovable(); const bool drive_is_hotpluggable = storageDrive.isHotpluggable(); QString size_str = formatByteSize(storageVolume.size()); if (isEncryptedContainer()) { if (!size_str.isEmpty()) { description = tr("%1 Encrypted Drive", "%1 is the size").arg(size_str); } else { description = tr("Encrypted Drive"); } } else if (drive_type == Solid::StorageDrive::HardDisk && !drive_is_removable) { if (!size_str.isEmpty()) { if (drive_is_hotpluggable) { description = tr("%1 External Hard Drive", "%1 is the size").arg(size_str); } else { description = tr("%1 Hard Drive", "%1 is the size").arg(size_str); } } else { if (drive_is_hotpluggable) { description = tr("External Hard Drive"); } else { description = tr("Hard Drive"); } } } else if (drive_type == Solid::StorageDrive::Floppy) { description = tr("Floppy Disk"); } else { if (drive_is_removable) { description = tr("%1 Removable Media", "%1 is the size").arg(size_str); } else { description = tr("%1 Media", "%1 is the size").arg(size_str); } } return description; } QString Device::icon() const { QString iconName = property("HintIconName").toString(); // non-cached if (!iconName.isEmpty()) { return iconName; } else if (isLoop()) { const QString backingFile = prop("BackingFile").toString(); if (!backingFile.isEmpty()) { QMimeType type = QMimeDatabase().mimeTypeForFile(backingFile); if (!type.isDefault()) { return type.iconName(); } } return QStringLiteral("drive-harddisk"); } else if (isSwap()) { return "drive-harddisk"; } else if (isDrive()) { const bool isRemovable = prop("Removable").toBool(); const QString conn = prop("ConnectionBus").toString(); if (isOpticalDrive()) { return "drive-optical"; } else if (isRemovable && !prop("Optical").toBool()) { if (conn == "usb") { return "drive-removable-media-usb"; } else { return "drive-removable-media"; } } } else if (isBlock()) { const QString drv = drivePath(); if (drv.isEmpty() || drv == "/") { return "drive-harddisk"; // stuff like loop devices or swap which don't have the Drive prop set } Device drive(drv); // handle media const QString media = drive.prop("Media").toString(); if (!media.isEmpty()) { if (drive.prop("Optical").toBool()) { // optical stuff bool isWritable = drive.prop("OpticalBlank").toBool(); const UDisks2::OpticalDisc disc(const_cast(this)); Solid::OpticalDisc::ContentTypes availContent = disc.availableContent(); if (availContent & Solid::OpticalDisc::VideoDvd) { // Video DVD return "media-optical-dvd-video"; } else if ((availContent & Solid::OpticalDisc::VideoCd) || (availContent & Solid::OpticalDisc::SuperVideoCd)) { // Video CD return "media-optical-video"; } else if ((availContent & Solid::OpticalDisc::Data) && (availContent & Solid::OpticalDisc::Audio)) { // Mixed CD return "media-optical-mixed-cd"; } else if (availContent & Solid::OpticalDisc::Audio) { // Audio CD return "media-optical-audio"; } else if (availContent & Solid::OpticalDisc::Data) { // Data CD return "media-optical-data"; } else if (isWritable) { return "media-optical-recordable"; } else { if (media.startsWith("optical_dvd") || media.startsWith("optical_hddvd")) { // DVD return "media-optical-dvd"; } else if (media.startsWith("optical_bd")) { // BluRay return "media-optical-blu-ray"; } } // fallback for every other optical disc return "media-optical"; } if (media == "flash_ms") { // Flash & Co. return "media-flash-memory-stick"; } else if (media == "flash_sd" || media == "flash_sdhc" || media == "flash_sdxc" || media == "flash_mmc") { return "media-flash-sd-mmc"; } else if (media == "flash_sm") { return "media-flash-smart-media"; } else if (media == "thumb") { return "drive-removable-media-usb-pendrive"; } else if (media.startsWith("flash")) { return "media-flash"; } else if (media == "floppy") { // the good ol' floppy return "media-floppy"; } } if (drive.prop("ConnectionBus").toString() == "sdio") { // hack for SD cards connected thru sdio bus return "media-flash-sd-mmc"; } return drive.icon(); } return "drive-harddisk"; // general fallback } QString Device::product() const { if (!isDrive()) { Device drive(drivePath()); return drive.prop("Model").toString(); } return prop("Model").toString(); } QString Device::vendor() const { if (!isDrive()) { Device drive(drivePath()); return drive.prop("Vendor").toString(); } return prop("Vendor").toString(); } QString Device::parentUdi() const { QString parent; if (propertyExists("Drive")) { // block parent = drivePath(); } else if (propertyExists("Table")) { // partition parent = prop("Table").value().path(); } else if (parent.isEmpty() || parent == "/") { parent = UD2_UDI_DISKS_PREFIX; } return parent; } QString Device::errorToString(const QString &error) const { if (error == UD2_ERROR_UNAUTHORIZED || error == UD2_ERROR_NOT_AUTHORIZED) { return tr("You are not authorized to perform this operation"); } else if (error == UD2_ERROR_BUSY) { return tr("The device is currently busy"); } else if (error == UD2_ERROR_FAILED) { return tr("The requested operation has failed"); } else if (error == UD2_ERROR_CANCELED) { return tr("The requested operation has been canceled"); } else if (error == UD2_ERROR_INVALID_OPTION) { return tr("An invalid or malformed option has been given"); } else if (error == UD2_ERROR_MISSING_DRIVER) { return tr("The kernel driver for this filesystem type is not available"); } else if (error == UD2_ERROR_ALREADY_MOUNTED) { return tr("The device is already mounted"); } else if (error == UD2_ERROR_NOT_MOUNTED) { return tr("The device is not mounted"); } else if (error == UD2_ERROR_MOUNTED_BY_OTHER_USER) { return tr("The device is mounted by another user"); } else if (error == UD2_ERROR_ALREADY_UNMOUNTING) { return tr("The device is already unmounting"); } else if (error == UD2_ERROR_TIMED_OUT) { return tr("The operation timed out"); } else if (error == UD2_ERROR_WOULD_WAKEUP) { return tr("The operation would wake up a disk that is in a deep-sleep state"); } else if (error == UD2_ERROR_ALREADY_CANCELLED) { return tr("The operation has already been canceled"); } else { return tr("An unspecified error has occurred"); } } Solid::ErrorType Device::errorToSolidError(const QString &error) const { if (error == UD2_ERROR_BUSY) { return Solid::DeviceBusy; } else if (error == UD2_ERROR_FAILED) { return Solid::OperationFailed; } else if (error == UD2_ERROR_CANCELED) { return Solid::UserCanceled; } else if (error == UD2_ERROR_INVALID_OPTION) { return Solid::InvalidOption; } else if (error == UD2_ERROR_MISSING_DRIVER) { return Solid::MissingDriver; } else { return Solid::UnauthorizedOperation; } } bool Device::isBlock() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_BLOCK)); } bool Device::isPartition() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_PARTITION)); } bool Device::isPartitionTable() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_PARTITIONTABLE)); } bool Device::isStorageVolume() const { return isPartition() || isPartitionTable() || isStorageAccess() || isOpticalDisc(); } bool Device::isStorageAccess() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM)) || isEncryptedContainer(); } bool Device::isDrive() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_DRIVE)); } bool Device::isOpticalDrive() const { return isDrive() && !prop("MediaCompatibility").toStringList().filter("optical_").isEmpty(); } bool Device::isOpticalDisc() const { const QString drv = drivePath(); if (drv.isEmpty() || drv == "/") { return false; } Device drive(drv); return drive.prop("Optical").toBool(); } bool Device::mightBeOpticalDisc() const { const QString drv = drivePath(); if (drv.isEmpty() || drv == "/") { return false; } Device drive(drv); return drive.isOpticalDrive(); } bool Device::isMounted() const { QVariant mountPoints = prop(QStringLiteral("MountPoints")); return mountPoints.isValid() && !qdbus_cast(mountPoints).isEmpty(); } bool Device::isEncryptedContainer() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED)); } bool Device::isEncryptedCleartext() const { const QString holderDevice = prop("CryptoBackingDevice").toString(); if (holderDevice.isEmpty() || holderDevice == "/") { return false; } else { return true; } } bool Device::isSwap() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_SWAP)); } bool Device::isLoop() const { return hasInterface(QStringLiteral(UD2_DBUS_INTERFACE_LOOP)); } QString Device::drivePath() const { return prop("Drive").value().path(); } diff --git a/src/solid/devices/backends/udisks2/udisksdevice.h b/src/solid/devices/backends/udisks2/udisksdevice.h index 147d554..1492564 100644 --- a/src/solid/devices/backends/udisks2/udisksdevice.h +++ b/src/solid/devices/backends/udisks2/udisksdevice.h @@ -1,105 +1,106 @@ /* Copyright 2010 Michael Zanetti Copyright 2010-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 UDISKS2DEVICE_H #define UDISKS2DEVICE_H #include "udisks2.h" #include #include #include #include #include #include namespace Solid { namespace Backends { namespace UDisks2 { class DeviceBackend; class Device: public Solid::Ifaces::Device { Q_OBJECT public: Device(const QString &udi); virtual ~Device(); QObject *createDeviceInterface(const Solid::DeviceInterface::Type &type) Q_DECL_OVERRIDE; bool queryDeviceInterface(const Solid::DeviceInterface::Type &type) const Q_DECL_OVERRIDE; QString description() const Q_DECL_OVERRIDE; QStringList emblems() const Q_DECL_OVERRIDE; QString icon() const Q_DECL_OVERRIDE; QString product() const Q_DECL_OVERRIDE; QString vendor() const Q_DECL_OVERRIDE; QString udi() const Q_DECL_OVERRIDE; QString parentUdi() const Q_DECL_OVERRIDE; QVariant prop(const QString &key) const; bool propertyExists(const QString &key) const; QVariantMap allProperties() const; + void invalidateCache(); bool hasInterface(const QString &name) const; QStringList interfaces() const; QString errorToString(const QString &error) const; Solid::ErrorType errorToSolidError(const QString &error) const; bool isBlock() const; bool isPartition() const; bool isPartitionTable() const; bool isStorageVolume() const; bool isStorageAccess() const; bool isDrive() const; bool isOpticalDrive() const; bool isOpticalDisc() const; bool mightBeOpticalDisc() const; bool isMounted() const; bool isEncryptedContainer() const; bool isEncryptedCleartext() const; bool isSwap() const; bool isLoop() const; QString drivePath() const; Q_SIGNALS: void changed(); void propertyChanged(const QMap &changes); protected: QPointer m_backend; private: QString loopDescription() const; QString storageDescription() const; QString volumeDescription() const; }; } } } #endif // UDISKS2DEVICE_H diff --git a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp index 7db2263..d08f35d 100644 --- a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp +++ b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp @@ -1,394 +1,394 @@ /* 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(); } } bool StorageAccess::isIgnored() const { if (m_device->prop("HintIgnore").toBool()) { return true; } const QString path = filePath(); bool inUserPath = path.startsWith(QLatin1String("/media/")) || path.startsWith(QLatin1String("/run/media/")) || path.startsWith(QDir::homePath()); return !inUserPath; } 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*/) { const QString ctPath = clearTextPath(); if (m_setupInProgress) { if (isLuksDevice() && !isAccessible()) { // unlocked device, now mount it mount(); } else { // Don't broadcast setupDone unless the setup is really done. (Fix kde#271156) m_setupInProgress = false; + m_device->invalidateCache(); 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->invalidateCache(); 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(); + emit setupDone(static_cast(error), errorString, m_device->udi()); } 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(); + emit teardownDone(static_cast(error), errorString, m_device->udi()); } 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))); }