Use KAuth to get privileges needed for detecting partitions.

This commit is contained in:
Andrius Štikonas 2016-07-21 13:16:29 +01:00
parent 99886f32fc
commit e64a82ff19
10 changed files with 422 additions and 284 deletions

View File

@ -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

View File

@ -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);

View File

@ -91,6 +91,7 @@ public:
};
Q_DECLARE_FLAGS(Flags, Flag)
Q_FLAG(Flag)
public:
PartitionTable(TableType type, qint64 first_usable, qint64 last_usable);

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,198 @@
/*************************************************************************
* Copyright (C) 2016 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#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<QVariant> 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<GPTDiskData*>(pedDisk->disk_specific);
PedGeometry* geom = reinterpret_cast<PedGeometry*>(&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<GPTDiskData*>(pedDisk->disk_specific);
PedGeometry* geom = reinterpret_cast<PedGeometry*>(&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<QVariant> partitionPath;
QList<QVariant> partitionType;
QList<QVariant> partitionStart;
QList<QVariant> partitionEnd;
QList<QVariant> partitionBusy;
QList<QVariant> availableFlags;
QList<QVariant> 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<qint32>(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<qint32>(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)

View File

@ -0,0 +1,66 @@
/*************************************************************************
* Copyright (C) 2016 by Andrius Štikonas <andrius@stikonas.eu> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#if !defined(SCAN__H)
#define SCAN__H
#include <KAuth>
#include <parted/parted.h>
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

View File

@ -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 <QString>
#include <QStringList>
#include <KAuth>
#include <KLocalizedString>
#include <KMountPoint>
#include <KDiskFreeSpaceInfo>
#include <KPluginFactory>
#include <parted/parted.h>
#include <unistd.h>
K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin<LibPartedBackend>();)
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<GPTDiskData*>(pedDisk->disk_specific);
PedGeometry* geom = reinterpret_cast<PedGeometry*>(&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<GPTDiskData*>(pedDisk->disk_specific);
PedGeometry* geom = reinterpret_cast<PedGeometry*>(&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<QVariant>&) :
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 <filename>%1</filename>", 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<Partition*> partitions;
QList<QVariant> partitionPath = job->data()[QLatin1String("partitionPath")].toList();
QList<QVariant> partitionType = job->data()[QLatin1String("partitionType")].toList();
QList<QVariant> partitionStart = job->data()[QLatin1String("partitionStart")].toList();
QList<QVariant> partitionEnd = job->data()[QLatin1String("partitionEnd")].toList();
QList<QVariant> partitionBusy = job->data()[QLatin1String("partitionBusy")].toList();
while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) {
if (pedPartition->num < 1)
continue;
quint32 totalPartitions = partitionPath.size();
QList<Partition*> 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::luks*>(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<QVariant> availableFlags = job->data()[QLatin1String("availableFlags")].toList();
PartitionTable::Flags available = static_cast<PartitionTable::Flag>(availableFlags[i].toInt());
QList<QVariant> activeFlags = job->data()[QLatin1String("activeFlags")].toList();
PartitionTable::Flags active = static_cast<PartitionTable::Flag>(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 <filename>%1</filename>", 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<Device*> LibPartedBackend::scanDevices(bool excludeReadOnly)
{
QList<Device*> result;
QVariantMap args;
args[QLatin1String("excludeReadOnly")] = excludeReadOnly;
ped_device_probe_all();
PedDevice* pedDevice = nullptr;
QVector<QString> 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<QVariant> 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;

View File

@ -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<Device*> 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