diff --git a/CMakeLists.txt b/CMakeLists.txt index 57c03cb..3747397 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,123 +1,124 @@ cmake_minimum_required(VERSION 3.0) set(KF5_VERSION "5.50.0") # handled by release scripts project(Solid VERSION ${KF5_VERSION}) include(FeatureSummary) find_package(ECM 5.49.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) +include(ECMQtDeclareLoggingCategory) set(REQUIRED_QT_VERSION 5.8.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Xml DBus Widgets) if(WIN32) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Network) endif() include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGenerateHeaders) include(ECMMarkNonGuiExecutable) include(ECMAddQch) find_package(FLEX REQUIRED) set_package_properties(FLEX PROPERTIES URL "http://flex.sourceforge.net" DESCRIPTION "Fast Lexical Analyzer" TYPE REQUIRED PURPOSE "Required for the Predicate parser" ) find_package(BISON REQUIRED) set_package_properties(BISON PROPERTIES URL "http://www.gnu.org/software/bison" DESCRIPTION "general-purpose parser generator" TYPE REQUIRED PURPOSE "Required for the Predicate parser" ) if (CMAKE_SYSTEM_NAME MATCHES Linux) find_package( UDev ) set_package_properties(UDev PROPERTIES URL "http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html" DESCRIPTION "UDev support for Solid" TYPE REQUIRED PURPOSE "Allows Solid to use UDev to provide information about devices on Linux" ) endif() include(ECMPoQmTools) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX SOLID VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/solid_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfigVersion.cmake" SOVERSION 5) # TODO: Remove these remove_definitions(-DQT_NO_CAST_FROM_ASCII) remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() option(WITH_NEW_SOLID_JOB "WIP: base Job class" Off) add_feature_info(Solid::Job WITH_NEW_SOLID_JOB "WIP: Base class for Solid Asynchronous apis") option(WITH_NEW_POWER_ASYNC_API "WIP: Asynchronous API for power management" Off) add_feature_info(Solid::PowerManagement WITH_NEW_POWER_ASYNC_API "WIP: Asynchronous API for power management") option(WITH_NEW_POWER_ASYNC_FREEDESKTOP "WIP: Freedesktop backend for the asynchronous api" Off) add_feature_info(Solid::PowerManagement WITH_NEW_POWER_ASYNC_FREEDESKTOP "WIP: Freedesktop backend for the asynchronous api") add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(autotests) endif() # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Solid") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Solid_QCH FILE KF5SolidQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5SolidQchTargets.cmake\")") endif() configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5SolidConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5SolidTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5SolidTargets.cmake NAMESPACE KF5:: ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/solid_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/solid/devices/backends/udisks2/CMakeLists.txt b/src/solid/devices/backends/udisks2/CMakeLists.txt index 3439006..6d12304 100644 --- a/src/solid/devices/backends/udisks2/CMakeLists.txt +++ b/src/solid/devices/backends/udisks2/CMakeLists.txt @@ -1,14 +1,20 @@ set(solid_LIB_SRCS ${solid_LIB_SRCS} devices/backends/udisks2/udisksmanager.cpp devices/backends/udisks2/udisksdevice.cpp devices/backends/udisks2/udisksdevicebackend.cpp devices/backends/udisks2/udisksblock.cpp devices/backends/udisks2/udisksstoragevolume.cpp devices/backends/udisks2/udisksdeviceinterface.cpp devices/backends/udisks2/udisksopticaldisc.cpp devices/backends/udisks2/udisksopticaldrive.cpp devices/backends/udisks2/udisksstoragedrive.cpp devices/backends/udisks2/udisksstorageaccess.cpp devices/backends/udisks2/udisksgenericinterface.cpp devices/backends/udisks2/dbus/manager.cpp -) \ No newline at end of file +) + +ecm_qt_declare_logging_category(solid_LIB_SRCS + HEADER udisks_debug.h + IDENTIFIER Solid::Backends::UDisks2::UDISKS2 + DEFAULT_SEVERITY Warning + CATEGORY_NAME org.kde.solid.udisks2) diff --git a/src/solid/devices/backends/udisks2/udisksblock.cpp b/src/solid/devices/backends/udisks2/udisksblock.cpp index 0622ec7..c0c39da 100644 --- a/src/solid/devices/backends/udisks2/udisksblock.cpp +++ b/src/solid/devices/backends/udisks2/udisksblock.cpp @@ -1,96 +1,97 @@ /* Copyright 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 . */ #if defined(Q_OS_LINUX) #include #else // taken from linux/kdev_t.h #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #endif #include #include #include #include +#include "udisks_debug.h" #include "udisksblock.h" using namespace Solid::Backends::UDisks2; Block::Block(Device *dev) : DeviceInterface(dev) { m_devNum = m_device->prop("DeviceNumber").toULongLong(); m_devFile = QFile::decodeName(m_device->prop("Device").toByteArray()); // we have a drive (non-block device for udisks), so let's find the corresponding (real) block device if (m_devNum == 0 || m_devFile.isEmpty()) { const QString path = "/org/freedesktop/UDisks2/block_devices"; QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, path, 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 = path + "/" + nodeElem.attribute("name"); Device device(udi); if (device.drivePath() == dev->udi()) { m_devNum = device.prop("DeviceNumber").toULongLong(); m_devFile = QFile::decodeName(device.prop("Device").toByteArray()); break; } } } } else { - qWarning() << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message(); + qCWarning(UDISKS2) << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message(); } } //qDebug() << "devnum:" << m_devNum << "dev file:" << m_devFile; } Block::~Block() { } QString Block::device() const { return m_devFile; } int Block::deviceMinor() const { return MINOR(m_devNum); } int Block::deviceMajor() const { return MAJOR(m_devNum); } diff --git a/src/solid/devices/backends/udisks2/udisksdevice.cpp b/src/solid/devices/backends/udisks2/udisksdevice.cpp index 341150e..3a83879 100644 --- a/src/solid/devices/backends/udisks2/udisksdevice.cpp +++ b/src/solid/devices/backends/udisks2/udisksdevice.cpp @@ -1,879 +1,880 @@ /* 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 "udisks_debug.h" #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; + qCDebug(UDISKS2) << "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 (storageDrive.size() > 0) { 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 (storageVolume.size() > 0) { 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 (storageVolume.size() > 0) { 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/udisksdevicebackend.cpp b/src/solid/devices/backends/udisks2/udisksdevicebackend.cpp index 2349d54..010547b 100644 --- a/src/solid/devices/backends/udisks2/udisksdevicebackend.cpp +++ b/src/solid/devices/backends/udisks2/udisksdevicebackend.cpp @@ -1,256 +1,257 @@ /* Copyright 2010 Michael Zanetti Copyright 2010-2012 Lukáš Tinkl Copyright 2012 Dan Vrátil 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 "udisks_debug.h" #include "udisksdevicebackend.h" #include #include #include #include "solid/deviceinterface.h" #include "solid/genericinterface.h" using namespace Solid::Backends::UDisks2; /* Static cache for DeviceBackends for all UDIs */ QMap DeviceBackend::s_backends; DeviceBackend *DeviceBackend::backendForUDI(const QString &udi, bool create) { DeviceBackend *backend = nullptr; if (udi.isEmpty()) { return backend; } backend = s_backends.value(udi); if (!backend && create) { backend = new DeviceBackend(udi); s_backends.insert(udi, backend); } return backend; } void DeviceBackend::destroyBackend(const QString &udi) { delete s_backends.take(udi); } DeviceBackend::DeviceBackend(const QString &udi) : m_udi(udi) { //qDebug() << "Creating backend for device" << m_udi; m_device = new QDBusInterface(UD2_DBUS_SERVICE, m_udi, QString(), // no interface, we aggregate them QDBusConnection::systemBus(), this); if (m_device->isValid()) { QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, m_udi, DBUS_INTERFACE_PROPS, "PropertiesChanged", this, SLOT(slotPropertiesChanged(QString,QVariantMap,QStringList))); QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, UD2_DBUS_PATH, DBUS_INTERFACE_MANAGER, "InterfacesAdded", this, SLOT(slotInterfacesAdded(QDBusObjectPath,VariantMapMap))); QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, UD2_DBUS_PATH, DBUS_INTERFACE_MANAGER, "InterfacesRemoved", this, SLOT(slotInterfacesRemoved(QDBusObjectPath,QStringList))); initInterfaces(); } } DeviceBackend::~DeviceBackend() { //qDebug() << "Destroying backend for device" << m_udi; } void DeviceBackend::initInterfaces() { m_interfaces.clear(); const QString xmlData = introspect(); if (xmlData.isEmpty()) { - qDebug() << m_udi << "has no interfaces!"; + qCDebug(UDISKS2) << m_udi << "has no interfaces!"; return; } QDomDocument dom; dom.setContent(xmlData); QDomNodeList ifaceNodeList = dom.elementsByTagName("interface"); for (int i = 0; i < ifaceNodeList.count(); i++) { QDomElement ifaceElem = ifaceNodeList.item(i).toElement(); /* Accept only org.freedesktop.UDisks2.* interfaces so that when the device is unplugged, * m_interfaces goes empty and we can easily verify that the device is gone. */ if (!ifaceElem.isNull() && ifaceElem.attribute("name").startsWith(UD2_DBUS_SERVICE)) { m_interfaces.append(ifaceElem.attribute("name")); } } //qDebug() << m_udi << "has interfaces:" << m_interfaces; } QStringList DeviceBackend::interfaces() const { return m_interfaces; } const QString &DeviceBackend::udi() const { return m_udi; } QVariant DeviceBackend::prop(const QString &key) const { checkCache(key); return m_propertyCache.value(key); } bool DeviceBackend::propertyExists(const QString &key) const { checkCache(key); /* checkCache() will put an invalid QVariant in cache when the property * does not exist, so check for validity, not for an actual presence. */ return m_propertyCache.value(key).isValid(); } QVariantMap DeviceBackend::allProperties() const { QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, m_udi, DBUS_INTERFACE_PROPS, "GetAll"); Q_FOREACH (const QString &iface, m_interfaces) { call.setArguments(QVariantList() << iface); QDBusPendingReply reply = QDBusConnection::systemBus().call(call); if (reply.isValid()) { auto props = reply.value(); // Can not use QMap<>::unite(), as it allows multiple values per key for (auto it = props.cbegin(); it != props.cend(); ++it) { m_propertyCache.insert(it.key(), it.value()); } } else { - qWarning() << "Error getting props:" << reply.error().name() << reply.error().message(); + qCWarning(UDISKS2) << "Error getting props:" << reply.error().name() << reply.error().message(); } //qDebug() << "After iface" << iface << ", cache now contains" << m_propertyCache.size() << "items"; } return m_propertyCache; } void DeviceBackend::invalidateProperties() { m_propertyCache.clear(); } QString DeviceBackend::introspect() const { QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, m_udi, DBUS_INTERFACE_INTROSPECT, "Introspect"); QDBusPendingReply reply = QDBusConnection::systemBus().call(call); if (reply.isValid()) { return reply.value(); } else { return QString(); } } void DeviceBackend::checkCache(const QString &key) const { if (m_propertyCache.isEmpty()) { // recreate the cache allProperties(); } if (m_propertyCache.contains(key)) { return; } QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, m_udi, DBUS_INTERFACE_PROPS, "Get"); /* * Interface is set to an empty string as in this QDBusInterface is a meta-object of multiple interfaces on the same path * The DBus properties also interface supports this, and will find the appropriate interface if none is explicitly set. * This matches what QDBusAbstractInterface would do */ call.setArguments(QVariantList() << QString() << key); QDBusPendingReply reply = QDBusConnection::systemBus().call(call); /* We don't check for error here and store the item in the cache anyway so next time we don't have to * do the DBus call to find out it does not exist but just check whether * prop(key).isValid() */ m_propertyCache.insert(key, reply.value()); } void DeviceBackend::slotPropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps) { if (!ifaceName.startsWith(UD2_DBUS_SERVICE)) { return; } //qDebug() << m_udi << "'s interface" << ifaceName << "changed props:"; QMap changeMap; Q_FOREACH (const QString &key, invalidatedProps) { m_propertyCache.remove(key); changeMap.insert(key, Solid::GenericInterface::PropertyModified); //qDebug() << "\t invalidated:" << key; } QMapIterator i(changedProps); while (i.hasNext()) { i.next(); const QString key = i.key(); m_propertyCache.insert(key, i.value()); // replace the value changeMap.insert(key, Solid::GenericInterface::PropertyModified); //qDebug() << "\t modified:" << key << ":" << m_propertyCache.value(key); } emit propertyChanged(changeMap); emit changed(); } void DeviceBackend::slotInterfacesAdded(const QDBusObjectPath &object_path, const VariantMapMap &interfaces_and_properties) { if (object_path.path() != m_udi) { return; } Q_FOREACH (const QString &iface, interfaces_and_properties.keys()) { /* Don't store generic DBus interfaces */ if (iface.startsWith(UD2_DBUS_SERVICE)) { m_interfaces.append(iface); } } } void DeviceBackend::slotInterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces) { if (object_path.path() != m_udi) { return; } Q_FOREACH (const QString &iface, interfaces) { m_interfaces.removeAll(iface); } // We don't know which property belongs to which interface, so remove all m_propertyCache.clear(); if (!m_interfaces.isEmpty()) { allProperties(); } } diff --git a/src/solid/devices/backends/udisks2/udisksmanager.cpp b/src/solid/devices/backends/udisks2/udisksmanager.cpp index 1783391..4792d2f 100644 --- a/src/solid/devices/backends/udisks2/udisksmanager.cpp +++ b/src/solid/devices/backends/udisks2/udisksmanager.cpp @@ -1,299 +1,300 @@ /* Copyright 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 "udisksmanager.h" #include "udisksdevicebackend.h" +#include "udisks_debug.h" #include #include #include #include "../shared/rootdevice.h" using namespace Solid::Backends::UDisks2; using namespace Solid::Backends::Shared; Manager::Manager(QObject *parent) : Solid::Ifaces::DeviceManager(parent), m_manager(UD2_DBUS_SERVICE, UD2_DBUS_PATH, QDBusConnection::systemBus()) { m_supportedInterfaces << Solid::DeviceInterface::GenericInterface << Solid::DeviceInterface::Block << Solid::DeviceInterface::StorageAccess << Solid::DeviceInterface::StorageDrive << Solid::DeviceInterface::OpticalDrive << Solid::DeviceInterface::OpticalDisc << Solid::DeviceInterface::StorageVolume; qDBusRegisterMetaType >(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); bool serviceFound = m_manager.isValid(); if (!serviceFound) { // find out whether it will be activated automatically QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListActivatableNames"); QDBusReply reply = QDBusConnection::systemBus().call(message); if (reply.isValid() && reply.value().contains(UD2_DBUS_SERVICE)) { QDBusConnection::systemBus().interface()->startService(UD2_DBUS_SERVICE); serviceFound = true; } } if (serviceFound) { connect(&m_manager, SIGNAL(InterfacesAdded(QDBusObjectPath,VariantMapMap)), this, SLOT(slotInterfacesAdded(QDBusObjectPath,VariantMapMap))); connect(&m_manager, SIGNAL(InterfacesRemoved(QDBusObjectPath,QStringList)), this, SLOT(slotInterfacesRemoved(QDBusObjectPath,QStringList))); } } Manager::~Manager() { while (!m_deviceCache.isEmpty()) { QString udi = m_deviceCache.takeFirst(); DeviceBackend::destroyBackend(udi); } } QObject *Manager::createDevice(const QString &udi) { if (udi == udiPrefix()) { RootDevice *root = new RootDevice(udi); root->setProduct(tr("Storage")); root->setDescription(tr("Storage devices")); root->setIcon("server-database"); // Obviously wasn't meant for that, but maps nicely in oxygen icon set :-p return root; } else if (deviceCache().contains(udi)) { return new Device(udi); } else { return nullptr; } } QStringList Manager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type) { QStringList result; if (!parentUdi.isEmpty()) { Q_FOREACH (const QString &udi, deviceCache()) { Device device(udi); if (device.queryDeviceInterface(type) && device.parentUdi() == parentUdi) { result << udi; } } return result; } else if (type != Solid::DeviceInterface::Unknown) { Q_FOREACH (const QString &udi, deviceCache()) { Device device(udi); if (device.queryDeviceInterface(type)) { result << udi; } } return result; } return deviceCache(); } QStringList Manager::allDevices() { introspect("/org/freedesktop/UDisks2/block_devices", true /*checkOptical*/); introspect("/org/freedesktop/UDisks2/drives"); return m_deviceCache; } void Manager::introspect(const QString &path, bool checkOptical) { QDBusMessage call = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, path, DBUS_INTERFACE_INTROSPECT, "Introspect"); QDBusPendingReply reply = QDBusConnection::systemBus().call(call); 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 = path + "/" + nodeElem.attribute("name"); if (checkOptical) { Device device(udi); if (device.mightBeOpticalDisc()) { QDBusConnection::systemBus().connect(UD2_DBUS_SERVICE, udi, DBUS_INTERFACE_PROPS, "PropertiesChanged", this, SLOT(slotMediaChanged(QDBusMessage))); if (!device.isOpticalDisc()) { // skip empty CD disc continue; } } } m_deviceCache.append(udi); } } } else { - qWarning() << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message(); + qCWarning(UDISKS2) << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message(); } } QSet< Solid::DeviceInterface::Type > Manager::supportedInterfaces() const { return m_supportedInterfaces; } QString Manager::udiPrefix() const { return UD2_UDI_DISKS_PREFIX; } void Manager::slotInterfacesAdded(const QDBusObjectPath &object_path, const VariantMapMap &interfaces_and_properties) { const QString udi = object_path.path(); /* Ignore jobs */ if (udi.startsWith(UD2_DBUS_PATH_JOBS)) { return; } - qDebug() << udi << "has new interfaces:" << interfaces_and_properties.keys(); + qCDebug(UDISKS2) << udi << "has new interfaces:" << interfaces_and_properties.keys(); updateBackend(udi); // new device, we don't know it yet if (!m_deviceCache.contains(udi)) { m_deviceCache.append(udi); emit deviceAdded(udi); } // re-emit in case of 2-stage devices like N9 or some Android phones else if (m_deviceCache.contains(udi) && interfaces_and_properties.keys().contains(UD2_DBUS_INTERFACE_FILESYSTEM)) { emit deviceAdded(udi); } } void Manager::slotInterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces) { const QString udi = object_path.path(); if (udi.isEmpty()) { return; } /* Ignore jobs */ if (udi.startsWith(UD2_DBUS_PATH_JOBS)) { return; } - qDebug() << udi << "lost interfaces:" << interfaces; + qCDebug(UDISKS2) << udi << "lost interfaces:" << interfaces; /* * Determine left interfaces. The device backend may have processed the * InterfacesRemoved signal already, but the result set is the same * independent if the backend or the manager processes the signal first. */ Device device(udi); auto leftInterfaces = device.interfaces().toSet(); leftInterfaces.subtract(interfaces.toSet()); if (leftInterfaces.isEmpty()) { // remove the device if the last interface is removed emit deviceRemoved(udi); m_deviceCache.removeAll(udi); DeviceBackend::destroyBackend(udi); } else { /* * Changes in the interface composition may change if a device * matches a Predicate. We have to do a remove-and-readd cycle * as there is no dedicated signal for Predicate reevaluation. */ emit deviceRemoved(udi); emit deviceAdded(udi); } } void Manager::slotMediaChanged(const QDBusMessage &msg) { const QVariantMap properties = qdbus_cast(msg.arguments().at(1)); if (!properties.contains("Size")) { // react only on Size changes return; } const QString udi = msg.path(); updateBackend(udi); qulonglong size = properties.value("Size").toULongLong(); - qDebug() << "MEDIA CHANGED in" << udi << "; size is:" << size; + qCDebug(UDISKS2) << "MEDIA CHANGED in" << udi << "; size is:" << size; if (!m_deviceCache.contains(udi) && size > 0) { // we don't know the optdisc, got inserted m_deviceCache.append(udi); emit deviceAdded(udi); } if (m_deviceCache.contains(udi) && size == 0) { // we know the optdisc, got removed emit deviceRemoved(udi); m_deviceCache.removeAll(udi); DeviceBackend::destroyBackend(udi); } } const QStringList &Manager::deviceCache() { if (m_deviceCache.isEmpty()) { allDevices(); } return m_deviceCache; } void Manager::updateBackend(const QString &udi) { DeviceBackend *backend = DeviceBackend::backendForUDI(udi); if (!backend) { return; } //This doesn't emit "changed" signals. Signals are emitted later by DeviceBackend's slots backend->allProperties(); QVariant driveProp = backend->prop("Drive"); if (!driveProp.isValid()) { return; } QDBusObjectPath drivePath = qdbus_cast(driveProp); DeviceBackend *driveBackend = DeviceBackend::backendForUDI(drivePath.path(), false); if (!driveBackend) { return; } driveBackend->invalidateProperties(); } diff --git a/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp b/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp index a03166d..3ad58dd 100644 --- a/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp +++ b/src/solid/devices/backends/udisks2/udisksopticaldisc.cpp @@ -1,431 +1,434 @@ /* 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 #include #include #include #include #include #include #include #include #include "../shared/udevqt.h" #include "udisks2.h" +#include "udisks_debug.h" #include "udisksopticaldisc.h" #include "soliddefs_p.h" // inspired by http://cgit.freedesktop.org/hal/tree/hald/linux/probing/probe-volume.c static Solid::OpticalDisc::ContentType advancedDiscDetect(const QByteArray &device_file) { /* the discs block size */ unsigned short bs; /* the path table size */ unsigned short ts; /* the path table location (in blocks) */ unsigned int tl; /* length of the directory name in current path table entry */ unsigned char len_di = 0; /* the number of the parent directory's path table entry */ unsigned int parent = 0; /* filename for the current path table entry */ char dirname[256]; /* our position into the path table */ int pos = 0; /* the path table record we're on */ int curr_record = 1; + /* import debug category */ + using Solid::Backends::UDisks2::UDISKS2; Solid::OpticalDisc::ContentType result = Solid::OpticalDisc::NoContent; int fd = open(device_file.constData(), O_RDONLY); /* read the block size */ lseek(fd, 0x8080, SEEK_CUR); if (read(fd, &bs, 2) != 2) { - qDebug("Advanced probing on %s failed while reading block size", qPrintable(device_file)); + qCDebug(UDISKS2, "Advanced probing on %s failed while reading block size", qPrintable(device_file)); goto out; } /* read in size of path table */ lseek(fd, 2, SEEK_CUR); if (read(fd, &ts, 2) != 2) { - qDebug("Advanced probing on %s failed while reading path table size", qPrintable(device_file)); + qCDebug(UDISKS2, "Advanced probing on %s failed while reading path table size", qPrintable(device_file)); goto out; } /* read in which block path table is in */ lseek(fd, 6, SEEK_CUR); if (read(fd, &tl, 4) != 4) { - qDebug("Advanced probing on %s failed while reading path table block", qPrintable(device_file)); + qCDebug(UDISKS2, "Advanced probing on %s failed while reading path table block", qPrintable(device_file)); goto out; } /* seek to the path table */ lseek(fd, bs * tl, SEEK_SET); /* loop through the path table entries */ while (pos < ts) { /* get the length of the filename of the current entry */ if (read(fd, &len_di, 1) != 1) { - qDebug("Advanced probing on %s failed, cannot read more entries", qPrintable(device_file)); + qCDebug(UDISKS2, "Advanced probing on %s failed, cannot read more entries", qPrintable(device_file)); break; } /* get the record number of this entry's parent i'm pretty sure that the 1st entry is always the top directory */ lseek(fd, 5, SEEK_CUR); if (read(fd, &parent, 2) != 2) { - qDebug("Advanced probing on %s failed, couldn't read parent entry", qPrintable(device_file)); + qCDebug(UDISKS2, "Advanced probing on %s failed, couldn't read parent entry", qPrintable(device_file)); break; } /* read the name */ if (read(fd, dirname, len_di) != len_di) { - qDebug("Advanced probing on %s failed, couldn't read the entry name", qPrintable(device_file)); + qCDebug(UDISKS2, "Advanced probing on %s failed, couldn't read the entry name", qPrintable(device_file)); break; } dirname[len_di] = 0; /* if we found a folder that has the root as a parent, and the directory name matches one of the special directories then set the properties accordingly */ if (parent == 1) { if (!strcasecmp(dirname, "VIDEO_TS")) { - qDebug("Disc in %s is a Video DVD", qPrintable(device_file)); + qCDebug(UDISKS2, "Disc in %s is a Video DVD", qPrintable(device_file)); result = Solid::OpticalDisc::VideoDvd; break; } else if (!strcasecmp(dirname, "BDMV")) { - qDebug("Disc in %s is a Blu-ray video disc", qPrintable(device_file)); + qCDebug(UDISKS2, "Disc in %s is a Blu-ray video disc", qPrintable(device_file)); result = Solid::OpticalDisc::VideoBluRay; break; } else if (!strcasecmp(dirname, "VCD")) { - qDebug("Disc in %s is a Video CD", qPrintable(device_file)); + qCDebug(UDISKS2, "Disc in %s is a Video CD", qPrintable(device_file)); result = Solid::OpticalDisc::VideoCd; break; } else if (!strcasecmp(dirname, "SVCD")) { - qDebug("Disc in %s is a Super Video CD", qPrintable(device_file)); + qCDebug(UDISKS2, "Disc in %s is a Super Video CD", qPrintable(device_file)); result = Solid::OpticalDisc::SuperVideoCd; break; } } /* all path table entries are padded to be even, so if this is an odd-length table, seek a byte to fix it */ if (len_di % 2 == 1) { lseek(fd, 1, SEEK_CUR); pos++; } /* update our position */ pos += 8 + len_di; curr_record++; } close(fd); return result; out: /* go back to the start of the file */ lseek(fd, 0, SEEK_SET); close(fd); return result; } using namespace Solid::Backends::UDisks2; class ContentTypesCache { public: ContentTypesCache() : m_n(0) { } void add(const OpticalDisc::Identity &key, Solid::OpticalDisc::ContentTypes content) { if (!find(key)) { m_n = qMin(m_n + 1, sizeof(m_info) / sizeof(*m_info)); moveToFront(m_n - 1); front().first = key; } front().second = content; } bool find(const OpticalDisc::Identity &key) { for (size_t i = 0; i < m_n; i++) { if (m_info[i].first == key) { moveToFront(i); return true; } } return false; } QPair &front() { return *m_info; } private: void moveToFront(size_t i) { while (i) { qSwap(m_info[i - 1], m_info[i]); --i; } } size_t m_n; QPair m_info[100]; }; class SharedContentTypesCache { private: ContentTypesCache *m_pointer; QSystemSemaphore m_semaphore; QSharedMemory m_shmem; struct Unlocker { public: Unlocker(QSharedMemory *mem) : m_mem(mem) { } ~Unlocker() { m_mem->unlock(); } private: QSharedMemory *m_mem; }; struct Releaser { public: Releaser(QSystemSemaphore *sem) : m_sem(sem) { } ~Releaser() { m_sem->release(); } private: QSystemSemaphore *m_sem; }; static QString getKey() { static const QString keyTemplate("solid-disk-info-1-%1-%2"); static const QString tableSize( QString::number(sizeof(ContentTypesCache))); return keyTemplate.arg(tableSize, QString::number(geteuid())); } public: SharedContentTypesCache() : m_pointer(nullptr), m_semaphore(getKey() + "sem", 1), m_shmem(getKey() + "mem") { if (!m_semaphore.acquire()) { return; } Releaser releaser(&m_semaphore); if (m_shmem.attach()) { m_pointer = reinterpret_cast(m_shmem.data()); return; } if (!m_shmem.create(sizeof(ContentTypesCache))) { return; } if (!m_shmem.lock()) { m_shmem.detach(); return; } Unlocker unlocker(&m_shmem); m_pointer = new(m_shmem.data()) ContentTypesCache; } Solid::OpticalDisc::ContentTypes getContent(const OpticalDisc::Identity &info, const QByteArray &file) { if (!m_pointer) { return advancedDiscDetect(file); } if (!m_semaphore.acquire()) { return advancedDiscDetect(file); } Releaser releaser(&m_semaphore); if (!m_shmem.lock()) { return advancedDiscDetect(file); } Unlocker unlocker(&m_shmem); if (!m_pointer->find(info)) { m_pointer->add(info, advancedDiscDetect(file)); } Solid::OpticalDisc::ContentTypes content = m_pointer->front().second; return content; } ~SharedContentTypesCache() { m_semaphore.acquire(); Releaser releaser(&m_semaphore); m_shmem.detach(); } }; Q_GLOBAL_STATIC(SharedContentTypesCache, sharedContentTypesCache) OpticalDisc::Identity::Identity() : m_detectTime(0), m_size(0), m_labelHash(0) { } OpticalDisc::Identity::Identity(const Device &device, const Device &drive) : m_detectTime(drive.prop("TimeMediaDetected").toLongLong()), m_size(device.prop("Size").toLongLong()), m_labelHash(qHash(device.prop("IdLabel").toString())) { } bool OpticalDisc::Identity::operator ==(const OpticalDisc::Identity &b) const { return m_detectTime == b.m_detectTime && m_size == b.m_size && m_labelHash == b.m_labelHash; } OpticalDisc::OpticalDisc(Device *dev) : StorageVolume(dev) { #if UDEV_FOUND UdevQt::Client client(this); m_udevDevice = client.deviceByDeviceFile(device()); //qDebug() << "udev device:" << m_udevDevice.name() << "valid:" << m_udevDevice.isValid(); /*qDebug() << "\tProperties:" << */ m_udevDevice.deviceProperties(); // initialize the properties DB so that it doesn't crash further down, #298416 #endif m_drive = new Device(m_device->drivePath()); } OpticalDisc::~OpticalDisc() { delete m_drive; } qulonglong OpticalDisc::capacity() const { return m_device->prop("Size").toULongLong(); } bool OpticalDisc::isRewritable() const { // the hard way, udisks has no notion of a disc "rewritability" const QString mediaType = media(); return mediaType == "optical_cd_rw" || mediaType == "optical_dvd_rw" || mediaType == "optical_dvd_ram" || mediaType == "optical_dvd_plus_rw" || mediaType == "optical_dvd_plus_rw_dl" || mediaType == "optical_bd_re" || mediaType == "optical_hddvd_rw"; } bool OpticalDisc::isBlank() const { return m_drive->prop("OpticalBlank").toBool(); } bool OpticalDisc::isAppendable() const { //qDebug() << "appendable prop" << m_udevDevice.deviceProperty("ID_CDROM_MEDIA_STATE"); #if UDEV_FOUND return m_udevDevice.deviceProperty("ID_CDROM_MEDIA_STATE").toString() == QLatin1String("appendable"); #elif defined(Q_OS_FREEBSD) return m_device->prop("bsdisks_IsAppendable").toBool(); #else #error Implement this or stub this out for your platform #endif } Solid::OpticalDisc::DiscType OpticalDisc::discType() const { QMap map; map[Solid::OpticalDisc::CdRom] = "optical_cd"; map[Solid::OpticalDisc::CdRecordable] = "optical_cd_r"; map[Solid::OpticalDisc::CdRewritable] = "optical_cd_rw"; map[Solid::OpticalDisc::DvdRom] = "optical_dvd"; map[Solid::OpticalDisc::DvdRecordable] = "optical_dvd_r"; map[Solid::OpticalDisc::DvdRewritable] = "optical_dvd_rw"; map[Solid::OpticalDisc::DvdRam] = "optical_dvd_ram"; map[Solid::OpticalDisc::DvdPlusRecordable] = "optical_dvd_plus_r"; map[Solid::OpticalDisc::DvdPlusRewritable] = "optical_dvd_plus_rw"; map[Solid::OpticalDisc::DvdPlusRecordableDuallayer] = "optical_dvd_plus_r_dl"; map[Solid::OpticalDisc::DvdPlusRewritableDuallayer] = "optical_dvd_plus_rw_dl"; map[Solid::OpticalDisc::BluRayRom] = "optical_bd"; map[Solid::OpticalDisc::BluRayRecordable] = "optical_bd_r"; map[Solid::OpticalDisc::BluRayRewritable] = "optical_bd_re"; map[Solid::OpticalDisc::HdDvdRom] = "optical_hddvd"; map[Solid::OpticalDisc::HdDvdRecordable] = "optical_hddvd_r"; map[Solid::OpticalDisc::HdDvdRewritable] = "optical_hddvd_rw"; // TODO add these to Solid //map[Solid::OpticalDisc::MagnetoOptical] ="optical_mo"; //map[Solid::OpticalDisc::MountRainer] ="optical_mrw"; //map[Solid::OpticalDisc::MountRainerWritable] ="optical_mrw_w"; return map.key(media(), Solid::OpticalDisc::UnknownDiscType); // FIXME optimize, lookup by value, not key } Solid::OpticalDisc::ContentTypes OpticalDisc::availableContent() const { if (isBlank()) { return Solid::OpticalDisc::NoContent; } Solid::OpticalDisc::ContentTypes content = Solid::OpticalDisc::NoContent; const bool hasData = m_drive->prop("OpticalNumDataTracks").toUInt() > 0; const bool hasAudio = m_drive->prop("OpticalNumAudioTracks").toUInt() > 0; if (hasData) { content |= Solid::OpticalDisc::Data; Identity newIdentity(*m_device, *m_drive); if (!(m_identity == newIdentity)) { QByteArray deviceFile(m_device->prop("Device").toByteArray()); m_cachedContent = sharedContentTypesCache->getContent(newIdentity, deviceFile); m_identity = newIdentity; } content |= m_cachedContent; } if (hasAudio) { content |= Solid::OpticalDisc::Audio; } return content; } QString OpticalDisc::media() const { return m_drive->prop("Media").toString(); } diff --git a/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp b/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp index d74fd7a..8505c7f 100644 --- a/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp +++ b/src/solid/devices/backends/udisks2/udisksopticaldrive.cpp @@ -1,228 +1,229 @@ /* 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 #include #include #include #include #include #include #include #include +#include "udisks_debug.h" #include "udisksopticaldrive.h" #include "udisks2.h" #include "udisksdevice.h" #include "dbus/manager.h" using namespace Solid::Backends::UDisks2; OpticalDrive::OpticalDrive(Device *device) : StorageDrive(device) , m_ejectInProgress(false) , m_readSpeed(0) , m_writeSpeed(0) , m_speedsInit(false) { m_device->registerAction("eject", this, SLOT(slotEjectRequested()), SLOT(slotEjectDone(int,QString))); connect(m_device, SIGNAL(changed()), this, SLOT(slotChanged())); } OpticalDrive::~OpticalDrive() { } bool OpticalDrive::eject() { if (m_ejectInProgress) { return false; } m_ejectInProgress = true; m_device->broadcastActionRequested("eject"); const QString path = m_device->udi(); QDBusConnection c = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "Solid::Udisks2::OpticalDrive::" + path); // if the device is mounted, unmount first QString blockPath; org::freedesktop::DBus::ObjectManager manager(UD2_DBUS_SERVICE, UD2_DBUS_PATH, c); QDBusPendingReply reply = manager.GetManagedObjects(); reply.waitForFinished(); if (!reply.isError()) { // enum devices Q_FOREACH (const QDBusObjectPath &objPath, reply.value().keys()) { const QString udi = objPath.path(); //qDebug() << "Inspecting" << udi; if (udi == UD2_DBUS_PATH_MANAGER || udi == UD2_UDI_DISKS_PREFIX || udi.startsWith(UD2_DBUS_PATH_JOBS)) { continue; } Device device(udi); if (device.drivePath() == path && device.isMounted()) { //qDebug() << "Got mounted block device:" << udi; blockPath = udi; break; } } } else { // show error - qWarning() << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message(); + qCWarning(UDISKS2) << "Failed enumerating UDisks2 objects:" << reply.error().name() << "\n" << reply.error().message(); } if (!blockPath.isEmpty()) { //qDebug() << "Calling unmount on" << blockPath; QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, blockPath, UD2_DBUS_INTERFACE_FILESYSTEM, "Unmount"); msg << QVariantMap(); // options, unused now c.call(msg, QDBus::BlockWithGui); } QDBusMessage msg = QDBusMessage::createMethodCall(UD2_DBUS_SERVICE, path, UD2_DBUS_INTERFACE_DRIVE, "Eject"); msg << QVariantMap(); return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError))); } void OpticalDrive::slotDBusReply(const QDBusMessage &/*reply*/) { m_ejectInProgress = false; m_device->broadcastActionDone("eject"); } void OpticalDrive::slotDBusError(const QDBusError &error) { m_ejectInProgress = false; m_device->broadcastActionDone("eject", m_device->errorToSolidError(error.name()), m_device->errorToString(error.name()) + ": " + error.message()); } void OpticalDrive::slotEjectRequested() { m_ejectInProgress = true; emit ejectRequested(m_device->udi()); } void OpticalDrive::slotEjectDone(int error, const QString &errorString) { m_ejectInProgress = false; emit ejectDone(static_cast(error), errorString, m_device->udi()); } void OpticalDrive::initReadWriteSpeeds() const { #if 0 int read_speed, write_speed; char *write_speeds = 0; QByteArray device_file = QFile::encodeName(m_device->property("Device").toString()); //qDebug("Doing open (\"%s\", O_RDONLY | O_NONBLOCK)", device_file.constData()); int fd = open(device_file, O_RDONLY | O_NONBLOCK); if (fd < 0) { qWarning("Cannot open %s: %s", device_file.constData(), strerror(errno)); return; } if (get_read_write_speed(fd, &read_speed, &write_speed, &write_speeds) >= 0) { m_readSpeed = read_speed; m_writeSpeed = write_speed; QStringList list = QString::fromLatin1(write_speeds).split(',', QString::SkipEmptyParts); Q_FOREACH (const QString &speed, list) { m_writeSpeeds.append(speed.toInt()); } free(write_speeds); m_speedsInit = true; } close(fd); #endif } QList OpticalDrive::writeSpeeds() const { if (!m_speedsInit) { initReadWriteSpeeds(); } //qDebug() << "solid write speeds:" << m_writeSpeeds; return m_writeSpeeds; } int OpticalDrive::writeSpeed() const { if (!m_speedsInit) { initReadWriteSpeeds(); } return m_writeSpeed; } int OpticalDrive::readSpeed() const { if (!m_speedsInit) { initReadWriteSpeeds(); } return m_readSpeed; } Solid::OpticalDrive::MediumTypes OpticalDrive::supportedMedia() const { const QStringList mediaTypes = m_device->prop("MediaCompatibility").toStringList(); Solid::OpticalDrive::MediumTypes supported; QMap map; map[Solid::OpticalDrive::Cdr] = "optical_cd_r"; map[Solid::OpticalDrive::Cdrw] = "optical_cd_rw"; map[Solid::OpticalDrive::Dvd] = "optical_dvd"; map[Solid::OpticalDrive::Dvdr] = "optical_dvd_r"; map[Solid::OpticalDrive::Dvdrw] = "optical_dvd_rw"; map[Solid::OpticalDrive::Dvdram] = "optical_dvd_ram"; map[Solid::OpticalDrive::Dvdplusr] = "optical_dvd_plus_r"; map[Solid::OpticalDrive::Dvdplusrw] = "optical_dvd_plus_rw"; map[Solid::OpticalDrive::Dvdplusdl] = "optical_dvd_plus_r_dl"; map[Solid::OpticalDrive::Dvdplusdlrw] = "optical_dvd_plus_rw_dl"; map[Solid::OpticalDrive::Bd] = "optical_bd"; map[Solid::OpticalDrive::Bdr] = "optical_bd_r"; map[Solid::OpticalDrive::Bdre] = "optical_bd_re"; map[Solid::OpticalDrive::HdDvd] = "optical_hddvd"; map[Solid::OpticalDrive::HdDvdr] = "optical_hddvd_r"; map[Solid::OpticalDrive::HdDvdrw] = "optical_hddvd_rw"; // TODO add these to Solid //map[Solid::OpticalDrive::Mo] ="optical_mo"; //map[Solid::OpticalDrive::Mr] ="optical_mrw"; //map[Solid::OpticalDrive::Mrw] ="optical_mrw_w"; Q_FOREACH (const Solid::OpticalDrive::MediumType &type, map.keys()) { if (mediaTypes.contains(map[type])) { supported |= type; } } return supported; } void OpticalDrive::slotChanged() { m_speedsInit = false; // reset the read/write speeds, changes eg. with an inserted media } diff --git a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp index d08f35d..fb97296 100644 --- a/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp +++ b/src/solid/devices/backends/udisks2/udisksstorageaccess.cpp @@ -1,394 +1,395 @@ /* 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 "udisks_debug.h" #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(); 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; 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(); + qCWarning(UDISKS2) << "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))); }