diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index 72c0693..dd0b4be 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -1,595 +1,592 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * Copyright (C) 2016 by Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ /** @file */ #include "core/partitiontable.h" #include "core/partition.h" #include "core/device.h" #include "core/diskdevice.h" #include "core/lvmdevice.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/globallog.h" #include #include #include /** Creates a new PartitionTable object with type MSDOS @param type name of the PartitionTable type (e.g. "msdos" or "gpt") */ PartitionTable::PartitionTable(TableType type, qint64 firstUsable, qint64 lastUsable) : PartitionNode() , m_Children() , m_MaxPrimaries(maxPrimariesForTableType(type)) , m_Type(type) , m_FirstUsable(firstUsable) , m_LastUsable(lastUsable) { } /** Copy constructor for PartitionTable. * @param other the other PartitionTable. */ PartitionTable::PartitionTable(const PartitionTable& other) : PartitionNode() , m_Children() , m_MaxPrimaries(other.m_MaxPrimaries) , m_Type(other.m_Type) , m_FirstUsable(other.m_FirstUsable) , m_LastUsable(other.m_LastUsable) { for (Partitions::const_iterator it = other.m_Children.constBegin(); it != other.m_Children.constEnd(); ++it) { m_Children.append(new Partition(**it, this)); } } /** Destroys a PartitionTable object, destroying all children */ PartitionTable::~PartitionTable() { clearChildren(); } /** Gets the number of free sectors before a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors before @returns the number of free sectors before the Partition */ qint64 PartitionTable::freeSectorsBefore(const Partition& p) const { const Partition* pred = predecessor(p); // due to the space required for extended boot records the // below is NOT the same as pred->length() if (pred && pred->roles().has(PartitionRole::Unallocated)) return p.firstSector() - pred->firstSector(); return 0; } /** Gets the number of free sectors after a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors after @returns the number of free sectors after the Partition */ qint64 PartitionTable::freeSectorsAfter(const Partition& p) const { const Partition* succ = successor(p); // due to the space required for extended boot records the // below is NOT the same as succ->length() if (succ && succ->roles().has(PartitionRole::Unallocated)) return succ->lastSector() - p.lastSector(); return 0; } qint64 PartitionTable::freeSectors() const { qint64 sectors = 0; for (const auto &p : children()) { if (p->roles().has(PartitionRole::Unallocated)) { sectors += p->length(); } } return sectors; } /** @return true if the PartitionTable has an extended Partition */ bool PartitionTable::hasExtended() const { for (const auto &p : children()) if (p->roles().has(PartitionRole::Extended)) return true; return false; } /** @return pointer to the PartitionTable's extended Partition or nullptr if none exists */ Partition* PartitionTable::extended() const { for (const auto &p : children()) if (p->roles().has(PartitionRole::Extended)) return p; return nullptr; } /** Gets valid PartitionRoles for a Partition @param p the Partition @return valid roles for the given Partition */ PartitionRole::Roles PartitionTable::childRoles(const Partition& p) const { Q_ASSERT(p.parent()); PartitionRole::Roles r = p.parent()->isRoot() ? PartitionRole::Primary : PartitionRole::Logical; if (r == PartitionRole::Primary && hasExtended() == false && tableTypeSupportsExtended(type())) r |= PartitionRole::Extended; return r; } /** @return the number of primaries in this PartitionTable */ int PartitionTable::numPrimaries() const { int result = 0; for (const auto &p : children()) if (p->roles().has(PartitionRole::Primary) || p->roles().has(PartitionRole::Extended)) result++; return result; } /** Appends a Partition to this PartitionTable @param partition pointer of the partition to append. Must not be nullptr. */ void PartitionTable::append(Partition* partition) { children().append(partition); std::sort(children().begin(), children().end(), [] (const Partition *a, const Partition *b) -> bool {return a->firstSector() < b->firstSector();}); } /** @param f the flag to get the name for @returns the flags name or an empty QString if the flag is not known */ QString PartitionTable::flagName(Flag f) { switch (f) { case PartitionTable::FlagBoot: return xi18nc("@item partition flag", "boot"); case PartitionTable::FlagRoot: return xi18nc("@item partition flag", "root"); case PartitionTable::FlagSwap: return xi18nc("@item partition flag", "swap"); case PartitionTable::FlagHidden: return xi18nc("@item partition flag", "hidden"); case PartitionTable::FlagRaid: return xi18nc("@item partition flag", "raid"); case PartitionTable::FlagLvm: return xi18nc("@item partition flag", "lvm"); case PartitionTable::FlagLba: return xi18nc("@item partition flag", "lba"); case PartitionTable::FlagHpService: return xi18nc("@item partition flag", "hpservice"); case PartitionTable::FlagPalo: return xi18nc("@item partition flag", "palo"); case PartitionTable::FlagPrep: return xi18nc("@item partition flag", "prep"); case PartitionTable::FlagMsftReserved: return xi18nc("@item partition flag", "msft-reserved"); case PartitionTable::FlagBiosGrub: return xi18nc("@item partition flag", "bios-grub"); case PartitionTable::FlagAppleTvRecovery: return xi18nc("@item partition flag", "apple-tv-recovery"); case PartitionTable::FlagDiag: return xi18nc("@item partition flag", "diag"); case PartitionTable::FlagLegacyBoot: return xi18nc("@item partition flag", "legacy-boot"); case PartitionTable::FlagMsftData: return xi18nc("@item partition flag", "msft-data"); case PartitionTable::FlagIrst: return xi18nc("@item partition flag", "irst"); - case PartitionTable::FlagEsp: - return xi18nc("@item partition flag", "esp"); default: break; } return QString(); } /** @return list of all flags */ const QList PartitionTable::flagList() { QList rval; rval.append(PartitionTable::FlagBoot); rval.append(PartitionTable::FlagRoot); rval.append(PartitionTable::FlagSwap); rval.append(PartitionTable::FlagHidden); rval.append(PartitionTable::FlagRaid); rval.append(PartitionTable::FlagLvm); rval.append(PartitionTable::FlagLba); rval.append(PartitionTable::FlagHpService); rval.append(PartitionTable::FlagPalo); rval.append(PartitionTable::FlagPrep); rval.append(PartitionTable::FlagMsftReserved); rval.append(PartitionTable::FlagBiosGrub); rval.append(PartitionTable::FlagAppleTvRecovery); rval.append(PartitionTable::FlagDiag); rval.append(PartitionTable::FlagLegacyBoot); rval.append(PartitionTable::FlagMsftData); rval.append(PartitionTable::FlagIrst); - rval.append(PartitionTable::FlagEsp); return rval; } /** @param flags the flags to get the names for @returns QStringList of the flags' names */ QStringList PartitionTable::flagNames(Flags flags) { QStringList rval; int f = 1; QString s; while (!(s = flagName(static_cast(f))).isEmpty()) { if (flags & f) rval.append(s); f <<= 1; } return rval; } bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end) { if (d.type() == Device::Type::Disk_Device) { const DiskDevice& device = dynamic_cast(d); if (!parent.isRoot()) { Partition* extended = dynamic_cast(&parent); if (extended == nullptr) { qWarning() << "extended is null. start: " << start << ", end: " << end << ", device: " << device.deviceNode(); return false; } // Leave a track (cylinder aligned) or sector alignment sectors (sector based) free at the // start for a new partition's metadata start += device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); // .. and also at the end for the metadata for a partition to follow us, if we're not // at the end of the extended partition if (end < extended->lastSector()) end -= device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); } return end - start + 1 >= PartitionAlignment::sectorAlignment(device); } else if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { if (end - start + 1 > 0) { return true; } } return false; } /** Creates a new unallocated Partition on the given Device. @param device the Device to create the new Partition on @param parent the parent PartitionNode for the new Partition @param start the new Partition's start sector @param end the new Partition's end sector @return pointer to the newly created Partition object or nullptr if the Partition could not be created */ Partition* createUnallocated(const Device& device, PartitionNode& parent, qint64 start, qint64 end) { PartitionRole::Roles r = PartitionRole::Unallocated; if (!parent.isRoot()) r |= PartitionRole::Logical; // Mark unallocated space in LVM VG as LVM LV so that pasting can be easily disabled (it does not work yet) if (device.type() == Device::Type::LVM_Device) r |= PartitionRole::Lvm_Lv; if (!PartitionTable::getUnallocatedRange(device, parent, start, end)) return nullptr; return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Type::Unknown, start, end, device.logicalSize()), start, end, QString()); } /** Removes all unallocated children from a PartitionNode @param p pointer to the parent to remove unallocated children from */ void PartitionTable::removeUnallocated(PartitionNode* p) { Q_ASSERT(p); qint32 i = 0; while (i < p->children().size()) { Partition* child = p->children()[i]; if (child->roles().has(PartitionRole::Unallocated)) { p->remove(child); delete child; continue; } if (child->roles().has(PartitionRole::Extended)) removeUnallocated(child); i++; } } /** @overload */ void PartitionTable::removeUnallocated() { removeUnallocated(this); } /** Inserts unallocated children for a Device's PartitionTable with the given parent. This method inserts unallocated Partitions for a parent, usually the Device this PartitionTable is on. It will also insert unallocated Partitions in any extended Partitions it finds. @warning This method assumes that no unallocated Partitions exist when it is called. @param d the Device this PartitionTable and @p p are on @param p the parent PartitionNode (may be this or an extended Partition) @param start the first sector to begin looking for free space */ void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 start) { Q_ASSERT(p); qint64 lastEnd = start; if (d.type() == Device::Type::LVM_Device && !p->children().isEmpty()) { // rearranging the sectors of all partitions to keep unallocated space at the end lastEnd = 0; std::sort(children().begin(), children().end(), [](const Partition* p1, const Partition* p2) { return p1->deviceNode() < p2->deviceNode(); }); for (const auto &child : children()) { qint64 totalSectors = child->length(); child->setFirstSector(lastEnd); child->setLastSector(lastEnd + totalSectors - 1); lastEnd += totalSectors; } } else { const auto pChildren = p->children(); for (const auto &child : pChildren) { p->insert(createUnallocated(d, *p, lastEnd, child->firstSector() - 1)); if (child->roles().has(PartitionRole::Extended)) insertUnallocated(d, child, child->firstSector()); lastEnd = child->lastSector() + 1; } } if (d.type() == Device::Type::LVM_Device) { const LvmDevice& lvm = static_cast(d); p->insert(createUnallocated(d, *p, lastEnd, lastEnd + lvm.freePE() - 1)); } else { // Take care of the free space between the end of the last child and the end // of the device or the extended partition. qint64 parentEnd = lastUsable(); if (!p->isRoot()) { Partition* extended = dynamic_cast(p); parentEnd = extended ? extended->lastSector() : -1; Q_ASSERT(extended); } if (parentEnd >= firstUsable() && parentEnd >= lastEnd) p->insert(createUnallocated(d, *p, lastEnd, parentEnd)); } } /** Updates the unallocated Partitions for this PartitionTable. @param d the Device this PartitionTable is on */ void PartitionTable::updateUnallocated(const Device& d) { removeUnallocated(); insertUnallocated(d, this, firstUsable()); } qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t) { Q_UNUSED(t) if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { return 0; } const DiskDevice& diskDevice = dynamic_cast(d); return PartitionAlignment::sectorAlignment(diskDevice); } qint64 PartitionTable::defaultLastUsable(const Device& d, TableType t) { if (t == gpt) return d.totalLogical() - 1 - 32 - 1; return d.totalLogical() - 1; } static struct { const QLatin1String name; /**< name of partition table type */ quint32 maxPrimaries; /**< max numbers of primary partitions supported */ bool canHaveExtended; /**< does partition table type support extended partitions */ bool isReadOnly; /**< does KDE Partition Manager support this only in read only mode */ PartitionTable::TableType type; /**< enum type */ } tableTypes[] = { { QLatin1String("aix"), 4, false, true, PartitionTable::aix }, { QLatin1String("bsd"), 8, false, true, PartitionTable::bsd }, { QLatin1String("dasd"), 1, false, true, PartitionTable::dasd }, { QLatin1String("msdos"), 4, true, false, PartitionTable::msdos }, { QLatin1String("msdos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QLatin1String("dos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QLatin1String("dvh"), 16, true, true, PartitionTable::dvh }, { QLatin1String("gpt"), 128, false, false, PartitionTable::gpt }, { QLatin1String("loop"), 1, false, true, PartitionTable::loop }, { QLatin1String("mac"), 0xffff, false, true, PartitionTable::mac }, { QLatin1String("pc98"), 16, false, true, PartitionTable::pc98 }, { QLatin1String("amiga"), 128, false, true, PartitionTable::amiga }, { QLatin1String("sun"), 8, false, true, PartitionTable::sun }, { QLatin1String("vmd"), 0xffff, false, false, PartitionTable::vmd } }; PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) { for (const auto &type : tableTypes) if (n == type.name) return type.type; return PartitionTable::unknownTableType; } QString PartitionTable::tableTypeToName(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.name; return xi18nc("@item partition table name", "unknown"); } qint32 PartitionTable::maxPrimariesForTableType(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.maxPrimaries; return 1; } bool PartitionTable::tableTypeSupportsExtended(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.canHaveExtended; return false; } bool PartitionTable::tableTypeIsReadOnly(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.isReadOnly; return false; } /** Simple heuristic to determine if the PartitionTable is sector aligned (i.e. if its Partitions begin at sectors evenly divisable by PartitionAlignment::sectorAlignment(). @return true if is sector aligned, otherwise false */ bool PartitionTable::isSectorBased(const Device& d) const { if (d.type() == Device::Type::Disk_Device) { const DiskDevice& diskDevice = dynamic_cast(d); if (type() == PartitionTable::msdos) { // the default for empty partition tables is sector based if (numPrimaries() == 0) return true; quint32 numCylinderAligned = 0; quint32 numSectorAligned = 0; // see if we have more cylinder aligned partitions than sector // aligned ones. for (const auto &p : children()) { if (p->firstSector() % PartitionAlignment::sectorAlignment(diskDevice) == 0) numSectorAligned++; else if (p->firstSector() % diskDevice.cylinderSize() == 0) numCylinderAligned++; } return numSectorAligned >= numCylinderAligned; } return type() == PartitionTable::msdos_sectorbased; } return false; } void PartitionTable::setType(const Device& d, TableType t) { setFirstUsableSector(defaultFirstUsable(d, t)); setLastUsableSector(defaultLastUsable(d, t)); m_Type = t; updateUnallocated(d); } QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable) { stream << "type: \"" << ptable.typeName() << "\"\n" << "align: \"" << (ptable.type() == PartitionTable::msdos ? "cylinder" : "sector") << "\"\n" << "\n# number start end type roles label flags\n"; QList partitions; for (const auto &p : ptable.children()) { if (!p->roles().has(PartitionRole::Unallocated)) { partitions.append(p); if (p->roles().has(PartitionRole::Extended)) { const auto partChildren = p->children(); for (const auto &child : partChildren) { if (!child->roles().has(PartitionRole::Unallocated)) partitions.append(child); } } } } std::sort(partitions.begin(), partitions.end(), [](const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); }); for (const auto &p : qAsConst(partitions)) stream << *p; return stream; } diff --git a/src/core/partitiontable.h b/src/core/partitiontable.h index d0ba741..de5f3ef 100644 --- a/src/core/partitiontable.h +++ b/src/core/partitiontable.h @@ -1,204 +1,204 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * Copyright (C) 2016 by Teo Mrnjavac * * * * 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_PARTITIONTABLE_H) #define KPMCORE_PARTITIONTABLE_H #include "util/libpartitionmanagerexport.h" #include "core/partitionnode.h" #include "core/partitionrole.h" #include #include class Device; class Partition; class CoreBackend; class QTextStream; /** The partition table (a.k.a Disk Label) PartitionTable represents a partition table (or disk label). PartitionTable has child nodes that represent Partitions. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartitionTable : public PartitionNode { PartitionTable &operator=(const PartitionTable &) = delete; friend class CoreBackend; friend LIBKPMCORE_EXPORT QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); public: enum TableType : qint8 { unknownTableType = -1, aix, bsd, dasd, msdos, msdos_sectorbased, dvh, gpt, loop, mac, pc98, amiga, sun, vmd /* Volume Manager Device */ }; /** Partition flags */ enum Flag : qint32 { FlagNone = 0, FlagBoot = 1, FlagRoot = 2, FlagSwap = 4, FlagHidden = 8, FlagRaid = 16, FlagLvm = 32, FlagLba = 64, FlagHpService = 128, FlagPalo = 256, FlagPrep = 512, FlagMsftReserved = 1024, FlagBiosGrub = 2048, FlagAppleTvRecovery = 4096, FlagDiag = 8192, FlagLegacyBoot = 16384, FlagMsftData = 32768, FlagIrst = 65536, - FlagEsp = 131072 + FlagEsp [[deprecated]] = FlagBoot }; Q_DECLARE_FLAGS(Flags, Flag) Q_FLAG(Flag) public: PartitionTable(TableType type, qint64 firstUsable, qint64 lastUsable); PartitionTable(const PartitionTable& other); ~PartitionTable() override; public: PartitionNode* parent() override { return nullptr; /**< @return always nullptr for PartitionTable */ } const PartitionNode* parent() const override { return nullptr; /**< @return always nullptr for PartitionTable */ } bool isRoot() const override { return true; /**< @return always true for PartitionTable */ } bool isReadOnly() const { return tableTypeIsReadOnly(type()); /**< @return true if the PartitionTable is read only */ } Partitions& children() override { return m_Children; /**< @return the children in this PartitionTable */ } const Partitions& children() const override { return m_Children; /**< @return the children in this PartitionTable */ } void setType(const Device& d, TableType t); void append(Partition* partition) override; qint64 freeSectorsBefore(const Partition& p) const; qint64 freeSectorsAfter(const Partition& p) const; qint64 freeSectors() const; bool hasExtended() const; Partition* extended() const; PartitionRole::Roles childRoles(const Partition& p) const; qint32 numPrimaries() const; qint32 maxPrimaries() const { return m_MaxPrimaries; /**< @return max number of primary partitions this PartitionTable can handle */ } PartitionTable::TableType type() const { return m_Type; /**< @return the PartitionTable's type */ } const QString typeName() const { return tableTypeToName(type()); /**< @return the name of this PartitionTable type */ } qint64 firstUsable() const { return m_FirstUsable; } qint64 lastUsable() const { return m_LastUsable; } void setFirstUsableSector(qint64 s) { m_FirstUsable = s; } void setLastUsableSector(qint64 s) { m_LastUsable = s; } void updateUnallocated(const Device& d); void insertUnallocated(const Device& d, PartitionNode* p, qint64 start); bool isSectorBased(const Device& d) const; static const QList flagList(); static QString flagName(Flag f); static QStringList flagNames(Flags f); static bool getUnallocatedRange(const Device& device, PartitionNode& parent, qint64& start, qint64& end); static void removeUnallocated(PartitionNode* p); void removeUnallocated(); static qint64 defaultFirstUsable(const Device& d, TableType t); static qint64 defaultLastUsable(const Device& d, TableType t); static PartitionTable::TableType nameToTableType(const QString& n); static QString tableTypeToName(TableType l); static qint32 maxPrimariesForTableType(TableType l); static bool tableTypeSupportsExtended(TableType l); static bool tableTypeIsReadOnly(TableType l); protected: void setMaxPrimaries(qint32 n) { m_MaxPrimaries = n; } private: Partitions m_Children; qint32 m_MaxPrimaries; TableType m_Type; qint64 m_FirstUsable; qint64 m_LastUsable; }; Q_DECLARE_OPERATORS_FOR_FLAGS(PartitionTable::Flags) QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); #endif diff --git a/src/plugins/sfdisk/sfdiskbackend.cpp b/src/plugins/sfdisk/sfdiskbackend.cpp index 1e464f0..a563250 100644 --- a/src/plugins/sfdisk/sfdiskbackend.cpp +++ b/src/plugins/sfdisk/sfdiskbackend.cpp @@ -1,516 +1,513 @@ /************************************************************************* * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ /** @file */ #include "plugins/sfdisk/sfdiskbackend.h" #include "plugins/sfdisk/sfdiskdevice.h" #include "core/diskdevice.h" #include "core/lvmdevice.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" #include "core/raid/softwareraid.h" #include "fs/filesystemfactory.h" #include "fs/luks.h" #include "fs/luks2.h" #include "util/globallog.h" #include "util/externalcommand.h" #include "util/helpers.h" #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(SfdiskBackendFactory, "pmsfdiskbackendplugin.json", registerPlugin();) SfdiskBackend::SfdiskBackend(QObject*, const QList&) : CoreBackend() { } void SfdiskBackend::initFSSupport() { } QList SfdiskBackend::scanDevices(bool excludeReadOnly) { // TODO: add another bool option for loopDevices QList result; QStringList deviceNodes; ExternalCommand cmd(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--paths"), QStringLiteral("--sort"), QStringLiteral("name"), QStringLiteral("--json"), QStringLiteral("--output"), QStringLiteral("type,name") }); if (cmd.run(-1) && cmd.exitCode() == 0) { const QJsonDocument jsonDocument = QJsonDocument::fromJson(cmd.rawOutput()); const QJsonObject jsonObject = jsonDocument.object(); const QJsonArray jsonArray = jsonObject[QLatin1String("blockdevices")].toArray(); for (const auto &deviceLine : jsonArray) { QJsonObject deviceObject = deviceLine.toObject(); if (deviceObject[QLatin1String("type")].toString() != QLatin1String("disk")) continue; const QString deviceNode = deviceObject[QLatin1String("name")].toString(); if (excludeReadOnly) { QString deviceName = deviceNode; deviceName.remove(QStringLiteral("/dev/")); QFile f(QStringLiteral("/sys/block/%1/ro").arg(deviceName)); if (f.open(QIODevice::ReadOnly)) if (f.readLine().trimmed().toInt() == 1) continue; } deviceNodes << deviceNode; } int totalDevices = deviceNodes.length(); for (int i = 0; i < totalDevices; ++i) { const QString deviceNode = deviceNodes[i]; emitScanProgress(deviceNode, i * 100 / totalDevices); Device* device = scanDevice(deviceNode); if (device != nullptr) { result.append(device); } } SoftwareRAID::scanSoftwareRAID(result); LvmDevice::scanSystemLVM(result); // LVM scanner needs all other devices, so should be last } return result; } /** Create a Device for the given device_node and scan it for partitions. @param deviceNode the device node (e.g. "/dev/sda") @return the created Device object. callers need to free this. */ Device* SfdiskBackend::scanDevice(const QString& deviceNode) { ExternalCommand modelCommand(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--noheadings"), QStringLiteral("--output"), QStringLiteral("model"), deviceNode }); ExternalCommand sizeCommand(QStringLiteral("blockdev"), { QStringLiteral("--getsize64"), deviceNode }); ExternalCommand sizeCommand2(QStringLiteral("blockdev"), { QStringLiteral("--getss"), deviceNode }); ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), deviceNode }, QProcess::ProcessChannelMode::SeparateChannels ); if ( sizeCommand.run(-1) && sizeCommand.exitCode() == 0 && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 && jsonCommand.run(-1) ) { Device* d = nullptr; qint64 deviceSize = sizeCommand.output().trimmed().toLongLong(); int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong(); 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+:")); QRegularExpressionMatchIterator i = re.globalMatch(content); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString name = reMatch.captured(1); if ((QStringLiteral("/dev/md") + name) == deviceNode) { Log(Log::Level::information) << xi18nc("@info:status", "Software RAID Device found: %1", deviceNode); d = new SoftwareRAID( QStringLiteral("md") + name, SoftwareRAID::Status::Active ); break; } } } if ( d == nullptr && modelCommand.run(-1) && modelCommand.exitCode() == 0 ) { QString modelName = modelCommand.output(); modelName = modelName.left(modelName.length() - 1); Log(Log::Level::information) << xi18nc("@info:status", "Device found: %1", modelName); d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); } if ( d ) { if (jsonCommand.exitCode() != 0) return d; const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object(); const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject(); if (!updateDevicePartitionTable(*d, partitionTable)) return nullptr; return d; } } else { // Look if this device is a LVM VG ExternalCommand checkVG(QStringLiteral("lvm"), { QStringLiteral("vgdisplay"), deviceNode }); if (checkVG.run(-1) && checkVG.exitCode() == 0) { QList availableDevices = scanDevices(); LvmDevice::scanSystemLVM(availableDevices); for (Device *device : qAsConst(availableDevices)) if (device->deviceNode() == deviceNode) return device; } } return nullptr; } /** Scans a Device for Partitions. This method will scan a Device for all Partitions on it, detect the FileSystem for each Partition, try to determine the FileSystem usage, read the FileSystem label and store it all in newly created objects that are in the end added to the Device's PartitionTable. */ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions) { Q_ASSERT(d.partitionTable()); QList partitions; for (const auto &partition : jsonPartitions) { const QJsonObject partitionObject = partition.toObject(); const QString partitionNode = partitionObject[QLatin1String("node")].toString(); const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); const qint64 size = partitionObject[QLatin1String("size")].toVariant().toLongLong(); const QString partitionType = partitionObject[QLatin1String("type")].toString(); PartitionTable::Flags activeFlags = partitionObject[QLatin1String("bootable")].toBool() ? PartitionTable::FlagBoot : PartitionTable::FlagNone; - if (partitionType == QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B")) { + if (partitionType == QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B")) activeFlags |= PartitionTable::FlagBoot; - activeFlags |= PartitionTable::FlagEsp; - } else if (partitionType == QStringLiteral("21686148-6449-6E6F-744E-656564454649")) activeFlags |= PartitionTable::FlagBiosGrub; FileSystem::Type type = FileSystem::Type::Unknown; type = detectFileSystem(partitionNode); PartitionRole::Roles r = PartitionRole::Primary; if ( (d.partitionTable()->type() == PartitionTable::msdos || d.partitionTable()->type() == PartitionTable::msdos_sectorbased) && partitionType.toInt() == 5 ) { r = PartitionRole::Extended; type = FileSystem::Type::Extended; } // Find an extended partition this partition is in. PartitionNode* parent = d.partitionTable()->findPartitionBySector(start, PartitionRole(PartitionRole::Extended)); // None found, so it's a primary in the device's partition table. if (parent == nullptr) parent = d.partitionTable(); else r = PartitionRole::Logical; FileSystem* fs = FileSystemFactory::create(type, start, start + size - 1, d.logicalSize()); fs->scan(partitionNode); QString mountPoint; bool mounted; // sfdisk does not handle LUKS partitions if (fs->type() == FileSystem::Type::Luks || fs->type() == FileSystem::Type::Luks2) { r |= PartitionRole::Luks; FS::luks* luksFs = static_cast(fs); luksFs->initLUKS(); QString mapperNode = luksFs->mapperName(); mountPoint = FileSystem::detectMountPoint(fs, mapperNode); mounted = FileSystem::detectMountStatus(fs, mapperNode); } else { mountPoint = FileSystem::detectMountPoint(fs, partitionNode); mounted = FileSystem::detectMountStatus(fs, partitionNode); } Partition* part = new Partition(parent, d, PartitionRole(r), fs, start, start + size - 1, partitionNode, availableFlags(d.partitionTable()->type()), mountPoint, mounted, activeFlags); if (!part->roles().has(PartitionRole::Luks)) readSectorsUsed(d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); if (d.partitionTable()->type() == PartitionTable::TableType::gpt) { part->setLabel(partitionObject[QLatin1String("name")].toString()); part->setUUID(partitionObject[QLatin1String("uuid")].toString()); } if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(part->deviceNode())); parent->append(part); partitions.append(part); } d.partitionTable()->updateUnallocated(d); if (d.partitionTable()->isSectorBased(d)) d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased); for (const Partition * part : qAsConst(partitions)) PartitionAlignment::isAligned(d, *part); } bool SfdiskBackend::updateDevicePartitionTable(Device &d, const QJsonObject &jsonPartitionTable) { QString tableType = jsonPartitionTable[QLatin1String("label")].toString(); const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); qint64 firstUsableSector = 0, lastUsableSector; if ( d.type() == Device::Type::Disk_Device ) { const DiskDevice* diskDevice = static_cast(&d); lastUsableSector = diskDevice->totalSectors(); } else if ( d.type() == Device::Type::SoftwareRAID_Device ) { const SoftwareRAID* raidDevice = static_cast(&d); lastUsableSector = raidDevice->totalLogical() - 1; } if (type == PartitionTable::gpt) { firstUsableSector = jsonPartitionTable[QLatin1String("firstlba")].toVariant().toLongLong(); lastUsableSector = jsonPartitionTable[QLatin1String("lastlba")].toVariant().toLongLong(); } if (lastUsableSector < firstUsableSector) { return false; } setPartitionTableForDevice(d, new PartitionTable(type, firstUsableSector, lastUsableSector)); switch (type) { case PartitionTable::gpt: { // Read the maximum number of GPT partitions qint32 maxEntries; ExternalCommand ddCommand(QStringLiteral("dd"), { QStringLiteral("skip=1"), QStringLiteral("count=1"), (QStringLiteral("if=") + d.deviceNode()) }, QProcess::SeparateChannels); if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) { QByteArray gptHeader = ddCommand.rawOutput(); QByteArray gptMaxEntries = gptHeader.mid(80, 4); QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly); stream.setByteOrder(QDataStream::LittleEndian); stream >> maxEntries; } else maxEntries = 128; CoreBackend::setPartitionTableMaxPrimaries(*d.partitionTable(), maxEntries); } default: break; } scanDevicePartitions(d, jsonPartitionTable[QLatin1String("partitions")].toArray()); return true; } /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ void SfdiskBackend::readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint) { if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::Type::LinuxSwap && p.fileSystem().type() != FileSystem::Type::Lvm2_PV) { const QStorageInfo storage = QStorageInfo(mountPoint); if (p.isMounted() && storage.isValid()) p.fileSystem().setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / d.logicalSize()); } else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSize()); } FileSystem::Type SfdiskBackend::detectFileSystem(const QString& partitionPath) { FileSystem::Type rval = FileSystem::Type::Unknown; ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), partitionPath }); if (udevCommand.run(-1) && udevCommand.exitCode() == 0) { QRegularExpression re(QStringLiteral("ID_FS_TYPE=(\\w+)")); QRegularExpression re2(QStringLiteral("ID_FS_VERSION=(\\w+)")); QRegularExpressionMatch reFileSystemType = re.match(udevCommand.output()); QRegularExpressionMatch reFileSystemVersion = re2.match(udevCommand.output()); QString s; if (reFileSystemType.hasMatch()) { s = reFileSystemType.captured(1); } QString version; if (reFileSystemVersion.hasMatch()) { version = reFileSystemVersion.captured(1); } if (s == QStringLiteral("ext2")) rval = FileSystem::Type::Ext2; else if (s == QStringLiteral("ext3")) rval = FileSystem::Type::Ext3; else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Type::Ext4; else if (s == QStringLiteral("swap")) rval = FileSystem::Type::LinuxSwap; else if (s == QStringLiteral("ntfs")) rval = FileSystem::Type::Ntfs; else if (s == QStringLiteral("reiserfs")) rval = FileSystem::Type::ReiserFS; else if (s == QStringLiteral("reiser4")) rval = FileSystem::Type::Reiser4; else if (s == QStringLiteral("xfs")) rval = FileSystem::Type::Xfs; else if (s == QStringLiteral("jfs")) rval = FileSystem::Type::Jfs; else if (s == QStringLiteral("hfs")) rval = FileSystem::Type::Hfs; else if (s == QStringLiteral("hfsplus")) rval = FileSystem::Type::HfsPlus; else if (s == QStringLiteral("ufs")) rval = FileSystem::Type::Ufs; else if (s == QStringLiteral("vfat")) { if (version == QStringLiteral("FAT32")) rval = FileSystem::Type::Fat32; else if (version == QStringLiteral("FAT16")) rval = FileSystem::Type::Fat16; else if (version == QStringLiteral("FAT12")) rval = FileSystem::Type::Fat12; } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Type::Btrfs; else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Type::Ocfs2; else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Type::Zfs; else if (s == QStringLiteral("hpfs")) rval = FileSystem::Type::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) { if (version == QStringLiteral("1")) rval = FileSystem::Type::Luks; else if (version == QStringLiteral("2")) { rval = FileSystem::Type::Luks2; } } else if (s == QStringLiteral("exfat")) rval = FileSystem::Type::Exfat; else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Type::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Type::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::Type::F2fs; else if (s == QStringLiteral("udf")) rval = FileSystem::Type::Udf; else if (s == QStringLiteral("iso9660")) rval = FileSystem::Type::Iso9660; else if (s == QStringLiteral("linux_raid_member")) rval = FileSystem::Type::LinuxRaidMember; else qWarning() << "unknown file system type " << s << " on " << partitionPath; } return rval; } QString SfdiskBackend::readLabel(const QString& deviceNode) const { ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), deviceNode }); udevCommand.run(); QRegularExpression re(QStringLiteral("ID_FS_LABEL=(.*)")); QRegularExpressionMatch reFileSystemLabel = re.match(udevCommand.output()); if (reFileSystemLabel.hasMatch()) return reFileSystemLabel.captured(1); return QString(); } QString SfdiskBackend::readUUID(const QString& deviceNode) const { ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), deviceNode }); udevCommand.run(); QRegularExpression re(QStringLiteral("ID_FS_UUID=(.*)")); QRegularExpressionMatch reFileSystemUUID = re.match(udevCommand.output()); if (reFileSystemUUID.hasMatch()) return reFileSystemUUID.captured(1); return QString(); } PartitionTable::Flags SfdiskBackend::availableFlags(PartitionTable::TableType type) { PartitionTable::Flags flags; if (type == PartitionTable::gpt) { // These are not really flags but for now keep them for compatibility // We should implement changing partition type flags = PartitionTable::Flag::FlagBiosGrub | - PartitionTable::Flag::FlagEsp | PartitionTable::Flag::FlagBoot; } else if (type == PartitionTable::msdos || type == PartitionTable::msdos_sectorbased) flags = PartitionTable::FlagBoot; return flags; } std::unique_ptr SfdiskBackend::openDevice(const Device& d) { std::unique_ptr device = std::make_unique(d); if (!device->open()) device = nullptr; return device; } std::unique_ptr SfdiskBackend::openDeviceExclusive(const Device& d) { std::unique_ptr device = std::make_unique(d); if (!device->openExclusive()) device = nullptr; return device; } bool SfdiskBackend::closeDevice(std::unique_ptr coreDevice) { return coreDevice->close(); } #include "sfdiskbackend.moc" diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 81f8965..426eb8b 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -1,266 +1,268 @@ /************************************************************************* * 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(); 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::TableType::gpt: type = 0; break; case PartitionTable::TableType::msdos: case PartitionTable::TableType::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 ((m_device->partitionTable()->type() == PartitionTable::TableType::msdos || - m_device->partitionTable()->type() == PartitionTable::TableType::msdos_sectorbased) && - flag == PartitionTable::Flag::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::Flag::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 (m_device->partitionTable()->type() == PartitionTable::TableType::msdos || + m_device->partitionTable()->type() == PartitionTable::TableType::msdos_sectorbased) { + // We only allow setting one active partition per device + if (flag == PartitionTable::Flag::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::Flag::FlagBoot && state == false) { + ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QStringLiteral("-") } ); + if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) + return true; + else + return false; + } } - if ((flag == PartitionTable::Flag::FlagEsp || flag == PartitionTable::Flag::FlagBoot) && state == true) { + if (flag == PartitionTable::Flag::FlagBoot && 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::Flag::FlagEsp || flag == PartitionTable::Flag::FlagBoot) && state == false) + if (flag == PartitionTable::Flag::FlagBoot && state == false) setPartitionSystemType(report, partition); if (flag == PartitionTable::Flag::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::Flag::FlagBiosGrub && state == false) setPartitionSystemType(report, partition); return true; }