diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index e73f414..824b108 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -1,484 +1,516 @@ /************************************************************************* * Copyright (C) 2008 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 .* *************************************************************************/ /** @file */ #include "core/partitiontable.h" #include "core/partition.h" #include "core/device.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/globallog.h" #include #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 first_usable, qint64 last_usable) : PartitionNode(), m_Children(), m_MaxPrimaries(maxPrimariesForTableType(type)), m_Type(type), m_FirstUsable(first_usable), m_LastUsable(last_usable) { } /** 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; } /** @return true if the PartitionTable has an extended Partition */ bool PartitionTable::hasExtended() const { for (int i = 0; i < children().size(); i++) if (children()[i]->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 (int i = 0; i < children().size(); i++) if (children()[i]->roles().has(PartitionRole::Extended)) return children()[i]; 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; foreach(const Partition * 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); } /** @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 i18nc("@item partition flag", "boot"); - case PartitionTable::FlagRoot: return i18nc("@item partition flag", "root"); - case PartitionTable::FlagSwap: return i18nc("@item partition flag", "swap"); - case PartitionTable::FlagHidden: return i18nc("@item partition flag", "hidden"); - case PartitionTable::FlagRaid: return i18nc("@item partition flag", "raid"); - case PartitionTable::FlagLvm: return i18nc("@item partition flag", "lvm"); - case PartitionTable::FlagLba: return i18nc("@item partition flag", "lba"); - case PartitionTable::FlagHpService: return i18nc("@item partition flag", "hpservice"); - case PartitionTable::FlagPalo: return i18nc("@item partition flag", "palo"); - case PartitionTable::FlagPrep: return i18nc("@item partition flag", "prep"); - case PartitionTable::FlagMsftReserved: return i18nc("@item partition flag", "msft-reserved"); - + case PartitionTable::FlagBoot: + return i18nc("@item partition flag", "boot"); + case PartitionTable::FlagRoot: + return i18nc("@item partition flag", "root"); + case PartitionTable::FlagSwap: + return i18nc("@item partition flag", "swap"); + case PartitionTable::FlagHidden: + return i18nc("@item partition flag", "hidden"); + case PartitionTable::FlagRaid: + return i18nc("@item partition flag", "raid"); + case PartitionTable::FlagLvm: + return i18nc("@item partition flag", "lvm"); + case PartitionTable::FlagLba: + return i18nc("@item partition flag", "lba"); + case PartitionTable::FlagHpService: + return i18nc("@item partition flag", "hpservice"); + case PartitionTable::FlagPalo: + return i18nc("@item partition flag", "palo"); + case PartitionTable::FlagPrep: + return i18nc("@item partition flag", "prep"); + case PartitionTable::FlagMsftReserved: + return i18nc("@item partition flag", "msft-reserved"); + case PartitionTable::FlagBiosGrub: + return i18nc("@item partition flag", "bios-grub"); + case PartitionTable::FlagAppleTvRecovery: + return i18nc("@item partition flag", "apple-tv-recovery"); + case PartitionTable::FlagDiag: + return i18nc("@item partition flag", "diag"); + case PartitionTable::FlagLegacyBoot: + return i18nc("@item partition flag", "legacy-boot"); + case PartitionTable::FlagMsftData: + return i18nc("@item partition flag", "msft-data"); + case PartitionTable::FlagIrst: + return i18nc("@item partition flag", "irst"); + case PartitionTable::FlagEsp: + return i18nc("@item partition flag", "esp"); default: break; } return QString(); } /** @return list of all flags */ 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& device, PartitionNode& parent, qint64& start, qint64& end) { 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); } /** 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; if (!PartitionTable::getUnallocatedRange(device, parent, start, end)) return nullptr; return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Unknown, start, end), 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) const { Q_ASSERT(p); qint64 lastEnd = start; foreach(Partition * child, p->children()) { p->insert(createUnallocated(d, *p, lastEnd, child->firstSector() - 1)); if (child->roles().has(PartitionRole::Extended)) insertUnallocated(d, child, child->firstSector()); lastEnd = child->lastSector() + 1; } // 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()) 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) return PartitionAlignment::sectorAlignment(d); } qint64 PartitionTable::defaultLastUsable(const Device& d, TableType t) { if (t == gpt) return d.totalSectors() - 1 - 32 - 1; return d.totalSectors() - 1; } static struct { const QString 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[] = { { QStringLiteral("aix"), 4, false, true, PartitionTable::aix }, { QStringLiteral("bsd"), 8, false, true, PartitionTable::bsd }, { QStringLiteral("dasd"), 1, false, true, PartitionTable::dasd }, { QStringLiteral("msdos"), 4, true, false, PartitionTable::msdos }, { QStringLiteral("msdos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QStringLiteral("dvh"), 16, true, true, PartitionTable::dvh }, { QStringLiteral("gpt"), 128, false, false, PartitionTable::gpt }, { QStringLiteral("loop"), 1, false, true, PartitionTable::loop }, { QStringLiteral("mac"), 0xffff, false, true, PartitionTable::mac }, { QStringLiteral("pc98"), 16, false, true, PartitionTable::pc98 }, { QStringLiteral("amiga"), 128, false, true, PartitionTable::amiga }, { QStringLiteral("sun"), 8, false, true, PartitionTable::sun } }; PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (n == tableTypes[i].name) return tableTypes[i].type; return PartitionTable::unknownTableType; } QString PartitionTable::tableTypeToName(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].name; return i18nc("@item partition table name", "unknown"); } qint64 PartitionTable::maxPrimariesForTableType(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].maxPrimaries; return 1; } bool PartitionTable::tableTypeSupportsExtended(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].canHaveExtended; return false; } bool PartitionTable::tableTypeIsReadOnly(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].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 (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. foreach(const Partition * p, children()) if (p->firstSector() % PartitionAlignment::sectorAlignment(d) == 0) numSectorAligned++; else if (p->firstSector() % d.cylinderSize() == 0) numCylinderAligned++; return numSectorAligned >= numCylinderAligned; } return type() == PartitionTable::msdos_sectorbased; } void PartitionTable::setType(const Device& d, TableType t) { setFirstUsableSector(defaultFirstUsable(d, t)); setLastUsableSector(defaultLastUsable(d, t)); m_Type = t; updateUnallocated(d); } static bool isPartitionLessThan(const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); } 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; foreach(const Partition * p, ptable.children()) if (!p->roles().has(PartitionRole::Unallocated)) { partitions.append(p); if (p->roles().has(PartitionRole::Extended)) foreach(const Partition * child, p->children()) if (!child->roles().has(PartitionRole::Unallocated)) partitions.append(child); } qSort(partitions.begin(), partitions.end(), isPartitionLessThan); foreach(const Partition * p, partitions) stream << *p; return stream; } diff --git a/src/core/partitiontable.h b/src/core/partitiontable.h index 9ccfe60..cfcbbd7 100644 --- a/src/core/partitiontable.h +++ b/src/core/partitiontable.h @@ -1,191 +1,199 @@ /************************************************************************* * 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(PARTITIONTABLE__H) #define 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 { Q_DISABLE_COPY(PartitionTable) friend class CoreBackend; friend LIBKPMCORE_EXPORT QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); public: enum TableType { unknownTableType = -1, aix, bsd, dasd, msdos, msdos_sectorbased, dvh, gpt, loop, mac, pc98, amiga, sun }; /** Partition flags */ enum Flag { FlagNone = 0, FlagBoot = 1, FlagRoot = 2, FlagSwap = 4, FlagHidden = 8, FlagRaid = 16, FlagLvm = 32, FlagLba = 64, FlagHpService = 128, FlagPalo = 256, FlagPrep = 512, - FlagMsftReserved = 1024 + FlagMsftReserved = 1024, + FlagBiosGrub = 2048, + FlagAppleTvRecovery = 4096, + FlagDiag = 8192, + FlagLegacyBoot = 16384, + FlagMsftData = 32768, + FlagIrst = 65536, + FlagEsp = 131072 }; Q_DECLARE_FLAGS(Flags, Flag) public: PartitionTable(TableType type, qint64 first_usable, qint64 last_usable); ~PartitionTable(); public: PartitionNode* parent() { return nullptr; /**< @return always nullptr for PartitionTable */ } const PartitionNode* parent() const { return nullptr; /**< @return always nullptr for PartitionTable */ } bool isRoot() const { return true; /**< @return always true for PartitionTable */ } bool isReadOnly() const { return tableTypeIsReadOnly(type()); /**< @return true if the PartitionTable is read only */ } Partitions& children() { return m_Children; /**< @return the children in this PartitionTable */ } const Partitions& children() const { return m_Children; /**< @return the children in this PartitionTable */ } void setType(const Device& d, TableType t); void append(Partition* partition); qint64 freeSectorsBefore(const Partition& p) const; qint64 freeSectorsAfter(const Partition& p) const; bool hasExtended() const; Partition* extended() const; PartitionRole::Roles childRoles(const Partition& p) const; int numPrimaries() const; int 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 updateUnallocated(const Device& d); void insertUnallocated(const Device& d, PartitionNode* p, qint64 start) const; bool isSectorBased(const Device& d) const; static 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 qint64 maxPrimariesForTableType(TableType l); static bool tableTypeSupportsExtended(TableType l); static bool tableTypeIsReadOnly(TableType l); protected: void setMaxPrimaries(qint32 n) { m_MaxPrimaries = n; } void setFirstUsableSector(qint64 s) { m_FirstUsable = s; } void setLastUsableSector(qint64 s) { m_LastUsable = s; } 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/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp index 4c44a7c..e5a97c7 100644 --- a/src/plugins/libparted/libpartedbackend.cpp +++ b/src/plugins/libparted/libpartedbackend.cpp @@ -1,543 +1,551 @@ /************************************************************************* * Copyright (C) 2008-2012 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 .* *************************************************************************/ /** @file */ #include "plugins/libparted/libpartedbackend.h" #include "plugins/libparted/libparteddevice.h" #include "core/device.h" #include "core/partition.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "fs/fat16.h" #include "fs/hfs.h" #include "fs/hfsplus.h" #include "fs/luks.h" #include "util/globallog.h" #include "util/helpers.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin();) static struct { PedPartitionFlag pedFlag; PartitionTable::Flag flag; } flagmap[] = { - { PED_PARTITION_BOOT, PartitionTable::FlagBoot }, - { PED_PARTITION_ROOT, PartitionTable::FlagRoot }, - { PED_PARTITION_SWAP, PartitionTable::FlagSwap }, - { PED_PARTITION_HIDDEN, PartitionTable::FlagHidden }, - { PED_PARTITION_RAID, PartitionTable::FlagRaid }, - { PED_PARTITION_LVM, PartitionTable::FlagLvm }, - { PED_PARTITION_LBA, PartitionTable::FlagLba }, - { PED_PARTITION_HPSERVICE, PartitionTable::FlagHpService }, - { PED_PARTITION_PALO, PartitionTable::FlagPalo }, - { PED_PARTITION_PREP, PartitionTable::FlagPrep }, - { PED_PARTITION_MSFT_RESERVED, PartitionTable::FlagMsftReserved } + { PED_PARTITION_BOOT, PartitionTable::FlagBoot }, + { PED_PARTITION_ROOT, PartitionTable::FlagRoot }, + { PED_PARTITION_SWAP, PartitionTable::FlagSwap }, + { PED_PARTITION_HIDDEN, PartitionTable::FlagHidden }, + { PED_PARTITION_RAID, PartitionTable::FlagRaid }, + { PED_PARTITION_LVM, PartitionTable::FlagLvm }, + { PED_PARTITION_LBA, PartitionTable::FlagLba }, + { PED_PARTITION_HPSERVICE, PartitionTable::FlagHpService }, + { PED_PARTITION_PALO, PartitionTable::FlagPalo }, + { PED_PARTITION_PREP, PartitionTable::FlagPrep }, + { PED_PARTITION_MSFT_RESERVED, PartitionTable::FlagMsftReserved }, + { PED_PARTITION_BIOS_GRUB, PartitionTable::FlagBiosGrub }, + { PED_PARTITION_APPLE_TV_RECOVERY, PartitionTable::FlagAppleTvRecovery }, + { PED_PARTITION_DIAG, PartitionTable::FlagDiag }, // generic diagnostics flag + { PED_PARTITION_LEGACY_BOOT, PartitionTable::FlagLegacyBoot }, + { PED_PARTITION_MSFT_DATA, PartitionTable::FlagMsftData }, + { PED_PARTITION_IRST, PartitionTable::FlagIrst }, // Intel Rapid Start partition + { PED_PARTITION_ESP, PartitionTable::FlagEsp } // EFI system }; static QString s_lastPartedExceptionMessage; /** Callback to handle exceptions from libparted @param e the libparted exception to handle */ static PedExceptionOption pedExceptionHandler(PedException* e) { Log(Log::error) << i18nc("@info/plain", "LibParted Exception: %1", QString::fromLocal8Bit(e->message)); s_lastPartedExceptionMessage = QString::fromLocal8Bit(e->message); return PED_EXCEPTION_UNHANDLED; } // -------------------------------------------------------------------------- // The following structs and the typedef come from libparted's internal gpt sources. // It's very unfortunate there is no public API to get at the first and last usable // sector for GPT a partition table, so this is the only (libparted) way to get that // information (another way would be to read the GPT header and parse the // information ourselves; if the libparted devs begin changing these internal // structs for each point release and break our code, we'll have to do just that). typedef struct { uint32_t time_low; uint16_t time_mid; uint16_t time_hi_and_version; uint8_t clock_seq_hi_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; } /* __attribute__ ((packed)) */ efi_guid_t; struct __attribute__((packed)) _GPTDiskData { PedGeometry data_area; int entry_count; efi_guid_t uuid; }; typedef struct _GPTDiskData GPTDiskData; // -------------------------------------------------------------------------- /** Get the first sector a Partition may cover on a given Device @param d the Device in question @return the first sector usable by a Partition */ static quint64 firstUsableSector(const Device& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; quint64 rval = 0; if (pedDisk) rval = pedDisk->dev->bios_geom.sectors; if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); if (geom) rval = geom->start; else rval += 32; } return rval; } /** Get the last sector a Partition may cover on a given Device @param d the Device in question @return the last sector usable by a Partition */ static quint64 lastUsableSector(const Device& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; quint64 rval = 0; if (pedDisk) rval = static_cast< quint64 >( pedDisk->dev->bios_geom.sectors ) * pedDisk->dev->bios_geom.heads * pedDisk->dev->bios_geom.cylinders - 1; if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); if (geom) rval = geom->end; else rval -= 32; } return rval; } /** Reads sectors used on a FileSystem using libparted functions. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @return the number of sectors used */ #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT static qint64 readSectorsUsedLibParted(PedDisk* pedDisk, const Partition& p) { Q_ASSERT(pedDisk); qint64 rval = -1; PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, p.firstSector()); if (pedPartition) { PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom); if (pedFileSystem) { if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem)) { rval = pedConstraint->min_size; ped_constraint_destroy(pedConstraint); } ped_file_system_close(pedFileSystem); } } return rval; } #endif /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ static void readSectorsUsed(PedDisk* pedDisk, const Device& d, Partition& p, const QString& mountPoint) { Q_ASSERT(pedDisk); const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (p.isMounted() && freeSpaceInfo.isValid() && mountPoint != QStringLiteral()) p.fileSystem().setSectorsUsed(freeSpaceInfo.used() / d.logicalSectorSize()); else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSectorSize()); #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportCore) p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(pedDisk, p)); #else Q_UNUSED(pedDisk); #endif } static PartitionTable::Flags activeFlags(PedPartition* p) { PartitionTable::Flags flags = PartitionTable::FlagNone; // We might get here with a pedPartition just picked up from libparted that is // unallocated. Libparted doesn't like it if we ask for flags for unallocated // space. if (p->num <= 0) return flags; for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (ped_partition_is_flag_available(p, flagmap[i].pedFlag) && ped_partition_get_flag(p, flagmap[i].pedFlag)) flags |= flagmap[i].flag; return flags; } static PartitionTable::Flags availableFlags(PedPartition* p) { PartitionTable::Flags flags; // see above. if (p->num <= 0) return flags; for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (ped_partition_is_flag_available(p, flagmap[i].pedFlag)) { // Workaround: libparted claims the hidden flag is available for extended partitions, but // throws an error when we try to set or clear it. So skip this combination. Also see setFlag. if (p->type != PED_PARTITION_EXTENDED || flagmap[i].flag != PartitionTable::FlagHidden) flags |= flagmap[i].flag; } return flags; } /** Constructs a LibParted object. */ LibPartedBackend::LibPartedBackend(QObject*, const QList&) : CoreBackend() { ped_exception_set_handler(pedExceptionHandler); } void LibPartedBackend::initFSSupport() { #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT if (FS::fat16::m_Shrink == FileSystem::cmdSupportNone) FS::fat16::m_Shrink = FileSystem::cmdSupportBackend; if (FS::fat16::m_Grow == FileSystem::cmdSupportNone) FS::fat16::m_Grow = FileSystem::cmdSupportBackend; if (FS::hfs::m_Shrink == FileSystem::cmdSupportNone) FS::hfs::m_Shrink = FileSystem::cmdSupportBackend; if (FS::hfsplus::m_Shrink == FileSystem::cmdSupportNone) FS::hfsplus::m_Shrink = FileSystem::cmdSupportBackend; if (FS::hfs::m_GetUsed == FileSystem::cmdSupportNone) FS::hfs::m_GetUsed = FileSystem::cmdSupportBackend; if (FS::hfsplus::m_GetUsed == FileSystem::cmdSupportNone) FS::hfsplus::m_GetUsed = FileSystem::cmdSupportBackend; #endif } /** 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. @param pedDevice libparted pointer to the device @param d Device @param pedDisk libparted pointer to the partition table */ void LibPartedBackend::scanDevicePartitions(PedDevice*, Device& d, PedDisk* pedDisk) { Q_ASSERT(pedDisk); Q_ASSERT(d.partitionTable()); PedPartition* pedPartition = nullptr; KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPoints.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); QList partitions; while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { if (pedPartition->num < 1) continue; PartitionRole::Roles r = PartitionRole::None; FileSystem::Type type = detectFileSystem(pedPartition); switch (pedPartition->type) { case PED_PARTITION_NORMAL: r = PartitionRole::Primary; break; case PED_PARTITION_EXTENDED: r = PartitionRole::Extended; type = FileSystem::Extended; break; case PED_PARTITION_LOGICAL: r = PartitionRole::Logical; break; default: continue; } // Find an extended partition this partition is in. PartitionNode* parent = d.partitionTable()->findPartitionBySector(pedPartition->geom.start, PartitionRole(PartitionRole::Extended)); // None found, so it's a primary in the device's partition table. if (parent == nullptr) parent = d.partitionTable(); const QString node = QString::fromUtf8(ped_partition_get_path(pedPartition)); FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end); // libparted does not handle LUKS partitions QString mountPoint; bool mounted; if (fs->type() == FileSystem::Luks) { mountPoint = FS::luks::mapperName(node); mounted = (mountPoint != QString()) ? true : false; } else { mountPoint = mountPoints.findByDevice(node) ? mountPoints.findByDevice(node)->mountPoint() : QString(); mounted = ped_partition_is_busy(pedPartition); } Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end, node, availableFlags(pedPartition), mountPoint, mounted, activeFlags(pedPartition)); readSectorsUsed(pedDisk, d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); 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); foreach(const Partition * part, partitions) PartitionAlignment::isAligned(d, *part); ped_disk_destroy(pedDisk); } /** Create a Device for the given device_node and scan it for partitions. @param device_node the device node (e.g. "/dev/sda") @return the created Device object. callers need to free this. */ Device* LibPartedBackend::scanDevice(const QString& device_node) { PedDevice* pedDevice = ped_device_get(device_node.toLocal8Bit().constData()); if (pedDevice == nullptr) { Log(Log::warning) << xi18nc("@info/plain", "Could not access device %1", device_node); return nullptr; } Log(Log::information) << i18nc("@info/plain", "Device found: %1", QString::fromUtf8(pedDevice->model)); Device* d = new Device(QString::fromUtf8(pedDevice->model), QString::fromUtf8(pedDevice->path), pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size); PedDisk* pedDisk = ped_disk_new(pedDevice); if (pedDisk) { const PartitionTable::TableType type = PartitionTable::nameToTableType(QString::fromUtf8(pedDisk->type->name)); CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector(*d), lastUsableSector(*d))); CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), ped_disk_get_max_primary_partition_count(pedDisk)); scanDevicePartitions(pedDevice, *d, pedDisk); } return d; } QList LibPartedBackend::scanDevices(bool excludeReadOnly) { QList result; ped_device_probe_all(); PedDevice* pedDevice = nullptr; QVector path; quint32 totalDevices = 0; while (true) { pedDevice = ped_device_get_next(pedDevice); if (!pedDevice) break; if (pedDevice->type == PED_DEVICE_DM) continue; if (excludeReadOnly && ( pedDevice->type == PED_DEVICE_LOOP || pedDevice->type == PED_DEVICE_UNKNOWN || pedDevice->read_only)) continue; path.push_back(QString::fromUtf8(pedDevice->path)); ++totalDevices; } for (quint32 i = 0; i < totalDevices; ++i) { emitScanProgress(path[i], i * 100 / totalDevices); Device* d = scanDevice(path[i]); if (d) result.append(d); } return result; } CoreBackendDevice* LibPartedBackend::openDevice(const QString& device_node) { LibPartedDevice* device = new LibPartedDevice(device_node); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const QString& device_node) { LibPartedDevice* device = new LibPartedDevice(device_node); if (device == nullptr || !device->openExclusive()) { delete device; device = nullptr; } return device; } bool LibPartedBackend::closeDevice(CoreBackendDevice* core_device) { return core_device->close(); } /** Detects the type of a FileSystem given a PedDevice and a PedPartition @param pedDevice pointer to the pedDevice. Must not be nullptr. @param pedPartition pointer to the pedPartition. Must not be nullptr @return the detected FileSystem type (FileSystem::Unknown if not detected) */ FileSystem::Type LibPartedBackend::detectFileSystem(PedPartition* pedPartition) { FileSystem::Type rval = FileSystem::Unknown; blkid_cache cache; char* pedPath = nullptr; if (blkid_get_cache(&cache, nullptr) == 0 && (pedPath = ped_partition_get_path(pedPartition))) { blkid_dev dev; if ((dev = blkid_get_dev(cache, pedPath, BLKID_DEV_NORMAL)) != nullptr) { QString s = QString::fromUtf8(blkid_get_tag_value(cache, "TYPE", pedPath)); if (s == QStringLiteral("ext2")) rval = FileSystem::Ext2; else if (s == QStringLiteral("ext3")) rval = FileSystem::Ext3; else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Ext4; else if (s == QStringLiteral("swap")) rval = FileSystem::LinuxSwap; else if (s == QStringLiteral("ntfs")) rval = FileSystem::Ntfs; else if (s == QStringLiteral("reiserfs")) rval = FileSystem::ReiserFS; else if (s == QStringLiteral("reiser4")) rval = FileSystem::Reiser4; else if (s == QStringLiteral("xfs")) rval = FileSystem::Xfs; else if (s == QStringLiteral("jfs")) rval = FileSystem::Jfs; else if (s == QStringLiteral("hfs")) rval = FileSystem::Hfs; else if (s == QStringLiteral("hfsplus")) rval = FileSystem::HfsPlus; else if (s == QStringLiteral("ufs")) rval = FileSystem::Ufs; else if (s == QStringLiteral("vfat")) { // libblkid uses SEC_TYPE to distinguish between FAT16 and FAT32 QString st = QString::fromUtf8(blkid_get_tag_value(cache, "SEC_TYPE", pedPath)); if (st == QStringLiteral("msdos")) rval = FileSystem::Fat16; else rval = FileSystem::Fat32; } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Btrfs; else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Ocfs2; else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Zfs; else if (s == QStringLiteral("hpfs")) rval = FileSystem::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) rval = FileSystem::Luks; else if (s == QStringLiteral("exfat")) rval = FileSystem::Exfat; else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; else qWarning() << "blkid: unknown file system type " << s << " on " << pedPath; } blkid_put_cache(cache); free(pedPath); } return rval; } PedPartitionFlag LibPartedBackend::getPedFlag(PartitionTable::Flag flag) { for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (flagmap[i].flag == flag) return flagmap[i].pedFlag; return static_cast(-1); } QString LibPartedBackend::lastPartedExceptionMessage() { return s_lastPartedExceptionMessage; } #include "libpartedbackend.moc"