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 @@ -23,6 +23,7 @@ set(CORE_SRC core/smartparser.cpp core/smartattributeparseddata.cpp core/smartdiskinformation.cpp + core/softwareraid.cpp core/volumemanagerdevice.cpp ) @@ -41,5 +42,6 @@ set(CORE_LIB_HDRS 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 @@ -54,7 +54,8 @@ public: 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); 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 @@ -23,6 +23,7 @@ #include "core/diskdevice.h" #include "core/lvmdevice.h" +#include "core/softwareraid.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" @@ -106,6 +107,7 @@ QList SfdiskBackend::scanDevices(bool excludeReadOnly) } LvmDevice::scanSystemLVM(result); + SoftwareRAID::scanSoftwareRAID(result); } return result; @@ -126,61 +128,50 @@ Device* SfdiskBackend::scanDevice(const QString& 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(); - - QString tableType = partitionTable[QLatin1String("label")].toString(); - const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); - - 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; - } - - setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector)); - switch (type) { - case PartitionTable::gpt: + if ( modelCommand.run(-1) && modelCommand.exitCode() == 0 && !modelCommand.output().trimmed().isEmpty() ) { - // 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; - } - else - maxEntries = 128; - CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxEntries); + QString modelName = modelCommand.output(); + modelName = modelName.left(modelName.length() - 1); + + Log(Log::Level::information) << xi18nc("@info:status", "Disk Device found: %1", modelName); + + d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); } - default: - break; + else // check for software raid + { + 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 ); + } } - 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 { @@ -291,6 +282,64 @@ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartit 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 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 @@ -58,6 +58,7 @@ public: 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); };