diff --git a/CMakeLists.txt b/CMakeLists.txt index c4bb3e5..7ce346e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS # Load the frameworks we need find_package(KF5 REQUIRED + Auth I18n IconThemes KIO diff --git a/src/backend/corebackend.h b/src/backend/corebackend.h index 35d7367..b873641 100644 --- a/src/backend/corebackend.h +++ b/src/backend/corebackend.h @@ -58,10 +58,10 @@ Q_SIGNALS: /** * Emitted to inform about scan progress. - * @param device_node the device being scanned just now (e.g. "/dev/sda") + * @param deviceNode the device being scanned just now (e.g. "/dev/sda") * @param i the progress in percent (from 0 to 100) */ - void scanProgress(const QString& device_node, int i); + void scanProgress(const QString& deviceNode, int i); public: /** @@ -101,29 +101,29 @@ public: /** * Scan a single device in the system. - * @param device_node The path to the device that is to be scanned (e.g. /dev/sda) + * @param deviceNode The path to the device that is to be scanned (e.g. /dev/sda) * @return a pointer to a Device instance. The caller is responsible for deleting * this object. */ - virtual Device* scanDevice(const QString& device_node) = 0; + virtual Device* scanDevice(const QString& deviceNode) = 0; /** * Open a device for reading. - * @param device_node The path of the device that is to be opened (e.g. /dev/sda) + * @param deviceNode The path of the device that is to be opened (e.g. /dev/sda) * @return a pointer to a CoreBackendDevice or nullptr if the open failed. If a pointer to * an instance is returned, it's the caller's responsibility to delete the * object. */ - virtual CoreBackendDevice* openDevice(const QString& device_node) = 0; + virtual CoreBackendDevice* openDevice(const QString& deviceNode) = 0; /** * Open a device in exclusive mode for writing. - * @param device_node The path of the device that is to be opened (e.g. /dev/sda) + * @param deviceNode The path of the device that is to be opened (e.g. /dev/sda) * @return a pointer to a CoreBackendDevice or nullptr if the open failed. If a pointer to * an instance is returned, it's the caller's responsibility to delete the * object. */ - virtual CoreBackendDevice* openDeviceExclusive(const QString& device_node) = 0; + virtual CoreBackendDevice* openDeviceExclusive(const QString& deviceNode) = 0; /** * Close a CoreBackendDevice that has previously been opened. @@ -144,12 +144,12 @@ public: /** * Emit scan progress. - * @param device_node the path to the device just being scanned (e.g. /dev/sda) + * @param deviceNode the path to the device just being scanned (e.g. /dev/sda) * @param i the progress in percent (from 0 to 100) * This is used to emit a scanProgress() signal from the backend device scanning * code. */ - virtual void emitScanProgress(const QString& device_node, int i); + virtual void emitScanProgress(const QString& deviceNode, int i); protected: static void setPartitionTableForDevice(Device& d, PartitionTable* p); diff --git a/src/core/partitiontable.h b/src/core/partitiontable.h index a0ddeef..0881c08 100644 --- a/src/core/partitiontable.h +++ b/src/core/partitiontable.h @@ -91,6 +91,7 @@ public: }; Q_DECLARE_FLAGS(Flags, Flag) + Q_FLAG(Flag) public: PartitionTable(TableType type, qint64 first_usable, qint64 last_usable); diff --git a/src/plugins/libparted/CMakeLists.txt b/src/plugins/libparted/CMakeLists.txt index aca131e..d1a1858 100644 --- a/src/plugins/libparted/CMakeLists.txt +++ b/src/plugins/libparted/CMakeLists.txt @@ -34,8 +34,10 @@ set (pmlibpartedbackendplugin_SRCS add_library(pmlibpartedbackendplugin SHARED ${pmlibpartedbackendplugin_SRCS}) -target_link_libraries(pmlibpartedbackendplugin kpmcore ${LIBPARTED_LIBS} KF5::KIOCore KF5::I18n) +target_link_libraries(pmlibpartedbackendplugin kpmcore ${LIBPARTED_LIBS} KF5::Auth KF5::KIOCore KF5::I18n) install(TARGETS pmlibpartedbackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) kcoreaddons_desktop_to_json(pmlibpartedbackendplugin pmlibpartedbackendplugin.desktop) install(FILES pmlibpartedbackendplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + +add_subdirectory(helpers) diff --git a/src/plugins/libparted/helpers/CMakeLists.txt b/src/plugins/libparted/helpers/CMakeLists.txt new file mode 100644 index 0000000..fe13dd7 --- /dev/null +++ b/src/plugins/libparted/helpers/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(kpmcore_scan scan.cpp) +target_link_libraries(kpmcore_scan ${LIBPARTED_LIBS} KF5::Auth) +install(TARGETS kpmcore_scan DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) + +kauth_install_helper_files(kpmcore_scan org.kde.kpmcore.scan root) +kauth_install_actions(org.kde.kpmcore.scan org.kde.kpmcore.scan.actions) diff --git a/src/plugins/libparted/helpers/org.kde.kpmcore.scan.actions b/src/plugins/libparted/helpers/org.kde.kpmcore.scan.actions new file mode 100644 index 0000000..992d285 --- /dev/null +++ b/src/plugins/libparted/helpers/org.kde.kpmcore.scan.actions @@ -0,0 +1,17 @@ +[org.kde.kpmcore.scan.scandevices] +Name=Scan devices action +Description=Scan available devices +Policy=yes +Persistence=session + +[org.kde.kpmcore.scan.scandevice] +Name=Scan device action +Description=Scan device +Policy=yes +Persistence=session + +[org.kde.kpmcore.scan.readsectorsused] +Name=Read used sectors action +Description=Read used sectors +Policy=yes +Persistence=session diff --git a/src/plugins/libparted/helpers/scan.cpp b/src/plugins/libparted/helpers/scan.cpp new file mode 100644 index 0000000..2a26e96 --- /dev/null +++ b/src/plugins/libparted/helpers/scan.cpp @@ -0,0 +1,198 @@ +/************************************************************************* + * Copyright (C) 2016 by Andrius Štikonas * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#include "scan.h" +#include "core/partitiontable.h" +#include "plugins/libparted/pedflags.h" + +ActionReply Scan::scandevices(const QVariantMap& args) +{ + ActionReply reply; + + ped_device_probe_all(); + PedDevice* pedDevice = nullptr; + QList paths; + bool excludeReadOnly = args[QLatin1String("excludeReadOnly")].toBool(); + + while (true) { + pedDevice = ped_device_get_next(pedDevice); + if (!pedDevice) + break; + if (pedDevice->type == PED_DEVICE_DM) + continue; + if (excludeReadOnly && ( + pedDevice->type == PED_DEVICE_LOOP || + pedDevice->read_only)) + continue; + + paths.append(QString::fromUtf8(pedDevice->path)); + } + + QVariantMap returnArgs; + returnArgs[QLatin1String("paths")] = paths; + reply.setData(returnArgs); + return reply; +} + +ActionReply Scan::scandevice(const QVariantMap& args) +{ + ActionReply reply; + + QString deviceNode = args[QLatin1String("deviceNode")].toString(); + PedDevice* pedDevice = ped_device_get(deviceNode.toLocal8Bit().constData()); + + QVariantMap returnArgs; + if (!pedDevice) { + returnArgs[QLatin1String("pedDeviceError")] = true; + reply.setData(returnArgs); + return reply; + } + + returnArgs[QLatin1String("model")] = QString::fromUtf8(pedDevice->model); + returnArgs[QLatin1String("path")] = QString::fromUtf8(pedDevice->path); + returnArgs[QLatin1String("heads")] = pedDevice->bios_geom.heads; + returnArgs[QLatin1String("sectors")] = pedDevice->bios_geom.sectors; + returnArgs[QLatin1String("cylinders")] = pedDevice->bios_geom.cylinders; + returnArgs[QLatin1String("sectorSize")] = pedDevice->sector_size; + + PedDisk* pedDisk = ped_disk_new(pedDevice); + + if (!pedDisk) { + returnArgs[QLatin1String("pedDiskError")] = true; + reply.setData(returnArgs); + return reply; + } + + quint64 firstUsableSector = pedDisk->dev->bios_geom.sectors; + + if (strcmp(pedDisk->type->name, "gpt") == 0) { + GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); + PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); + + if (geom) + firstUsableSector = geom->start; + else + firstUsableSector += 32; + } + + quint64 lastUsableSector = 0; + lastUsableSector = static_cast< quint64 >( pedDisk->dev->bios_geom.sectors ) * + pedDisk->dev->bios_geom.heads * + pedDisk->dev->bios_geom.cylinders - 1; + + if (strcmp(pedDisk->type->name, "gpt") == 0) { + GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); + PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); + + if (geom) + lastUsableSector = geom->end; + else + lastUsableSector -= 32; + } + + returnArgs[QLatin1String("pedDeviceError")] = false; + returnArgs[QLatin1String("pedDiskError")] = false; + + returnArgs[QLatin1String("typeName")] = QString::fromUtf8(pedDisk->type->name); + returnArgs[QLatin1String("maxPrimaryPartitionCount")] = ped_disk_get_max_primary_partition_count(pedDisk); + returnArgs[QLatin1String("firstUsableSector")] = firstUsableSector; + returnArgs[QLatin1String("lastUsableSector")] = lastUsableSector; + + PedPartition* pedPartition = nullptr; + QList partitionPath; + QList partitionType; + QList partitionStart; + QList partitionEnd; + QList partitionBusy; + QList availableFlags; + QList activeFlags; + + while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { + if (pedPartition->num < 1) + continue; + + partitionPath.append(QLatin1String(ped_partition_get_path(pedPartition))); + partitionType.append(pedPartition->type); + partitionStart.append(pedPartition->geom.start); + partitionEnd.append(pedPartition->geom.end); + partitionBusy.append(ped_partition_is_busy(pedPartition)); + + // -------------------------------------------------------------------------- + // Get list of available flags + + PartitionTable::Flags flags; + + // We might get here with a pedPartition just picked up from libparted that is + // unallocated. Libparted doesn't like it if we ask for flags for unallocated + // space. + if (pedPartition->num > 0) + for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) + if (ped_partition_is_flag_available(pedPartition, flagmap[i].pedFlag)) + // Workaround: libparted claims the hidden flag is available for extended partitions, but + // throws an error when we try to set or clear it. So skip this combination. Also see setFlag. + if (pedPartition->type != PED_PARTITION_EXTENDED || flagmap[i].flag != PartitionTable::FlagHidden) + flags |= flagmap[i].flag; + + availableFlags.append(static_cast(flags)); + // -------------------------------------------------------------------------- + // Get list of active flags + + flags = PartitionTable::FlagNone; + if (pedPartition->num > 0) + for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) + if (ped_partition_is_flag_available(pedPartition, flagmap[i].pedFlag) && ped_partition_get_flag(pedPartition, flagmap[i].pedFlag)) + flags |= flagmap[i].flag; + + activeFlags.append(static_cast(flags)); + // -------------------------------------------------------------------------- + } + + returnArgs[QLatin1String("availableFlags")] = availableFlags; + returnArgs[QLatin1String("activeFlags")] = activeFlags; + returnArgs[QLatin1String("partitionPath")] = partitionPath; + returnArgs[QLatin1String("partitionType")] = partitionType; + returnArgs[QLatin1String("partitionStart")] = partitionStart; + returnArgs[QLatin1String("partitionEnd")] = partitionEnd; + returnArgs[QLatin1String("partitionBusy")] = partitionBusy; + + reply.setData(returnArgs); + return reply; +} + +ActionReply Scan::readsectorsused(const QVariantMap& args) +{ + ActionReply reply; + + QString deviceNode = args[QLatin1String("deviceNode")].toString(); + qint64 firstSector = args[QLatin1String("firstSector")].toLongLong(); + qint64 rval = -1; + + if (PedDevice* pedDevice = ped_device_get(deviceNode.toLocal8Bit().constData())) + if (PedDisk* pedDisk = ped_disk_new(pedDevice)) + if (PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, firstSector)) + if (PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom)) + if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem)) + rval = pedConstraint->min_size; + + QVariantMap returnArgs; + returnArgs[QLatin1String("sectorsUsed")] = rval; + + reply.setData(returnArgs); + return reply; +} + +KAUTH_HELPER_MAIN("org.kde.kpmcore.scan", Scan) diff --git a/src/plugins/libparted/helpers/scan.h b/src/plugins/libparted/helpers/scan.h new file mode 100644 index 0000000..cedf455 --- /dev/null +++ b/src/plugins/libparted/helpers/scan.h @@ -0,0 +1,66 @@ +/************************************************************************* + * Copyright (C) 2016 by Andrius Štikonas * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#if !defined(SCAN__H) + +#define SCAN__H + +#include +#include + +using namespace KAuth; + +class Scan : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + ActionReply scandevices(const QVariantMap& args); + ActionReply scandevice(const QVariantMap& args); + ActionReply readsectorsused(const QVariantMap& args); +}; + +// -------------------------------------------------------------------------- + +// The following structs and the typedef come from libparted's internal gpt sources. +// It's very unfortunate there is no public API to get at the first and last usable +// sector for GPT a partition table, so this is the only (libparted) way to get that +// information (another way would be to read the GPT header and parse the +// information ourselves; if the libparted devs begin changing these internal +// structs for each point release and break our code, we'll have to do just that). + +typedef struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} /* __attribute__ ((packed)) */ efi_guid_t; + + +struct __attribute__((packed)) _GPTDiskData { + PedGeometry data_area; + int entry_count; + efi_guid_t uuid; +}; + +typedef struct _GPTDiskData GPTDiskData; + +// -------------------------------------------------------------------------- + +#endif diff --git a/src/plugins/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp index 45e8c65..4574f63 100644 --- a/src/plugins/libparted/libpartedbackend.cpp +++ b/src/plugins/libparted/libpartedbackend.cpp @@ -22,6 +22,7 @@ #include "plugins/libparted/libpartedbackend.h" #include "plugins/libparted/libparteddevice.h" +#include "plugins/libparted/pedflags.h" #include "core/device.h" #include "core/partition.h" @@ -45,40 +46,16 @@ #include #include +#include #include #include #include #include -#include #include K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin();) -static struct { - PedPartitionFlag pedFlag; - PartitionTable::Flag flag; -} flagmap[] = { - { PED_PARTITION_BOOT, PartitionTable::FlagBoot }, - { PED_PARTITION_ROOT, PartitionTable::FlagRoot }, - { PED_PARTITION_SWAP, PartitionTable::FlagSwap }, - { PED_PARTITION_HIDDEN, PartitionTable::FlagHidden }, - { PED_PARTITION_RAID, PartitionTable::FlagRaid }, - { PED_PARTITION_LVM, PartitionTable::FlagLvm }, - { PED_PARTITION_LBA, PartitionTable::FlagLba }, - { PED_PARTITION_HPSERVICE, PartitionTable::FlagHpService }, - { PED_PARTITION_PALO, PartitionTable::FlagPalo }, - { PED_PARTITION_PREP, PartitionTable::FlagPrep }, - { PED_PARTITION_MSFT_RESERVED, PartitionTable::FlagMsftReserved }, - { PED_PARTITION_BIOS_GRUB, PartitionTable::FlagBiosGrub }, - { PED_PARTITION_APPLE_TV_RECOVERY, PartitionTable::FlagAppleTvRecovery }, - { PED_PARTITION_DIAG, PartitionTable::FlagDiag }, // generic diagnostics flag - { PED_PARTITION_LEGACY_BOOT, PartitionTable::FlagLegacyBoot }, - { PED_PARTITION_MSFT_DATA, PartitionTable::FlagMsftData }, - { PED_PARTITION_IRST, PartitionTable::FlagIrst }, // Intel Rapid Start partition - { PED_PARTITION_ESP, PartitionTable::FlagEsp } // EFI system -}; - static QString s_lastPartedExceptionMessage; /** Callback to handle exceptions from libparted @@ -91,133 +68,37 @@ static PedExceptionOption pedExceptionHandler(PedException* e) return PED_EXCEPTION_UNHANDLED; } -// -------------------------------------------------------------------------- - -// The following structs and the typedef come from libparted's internal gpt sources. -// It's very unfortunate there is no public API to get at the first and last usable -// sector for GPT a partition table, so this is the only (libparted) way to get that -// information (another way would be to read the GPT header and parse the -// information ourselves; if the libparted devs begin changing these internal -// structs for each point release and break our code, we'll have to do just that). - -typedef struct { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint8_t clock_seq_hi_and_reserved; - uint8_t clock_seq_low; - uint8_t node[6]; -} /* __attribute__ ((packed)) */ efi_guid_t; - - -struct __attribute__((packed)) _GPTDiskData { - PedGeometry data_area; - int entry_count; - efi_guid_t uuid; -}; - -typedef struct _GPTDiskData GPTDiskData; - -// -------------------------------------------------------------------------- - -/** Get the first sector a Partition may cover on a given Device - @param d the Device in question - @return the first sector usable by a Partition -*/ -static quint64 firstUsableSector(const Device& d) -{ - PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); - PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; - - quint64 rval = 0; - if (pedDisk) - rval = pedDisk->dev->bios_geom.sectors; - - if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { - GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); - PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); - - if (geom) - rval = geom->start; - else - rval += 32; - } - - ped_disk_destroy(pedDisk); - - return rval; -} - -/** Get the last sector a Partition may cover on a given Device - @param d the Device in question - @return the last sector usable by a Partition -*/ -static quint64 lastUsableSector(const Device& d) -{ - PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); - PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; - - quint64 rval = 0; - if (pedDisk) - rval = static_cast< quint64 >( pedDisk->dev->bios_geom.sectors ) * - pedDisk->dev->bios_geom.heads * - pedDisk->dev->bios_geom.cylinders - 1; - - if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { - GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); - PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); - - if (geom) - rval = geom->end; - else - rval -= 32; - } - - ped_disk_destroy(pedDisk); - - return rval; -} - /** Reads sectors used on a FileSystem using libparted functions. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @return the number of sectors used */ #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT -static qint64 readSectorsUsedLibParted(PedDisk* pedDisk, const Partition& p) +static qint64 readSectorsUsedLibParted(const Partition& p) { - Q_ASSERT(pedDisk); + QVariantMap args; + args[QLatin1String("deviceNode")] = p.deviceNode(); + args[QLatin1String("firstSector")] = p.firstSector(); - qint64 rval = -1; - - PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, p.firstSector()); - - if (pedPartition) { - PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom); - - if (pedFileSystem) { - if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem)) { - rval = pedConstraint->min_size; - ped_constraint_destroy(pedConstraint); - } - - ped_file_system_close(pedFileSystem); - } + KAuth::Action scanAction = QStringLiteral("org.kde.kpmcore.scan.readsectorsused"); + scanAction.setHelperId(QStringLiteral("org.kde.kpmcore.scan")); + scanAction.setArguments(args); + KAuth::ExecuteJob *job = scanAction.execute(); + if (!job->exec()) { + qWarning() << "KAuth returned an error code: " << job->errorString(); + return -1; } - return rval; + return job->data()[QLatin1String("sectorsUsed")].toLongLong(); } #endif /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. - @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ -static void readSectorsUsed(PedDisk* pedDisk, const Device& d, Partition& p, const QString& mountPoint) +static void readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint) { - Q_ASSERT(pedDisk); - const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (p.isMounted() && freeSpaceInfo.isValid() && mountPoint != QString()) @@ -225,49 +106,11 @@ static void readSectorsUsed(PedDisk* pedDisk, const Device& d, Partition& p, con else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSectorSize()); #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT - else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportCore) - p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(pedDisk, p)); -#else - Q_UNUSED(pedDisk); + else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportBackend) + p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(p)); #endif } -static PartitionTable::Flags activeFlags(PedPartition* p) -{ - PartitionTable::Flags flags = PartitionTable::FlagNone; - - // We might get here with a pedPartition just picked up from libparted that is - // unallocated. Libparted doesn't like it if we ask for flags for unallocated - // space. - if (p->num <= 0) - return flags; - - for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) - if (ped_partition_is_flag_available(p, flagmap[i].pedFlag) && ped_partition_get_flag(p, flagmap[i].pedFlag)) - flags |= flagmap[i].flag; - - return flags; -} - -static PartitionTable::Flags availableFlags(PedPartition* p) -{ - PartitionTable::Flags flags; - - // see above. - if (p->num <= 0) - return flags; - - for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) - if (ped_partition_is_flag_available(p, flagmap[i].pedFlag)) { - // Workaround: libparted claims the hidden flag is available for extended partitions, but - // throws an error when we try to set or clear it. So skip this combination. Also see setFlag. - if (p->type != PED_PARTITION_EXTENDED || flagmap[i].flag != PartitionTable::FlagHidden) - flags |= flagmap[i].flag; - } - - return flags; -} - /** Constructs a LibParted object. */ LibPartedBackend::LibPartedBackend(QObject*, const QList&) : CoreBackend() @@ -298,40 +141,78 @@ void LibPartedBackend::initFSSupport() #endif } -/** Scans a Device for Partitions. - - This method will scan a Device for all Partitions on it, detect the FileSystem for each Partition, - try to determine the FileSystem usage, read the FileSystem label and store it all in newly created - objects that are in the end added to the Device's PartitionTable. - - @param d Device - @param pedDisk libparted pointer to the partition table +/** Create a Device for the given deviceNode and scan it for partitions. + @param deviceNode the device node (e.g. "/dev/sda") + @return the created Device object. callers need to free this. */ -void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) +Device* LibPartedBackend::scanDevice(const QString& deviceNode) { - Q_ASSERT(pedDisk); - Q_ASSERT(d.partitionTable()); + QVariantMap args; + args[QLatin1String("deviceNode")] = deviceNode; - PedPartition* pedPartition = nullptr; + KAuth::Action scanAction = QStringLiteral("org.kde.kpmcore.scan.scandevice"); + scanAction.setHelperId(QStringLiteral("org.kde.kpmcore.scan")); + scanAction.setArguments(args); + KAuth::ExecuteJob *job = scanAction.execute(); + if (!job->exec()) { + qWarning() << "KAuth returned an error code: " << job->errorString(); + return nullptr; + } + + bool pedDeviceError = job->data()[QLatin1String("pedDeviceError")].toBool(); + + if (pedDeviceError) { + Log(Log::warning) << xi18nc("@info:status", "Could not access device %1", deviceNode); + return nullptr; + } + + QString model = job->data()[QLatin1String("model")].toString(); + QString path = job->data()[QLatin1String("path")].toString(); + int heads = job->data()[QLatin1String("heads")].toInt(); + int sectors = job->data()[QLatin1String("sectors")].toInt(); + int cylinders = job->data()[QLatin1String("cylinders")].toInt(); + int sectorSize = job->data()[QLatin1String("sectorSize")].toInt(); + bool pedDiskError = job->data()[QLatin1String("pedDiskError")].toBool(); + + Log(Log::information) << xi18nc("@info:status", "Device found: %1", model); + + Device* d = new Device(model, path, heads, sectors, cylinders, sectorSize); + + if (pedDiskError) + return d; + + QString typeName = job->data()[QLatin1String("typeName")].toString(); + qint32 maxPrimaryPartitionCount = job->data()[QLatin1String("maxPrimaryPartitionCount")].toInt(); + quint64 firstUsableSector = job->data()[QLatin1String("firstUsableSector")].toULongLong(); + quint64 lastUsableSector = job->data()[QLatin1String("lastUsableSector")].toULongLong(); + + const PartitionTable::TableType type = PartitionTable::nameToTableType(typeName); + CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector)); + CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxPrimaryPartitionCount); KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPoints.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); - QList partitions; + QList partitionPath = job->data()[QLatin1String("partitionPath")].toList(); + QList partitionType = job->data()[QLatin1String("partitionType")].toList(); + QList partitionStart = job->data()[QLatin1String("partitionStart")].toList(); + QList partitionEnd = job->data()[QLatin1String("partitionEnd")].toList(); + QList partitionBusy = job->data()[QLatin1String("partitionBusy")].toList(); - while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { - if (pedPartition->num < 1) - continue; + quint32 totalPartitions = partitionPath.size(); + QList partitions; + for (quint32 i = 0; i < totalPartitions; ++i) { + QString partitionNode = partitionPath[i].toString(); + int type = partitionType[i].toInt(); + qint64 start = partitionStart[i].toLongLong(); + qint64 end = partitionEnd[i].toLongLong(); + bool busy = partitionBusy[i].toBool(); PartitionRole::Roles r = PartitionRole::None; - FileSystem::Type type = FileSystem::Unknown; - char* pedPath = ped_partition_get_path(pedPartition); - if (pedPath) - type = detectFileSystem(QString::fromUtf8(pedPath)); - free(pedPath); + FileSystem::Type fsType = detectFileSystem(partitionNode); - switch (pedPartition->type) { + switch (type) { case PED_PARTITION_NORMAL: r = PartitionRole::Primary; break; @@ -350,30 +231,27 @@ void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) } // Find an extended partition this partition is in. - PartitionNode* parent = d.partitionTable()->findPartitionBySector(pedPartition->geom.start, PartitionRole(PartitionRole::Extended)); + PartitionNode* parent = d->partitionTable()->findPartitionBySector(start, PartitionRole(PartitionRole::Extended)); // None found, so it's a primary in the device's partition table. if (parent == nullptr) - parent = d.partitionTable(); + parent = d->partitionTable(); - pedPath = ped_partition_get_path(pedPartition); - const QString node = QString::fromUtf8(pedPath); - free(pedPath); - FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end); + FileSystem* fs = FileSystemFactory::create(fsType, start, end); // libparted does not handle LUKS partitions QString mountPoint; bool mounted = false; - if (type == FileSystem::Luks) { + if (fsType == FileSystem::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = dynamic_cast(fs); - QString mapperNode = FS::luks::mapperName(node); + QString mapperNode = FS::luks::mapperName(partitionNode); bool isCryptOpen = !mapperNode.isEmpty(); luksFs->setCryptOpen(isCryptOpen); - luksFs->setLogicalSectorSize(d.logicalSectorSize()); + luksFs->setLogicalSectorSize(d->logicalSectorSize()); if (isCryptOpen) { - luksFs->loadInnerFileSystem(node, mapperNode); + luksFs->loadInnerFileSystem(partitionNode, mapperNode); mountPoint = mountPoints.findByDevice(mapperNode) ? mountPoints.findByDevice(mapperNode)->mountPoint() : @@ -384,7 +262,7 @@ void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) if (mounted) { const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (freeSpaceInfo.isValid() && mountPoint != QString()) - luksFs->setSectorsUsed(freeSpaceInfo.used() / d.logicalSectorSize() + luksFs->getPayloadOffset(node)); + luksFs->setSectorsUsed(freeSpaceInfo.used() / d->logicalSectorSize() + luksFs->getPayloadOffset(partitionNode)); } } else { mounted = false; @@ -392,16 +270,20 @@ void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) luksFs->setMounted(mounted); } else { - mountPoint = mountPoints.findByDevice(node) ? - mountPoints.findByDevice(node)->mountPoint() : + mountPoint = mountPoints.findByDevice(partitionNode) ? + mountPoints.findByDevice(partitionNode)->mountPoint() : QString(); - mounted = ped_partition_is_busy(pedPartition); + mounted = busy; } - Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end, node, availableFlags(pedPartition), mountPoint, mounted, activeFlags(pedPartition)); + QList availableFlags = job->data()[QLatin1String("availableFlags")].toList(); + PartitionTable::Flags available = static_cast(availableFlags[i].toInt()); + QList activeFlags = job->data()[QLatin1String("activeFlags")].toList(); + PartitionTable::Flags active = static_cast(activeFlags[i].toInt()); + Partition* part = new Partition(parent, *d, PartitionRole(r), fs, start, end, partitionNode, available, mountPoint, mounted, active); if (!part->roles().has(PartitionRole::Luks)) - readSectorsUsed(pedDisk, d, *part, mountPoint); + readSectorsUsed(*d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); @@ -413,73 +295,39 @@ void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) partitions.append(part); } - d.partitionTable()->updateUnallocated(d); + d->partitionTable()->updateUnallocated(*d); - if (d.partitionTable()->isSectorBased(d)) - d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased); + if (d->partitionTable()->isSectorBased(*d)) + d->partitionTable()->setType(*d, PartitionTable::msdos_sectorbased); foreach(const Partition * part, partitions) - PartitionAlignment::isAligned(d, *part); + PartitionAlignment::isAligned(*d, *part); - ped_disk_destroy(pedDisk); -} -/** Create a Device for the given device_node and scan it for partitions. - @param device_node the device node (e.g. "/dev/sda") - @return the created Device object. callers need to free this. -*/ -Device* LibPartedBackend::scanDevice(const QString& device_node) -{ - PedDevice* pedDevice = ped_device_get(device_node.toLocal8Bit().constData()); - - if (pedDevice == nullptr) { - Log(Log::warning) << xi18nc("@info:status", "Could not access device %1", device_node); - return nullptr; - } - - Log(Log::information) << xi18nc("@info:status", "Device found: %1", QString::fromUtf8(pedDevice->model)); - - Device* d = new Device(QString::fromUtf8(pedDevice->model), QString::fromUtf8(pedDevice->path), pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size); - - PedDisk* pedDisk = ped_disk_new(pedDevice); - - if (pedDisk) { - const PartitionTable::TableType type = PartitionTable::nameToTableType(QString::fromUtf8(pedDisk->type->name)); - CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector(*d), lastUsableSector(*d))); - CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), ped_disk_get_max_primary_partition_count(pedDisk)); - - scanDevicePartitions(*d, pedDisk); - } - - ped_device_destroy(pedDevice); return d; } QList LibPartedBackend::scanDevices(bool excludeReadOnly) { QList result; + QVariantMap args; + args[QLatin1String("excludeReadOnly")] = excludeReadOnly; - ped_device_probe_all(); - PedDevice* pedDevice = nullptr; - QVector path; - quint32 totalDevices = 0; - while (true) { - pedDevice = ped_device_get_next(pedDevice); - if (!pedDevice) - break; - if (pedDevice->type == PED_DEVICE_DM) - continue; - if (excludeReadOnly && ( - pedDevice->type == PED_DEVICE_LOOP || - pedDevice->read_only)) - continue; - - path.push_back(QString::fromUtf8(pedDevice->path)); - ++totalDevices; + KAuth::Action scanAction = QStringLiteral("org.kde.kpmcore.scan.scandevices"); + scanAction.setHelperId(QStringLiteral("org.kde.kpmcore.scan")); + scanAction.setArguments(args); + KAuth::ExecuteJob *job = scanAction.execute(); + if (!job->exec()) { + qWarning() << "KAuth returned an error code: " << job->errorString(); + return result; } + QList paths = job->data()[QLatin1String("paths")].toList(); + + quint32 totalDevices = paths.size(); for (quint32 i = 0; i < totalDevices; ++i) { - emitScanProgress(path[i], i * 100 / totalDevices); - Device* d = scanDevice(path[i]); + QString path = paths[i].toString(); + emitScanProgress(path, i * 100 / totalDevices); + Device* d = scanDevice(path); if (d) result.append(d); } @@ -546,9 +394,9 @@ FileSystem::Type LibPartedBackend::detectFileSystem(const QString& partitionPath return rval; } -CoreBackendDevice* LibPartedBackend::openDevice(const QString& device_node) +CoreBackendDevice* LibPartedBackend::openDevice(const QString& deviceNode) { - LibPartedDevice* device = new LibPartedDevice(device_node); + LibPartedDevice* device = new LibPartedDevice(deviceNode); if (device == nullptr || !device->open()) { delete device; @@ -558,9 +406,9 @@ CoreBackendDevice* LibPartedBackend::openDevice(const QString& device_node) return device; } -CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const QString& device_node) +CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const QString& deviceNode) { - LibPartedDevice* device = new LibPartedDevice(device_node); + LibPartedDevice* device = new LibPartedDevice(deviceNode); if (device == nullptr || !device->openExclusive()) { delete device; diff --git a/src/plugins/libparted/libpartedbackend.h b/src/plugins/libparted/libpartedbackend.h index a3268b4..041ab0f 100644 --- a/src/plugins/libparted/libpartedbackend.h +++ b/src/plugins/libparted/libpartedbackend.h @@ -61,10 +61,10 @@ private: public: void initFSSupport() override; - CoreBackendDevice* openDevice(const QString& device_node) override; - CoreBackendDevice* openDeviceExclusive(const QString& device_node) override; + CoreBackendDevice* openDevice(const QString& deviceNode) override; + CoreBackendDevice* openDeviceExclusive(const QString& deviceNode) override; bool closeDevice(CoreBackendDevice* core_device) override; - Device* scanDevice(const QString& device_node) override; + Device* scanDevice(const QString& deviceNode) override; QList scanDevices(bool excludeReadOnly = false) override; FileSystem::Type detectFileSystem(const QString& partitionPath) override; @@ -72,7 +72,6 @@ public: private: static PedPartitionFlag getPedFlag(PartitionTable::Flag flag); - void scanDevicePartitions(Device& d, PedDisk* pedDisk); }; #endif