diff --git a/src/core/lvmdevice.cpp b/src/core/lvmdevice.cpp index 203fb0b..1b1d1e3 100644 --- a/src/core/lvmdevice.cpp +++ b/src/core/lvmdevice.cpp @@ -1,566 +1,555 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016-2018 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 "core/lvmdevice.h" #include "core/partition.h" #include "core/partitiontable.h" #include "core/volumemanagerdevice_p.h" #include "fs/filesystem.h" #include "fs/lvm2_pv.h" #include "fs/luks.h" #include "fs/filesystemfactory.h" #include "util/externalcommand.h" #include "util/helpers.h" #include "util/globallog.h" #include "util/report.h" #include #include #include #include #define d_ptr std::static_pointer_cast(d) class LvmDevicePrivate : public VolumeManagerDevicePrivate { public: qint64 m_peSize; qint64 m_totalPE; qint64 m_allocPE; qint64 m_freePE; QString m_UUID; mutable QStringList m_LVPathList; - QVector m_PVs; mutable std::unique_ptr> m_LVSizeMap; }; /** Constructs a representation of LVM device with initialized LV as Partitions * * @param vgName Volume Group name * @param iconName Icon representing LVM Volume group */ LvmDevice::LvmDevice(const QString& vgName, const QString& iconName) : VolumeManagerDevice(std::make_shared(), vgName, (QStringLiteral("/dev/") + vgName), getPeSize(vgName), getTotalPE(vgName), iconName, Device::Type::LVM_Device) { d_ptr->m_peSize = logicalSize(); d_ptr->m_totalPE = totalLogical(); d_ptr->m_freePE = getFreePE(vgName); d_ptr->m_allocPE = d_ptr->m_totalPE - d_ptr->m_freePE; d_ptr->m_UUID = getUUID(vgName); d_ptr->m_LVPathList = getLVs(vgName); d_ptr->m_LVSizeMap = std::make_unique>(); initPartitions(); } /** * shared list of PV's paths that will be added to any VGs. * (have been added to an operation, but not yet applied) */ QVector LvmDevice::s_DirtyPVs; /** * shared list of PVs paths that are member of VGs that will be deleted soon. */ QVector LvmDevice::s_OrphanPVs; LvmDevice::~LvmDevice() { } void LvmDevice::initPartitions() { qint64 firstUsable = 0; qint64 lastUsable = totalPE() - 1; PartitionTable* pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastUsable); for (const auto &p : scanPartitions(pTable)) { LVSizeMap()->insert(p->partitionPath(), p->length()); pTable->append(p); } if (pTable) pTable->updateUnallocated(*this); else pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastUsable); setPartitionTable(pTable); } /** * Scan LVM LV Partitions * * @param pTable Virtual PartitionTable of LVM device * @return an initialized Partition(LV) list */ const QList LvmDevice::scanPartitions(PartitionTable* pTable) const { QList pList; for (const auto &lvPath : partitionNodes()) { Partition *p = scanPartition(lvPath, pTable); pList.append(p); } return pList; } /** scan and construct a partition(LV) at a given path * * NOTE: * LVM partition has 2 different start and end sector values * 1. representing the actual LV start from 0 -> size of LV - 1 * 2. representing abstract LV's sector inside a VG partitionTable * start from last sector + 1 of last Partitions -> size of LV - 1 * Reason for this is for the LV Partition to work nicely with other parts of the codebase * without too many special cases. * * @param lvPath LVM Logical Volume path * @param pTable Abstract partition table representing partitions of LVM Volume Group * @return initialized Partition(LV) */ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTable) const { activateLV(lvPath); qint64 lvSize = getTotalLE(lvPath); qint64 startSector = mappedSector(lvPath, 0); qint64 endSector = startSector + lvSize - 1; FileSystem::Type type = FileSystem::detectFileSystem(lvPath); FileSystem* fs = FileSystemFactory::create(type, 0, lvSize - 1, logicalSize()); fs->scan(lvPath); PartitionRole::Roles r = PartitionRole::Lvm_Lv; QString mountPoint; bool mounted; // Handle LUKS partition if (fs->type() == FileSystem::Type::Luks) { 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, lvPath); mounted = FileSystem::detectMountStatus(fs, lvPath); if (mountPoint != QString() && fs->type() != FileSystem::Type::LinuxSwap) { const QStorageInfo storage = QStorageInfo(mountPoint); if (logicalSize() > 0 && fs->type() != FileSystem::Type::Luks && mounted && storage.isValid()) fs->setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / logicalSize() ); } else if (fs->supportGetUsed() == FileSystem::cmdSupportFileSystem) fs->setSectorsUsed(qCeil(fs->readUsedCapacity(lvPath) / static_cast(logicalSize()))); } if (fs->supportGetLabel() != FileSystem::cmdSupportNone) { fs->setLabel(fs->readLabel(lvPath)); } if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(lvPath)); Partition* part = new Partition(pTable, *this, PartitionRole(r), fs, startSector, endSector, lvPath, PartitionTable::Flag::FlagNone, mountPoint, mounted); return part; } /** scan and construct list of initialized LvmDevice objects. * * @param devices list of initialized Devices */ void LvmDevice::scanSystemLVM(QList& devices) { LvmDevice::s_OrphanPVs.clear(); QList lvmList; for (const auto &vgName : getVGs()) { lvmList.append(new LvmDevice(vgName)); } // Some LVM operations require additional information about LVM physical volumes which we store in LVM::pvList::list() LVM::pvList::list().clear(); LVM::pvList::list().append(FS::lvm2_pv::getPVs(devices)); // Look for LVM physical volumes in LVM VGs for (const auto &d : lvmList) { devices.append(d); LVM::pvList::list().append(FS::lvm2_pv::getPVinNode(d->partitionTable())); } // Inform LvmDevice about which physical volumes form that particular LvmDevice for (const auto &d : lvmList) for (const auto &p : qAsConst(LVM::pvList::list())) if (p.vgName() == d->name()) d->physicalVolumes().append(p.partition()); } qint64 LvmDevice::mappedSector(const QString& lvPath, qint64 sector) const { qint64 mSector = 0; QStringList lvpathList = partitionNodes(); qint32 devIndex = lvpathList.indexOf(lvPath); if (devIndex) { for (int i = 0; i < devIndex; i++) { mSector += LVSizeMap()->value(lvpathList[i]); } mSector += sector; } return mSector; } const QStringList LvmDevice::deviceNodes() const { QStringList pvList; for (const auto &p : physicalVolumes()) { if (p->roles().has(PartitionRole::Luks)) pvList << static_cast(&p->fileSystem())->mapperName(); else pvList << p->partitionPath(); } return pvList; } const QStringList& LvmDevice::partitionNodes() const { return d_ptr->m_LVPathList; } qint64 LvmDevice::partitionSize(QString& partitionPath) const { return LVSizeMap()->value(partitionPath); } const QStringList LvmDevice::getVGs() { QStringList vgList; QString output = getField(QStringLiteral("vg_name")); if (!output.isEmpty()) { const QStringList vgNameList = output.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const auto &vgName : vgNameList) { vgList.append(vgName.trimmed()); } } return vgList; } const QStringList LvmDevice::getLVs(const QString& vgName) { QStringList lvPathList; QString cmdOutput = getField(QStringLiteral("lv_path"), vgName); if (cmdOutput.size()) { const QStringList tempPathList = cmdOutput.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const auto &lvPath : tempPathList) { lvPathList.append(lvPath.trimmed()); } } return lvPathList; } qint64 LvmDevice::getPeSize(const QString& vgName) { QString val = getField(QStringLiteral("vg_extent_size"), vgName); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 LvmDevice::getTotalPE(const QString& vgName) { QString val = getField(QStringLiteral("vg_extent_count"), vgName); return val.isEmpty() ? -1 : val.toInt(); } qint64 LvmDevice::getAllocatedPE(const QString& vgName) { return getTotalPE(vgName) - getFreePE(vgName); } qint64 LvmDevice::getFreePE(const QString& vgName) { QString val = getField(QStringLiteral("vg_free_count"), vgName); return val.isEmpty() ? -1 : val.toInt(); } QString LvmDevice::getUUID(const QString& vgName) { QString val = getField(QStringLiteral("vg_uuid"), vgName); return val.isEmpty() ? QStringLiteral("---") : val; } /** Get LVM vgs command output with field name * * @param fieldName LVM field name * @param vgName the name of LVM Volume Group * @return raw output of command output, usually with many spaces within the returned string * */ QString LvmDevice::getField(const QString& fieldName, const QString& vgName) { QStringList args = { QStringLiteral("vgs"), QStringLiteral("--foreign"), QStringLiteral("--readonly"), QStringLiteral("--noheadings"), QStringLiteral("--units"), QStringLiteral("B"), QStringLiteral("--nosuffix"), QStringLiteral("--options"), fieldName }; if (!vgName.isEmpty()) { args << vgName; } ExternalCommand cmd(QStringLiteral("lvm"), args, QProcess::ProcessChannelMode::SeparateChannels); if (cmd.run(-1) && cmd.exitCode() == 0) { return cmd.output().trimmed(); } return QString(); } qint64 LvmDevice::getTotalLE(const QString& lvPath) { ExternalCommand cmd(QStringLiteral("lvm"), { QStringLiteral("lvdisplay"), lvPath}); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Current LE\\h+(\\d+)")); QRegularExpressionMatch match = re.match(cmd.output()); if (match.hasMatch()) { return match.captured(1).toInt(); } } Log(Log::Level::error) << xi18nc("@info:status", "An error occurred while running lvdisplay."); return -1; } bool LvmDevice::removeLV(Report& report, LvmDevice& d, Partition& p) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvremove"), QStringLiteral("--yes"), p.partitionPath()}); if (cmd.run(-1) && cmd.exitCode() == 0) { d.partitionTable()->remove(&p); return true; } return false; } bool LvmDevice::createLV(Report& report, LvmDevice& d, Partition& p, const QString& lvName) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(p.length()), QStringLiteral("--name"), lvName, d.name()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createLVSnapshot(Report& report, Partition& p, const QString& name, const qint64 extents) { QString numExtents = (extents > 0) ? QString::number(extents) : QString::number(p.length()); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), numExtents, QStringLiteral("--snapshot"), QStringLiteral("--name"), name, p.partitionPath() }); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::resizeLV(Report& report, Partition& p) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvresize"), QStringLiteral("--force"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(p.length()), p.partitionPath()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removePV(Report& report, LvmDevice& d, const QString& pvPath) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgreduce"), d.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::insertPV(Report& report, LvmDevice& d, const QString& pvPath) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgextend"), QStringLiteral("--yes"), d.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::movePV(Report& report, const QString& pvPath, const QStringList& destinations) { if (FS::lvm2_pv::getAllocatedPE(pvPath) <= 0) return true; QStringList args = { QStringLiteral("pvmove") }; args << pvPath; if (!destinations.isEmpty()) for (const auto &destPath : destinations) args << destPath.trimmed(); ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createVG(Report& report, const QString vgName, const QVector& pvList, const qint32 peSize) { QStringList args = { QStringLiteral("vgcreate"), QStringLiteral("--physicalextentsize"), QString::number(peSize) }; args << vgName; for (const auto &p : pvList) { if (p->roles().has(PartitionRole::Luks)) args << static_cast(&p->fileSystem())->mapperName(); else args << p->partitionPath(); } ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removeVG(Report& report, LvmDevice& d) { bool deactivated = deactivateVG(report, d); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgremove"), QStringLiteral("--force"), d.name() }); return (deactivated && cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::deactivateVG(Report& report, const LvmDevice& d) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("vgchange"), QStringLiteral("--activate"), QStringLiteral("n"), d.name() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::deactivateLV(Report& report, const Partition& p) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("lvchange"), QStringLiteral("--activate"), QStringLiteral("n"), p.partitionPath() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::activateVG(Report& report, const LvmDevice& d) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("vgchange"), QStringLiteral("--activate"), QStringLiteral("y"), d.name() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::activateLV(const QString& lvPath) { ExternalCommand deactivate(QStringLiteral("lvm"), { QStringLiteral("lvchange"), QStringLiteral("--activate"), QStringLiteral("y"), lvPath }); return deactivate.run(-1) && deactivate.exitCode() == 0; } qint64 LvmDevice::peSize() const { return d_ptr->m_peSize; } qint64 LvmDevice::totalPE() const { return d_ptr->m_totalPE; } qint64 LvmDevice::allocatedPE() const { return d_ptr->m_allocPE; } qint64 LvmDevice::freePE() const { return d_ptr->m_freePE; } QString LvmDevice::UUID() const { return d_ptr->m_UUID; } -QVector & LvmDevice::physicalVolumes() -{ - return d_ptr->m_PVs; -} - -const QVector & LvmDevice::physicalVolumes() const -{ - return d_ptr->m_PVs; -} - std::unique_ptr>& LvmDevice::LVSizeMap() const { return d_ptr->m_LVSizeMap; } diff --git a/src/core/lvmdevice.h b/src/core/lvmdevice.h index 089719b..bbf608f 100644 --- a/src/core/lvmdevice.h +++ b/src/core/lvmdevice.h @@ -1,109 +1,107 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #ifndef KPMCORE_LVMDEVICE_H #define KPMCORE_LVMDEVICE_H #include "core/device.h" #include "core/volumemanagerdevice.h" #include "util/libpartitionmanagerexport.h" #include #include #include #include #include #include class PartitionTable; class Report; class Partition; class SmartStatus; /** Representation of LVM Volume Group(VG). Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. @see Device, VolumeManagerDevice, PartitionTable, Partition */ class LIBKPMCORE_EXPORT LvmDevice : public VolumeManagerDevice { Q_DISABLE_COPY(LvmDevice) public: LvmDevice(const QString& name, const QString& iconName = QString()); ~LvmDevice(); public: const QStringList deviceNodes() const override; const QStringList& partitionNodes() const override; qint64 partitionSize(QString& partitionPath) const override; static QVector s_DirtyPVs; static QVector s_OrphanPVs; static void scanSystemLVM(QList& devices); static const QStringList getVGs(); static const QStringList getLVs(const QString& vgName); static qint64 getPeSize(const QString& vgName); static qint64 getTotalPE(const QString& vgName); static qint64 getAllocatedPE(const QString& vgName); static qint64 getFreePE(const QString& vgName); static QString getUUID(const QString& vgName); static QString getField(const QString& fieldName, const QString& vgName = QString()); static qint64 getTotalLE(const QString& lvPath); static bool removeLV(Report& report, LvmDevice& d, Partition& p); static bool createLV(Report& report, LvmDevice& d, Partition& p, const QString& lvName); static bool createLVSnapshot(Report& report, Partition& p, const QString& name, const qint64 extents = 0); static bool resizeLV(Report& report, Partition& p); static bool deactivateLV(Report& report, const Partition& p); static bool activateLV(const QString& deviceNode); static bool removePV(Report& report, LvmDevice& d, const QString& pvPath); static bool insertPV(Report& report, LvmDevice& d, const QString& pvPath); static bool movePV(Report& report, const QString& pvPath, const QStringList& destinations = QStringList()); static bool removeVG(Report& report, LvmDevice& d); static bool createVG(Report& report, const QString vgName, const QVector& pvList, const qint32 peSize = 4); // peSize in megabytes static bool deactivateVG(Report& report, const LvmDevice& d); static bool activateVG(Report& report, const LvmDevice& d); protected: void initPartitions() override; const QList scanPartitions(PartitionTable* pTable) const; Partition* scanPartition(const QString& lvPath, PartitionTable* pTable) const; qint64 mappedSector(const QString& lvPath, qint64 sector) const override; public: qint64 peSize() const; qint64 totalPE() const; qint64 allocatedPE() const; qint64 freePE() const; QString UUID() const; - QVector & physicalVolumes(); - const QVector & physicalVolumes() const; protected: std::unique_ptr>& LVSizeMap() const; }; #endif diff --git a/src/core/raid/softwareraid.cpp b/src/core/raid/softwareraid.cpp index bb19d88..fdc32b5 100644 --- a/src/core/raid/softwareraid.cpp +++ b/src/core/raid/softwareraid.cpp @@ -1,539 +1,545 @@ /************************************************************************* * 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 #include #include #define d_ptr std::static_pointer_cast(d) QString SoftwareRAID::s_raidConfigurationFile = QString(); class SoftwareRAIDPrivate : public VolumeManagerDevicePrivate { public: qint32 m_raidLevel; qint64 m_chunkSize; qint64 m_totalChunk; qint64 m_arraySize; QString m_UUID; QStringList m_devicePathList; SoftwareRAID::Status m_status; }; SoftwareRAID::SoftwareRAID(const QString& name, SoftwareRAID::Status status, const QString& iconName) : VolumeManagerDevice(std::make_shared(), name, (QStringLiteral("/dev/") + name), status == SoftwareRAID::Status::Inactive ? 0 : getChunkSize(QStringLiteral("/dev/") + name), status == SoftwareRAID::Status::Inactive ? 0 : 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()); d_ptr->m_status = status; 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; } QString SoftwareRAID::prettyName() const { QString raidInfo; if (status() == SoftwareRAID::Status::Active) raidInfo = xi18nc("@item:inlistbox [RAID level]", " [RAID %1]", raidLevel()); else if (status() == SoftwareRAID::Status::Recovery) raidInfo = xi18nc("@item:inlistbox [RAID level - Recovering]", " [RAID %1 - Recovering]", raidLevel()); else if (status() == SoftwareRAID::Status::Resync) raidInfo = xi18nc("@item:inlistbox [RAID level - Resyncing]", " [RAID %1 - Resyncing]", raidLevel()); else raidInfo = QStringLiteral(" [RAID]"); return VolumeManagerDevice::prettyName() + raidInfo; } bool SoftwareRAID::operator ==(const Device& other) const { bool equalDeviceNode = Device::operator ==(other); if (other.type() == Device::Type::SoftwareRAID_Device) { const SoftwareRAID& raid = static_cast(other); if (!equalDeviceNode) return raid.uuid() == uuid(); } return equalDeviceNode; } 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; } SoftwareRAID::Status SoftwareRAID::status() const { return d_ptr->m_status; } void SoftwareRAID::setStatus(SoftwareRAID::Status status) { d_ptr->m_status = status; } void SoftwareRAID::scanSoftwareRAID(QList& devices) { QStringList availableInConf; // TODO: Support custom config files. QString config = getRAIDConfiguration(); if (!config.isEmpty()) { QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/([\\/\\w-]+)")); QRegularExpressionMatchIterator i = re.globalMatch(config); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString deviceName = reMatch.captured(2).trimmed(); availableInConf << deviceName; } } QFile mdstat(QStringLiteral("/proc/mdstat")); if (mdstat.open(QIODevice::ReadOnly)) { QTextStream stream(&mdstat); QString content = stream.readAll(); mdstat.close(); QRegularExpression re(QStringLiteral("md([\\/\\w]+)\\s+:\\s+([\\w]+)")); QRegularExpressionMatchIterator i = re.globalMatch(content); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(1).trimmed(); QString status = reMatch.captured(2).trimmed(); SoftwareRAID* d = static_cast(CoreBackendManager::self()->backend()->scanDevice(deviceNode)); // Just to prevent segfault in some case if (d == nullptr) continue; const QStringList constAvailableInConf = availableInConf; for (const QString& path : constAvailableInConf) if (getUUID(QStringLiteral("/dev/") + path) == d->uuid()) availableInConf.removeAll(path); devices << d; if (status == QStringLiteral("inactive")) d->setStatus(SoftwareRAID::Status::Inactive); if (d->raidLevel() > 0) { QRegularExpression reMirrorStatus(d->name() + QStringLiteral("\\s+:\\s+(.*\\n\\s+)+\\[[=>.]+\\]\\s+(resync|recovery)")); QRegularExpressionMatch reMirrorStatusMatch = reMirrorStatus.match(content); if (reMirrorStatusMatch.hasMatch()) { if (reMirrorStatusMatch.captured(2) == QStringLiteral("resync")) d->setStatus(SoftwareRAID::Status::Resync); else if (reMirrorStatusMatch.captured(2) == QStringLiteral("recovery")) d->setStatus(SoftwareRAID::Status::Recovery); } } } } for (const QString& name : qAsConst(availableInConf)) { SoftwareRAID *raidDevice = new SoftwareRAID(name, SoftwareRAID::Status::Inactive); devices << raidDevice; } } 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).toInt(); } return -1; } qint64 SoftwareRAID::getChunkSize(const QString &path) { if (getRaidLevel(path) == 1) { QStringList devices = getDevicePathList(path); if (!devices.isEmpty()) { QString device = devices[0]; // Look sector size for the first device/partition on the list, as RAID 1 is composed by mirrored devices ExternalCommand sectorSize(QStringLiteral("blockdev"), { QStringLiteral("--getss"), device }); if (sectorSize.run(-1) && sectorSize.exitCode() == 0) return sectorSize.output().trimmed().toLongLong(); } } else { 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) { QString output = getDetail(path); if (!output.isEmpty()) { QRegularExpression re(QStringLiteral("UUID :\\s+([\\w:]+)")); QRegularExpressionMatch reMatch = re.match(output); if (reMatch.hasMatch()) return reMatch.captured(1); } // If UUID was not found in detail output, it should be searched in config file // TODO: Support custom config files. QString config = getRAIDConfiguration(); if (!config.isEmpty()) { QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/md([\\/\\w-]+)(.*)")); QRegularExpressionMatchIterator i = re.globalMatch(config); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(2).trimmed(); QString otherInfo = reMatch.captured(3).trimmed(); // Consider device node as name=host:deviceNode when the captured device node string has '-' character // It happens when user have included the device to config file using 'mdadm --examine --scan' if (deviceNode.contains(QLatin1Char('-'))) { QRegularExpression reName(QStringLiteral("name=[\\w:]+\\/dev\\/md\\/([\\/\\w]+)")); QRegularExpressionMatch nameMatch = reName.match(otherInfo); if (nameMatch.hasMatch()) deviceNode = nameMatch.captured(1); } if (deviceNode == path) { QRegularExpression reUUID(QStringLiteral("(UUID=|uuid=)([\\w:]+)")); QRegularExpressionMatch uuidMatch = reUUID.match(otherInfo); if (uuidMatch.hasMatch()) return uuidMatch.captured(2); } } } return QString(); } QStringList SoftwareRAID::getDevicePathList(const QString &path) { QStringList result; QString detail = getDetail(path); if (!detail.isEmpty()) { QRegularExpression re(QStringLiteral("\\s+\\/dev\\/(\\w+)")); QRegularExpressionMatchIterator i = re.globalMatch(detail); while (i.hasNext()) { QRegularExpressionMatch match = i.next(); QString device = QStringLiteral("/dev/") + match.captured(1); if (device != path) result << device; } } return result; } bool SoftwareRAID::isRaidPath(const QString &devicePath) { return !getDetail(devicePath).isEmpty(); } bool SoftwareRAID::createSoftwareRAID(Report &report, const QString &name, const QStringList devicePathList, const qint32 raidLevel, const qint32 chunkSize) { QString path = QStringLiteral("/dev/") + name; - QStringList args; - args << QStringLiteral("--create") << path; - args << QStringLiteral("--level=") + QString::number(raidLevel); - args << QStringLiteral("--chunk=") + QString::number(chunkSize); - args << QStringLiteral("--raid-devices=") + QString::number(devicePathList.size()); + QStringList args = { QStringLiteral("--create"), path, + QStringLiteral("--level=") + QString::number(raidLevel), + QStringLiteral("--chunk=") + QString::number(chunkSize), + QStringLiteral("--raid-devices=") + QString::number(devicePathList.size()) }; for (const QString &p : qAsConst(devicePathList)) { eraseDeviceMDSuperblock(p); args << p; } ExternalCommand cmd(report, QStringLiteral("mdadm"), args); if (!cmd.run(-1) || cmd.exitCode() != 0) return false; - updateConfigurationFile(path); + if (updateConfigurationFile(path)) + qDebug() << QStringLiteral("Updated RAID config: ") + path; return true; } bool SoftwareRAID::deleteSoftwareRAID(Report &report, SoftwareRAID &raidDevice) { Q_UNUSED(report) Q_UNUSED(raidDevice) + + // TODO: Need to stop and remove it from config file + // Erase md superblock for every physical partition of it + // The device should be removed from config file + // according to its UID, not by name + return false; } bool SoftwareRAID::assembleSoftwareRAID(const QString& deviceNode) { if (!isRaidPath(deviceNode)) return false; ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--assemble"), QStringLiteral("--scan"), deviceNode, QStringLiteral("--config=") + raidConfigurationFilePath() }); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::stopSoftwareRAID(Report& report, const QString& deviceNode) { if (!isRaidPath(deviceNode)) return false; ExternalCommand cmd(report, QStringLiteral("mdadm"), { QStringLiteral("--manage"), QStringLiteral("--stop"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::reassembleSoftwareRAID(Report& report, const QString &deviceNode) { // TODO: Include report return stopSoftwareRAID(report, deviceNode) && assembleSoftwareRAID(deviceNode); } QString SoftwareRAID::getRaidArrayName(const QString &partitionPath) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--query"), partitionPath }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression ex(QStringLiteral("device active raid\\d+\\s([\\/\\w]+).")); QRegularExpressionMatch match = ex.match(cmd.output()); if (match.hasMatch()) return match.captured(1); } return QString(); } void SoftwareRAID::initPartitions() { } qint64 SoftwareRAID::mappedSector(const QString &partitionPath, qint64 sector) const { Q_UNUSED(partitionPath); Q_UNUSED(sector); return -1; } bool SoftwareRAID::eraseDeviceMDSuperblock(const QString &path) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--zero-superblock"), path}); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::updateConfigurationFile(const QString &path) { ExternalCommand cmd(QStringLiteral("/usr/") + QStringLiteral(LIBEXECDIRPATH) + QStringLiteral("/kpmcore_mdadmupdateconf"), { path, raidConfigurationFilePath() }); return cmd.run(-1) && cmd.exitCode() == 0; } QString SoftwareRAID::getDetail(const QString &path) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--detail"), path }); return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); } QString SoftwareRAID::getRAIDConfiguration() { QFile config(raidConfigurationFilePath()); if (!config.open(QIODevice::ReadOnly)) return QString(); QTextStream stream(&config); QString result = stream.readAll(); config.close(); return result; } QString SoftwareRAID::getDeviceInformation(const QString &deviceName) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--detail"), QStringLiteral("--scan"), deviceName }); // TODO: Get only information about the device line. // Because if there is any error on config file, it will print more information than needed. return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); } void SoftwareRAID::setRaidConfigurationFilePath(const QString &filePath) { s_raidConfigurationFile = filePath; } QString SoftwareRAID::raidConfigurationFilePath() { if (s_raidConfigurationFile.isEmpty()) s_raidConfigurationFile = getDefaultRaidConfigFile(); return s_raidConfigurationFile; } QString SoftwareRAID::getDefaultRaidConfigFile() { if (QFile::exists(QStringLiteral("/etc/mdadm.conf"))) return QStringLiteral("/etc/mdadm.conf"); else if (QFile::exists(QStringLiteral("/etc/mdadm/mdadm.conf"))) return QStringLiteral("/etc/mdadm/mdadm.conf"); return QString(); } diff --git a/src/core/volumemanagerdevice.cpp b/src/core/volumemanagerdevice.cpp index 7c339af..aadc660 100644 --- a/src/core/volumemanagerdevice.cpp +++ b/src/core/volumemanagerdevice.cpp @@ -1,49 +1,62 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2018 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 "core/device_p.h" +#include "core/partition.h" #include "core/volumemanagerdevice.h" #include "core/volumemanagerdevice_p.h" +#define d_ptr std::static_pointer_cast(d) + /** Constructs an abstract Volume Manager Device with an empty PartitionTable. * * @param name the Device's name * @param deviceNode the Device's node * @param logicalExtentSize the logical extent size that device uses */ VolumeManagerDevice::VolumeManagerDevice(std::shared_ptr d, const QString& name, const QString& deviceNode, const qint64 logicalExtentSize, const qint64 totalLogical, const QString& iconName, Device::Type type) : Device(std::static_pointer_cast(d), name, deviceNode, logicalExtentSize, totalLogical, iconName, type) { } QString VolumeManagerDevice::prettyDeviceNodeList() const { return deviceNodes().join(QStringLiteral(", ")); } void VolumeManagerDevice::setTotalLogical(qint64 n) { Q_ASSERT(n > 0); - d->m_TotalLogical = n; + d_ptr->m_TotalLogical = n; +} + +QVector& VolumeManagerDevice::physicalVolumes() +{ + return d_ptr->m_PVs; +} + +const QVector& VolumeManagerDevice::physicalVolumes() const +{ + return d_ptr->m_PVs; } diff --git a/src/core/volumemanagerdevice.h b/src/core/volumemanagerdevice.h index f1750dd..6f3aa69 100644 --- a/src/core/volumemanagerdevice.h +++ b/src/core/volumemanagerdevice.h @@ -1,96 +1,101 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * 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_VOLUMEMANAGERDEVICE_H #define KPMCORE_VOLUMEMANAGERDEVICE_H #include "util/libpartitionmanagerexport.h" #include "core/device.h" #include #include #include #include +class Partition; class VolumeManagerDevicePrivate; /** A Volume Manager of physical devices represented as an abstract device. * * VolumeManagerDevice is an abstract device class for volume manager. e.g: LVM, SoftRAID. * example of physical device: /dev/sda, /dev/sdb1. * * Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. * * @see Device, PartitionTable, Partition */ class LIBKPMCORE_EXPORT VolumeManagerDevice : public Device { Q_DISABLE_COPY(VolumeManagerDevice) public: VolumeManagerDevice(std::shared_ptr d, const QString& name, const QString& deviceNode, const qint64 logicalSectorSize, const qint64 totalLogical, const QString& iconName = QString(), Device::Type type = Device::Type::Unknown_Device); /** * @return list of physical device's path that makes up volumeManagerDevice.(e.g: /dev/sda, /dev/sdb1) */ virtual const QStringList deviceNodes() const = 0; /** * @return list of logical partition's path. */ virtual const QStringList& partitionNodes() const = 0; /** * @return size of logical partition at the given path in bytes. */ virtual qint64 partitionSize(QString& partitionPath) const = 0; protected: /** Initialize device's partition table and partitions. * */ virtual void initPartitions() = 0; /** absolute sector as represented inside the device's partitionTable * * For VolumeMangerDevice to works with the rest of the codebase, partitions are stringed * one after another to create a representation of PartitionTable and partition just like * real disk device. * * @param partitionPath logical partition path * @sector sector value to be mapped (if 0, will return start sector of the partition) * @return absolute sector value as represented inside device's partitionTable */ virtual qint64 mappedSector(const QString& partitionPath, qint64 sector) const = 0; public: /** join deviceNodes together into comma-separated list * * @return comma-separated list of deviceNodes */ virtual QString prettyDeviceNodeList() const; /** Resize device total number of logical sectors. * * @param n Number of sectors. */ void setTotalLogical(qint64 n); + + QVector& physicalVolumes(); + + const QVector& physicalVolumes() const; }; #endif diff --git a/src/core/volumemanagerdevice_p.h b/src/core/volumemanagerdevice_p.h index 6061c26..32afe04 100644 --- a/src/core/volumemanagerdevice_p.h +++ b/src/core/volumemanagerdevice_p.h @@ -1,27 +1,33 @@ /************************************************************************* * Copyright (C) 2018 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 .* *************************************************************************/ #ifndef KPMCORE_VOLUMEMANAGERDEVICE_P_H #define KPMCORE_VOLUMEMANAGERDEVICE_P_H #include "core/device_p.h" +#include + +class Partition; + class VolumeManagerDevicePrivate : public DevicePrivate { +public: + QVector m_PVs; }; #endif diff --git a/src/ops/deactivatevolumegroupoperation.cpp b/src/ops/deactivatevolumegroupoperation.cpp index ad7551f..71784e7 100644 --- a/src/ops/deactivatevolumegroupoperation.cpp +++ b/src/ops/deactivatevolumegroupoperation.cpp @@ -1,80 +1,83 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * 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 "ops/deactivatevolumegroupoperation.h" #include "jobs/deactivatevolumegroupjob.h" #include "jobs/deactivatelogicalvolumejob.h" #include "core/volumemanagerdevice.h" #include "core/partitiontable.h" #include "core/partition.h" #include "core/raid/softwareraid.h" #include #include /** Creates a new RemoveVolumeGroupOperation. */ DeactivateVolumeGroupOperation::DeactivateVolumeGroupOperation(VolumeManagerDevice& d) : Operation(), m_DeactivateVolumeGroupJob(new DeactivateVolumeGroupJob(d)), m_DeactivateLogicalVolumeJob(new DeactivateLogicalVolumeJob(d)), m_Device(d), m_PartitionTable(d.partitionTable()) { addJob(deactivateLogicalVolumeJob()); addJob(deactivateVolumeGroupJob()); } QString DeactivateVolumeGroupOperation::description() const { return xi18nc("@info/plain", "Deactivate volume group."); } void DeactivateVolumeGroupOperation::preview() { m_PartitionTable = device().partitionTable(); device().setPartitionTable(new PartitionTable(PartitionTable::vmd, 0, device().totalLogical() - 1)); } void DeactivateVolumeGroupOperation::undo() { device().setPartitionTable(m_PartitionTable); } /** loop through given device's partitions to check if any is mounted. * * @param dev VolumeManagerDevice with initialized partitions * @return false if any of the device's partition is mounted. */ bool DeactivateVolumeGroupOperation::isDeactivatable(const VolumeManagerDevice* dev) { - if (dev->type() == Device::Type::LVM_Device) { + if (dev->type() != Device::Type::SoftwareRAID_Device && + dev->type() != Device::Type::LVM_Device) + return false; + + if (dev->partitionTable()) { for (const auto &p : dev->partitionTable()->children()) if (p->isMounted()) return false; - - return true; } - else if (dev->type() == Device::Type::SoftwareRAID_Device) { + + if (dev->type() == Device::Type::SoftwareRAID_Device) { const SoftwareRAID* raid = static_cast(dev); return raid->status() == SoftwareRAID::Status::Active; } - return false; + return true; } diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 470e783..2f0fd7e 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -1,264 +1,267 @@ /************************************************************************* * 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) { if (m_device->type() == Device::Type::SoftwareRAID_Device) ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--stop-exec-queue") }).run(); ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); ExternalCommand(QStringLiteral("blockdev"), { QStringLiteral("--rereadpt"), m_device->deviceNode() }).run(); + + QThread::msleep(1000); + ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("trigger") }).run(); if (m_device->type() == Device::Type::SoftwareRAID_Device) ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--start-exec-queue") }).run(); 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) ) { QRegularExpression re(QStringLiteral("Created a new partition (\\d+)")); QRegularExpressionMatch rem = re.match(createCommand.output()); if (rem.hasMatch()) { if ( partition.devicePath().back().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.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; }