diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 504890a..21e3a0b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,45 +1,47 @@ set(CORE_SRC core/copysource.cpp core/copysourcedevice.cpp core/copysourcefile.cpp core/copysourceshred.cpp core/copytarget.cpp core/copytargetdevice.cpp core/copytargetfile.cpp core/device.cpp core/devicescanner.cpp core/diskdevice.cpp core/fstab.cpp core/lvmdevice.cpp core/operationrunner.cpp core/operationstack.cpp core/partition.cpp core/partitionalignment.cpp core/partitionnode.cpp core/partitionrole.cpp core/partitiontable.cpp core/smartstatus.cpp core/smartattribute.cpp core/smartparser.cpp core/smartattributeparseddata.cpp core/smartdiskinformation.cpp + core/softwareraid.cpp core/volumemanagerdevice.cpp ) set(CORE_LIB_HDRS core/device.h core/devicescanner.h core/diskdevice.h core/fstab.h core/lvmdevice.h core/operationrunner.h core/operationstack.h core/partition.h core/partitionalignment.h core/partitionnode.h core/partitionrole.h core/partitiontable.h core/smartattribute.h core/smartstatus.h + core/softwareraid.h core/volumemanagerdevice.h ) diff --git a/src/core/device.h b/src/core/device.h index d74184b..7efe179 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -1,114 +1,115 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * * * 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 .* *************************************************************************/ #ifndef KPMCORE_DEVICE_H #define KPMCORE_DEVICE_H #include "util/libpartitionmanagerexport.h" #include #include #include class PartitionTable; class CreatePartitionTableOperation; class CoreBackend; class SmartStatus; class DevicePrivate; /** A device description. Represents a device like /dev/sda. Contains information about the device (name, status, size ..) but does not operate on the device itself. @see CoreBackendDevice Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. @see PartitionTable, Partition @author Volker Lanz */ class LIBKPMCORE_EXPORT Device : public QObject { Device &operator=(const Device &) = delete; friend class CreatePartitionTableOperation; friend class CoreBackend; public: enum class Type { Unknown_Device, Disk_Device, LVM_Device, /* VG */ - RAID_Device, /* software RAID device */ + SoftwareRAID_Device, /* software RAID device, i.e. mdraid */ + HardwareRAID_Device, /* hardware RAID device (or fake raid), i.e. dmraid */ }; explicit Device(std::shared_ptr d_ptr, const QString& name, const QString& deviceNode, const qint64 logicalSectorSize, const qint64 totalLogicalSectors, const QString& iconName = QString(), Device::Type type = Device::Type::Disk_Device); public: explicit Device(const Device& other); virtual ~Device(); virtual bool operator==(const Device& other) const; virtual bool operator!=(const Device& other) const; /**< @return the Device's name, usually some manufacturer string */ virtual QString& name(); virtual const QString& name() const; /**< @return the Device's node, for example "/dev/sda" */ virtual const QString& deviceNode() const; /**< @return the logical sector size the Device uses (would be extent size for e.g. LVM devices) */ virtual qint64 logicalSize() const; /**< @return the total number of logical sectors on the device */ virtual qint64 totalLogical() const; /**< @return the Device's PartitionTable */ virtual PartitionTable* partitionTable(); virtual const PartitionTable* partitionTable() const; /** * Change the description of the partition table for different one. * The device itself is not changed; use CreatePartitionTableOperation * for that. The Device instance becomes the owner of @p ptable . */ virtual void setPartitionTable(PartitionTable* ptable); virtual qint64 capacity() const { /**< @return the Device's capacity in bytes */ return logicalSize() * totalLogical(); } /**< @return suggested icon name for this Device */ virtual const QString& iconName() const; /**< @param name set the new Icon for this Device */ virtual void setIconName(const QString& name); virtual SmartStatus& smartStatus(); virtual const SmartStatus& smartStatus() const; virtual Device::Type type() const; virtual QString prettyName() const; protected: std::shared_ptr d; }; #endif diff --git a/src/core/softwareraid.cpp b/src/core/softwareraid.cpp new file mode 100644 index 0000000..e2779c0 --- /dev/null +++ b/src/core/softwareraid.cpp @@ -0,0 +1,216 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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 "softwareraid.h" + +#include "backend/corebackend.h" +#include "backend/corebackendmanager.h" +#include "core/partition.h" +#include "core/volumemanagerdevice_p.h" +#include "fs/filesystem.h" +#include "fs/filesystemfactory.h" +#include "util/externalcommand.h" + +#include + +#define d_ptr std::static_pointer_cast(d) + +class SoftwareRAIDPrivate : public VolumeManagerDevicePrivate +{ +public: + qint32 m_raidLevel; + qint64 m_chunkSize; + qint64 m_totalChunk; + qint64 m_arraySize; + QString m_UUID; + QStringList m_devicePathList; +}; + +SoftwareRAID::SoftwareRAID(const QString& name, const QString& iconName) + : VolumeManagerDevice(std::make_shared(), + name, + (QStringLiteral("/dev/") + name), + getChunkSize(QStringLiteral("/dev/") + name), + getTotalChunk(QStringLiteral("/dev/") + name), + iconName, + Device::Type::SoftwareRAID_Device) +{ + d_ptr->m_raidLevel = getRaidLevel(deviceNode()); + d_ptr->m_chunkSize = logicalSize(); + d_ptr->m_totalChunk = totalLogical(); + d_ptr->m_arraySize = getArraySize(deviceNode()); + d_ptr->m_UUID = getUUID(deviceNode()); + d_ptr->m_devicePathList = getDevicePathList(deviceNode()); + + initPartitions(); +} + +const QStringList SoftwareRAID::deviceNodes() const +{ + return d_ptr->m_devicePathList; +} + +const QStringList& SoftwareRAID::partitionNodes() const +{ + return {}; +} + +qint64 SoftwareRAID::partitionSize(QString &partitionPath) const +{ + Q_UNUSED(partitionPath); + return 0; +} + +bool SoftwareRAID::growArray(Report &report, const QStringList &devices) +{ + Q_UNUSED(report); + Q_UNUSED(devices); + return false; +} + +bool SoftwareRAID::shrinkArray(Report &report, const QStringList &devices) +{ + Q_UNUSED(report); + Q_UNUSED(devices); + return false; +} + +void SoftwareRAID::scanSoftwareRAID(QList& devices) +{ + ExternalCommand scanRaid(QStringLiteral("cat"), { QStringLiteral("/proc/mdstat") }); + + if (scanRaid.run(-1) && scanRaid.exitCode() == 0) { + QRegularExpression re(QStringLiteral("md(\\d+)\\s+:")); + QRegularExpressionMatchIterator i = re.globalMatch(scanRaid.output()); + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + + QString deviceNode = QStringLiteral("/dev/") + QStringLiteral("md") + reMatch.captured(1).trimmed(); + + Device* d = CoreBackendManager::self()->backend()->scanDevice(deviceNode); + + if ( d ) + devices << d; + } + } +} + +qint32 SoftwareRAID::getRaidLevel(const QString &path) +{ + QString output = getDetail(path); + + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("Raid Level :\\s+\\w+(\\d+)")); + QRegularExpressionMatch reMatch = re.match(output); + if (reMatch.hasMatch()) + return reMatch.captured(1).toLongLong(); + } + + return -1; +} + +qint64 SoftwareRAID::getChunkSize(const QString &path) +{ + QString output = getDetail(path); + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("Chunk Size :\\s+(\\d+)")); + QRegularExpressionMatch reMatch = re.match(output); + if (reMatch.hasMatch()) + return reMatch.captured(1).toLongLong() * 1024; + } + return -1; + +} + +qint64 SoftwareRAID::getTotalChunk(const QString &path) +{ + return getArraySize(path) / getChunkSize(path); +} + +qint64 SoftwareRAID::getArraySize(const QString &path) +{ + QString output = getDetail(path); + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("Array Size :\\s+(\\d+)")); + QRegularExpressionMatch reMatch = re.match(output); + if (reMatch.hasMatch()) + return reMatch.captured(1).toLongLong() * 1024; + } + return -1; + +} + +QString SoftwareRAID::getUUID(const QString &path) +{ + Q_UNUSED(path); + return QStringLiteral(); +} + +QStringList SoftwareRAID::getDevicePathList(const QString &path) +{ + Q_UNUSED(path); + return {}; +} + +bool SoftwareRAID::createSoftwareRAID(Report &report, + const QString &name, + const QStringList devicePathList, + const qint32 raidLevel, + const qint32 chunkSize) +{ + return false; +} + +bool SoftwareRAID::deleteSoftwareRAID(Report &report, + SoftwareRAID &raidDevice) +{ + Q_UNUSED(report); + Q_UNUSED(raidDevice); + return false; +} + +bool SoftwareRAID::assembleSoftwareRAID(Report &report, const SoftwareRAID &raidDevice) +{ + Q_UNUSED(report); + Q_UNUSED(raidDevice); + return false; +} + +bool SoftwareRAID::stopSoftwareRAID(Report &report, const SoftwareRAID &raidDevice) +{ + Q_UNUSED(report); + Q_UNUSED(raidDevice); + return false; +} + +void SoftwareRAID::initPartitions() +{ + +} + +qint64 SoftwareRAID::mappedSector(const QString &partitionPath, qint64 sector) const +{ + return -1; +} + +QString SoftwareRAID::getDetail(const QString &path) +{ + ExternalCommand cmd(QStringLiteral("mdadm"), + { QStringLiteral("--detail"), + path }); + return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QStringLiteral(); +} diff --git a/src/core/softwareraid.h b/src/core/softwareraid.h new file mode 100644 index 0000000..eab9eb6 --- /dev/null +++ b/src/core/softwareraid.h @@ -0,0 +1,74 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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(KPMCORE_SOFTWARERAID_H) +#define KPMCORE_SOFTWARERAID_H + +#include "core/volumemanagerdevice.h" +#include "util/libpartitionmanagerexport.h" +#include "util/report.h" + +class LIBKPMCORE_EXPORT SoftwareRAID : public VolumeManagerDevice +{ + Q_DISABLE_COPY(SoftwareRAID) + +public: + SoftwareRAID(const QString& name, const QString& iconName = QString()); + + const QStringList deviceNodes() const override; + const QStringList& partitionNodes() const override; + qint64 partitionSize(QString &partitionPath) const override; + + virtual bool growArray(Report& report, const QStringList& devices); + + virtual bool shrinkArray(Report& report, const QStringList& devices); + +public: + static void scanSoftwareRAID(QList& devices); + + static qint32 getRaidLevel(const QString& path); + static qint64 getChunkSize(const QString& path); + static qint64 getTotalChunk(const QString& path); + static qint64 getArraySize(const QString& path); + static QString getUUID(const QString& path); + static QStringList getDevicePathList(const QString& path); + + static bool createSoftwareRAID(Report& report, + const QString& name, + const QStringList devicePathList, + const qint32 raidLevel, + const qint32 chunkSize); + + static bool deleteSoftwareRAID(Report& report, + SoftwareRAID& raidDevice); + + static bool assembleSoftwareRAID(Report& report, + const SoftwareRAID& raidDevice); + + static bool stopSoftwareRAID(Report& report, + const SoftwareRAID& raidDevice); + +protected: + void initPartitions() override; + + qint64 mappedSector(const QString &partitionPath, qint64 sector) const override; + +private: + static QString getDetail(const QString& path); +}; + +#endif // SOFTWARERAID_H diff --git a/src/plugins/sfdisk/sfdiskbackend.cpp b/src/plugins/sfdisk/sfdiskbackend.cpp index 33f8ed0..9989ee8 100644 --- a/src/plugins/sfdisk/sfdiskbackend.cpp +++ b/src/plugins/sfdisk/sfdiskbackend.cpp @@ -1,448 +1,497 @@ /************************************************************************* * Copyright (C) 2017 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 .* *************************************************************************/ /** @file */ #include "plugins/sfdisk/sfdiskbackend.h" #include "plugins/sfdisk/sfdiskdevice.h" #include "core/diskdevice.h" #include "core/lvmdevice.h" +#include "core/softwareraid.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" #include "fs/filesystemfactory.h" #include "fs/luks.h" #include "fs/luks2.h" #include "util/globallog.h" #include "util/externalcommand.h" #include "util/helpers.h" #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(SfdiskBackendFactory, "pmsfdiskbackendplugin.json", registerPlugin();) SfdiskBackend::SfdiskBackend(QObject*, const QList&) : CoreBackend() { } void SfdiskBackend::initFSSupport() { } QList SfdiskBackend::scanDevices(bool excludeReadOnly) { // TODO: add another bool option for loopDevices QList result; QStringList deviceNodes; ExternalCommand cmd(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--paths"), QStringLiteral("--sort"), QStringLiteral("name"), QStringLiteral("--json"), QStringLiteral("--output"), QStringLiteral("type,name") }); if (cmd.run(-1) && cmd.exitCode() == 0) { const QJsonDocument jsonDocument = QJsonDocument::fromJson(cmd.rawOutput()); const QJsonObject jsonObject = jsonDocument.object(); const QJsonArray jsonArray = jsonObject[QLatin1String("blockdevices")].toArray(); for (const auto &deviceLine : jsonArray) { QJsonObject deviceObject = deviceLine.toObject(); if (deviceObject[QLatin1String("type")].toString() != QLatin1String("disk")) continue; const QString deviceNode = deviceObject[QLatin1String("name")].toString(); if (excludeReadOnly) { QString deviceName = deviceNode; deviceName.remove(QStringLiteral("/dev/")); QFile f(QStringLiteral("/sys/block/%1/ro").arg(deviceName)); if (f.open(QIODevice::ReadOnly)) if (f.readLine().trimmed().toInt() == 1) continue; } deviceNodes << deviceNode; } int totalDevices = deviceNodes.length(); for (int i = 0; i < totalDevices; ++i) { const QString deviceNode = deviceNodes[i]; emitScanProgress(deviceNode, i * 100 / totalDevices); Device* device = scanDevice(deviceNode); if (device != nullptr) { result.append(device); } } LvmDevice::scanSystemLVM(result); + SoftwareRAID::scanSoftwareRAID(result); } return result; } /** Create a Device for the given device_node and scan it for partitions. @param deviceNode the device node (e.g. "/dev/sda") @return the created Device object. callers need to free this. */ Device* SfdiskBackend::scanDevice(const QString& deviceNode) { ExternalCommand modelCommand(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--noheadings"), QStringLiteral("--output"), QStringLiteral("model"), deviceNode }); ExternalCommand sizeCommand(QStringLiteral("blockdev"), { QStringLiteral("--getsize64"), deviceNode }); ExternalCommand sizeCommand2(QStringLiteral("blockdev"), { QStringLiteral("--getss"), deviceNode }); ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), deviceNode } ); - if ( modelCommand.run(-1) && modelCommand.exitCode() == 0 - && sizeCommand.run(-1) && sizeCommand.exitCode() == 0 - && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 - && jsonCommand.run(-1) ) + if ( sizeCommand.run(-1) && sizeCommand.exitCode() == 0 + && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 + && jsonCommand.run(-1) ) { - QString modelName = modelCommand.output(); - modelName = modelName.left(modelName.length() - 1); + Device* d = nullptr; qint64 deviceSize = sizeCommand.output().trimmed().toLongLong(); - - Log(Log::Level::information) << xi18nc("@info:status", "Device found: %1", modelName); int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong(); - DiskDevice* d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); - - if (jsonCommand.exitCode() != 0) - return d; - const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object(); - const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject(); + if ( modelCommand.run(-1) && modelCommand.exitCode() == 0 && !modelCommand.output().trimmed().isEmpty() ) + { + QString modelName = modelCommand.output(); + modelName = modelName.left(modelName.length() - 1); - QString tableType = partitionTable[QLatin1String("label")].toString(); - const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); + Log(Log::Level::information) << xi18nc("@info:status", "Disk Device found: %1", modelName); - qint64 firstUsableSector = 0, lastUsableSector = d->totalSectors(); - if (type == PartitionTable::gpt) { - firstUsableSector = partitionTable[QLatin1String("firstlba")].toVariant().toLongLong(); - lastUsableSector = partitionTable[QLatin1String("lastlba")].toVariant().toLongLong(); - } - if (lastUsableSector < firstUsableSector) { - return nullptr; + d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); } - - setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector)); - switch (type) { - case PartitionTable::gpt: + else // check for software raid { - // Read the maximum number of GPT partitions - qint32 maxEntries; - ExternalCommand ddCommand(QStringLiteral("dd"), { QStringLiteral("skip=1"), QStringLiteral("count=1"), QStringLiteral("if=") + deviceNode}, QProcess::SeparateChannels); - if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) { - QByteArray gptHeader = ddCommand.rawOutput(); - QByteArray gptMaxEntries = gptHeader.mid(80, 4); - QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly); - stream.setByteOrder(QDataStream::LittleEndian); - stream >> maxEntries; + ExternalCommand softwareRaidCommand(QStringLiteral("mdadm"), { QStringLiteral("--detail"), deviceNode }); + + if ( softwareRaidCommand.run(-1) && softwareRaidCommand.exitCode() == 0 ) + { + Log(Log::Level::information) << xi18nc("@info:status", "Software RAID Device found: %1", deviceNode); + + QString deviceName = deviceNode.mid(5); + + d = new SoftwareRAID( deviceName ); } - else - maxEntries = 128; - CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxEntries); - } - default: - break; } - scanDevicePartitions(*d, partitionTable[QLatin1String("partitions")].toArray()); - return d; + if ( d ) + { + if (jsonCommand.exitCode() != 0) + return d; + + const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object(); + const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject(); + + if (!updateDevicePartitionTable(*d, partitionTable)) + return nullptr; + + return d; + } } else { // Look if this device is a LVM VG ExternalCommand checkVG(QStringLiteral("lvm"), { QStringLiteral("vgdisplay"), deviceNode }); if (checkVG.run(-1) && checkVG.exitCode() == 0) { qDebug() << "Trying to find LVM VG"; QList availableDevices = scanDevices(); LvmDevice::scanSystemLVM(availableDevices); for (Device *device : qAsConst(availableDevices)) if (device->deviceNode() == deviceNode) return device; } } return nullptr; } /** 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. */ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions) { Q_ASSERT(d.partitionTable()); QList partitions; for (const auto &partition : jsonPartitions) { const QJsonObject partitionObject = partition.toObject(); const QString partitionNode = partitionObject[QLatin1String("node")].toString(); const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); const qint64 size = partitionObject[QLatin1String("size")].toVariant().toLongLong(); const QString partitionType = partitionObject[QLatin1String("type")].toString(); PartitionTable::Flag activeFlags = partitionObject[QLatin1String("bootable")].toBool() ? PartitionTable::FlagBoot : PartitionTable::FlagNone; if (partitionType == QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B")) activeFlags = PartitionTable::FlagEsp; else if (partitionType == QStringLiteral("21686148-6449-6E6F-744E-656564454649")) activeFlags = PartitionTable::FlagBiosGrub; FileSystem::Type type = FileSystem::Type::Unknown; type = detectFileSystem(partitionNode); PartitionRole::Roles r = PartitionRole::Primary; if ( (d.partitionTable()->type() == PartitionTable::msdos || d.partitionTable()->type() == PartitionTable::msdos_sectorbased) && partitionType.toInt() == 5 ) { r = PartitionRole::Extended; type = FileSystem::Type::Extended; } // Find an extended partition this partition is in. 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(); else r = PartitionRole::Logical; FileSystem* fs = FileSystemFactory::create(type, start, start + size - 1, d.logicalSize()); fs->scan(partitionNode); QString mountPoint; bool mounted; // sfdisk does not handle LUKS partitions if (fs->type() == FileSystem::Type::Luks || fs->type() == FileSystem::Type::Luks2) { r |= PartitionRole::Luks; FS::luks* luksFs = static_cast(fs); luksFs->initLUKS(); QString mapperNode = luksFs->mapperName(); mountPoint = FileSystem::detectMountPoint(fs, mapperNode); mounted = FileSystem::detectMountStatus(fs, mapperNode); } else { mountPoint = FileSystem::detectMountPoint(fs, partitionNode); mounted = FileSystem::detectMountStatus(fs, partitionNode); } Partition* part = new Partition(parent, d, PartitionRole(r), fs, start, start + size - 1, partitionNode, availableFlags(d.partitionTable()->type()), mountPoint, mounted, activeFlags); if (!part->roles().has(PartitionRole::Luks)) readSectorsUsed(d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); if (d.partitionTable()->type() == PartitionTable::TableType::gpt) { part->setLabel(partitionObject[QLatin1String("name")].toString()); part->setUUID(partitionObject[QLatin1String("uuid")].toString()); } if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(part->deviceNode())); parent->append(part); partitions.append(part); } d.partitionTable()->updateUnallocated(d); if (d.partitionTable()->isSectorBased(d)) d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased); for (const Partition * part : qAsConst(partitions)) PartitionAlignment::isAligned(d, *part); } +bool SfdiskBackend::updateDevicePartitionTable(Device &d, const QJsonObject &jsonPartitionTable) +{ + QString tableType = jsonPartitionTable[QLatin1String("label")].toString(); + const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); + + qint64 firstUsableSector = 0, lastUsableSector; + + if ( d.type() == Device::Type::Disk_Device ) + { + const DiskDevice* diskDevice = static_cast(&d); + + lastUsableSector = diskDevice->totalSectors(); + } + else if ( d.type() == Device::Type::SoftwareRAID_Device ) + { + const SoftwareRAID* raidDevice = static_cast(&d); + + lastUsableSector = raidDevice->totalLogical() - 1; + } + + if (type == PartitionTable::gpt) { + firstUsableSector = jsonPartitionTable[QLatin1String("firstlba")].toVariant().toLongLong(); + lastUsableSector = jsonPartitionTable[QLatin1String("lastlba")].toVariant().toLongLong(); + } + + if (lastUsableSector < firstUsableSector) { + return false; + } + + setPartitionTableForDevice(d, new PartitionTable(type, firstUsableSector, lastUsableSector)); + switch (type) { + case PartitionTable::gpt: + { + // Read the maximum number of GPT partitions + qint32 maxEntries; + ExternalCommand ddCommand(QStringLiteral("dd"), + { QStringLiteral("skip=1"), QStringLiteral("count=1"), (QStringLiteral("if=") + d.deviceNode()) }, + QProcess::SeparateChannels); + if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) { + QByteArray gptHeader = ddCommand.rawOutput(); + QByteArray gptMaxEntries = gptHeader.mid(80, 4); + QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream >> maxEntries; + } + else + maxEntries = 128; + CoreBackend::setPartitionTableMaxPrimaries(*d.partitionTable(), maxEntries); + } + default: + break; + } + + scanDevicePartitions(d, jsonPartitionTable[QLatin1String("partitions")].toArray()); + + return true; +} + /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ void SfdiskBackend::readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint) { if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::Type::LinuxSwap && p.fileSystem().type() != FileSystem::Type::Lvm2_PV) { const QStorageInfo storage = QStorageInfo(mountPoint); if (p.isMounted() && storage.isValid()) p.fileSystem().setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / d.logicalSize()); } else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSize()); } FileSystem::Type SfdiskBackend::detectFileSystem(const QString& partitionPath) { FileSystem::Type rval = FileSystem::Type::Unknown; ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), partitionPath }); if (udevCommand.run(-1) && udevCommand.exitCode() == 0) { QRegularExpression re(QStringLiteral("ID_FS_TYPE=(\\w+)")); QRegularExpression re2(QStringLiteral("ID_FS_VERSION=(\\w+)")); QRegularExpressionMatch reFileSystemType = re.match(udevCommand.output()); QRegularExpressionMatch reFileSystemVersion = re2.match(udevCommand.output()); QString s; if (reFileSystemType.hasMatch()) { s = reFileSystemType.captured(1); } QString version; if (reFileSystemVersion.hasMatch()) { version = reFileSystemVersion.captured(1); } if (s == QStringLiteral("ext2")) rval = FileSystem::Type::Ext2; else if (s == QStringLiteral("ext3")) rval = FileSystem::Type::Ext3; else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Type::Ext4; else if (s == QStringLiteral("swap")) rval = FileSystem::Type::LinuxSwap; else if (s == QStringLiteral("ntfs-3g")) rval = FileSystem::Type::Ntfs; else if (s == QStringLiteral("reiserfs")) rval = FileSystem::Type::ReiserFS; else if (s == QStringLiteral("reiser4")) rval = FileSystem::Type::Reiser4; else if (s == QStringLiteral("xfs")) rval = FileSystem::Type::Xfs; else if (s == QStringLiteral("jfs")) rval = FileSystem::Type::Jfs; else if (s == QStringLiteral("hfs")) rval = FileSystem::Type::Hfs; else if (s == QStringLiteral("hfsplus")) rval = FileSystem::Type::HfsPlus; else if (s == QStringLiteral("ufs")) rval = FileSystem::Type::Ufs; else if (s == QStringLiteral("vfat")) { if (version == QStringLiteral("FAT32")) rval = FileSystem::Type::Fat32; else if (version == QStringLiteral("FAT16")) rval = FileSystem::Type::Fat16; else if (version == QStringLiteral("FAT12")) rval = FileSystem::Type::Fat12; } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Type::Btrfs; else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Type::Ocfs2; else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Type::Zfs; else if (s == QStringLiteral("hpfs")) rval = FileSystem::Type::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) { if (version == QStringLiteral("1")) rval = FileSystem::Type::Luks; else if (version == QStringLiteral("2")) { rval = FileSystem::Type::Luks2; } } else if (s == QStringLiteral("exfat")) rval = FileSystem::Type::Exfat; else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Type::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Type::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::Type::F2fs; else if (s == QStringLiteral("udf")) rval = FileSystem::Type::Udf; else if (s == QStringLiteral("iso9660")) rval = FileSystem::Type::Iso9660; else qWarning() << "unknown file system type " << s << " on " << partitionPath; } return rval; } QString SfdiskBackend::readLabel(const QString& deviceNode) const { ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), deviceNode }); udevCommand.run(); QRegularExpression re(QStringLiteral("ID_FS_LABEL=(.*)")); QRegularExpressionMatch reFileSystemLabel = re.match(udevCommand.output()); if (reFileSystemLabel.hasMatch()) return reFileSystemLabel.captured(1); return QString(); } QString SfdiskBackend::readUUID(const QString& deviceNode) const { ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), deviceNode }); udevCommand.run(); QRegularExpression re(QStringLiteral("ID_FS_UUID=(.*)")); QRegularExpressionMatch reFileSystemUUID = re.match(udevCommand.output()); if (reFileSystemUUID.hasMatch()) return reFileSystemUUID.captured(1); return QString(); } PartitionTable::Flags SfdiskBackend::availableFlags(PartitionTable::TableType type) { PartitionTable::Flags flags; if (type == PartitionTable::gpt) { // These are not really flags but for now keep them for compatibility // We should implement changing partition type flags = PartitionTable::FlagBiosGrub | PartitionTable::FlagEsp; } else if (type == PartitionTable::msdos || type == PartitionTable::msdos_sectorbased) flags = PartitionTable::FlagBoot; return flags; } std::unique_ptr SfdiskBackend::openDevice(const Device& d) { std::unique_ptr device = std::make_unique(d); if (!device->open()) device = nullptr; return device; } std::unique_ptr SfdiskBackend::openDeviceExclusive(const Device& d) { std::unique_ptr device = std::make_unique(d); if (!device->openExclusive()) device = nullptr; return device; } bool SfdiskBackend::closeDevice(std::unique_ptr coreDevice) { return coreDevice->close(); } #include "sfdiskbackend.moc" diff --git a/src/plugins/sfdisk/sfdiskbackend.h b/src/plugins/sfdisk/sfdiskbackend.h index d60deb1..dcc7952 100644 --- a/src/plugins/sfdisk/sfdiskbackend.h +++ b/src/plugins/sfdisk/sfdiskbackend.h @@ -1,64 +1,65 @@ /************************************************************************* * Copyright (C) 2017 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(SFDISKBACKEND__H) #define SFDISKBACKEND__H #include "backend/corebackend.h" #include "core/partition.h" #include "fs/filesystem.h" #include #include class Device; class KPluginFactory; class QString; /** Backend plugin for sfdisk @author Andrius Štikonas */ class SfdiskBackend : public CoreBackend { friend class KPluginFactory; Q_DISABLE_COPY(SfdiskBackend) private: SfdiskBackend(QObject* parent, const QList& args); public: void initFSSupport() override; QList scanDevices(bool excludeReadOnly = false) override; std::unique_ptr openDevice(const Device& d) override; std::unique_ptr openDeviceExclusive(const Device& d) override; bool closeDevice(std::unique_ptr coreDevice) override; Device* scanDevice(const QString& deviceNode) override; FileSystem::Type detectFileSystem(const QString& partitionPath) override; QString readLabel(const QString& deviceNode) const override; QString readUUID(const QString& deviceNode) const override; private: static void readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint); void scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions); + bool updateDevicePartitionTable(Device& d, const QJsonObject& jsonPartitionTable); static PartitionTable::Flags availableFlags(PartitionTable::TableType type); }; #endif