2015-06-04 01:29:22 +01:00
|
|
|
/*************************************************************************
|
|
|
|
* Copyright (C) 2008 by Volker Lanz <vl@fidra.de> *
|
2016-03-02 19:00:31 +00:00
|
|
|
* Copyright (C) 2016 by Andrius Štikonas <andrius@stikonas.eu> *
|
2016-03-02 10:43:27 +00:00
|
|
|
* Copyright (C) 2016 by Teo Mrnjavac <teo@kde.org> *
|
2015-06-04 01:29:22 +01:00
|
|
|
* *
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.*
|
|
|
|
*************************************************************************/
|
|
|
|
|
|
|
|
/** @file
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "core/partitiontable.h"
|
|
|
|
#include "core/partition.h"
|
|
|
|
#include "core/device.h"
|
2016-05-31 19:28:31 +01:00
|
|
|
#include "core/diskdevice.h"
|
2016-06-25 05:36:24 +01:00
|
|
|
#include "core/lvmdevice.h"
|
2015-06-04 01:29:22 +01:00
|
|
|
#include "core/partitionalignment.h"
|
|
|
|
#include "fs/filesystem.h"
|
|
|
|
#include "fs/filesystemfactory.h"
|
|
|
|
|
|
|
|
#include "util/globallog.h"
|
|
|
|
|
|
|
|
#include <KLocalizedString>
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QTextStream>
|
|
|
|
|
|
|
|
/** Creates a new PartitionTable object with type MSDOS
|
2015-07-13 15:16:36 +01:00
|
|
|
@param type name of the PartitionTable type (e.g. "msdos" or "gpt")
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
2016-09-07 16:36:53 +01:00
|
|
|
PartitionTable::PartitionTable(TableType type, qint64 firstUsable, qint64 lastUsable)
|
|
|
|
: PartitionNode()
|
|
|
|
, m_Children()
|
|
|
|
, m_MaxPrimaries(maxPrimariesForTableType(type))
|
|
|
|
, m_Type(type)
|
|
|
|
, m_FirstUsable(firstUsable)
|
|
|
|
, m_LastUsable(lastUsable)
|
2015-06-04 01:29:22 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-09-07 16:36:53 +01:00
|
|
|
/** 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)
|
|
|
|
{
|
2016-12-01 18:00:15 +00:00
|
|
|
m_Children.append(new Partition(**it, this));
|
2016-09-07 16:36:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-04 01:29:22 +01:00
|
|
|
/** Destroys a PartitionTable object, destroying all children */
|
|
|
|
PartitionTable::~PartitionTable()
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
clearChildren();
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the number of free sectors before a given child Partition in this PartitionTable.
|
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
@param p the Partition for which to get the free sectors before
|
|
|
|
@returns the number of free sectors before the Partition
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
qint64 PartitionTable::freeSectorsBefore(const Partition& p) const
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
const Partition* pred = predecessor(p);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
// 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();
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return 0;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the number of free sectors after a given child Partition in this PartitionTable.
|
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
@param p the Partition for which to get the free sectors after
|
|
|
|
@returns the number of free sectors after the Partition
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
qint64 PartitionTable::freeSectorsAfter(const Partition& p) const
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
const Partition* succ = successor(p);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
// 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();
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return 0;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
2016-06-29 11:37:21 +01:00
|
|
|
qint64 PartitionTable::freeSectors() const
|
|
|
|
{
|
|
|
|
qint64 sectors = 0;
|
2016-08-11 14:26:54 +01:00
|
|
|
for (const auto &p : children()) {
|
2016-08-08 18:37:21 +01:00
|
|
|
if (p->roles().has(PartitionRole::Unallocated)) {
|
|
|
|
sectors += p->length();
|
|
|
|
}
|
2016-06-29 11:37:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return sectors;
|
|
|
|
}
|
|
|
|
|
2015-06-04 01:29:22 +01:00
|
|
|
/** @return true if the PartitionTable has an extended Partition */
|
|
|
|
bool PartitionTable::hasExtended() const
|
|
|
|
{
|
2016-08-11 14:26:54 +01:00
|
|
|
for (const auto &p : children())
|
2016-08-08 20:45:41 +01:00
|
|
|
if (p->roles().has(PartitionRole::Extended))
|
2015-07-13 15:16:36 +01:00
|
|
|
return true;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return false;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
2015-07-22 14:48:03 +01:00
|
|
|
/** @return pointer to the PartitionTable's extended Partition or nullptr if none exists */
|
2015-06-04 01:29:22 +01:00
|
|
|
Partition* PartitionTable::extended() const
|
|
|
|
{
|
2016-08-11 14:26:54 +01:00
|
|
|
for (const auto &p : children())
|
2016-08-08 20:45:41 +01:00
|
|
|
if (p->roles().has(PartitionRole::Extended))
|
|
|
|
return p;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-22 14:48:03 +01:00
|
|
|
return nullptr;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets valid PartitionRoles for a Partition
|
2015-07-13 15:16:36 +01:00
|
|
|
@param p the Partition
|
|
|
|
@return valid roles for the given Partition
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
PartitionRole::Roles PartitionTable::childRoles(const Partition& p) const
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
Q_ASSERT(p.parent());
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
PartitionRole::Roles r = p.parent()->isRoot() ? PartitionRole::Primary : PartitionRole::Logical;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
if (r == PartitionRole::Primary && hasExtended() == false && tableTypeSupportsExtended(type()))
|
|
|
|
r |= PartitionRole::Extended;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return r;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @return the number of primaries in this PartitionTable */
|
|
|
|
int PartitionTable::numPrimaries() const
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
int result = 0;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-08-11 14:26:54 +01:00
|
|
|
for (const auto &p : children())
|
2016-06-01 21:00:31 +01:00
|
|
|
if (p->roles().has(PartitionRole::Primary) || p->roles().has(PartitionRole::Extended))
|
|
|
|
result++;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return result;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Appends a Partition to this PartitionTable
|
2015-07-22 14:48:03 +01:00
|
|
|
@param partition pointer of the partition to append. Must not be nullptr.
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
void PartitionTable::append(Partition* partition)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
children().append(partition);
|
2017-12-21 23:16:12 +00:00
|
|
|
std::sort(children().begin(), children().end(), [] (const Partition *a, const Partition *b) -> bool {return a->firstSector() < b->firstSector();});
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @param f the flag to get the name for
|
2015-07-13 15:16:36 +01:00
|
|
|
@returns the flags name or an empty QString if the flag is not known
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
QString PartitionTable::flagName(Flag f)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
switch (f) {
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Boot:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "boot");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Root:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "root");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Swap:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "swap");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Hidden:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "hidden");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Raid:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "raid");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Lvm:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "lvm");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Lba:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "lba");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::HpService:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "hpservice");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Palo:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "palo");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Prep:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "prep");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::MsftReserved:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "msft-reserved");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::BiosGrub:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "bios-grub");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::AppleTvRecovery:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "apple-tv-recovery");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Diag:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "diag");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::LegacyBoot:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "legacy-boot");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::MsftData:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "msft-data");
|
2019-02-09 18:04:44 +00:00
|
|
|
case PartitionTable::Flag::Irst:
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition flag", "irst");
|
2015-07-13 15:16:36 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QString();
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @return list of all flags */
|
2016-08-08 18:37:21 +01:00
|
|
|
const QList<PartitionTable::Flag> PartitionTable::flagList()
|
2015-06-04 01:29:22 +01:00
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
QList<PartitionTable::Flag> rval;
|
|
|
|
|
2019-02-09 18:04:44 +00:00
|
|
|
rval.append(PartitionTable::Flag::Boot);
|
|
|
|
rval.append(PartitionTable::Flag::Root);
|
|
|
|
rval.append(PartitionTable::Flag::Swap);
|
|
|
|
rval.append(PartitionTable::Flag::Hidden);
|
|
|
|
rval.append(PartitionTable::Flag::Raid);
|
|
|
|
rval.append(PartitionTable::Flag::Lvm);
|
|
|
|
rval.append(PartitionTable::Flag::Lba);
|
|
|
|
rval.append(PartitionTable::Flag::HpService);
|
|
|
|
rval.append(PartitionTable::Flag::Palo);
|
|
|
|
rval.append(PartitionTable::Flag::Prep);
|
|
|
|
rval.append(PartitionTable::Flag::MsftReserved);
|
|
|
|
rval.append(PartitionTable::Flag::BiosGrub);
|
|
|
|
rval.append(PartitionTable::Flag::AppleTvRecovery);
|
|
|
|
rval.append(PartitionTable::Flag::Diag);
|
|
|
|
rval.append(PartitionTable::Flag::LegacyBoot);
|
|
|
|
rval.append(PartitionTable::Flag::MsftData);
|
|
|
|
rval.append(PartitionTable::Flag::Irst);
|
2015-07-13 15:16:36 +01:00
|
|
|
|
|
|
|
return rval;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @param flags the flags to get the names for
|
2015-07-13 15:16:36 +01:00
|
|
|
@returns QStringList of the flags' names
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
QStringList PartitionTable::flagNames(Flags flags)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
QStringList rval;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
int f = 1;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
QString s;
|
|
|
|
while (!(s = flagName(static_cast<PartitionTable::Flag>(f))).isEmpty()) {
|
|
|
|
if (flags & f)
|
|
|
|
rval.append(s);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
f <<= 1;
|
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return rval;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
2019-02-28 00:14:18 +00:00
|
|
|
/** @param list QStringList of the flags' names
|
|
|
|
@returns flags corresponding to names
|
|
|
|
*/
|
|
|
|
PartitionTable::Flags PartitionTable::flagsFromList(const QStringList list)
|
|
|
|
{
|
|
|
|
Flags flags;
|
|
|
|
|
|
|
|
for (const auto &flag : flagList())
|
|
|
|
if (list.contains(flagName(flag)))
|
|
|
|
flags.setFlag(flag);
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end)
|
2015-06-04 01:29:22 +01:00
|
|
|
{
|
2018-04-09 02:57:45 +01:00
|
|
|
if (d.type() == Device::Type::Disk_Device) {
|
2016-05-31 19:28:31 +01:00
|
|
|
const DiskDevice& device = dynamic_cast<const DiskDevice&>(d);
|
|
|
|
if (!parent.isRoot()) {
|
|
|
|
Partition* extended = dynamic_cast<Partition*>(&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);
|
2015-07-13 15:16:36 +01:00
|
|
|
}
|
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
return end - start + 1 >= PartitionAlignment::sectorAlignment(device);
|
2018-07-10 23:09:22 +01:00
|
|
|
} else if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) {
|
2016-07-20 22:54:09 +01:00
|
|
|
if (end - start + 1 > 0) {
|
2016-06-25 05:36:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
2015-07-13 15:16:36 +01:00
|
|
|
}
|
2016-05-31 19:28:31 +01:00
|
|
|
return false;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
|
2015-06-04 01:29:22 +01:00
|
|
|
/** Creates a new unallocated Partition on the given Device.
|
2015-07-13 15:16:36 +01:00
|
|
|
@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
|
2015-07-22 14:48:03 +01:00
|
|
|
@return pointer to the newly created Partition object or nullptr if the Partition could not be created
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
Partition* createUnallocated(const Device& device, PartitionNode& parent, qint64 start, qint64 end)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
PartitionRole::Roles r = PartitionRole::Unallocated;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
if (!parent.isRoot())
|
|
|
|
r |= PartitionRole::Logical;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-11-10 14:52:02 +00:00
|
|
|
// Mark unallocated space in LVM VG as LVM LV so that pasting can be easily disabled (it does not work yet)
|
2018-04-09 02:57:45 +01:00
|
|
|
if (device.type() == Device::Type::LVM_Device)
|
2016-11-10 14:52:02 +00:00
|
|
|
r |= PartitionRole::Lvm_Lv;
|
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
if (!PartitionTable::getUnallocatedRange(device, parent, start, end))
|
2015-07-22 14:48:03 +01:00
|
|
|
return nullptr;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2018-04-07 19:54:30 +01:00
|
|
|
return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Type::Unknown, start, end, device.logicalSize()), start, end, QString());
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Removes all unallocated children from a PartitionNode
|
2015-07-13 15:16:36 +01:00
|
|
|
@param p pointer to the parent to remove unallocated children from
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
void PartitionTable::removeUnallocated(PartitionNode* p)
|
|
|
|
{
|
2015-07-23 16:52:22 +01:00
|
|
|
Q_ASSERT(p);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
qint32 i = 0;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
while (i < p->children().size()) {
|
|
|
|
Partition* child = p->children()[i];
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
if (child->roles().has(PartitionRole::Unallocated)) {
|
|
|
|
p->remove(child);
|
|
|
|
delete child;
|
|
|
|
continue;
|
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
if (child->roles().has(PartitionRole::Extended))
|
|
|
|
removeUnallocated(child);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
i++;
|
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-07-13 15:16:36 +01:00
|
|
|
@overload
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
void PartitionTable::removeUnallocated()
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
removeUnallocated(this);
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Inserts unallocated children for a Device's PartitionTable with the given parent.
|
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
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.
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
@warning This method assumes that no unallocated Partitions exist when it is called.
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
@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
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
2016-11-19 01:36:15 +00:00
|
|
|
void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 start)
|
2015-06-04 01:29:22 +01:00
|
|
|
{
|
2015-07-23 16:52:22 +01:00
|
|
|
Q_ASSERT(p);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
qint64 lastEnd = start;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2018-04-09 02:57:45 +01:00
|
|
|
if (d.type() == Device::Type::LVM_Device && !p->children().isEmpty()) {
|
2016-11-10 14:37:48 +00:00
|
|
|
// rearranging the sectors of all partitions to keep unallocated space at the end
|
2016-07-20 22:54:09 +01:00
|
|
|
lastEnd = 0;
|
2017-05-03 19:24:20 +01:00
|
|
|
std::sort(children().begin(), children().end(), [](const Partition* p1, const Partition* p2) { return p1->deviceNode() < p2->deviceNode(); });
|
2016-08-11 14:26:54 +01:00
|
|
|
for (const auto &child : children()) {
|
2016-11-10 14:37:48 +00:00
|
|
|
qint64 totalSectors = child->length();
|
2016-07-20 22:54:09 +01:00
|
|
|
child->setFirstSector(lastEnd);
|
2016-11-10 14:37:48 +00:00
|
|
|
child->setLastSector(lastEnd + totalSectors - 1);
|
2016-07-20 22:54:09 +01:00
|
|
|
|
2016-11-10 14:37:48 +00:00
|
|
|
lastEnd += totalSectors;
|
2016-07-20 22:54:09 +01:00
|
|
|
}
|
2016-07-17 21:50:04 +01:00
|
|
|
} else {
|
2016-08-11 14:26:54 +01:00
|
|
|
const auto pChildren = p->children();
|
|
|
|
for (const auto &child : pChildren) {
|
2016-07-17 21:50:04 +01:00
|
|
|
p->insert(createUnallocated(d, *p, lastEnd, child->firstSector() - 1));
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-07-17 21:50:04 +01:00
|
|
|
if (child->roles().has(PartitionRole::Extended))
|
|
|
|
insertUnallocated(d, child, child->firstSector());
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-07-17 21:50:04 +01:00
|
|
|
lastEnd = child->lastSector() + 1;
|
|
|
|
}
|
2015-07-13 15:16:36 +01:00
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2018-07-24 21:39:55 +01:00
|
|
|
if (d.type() == Device::Type::LVM_Device)
|
|
|
|
{
|
|
|
|
const LvmDevice& lvm = static_cast<const LvmDevice&>(d);
|
|
|
|
p->insert(createUnallocated(d, *p, lastEnd, lastEnd + lvm.freePE() - 1));
|
2015-07-13 15:16:36 +01:00
|
|
|
}
|
2018-07-24 21:39:55 +01:00
|
|
|
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<Partition*>(p);
|
|
|
|
parentEnd = extended ? extended->lastSector() : -1;
|
|
|
|
Q_ASSERT(extended);
|
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2018-07-24 21:39:55 +01:00
|
|
|
if (parentEnd >= firstUsable() && parentEnd >= lastEnd)
|
|
|
|
p->insert(createUnallocated(d, *p, lastEnd, parentEnd));
|
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Updates the unallocated Partitions for this PartitionTable.
|
2015-07-13 15:16:36 +01:00
|
|
|
@param d the Device this PartitionTable is on
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
void PartitionTable::updateUnallocated(const Device& d)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
removeUnallocated();
|
|
|
|
insertUnallocated(d, this, firstUsable());
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t)
|
|
|
|
{
|
2015-07-14 17:40:12 +01:00
|
|
|
Q_UNUSED(t)
|
2018-07-11 16:01:26 +01:00
|
|
|
if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) {
|
2016-06-18 20:54:52 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2016-06-28 19:55:37 +01:00
|
|
|
|
|
|
|
const DiskDevice& diskDevice = dynamic_cast<const DiskDevice&>(d);
|
|
|
|
return PartitionAlignment::sectorAlignment(diskDevice);
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
qint64 PartitionTable::defaultLastUsable(const Device& d, TableType t)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
if (t == gpt)
|
2016-05-31 19:28:31 +01:00
|
|
|
return d.totalLogical() - 1 - 32 - 1;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
return d.totalLogical() - 1;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
static struct {
|
2017-09-14 14:46:54 +01:00
|
|
|
const QLatin1String name; /**< name of partition table type */
|
2015-07-13 15:16:36 +01:00
|
|
|
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[] = {
|
2017-09-14 14:46:54 +01:00
|
|
|
{ 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 },
|
2017-09-17 15:33:25 +01:00
|
|
|
{ QLatin1String("dos"), 4, true, false, PartitionTable::msdos_sectorbased },
|
2017-09-14 14:46:54 +01:00
|
|
|
{ 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 }
|
2015-06-04 01:29:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
PartitionTable::TableType PartitionTable::nameToTableType(const QString& n)
|
|
|
|
{
|
2016-08-08 20:45:41 +01:00
|
|
|
for (const auto &type : tableTypes)
|
|
|
|
if (n == type.name)
|
|
|
|
return type.type;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return PartitionTable::unknownTableType;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QString PartitionTable::tableTypeToName(TableType l)
|
|
|
|
{
|
2016-08-08 20:45:41 +01:00
|
|
|
for (const auto &type : tableTypes)
|
|
|
|
if (l == type.type)
|
|
|
|
return type.name;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-07-17 23:41:00 +01:00
|
|
|
return xi18nc("@item partition table name", "unknown");
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
2017-09-11 12:01:39 +01:00
|
|
|
qint32 PartitionTable::maxPrimariesForTableType(TableType l)
|
2015-06-04 01:29:22 +01:00
|
|
|
{
|
2016-08-08 20:45:41 +01:00
|
|
|
for (const auto &type : tableTypes)
|
|
|
|
if (l == type.type)
|
|
|
|
return type.maxPrimaries;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return 1;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool PartitionTable::tableTypeSupportsExtended(TableType l)
|
|
|
|
{
|
2016-08-08 20:45:41 +01:00
|
|
|
for (const auto &type : tableTypes)
|
|
|
|
if (l == type.type)
|
|
|
|
return type.canHaveExtended;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return false;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool PartitionTable::tableTypeIsReadOnly(TableType l)
|
|
|
|
{
|
2016-08-08 20:45:41 +01:00
|
|
|
for (const auto &type : tableTypes)
|
|
|
|
if (l == type.type)
|
|
|
|
return type.isReadOnly;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return false;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Simple heuristic to determine if the PartitionTable is sector aligned (i.e.
|
2015-07-15 17:38:41 +01:00
|
|
|
if its Partitions begin at sectors evenly divisable by PartitionAlignment::sectorAlignment().
|
2015-07-13 15:16:36 +01:00
|
|
|
@return true if is sector aligned, otherwise false
|
2015-06-04 01:29:22 +01:00
|
|
|
*/
|
|
|
|
bool PartitionTable::isSectorBased(const Device& d) const
|
|
|
|
{
|
2018-04-09 02:57:45 +01:00
|
|
|
if (d.type() == Device::Type::Disk_Device) {
|
2016-05-31 19:28:31 +01:00
|
|
|
const DiskDevice& diskDevice = dynamic_cast<const DiskDevice&>(d);
|
|
|
|
|
|
|
|
if (type() == PartitionTable::msdos) {
|
|
|
|
// the default for empty partition tables is sector based
|
|
|
|
if (numPrimaries() == 0)
|
|
|
|
return true;
|
2015-07-13 15:16:36 +01:00
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
quint32 numCylinderAligned = 0;
|
|
|
|
quint32 numSectorAligned = 0;
|
2015-07-13 15:16:36 +01:00
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
// see if we have more cylinder aligned partitions than sector
|
|
|
|
// aligned ones.
|
2016-08-08 20:45:41 +01:00
|
|
|
for (const auto &p : children()) {
|
2016-08-08 18:37:21 +01:00
|
|
|
if (p->firstSector() % PartitionAlignment::sectorAlignment(diskDevice) == 0)
|
|
|
|
numSectorAligned++;
|
|
|
|
else if (p->firstSector() % diskDevice.cylinderSize() == 0)
|
|
|
|
numCylinderAligned++;
|
|
|
|
}
|
2015-07-13 15:16:36 +01:00
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
return numSectorAligned >= numCylinderAligned;
|
|
|
|
}
|
|
|
|
return type() == PartitionTable::msdos_sectorbased;
|
2015-07-13 15:16:36 +01:00
|
|
|
}
|
|
|
|
|
2016-05-31 19:28:31 +01:00
|
|
|
return false;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PartitionTable::setType(const Device& d, TableType t)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
setFirstUsableSector(defaultFirstUsable(d, t));
|
|
|
|
setLastUsableSector(defaultLastUsable(d, t));
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
m_Type = t;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
updateUnallocated(d);
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable)
|
|
|
|
{
|
2015-07-13 15:16:36 +01:00
|
|
|
stream << "type: \"" << ptable.typeName() << "\"\n"
|
|
|
|
<< "align: \"" << (ptable.type() == PartitionTable::msdos ? "cylinder" : "sector") << "\"\n"
|
|
|
|
<< "\n# number start end type roles label flags\n";
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
QList<const Partition*> partitions;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2016-08-11 14:26:54 +01:00
|
|
|
for (const auto &p : ptable.children()) {
|
2016-06-01 21:00:31 +01:00
|
|
|
if (!p->roles().has(PartitionRole::Unallocated)) {
|
|
|
|
partitions.append(p);
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2017-06-01 11:16:09 +01:00
|
|
|
if (p->roles().has(PartitionRole::Extended)) {
|
|
|
|
const auto partChildren = p->children();
|
|
|
|
for (const auto &child : partChildren) {
|
2016-08-08 18:37:21 +01:00
|
|
|
if (!child->roles().has(PartitionRole::Unallocated))
|
|
|
|
partitions.append(child);
|
|
|
|
}
|
2017-06-01 11:16:09 +01:00
|
|
|
}
|
2016-06-01 21:00:31 +01:00
|
|
|
}
|
2016-08-08 18:37:21 +01:00
|
|
|
}
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2017-05-03 19:24:20 +01:00
|
|
|
std::sort(partitions.begin(), partitions.end(), [](const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); });
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2017-06-01 11:16:09 +01:00
|
|
|
for (const auto &p : qAsConst(partitions))
|
2016-06-01 21:00:31 +01:00
|
|
|
stream << *p;
|
2015-06-04 01:29:22 +01:00
|
|
|
|
2015-07-13 15:16:36 +01:00
|
|
|
return stream;
|
2015-06-04 01:29:22 +01:00
|
|
|
}
|