diff --git a/src/core/raid/softwareraid.cpp b/src/core/raid/softwareraid.cpp index 3fc6938..4f81fe2 100644 --- a/src/core/raid/softwareraid.cpp +++ b/src/core/raid/softwareraid.cpp @@ -1,253 +1,264 @@ /************************************************************************* * 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; } qint32 SoftwareRAID::raidLevel() const { return d_ptr->m_raidLevel; } qint64 SoftwareRAID::chunkSize() const { return d_ptr->m_chunkSize; } qint64 SoftwareRAID::totalChunk() const { return d_ptr->m_totalChunk; } qint64 SoftwareRAID::arraySize() const { return d_ptr->m_arraySize; } QString SoftwareRAID::uuid() const { return d_ptr->m_UUID; } QStringList SoftwareRAID::devicePathList() const { return d_ptr->m_devicePathList; } 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(); } 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::isRaidPath(const QString &path) { return !getDetail(path).isEmpty(); } 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(const SoftwareRAID &raidDevice) +bool SoftwareRAID::assembleSoftwareRAID(const QString& deviceNode) { + if (!isRaidPath(deviceNode)) + return false; + ExternalCommand cmd(QStringLiteral("mdadm"), - { QStringLiteral("--assemble"), QStringLiteral("--scan"), raidDevice.deviceNode() }); + { QStringLiteral("--assemble"), QStringLiteral("--scan"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } -bool SoftwareRAID::stopSoftwareRAID(const SoftwareRAID &raidDevice) +bool SoftwareRAID::stopSoftwareRAID(const QString& deviceNode) { + if (!isRaidPath(deviceNode)) + return false; + ExternalCommand cmd(QStringLiteral("mdadm"), - { QStringLiteral("--stop"), raidDevice.deviceNode() }); + { QStringLiteral("--stop"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } +bool SoftwareRAID::reassembleSoftwareRAID(const QString &deviceNode) +{ + return stopSoftwareRAID(deviceNode) && assembleSoftwareRAID(deviceNode); +} + 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/raid/softwareraid.h b/src/core/raid/softwareraid.h index 6145388..580bf84 100644 --- a/src/core/raid/softwareraid.h +++ b/src/core/raid/softwareraid.h @@ -1,81 +1,83 @@ /************************************************************************* * 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); qint32 raidLevel() const; qint64 chunkSize() const; qint64 totalChunk() const; qint64 arraySize() const; QString uuid() const; QStringList devicePathList() const; 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 isRaidPath(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(const SoftwareRAID& raidDevice); + static bool assembleSoftwareRAID(const QString& deviceNode); - static bool stopSoftwareRAID(const SoftwareRAID& raidDevice); + static bool stopSoftwareRAID(const QString& deviceNode); + + static bool reassembleSoftwareRAID(const QString& deviceNode); 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/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index c794d01..a6637d1 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -1,266 +1,261 @@ /************************************************************************* * 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 .* *************************************************************************/ #include "plugins/sfdisk/sfdiskpartitiontable.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/partition.h" #include "core/device.h" #include "core/raid/softwareraid.h" #include "fs/filesystem.h" #include "util/report.h" #include "util/externalcommand.h" #include #include #include #include #include SfdiskPartitionTable::SfdiskPartitionTable(const Device* d) : CoreBackendPartitionTable(), m_device(d) { } SfdiskPartitionTable::~SfdiskPartitionTable() { } bool SfdiskPartitionTable::open() { return true; } bool SfdiskPartitionTable::commit(quint32 timeout) { ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); ExternalCommand(QStringLiteral("blockdev"), { QStringLiteral("--rereadpt"), m_device->deviceNode() }).run(); ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("trigger") }).run(); - if (m_device->type() == Device::Type::SoftwareRAID_Device) - { - const SoftwareRAID& raid = static_cast(*m_device); - SoftwareRAID::stopSoftwareRAID(raid); - SoftwareRAID::assembleSoftwareRAID(raid); - } + SoftwareRAID::reassembleSoftwareRAID(m_device->deviceNode()); return true; } QString SfdiskPartitionTable::createPartition(Report& report, const Partition& partition) { if ( !(partition.roles().has(PartitionRole::Extended) || partition.roles().has(PartitionRole::Logical) || partition.roles().has(PartitionRole::Primary) ) ) { report.line() << xi18nc("@info:progress", "Unknown partition role for new partition %1 (roles: %2)", partition.deviceNode(), partition.roles().toString()); return QString(); } QByteArray type = QByteArray(); if (partition.roles().has(PartitionRole::Extended)) type = QByteArrayLiteral(" type=5"); // NOTE: at least on GPT partition types "are" partition flags ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--append"), partition.devicePath() } ); if ( createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) + type + QByteArrayLiteral(" size=") + QByteArray::number(partition.length()) + QByteArrayLiteral("\nwrite\n")) && createCommand.start(-1) && createCommand.waitFor() ) { QRegularExpression re(QStringLiteral("Created a new partition (\\d+)")); QRegularExpressionMatch rem = re.match(createCommand.output()); if (rem.hasMatch()) { // TODO: Use back() with Qt 5.10 if ( partition.devicePath()[partition.devicePath().size() - 1].isDigit() ) return partition.devicePath() + QLatin1Char('p') + rem.captured(1); else return partition.devicePath() + rem.captured(1); } } report.line() << xi18nc("@info:progress", "Failed to add partition %1 to device %2.", partition.deviceNode(), m_device->deviceNode()); return QString(); } bool SfdiskPartitionTable::deletePartition(Report& report, const Partition& partition) { ExternalCommand deleteCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--delete"), partition.devicePath(), QString::number(partition.number()) } ); if (deleteCommand.run(-1) && deleteCommand.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Could not delete partition %1.", partition.devicePath()); return false; } bool SfdiskPartitionTable::updateGeometry(Report& report, const Partition& partition, qint64 sectorStart, qint64 sectorEnd) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), partition.devicePath(), QStringLiteral("-N"), QString::number(partition.number()) } ); if ( sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) + QByteArrayLiteral(" size=") + QByteArray::number(sectorEnd - sectorStart + 1) + QByteArrayLiteral("\nY\n")) && sfdiskCommand.start(-1) && sfdiskCommand.waitFor() && sfdiskCommand.exitCode() == 0) { return true; } report.line() << xi18nc("@info:progress", "Could not set geometry for partition %1 while trying to resize/move it.", partition.devicePath()); return false; } bool SfdiskPartitionTable::clobberFileSystem(Report& report, const Partition& partition) { ExternalCommand wipeCommand(report, QStringLiteral("wipefs"), { QStringLiteral("--all"), partition.partitionPath() } ); if (wipeCommand.run(-1) && wipeCommand.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Failed to erase filesystem signature on partition %1.", partition.partitionPath()); return false; } bool SfdiskPartitionTable::resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) { // sfdisk does not have any partition resize capabilities Q_UNUSED(report) Q_UNUSED(partition) Q_UNUSED(newLength) return false; } FileSystem::Type SfdiskPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector) { FileSystem::Type type = FileSystem::Type::Unknown; ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), device.deviceNode() } ); if (jsonCommand.run(-1) && jsonCommand.exitCode() == 0) { const QJsonArray partitionTable = QJsonDocument::fromJson(jsonCommand.rawOutput()).object()[QLatin1String("partitiontable")].toObject()[QLatin1String("partitions")].toArray(); for (const auto &partition : partitionTable) { const QJsonObject partitionObject = partition.toObject(); const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); if (start == sector) { const QString deviceNode = partitionObject[QLatin1String("node")].toString(); type = CoreBackendManager::self()->backend()->detectFileSystem(deviceNode); return type; } } } report.line() << xi18nc("@info:progress", "Could not determine file system of partition at sector %1 on device %2.", sector, device.deviceNode()); return type; } static struct { FileSystem::Type type; QLatin1String partitionType[2]; // GPT, MBR } typemap[] = { { FileSystem::Type::Btrfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext3, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::LinuxSwap, { QLatin1String("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"), QLatin1String("82") } }, { FileSystem::Type::Fat12, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Type::Fat16, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Type::Fat32, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::Nilfs2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ntfs, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::Exfat, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::ReiserFS, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Reiser4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Xfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Jfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Hfs, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af")} }, { FileSystem::Type::HfsPlus, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af") } }, { FileSystem::Type::Udf, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } } // Add ZFS too }; static QLatin1String getPartitionType(FileSystem::Type t, PartitionTable::TableType tableType) { quint8 type; switch (tableType) { case PartitionTable::gpt: type = 0; break; case PartitionTable::msdos: case PartitionTable::msdos_sectorbased: type = 1; break; default:; return QLatin1String(); } for (quint32 i = 0; i < sizeof(typemap) / sizeof(typemap[0]); i++) if (typemap[i].type == t) return typemap[i].partitionType[type]; return QLatin1String(); } bool SfdiskPartitionTable::setPartitionSystemType(Report& report, const Partition& partition) { QString partitionType = getPartitionType(partition.fileSystem().type(), m_device->partitionTable()->type()); if (partitionType.isEmpty()) return true; ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), partitionType } ); return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; } bool SfdiskPartitionTable::setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) { // We only allow setting one active partition per device if (flag == PartitionTable::FlagBoot && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QString::number(partition.number()) } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } else if (flag == PartitionTable::FlagBoot && state == false) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QStringLiteral("-") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; // FIXME: Do not return false since we have no way of checking if partition table is MBR } if (flag == PartitionTable::FlagEsp && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } if (flag == PartitionTable::FlagEsp && state == false) setPartitionSystemType(report, partition); if (flag == PartitionTable::FlagBiosGrub && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), QStringLiteral("21686148-6449-6E6F-744E-656564454649") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } if (flag == PartitionTable::FlagBiosGrub && state == false) setPartitionSystemType(report, partition); return true; }