From 1dde035705e57c118a41fe1f92c4760419e03247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20PORTAY?= Date: Wed, 18 Mar 2020 14:24:27 -0400 Subject: [PATCH 1/2] Add new job to set the GPT partition UUID The GPT partition layout supports unique GUID partitions. The CLI sfdisk sets the partition UUID using the option --part-uuid. See the examples below: $ cat <. + * @param report the report to write information to + * @param partition the partition to set the UUID for + * @param uuid the new UUID for the partition + * @return true on success + */ + virtual bool setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) = 0; + /** * Set the system type (e.g. 83 for Linux) of a partition. The type to set is taken from * the partition's file system. diff --git a/src/jobs/CMakeLists.txt b/src/jobs/CMakeLists.txt index aa2665a..2aa58b7 100644 --- a/src/jobs/CMakeLists.txt +++ b/src/jobs/CMakeLists.txt @@ -7,6 +7,7 @@ set(JOBS_SRC jobs/createpartitionjob.cpp jobs/createpartitiontablejob.cpp jobs/setpartitionlabeljob.cpp + jobs/setpartitionuuidjob.cpp jobs/createvolumegroupjob.cpp jobs/removevolumegroupjob.cpp jobs/deactivatevolumegroupjob.cpp diff --git a/src/jobs/setpartitionuuidjob.cpp b/src/jobs/setpartitionuuidjob.cpp new file mode 100644 index 0000000..11c5ee6 --- /dev/null +++ b/src/jobs/setpartitionuuidjob.cpp @@ -0,0 +1,86 @@ +/************************************************************************* + * Copyright (C) 2020 by Gaël PORTAY * + * * + * 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 "jobs/setpartitionuuidjob.h" + +#include "backend/corebackend.h" +#include "backend/corebackendmanager.h" +#include "backend/corebackenddevice.h" +#include "backend/corebackendpartitiontable.h" + +#include "core/partition.h" +#include "core/device.h" + +#include "util/report.h" + +#include +#include + +/** Creates a new SetPartitionUUIDJob (GPT only) + @param d the Device the Partition to be created will be on + @param p the Partition whose UUID is to be set is on + @param newUUID the new UUID +*/ +SetPartitionUUIDJob::SetPartitionUUIDJob(Device& d, Partition& p, const QString& newUUID) : + Job(), + m_Device(d), + m_Partition(p), + m_UUID(newUUID) +{ +} + +bool SetPartitionUUIDJob::run(Report& parent) +{ + Q_ASSERT(partition().devicePath() == device().deviceNode()); + + bool rval = true; + + Report* report = jobStarted(parent); + + // The UUID is supported by GPT only, if the partition table is not GPT, just ignore the + // request and say all is well. This helps in operations because we don't have to check for + // support to avoid having a failed job. + if (m_Device.partitionTable()->type() != PartitionTable::gpt) + report->line() << xi18nc("@info:progress", "Partition table of partition %1 does not support setting UUIDs. Job ignored.", partition().deviceNode()); + else { + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(m_Device); + if (backendDevice) { + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); + + if (backendPartitionTable) { + if (backendPartitionTable->setPartitionUUID(*report, partition(), m_UUID)) { + rval = true; + partition().setUUID(m_UUID); + backendPartitionTable->commit(); + } else + report->line() << xi18nc("@info:progress", "Failed to set the UUID for the partition %1.", partition().deviceNode()); + } else + report->line() << xi18nc("@info:progress", "Could not open partition table on device %1 to set the UUID for the partition %2.", device().deviceNode(), partition().deviceNode()); + } else + report->line() << xi18nc("@info:progress", "Could not open device %1 to set the UUID for partition %2.", device().deviceNode(), partition().deviceNode()); + + } + + jobFinished(*report, rval); + + return rval; +} + +QString SetPartitionUUIDJob::description() const +{ + return xi18nc("@info:progress", "Set the UUID on partition %1 to \"%2\"", partition().deviceNode(), uuid()); +} diff --git a/src/jobs/setpartitionuuidjob.h b/src/jobs/setpartitionuuidjob.h new file mode 100644 index 0000000..d0e8ba1 --- /dev/null +++ b/src/jobs/setpartitionuuidjob.h @@ -0,0 +1,70 @@ +/************************************************************************* + * Copyright (C) 2020 by Gaël PORTAY * + * * + * 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_SETPARTITIONUUIDJOB_H) + +#define KPMCORE_SETPARTITIONUUIDJOB_H + +#include "jobs/job.h" + +class Partition; +class Device; +class Report; + +class QString; + +/** Set a Partition UUID (GPT only). + @author Gaël PORTAY +*/ +class SetPartitionUUIDJob : public Job +{ +public: + SetPartitionUUIDJob(Device& d, Partition& p, const QString& newUUID); + +public: + bool run(Report& parent) override; + QString description() const override; + +protected: + Partition& partition() { + return m_Partition; + } + const Partition& partition() const { + return m_Partition; + } + + Device& device() { + return m_Device; + } + const Device& device() const { + return m_Device; + } + + const QString& uuid() const { + return m_UUID; + } + void setUUID(const QString& u) { + m_UUID = u; + } + +private: + Device& m_Device; + Partition& m_Partition; + QString m_UUID; +}; + +#endif diff --git a/src/ops/newoperation.cpp b/src/ops/newoperation.cpp index c5debd5..e8de081 100644 --- a/src/ops/newoperation.cpp +++ b/src/ops/newoperation.cpp @@ -25,6 +25,7 @@ #include "jobs/createpartitionjob.h" #include "jobs/createfilesystemjob.h" #include "jobs/setpartitionlabeljob.h" +#include "jobs/setpartitionuuidjob.h" #include "jobs/setfilesystemlabeljob.h" #include "jobs/setpartflagsjob.h" #include "jobs/checkfilesystemjob.h" @@ -48,6 +49,7 @@ NewOperation::NewOperation(Device& d, Partition* p) : m_NewPartition(p), m_CreatePartitionJob(new CreatePartitionJob(targetDevice(), newPartition())), m_SetPartitionLabelJob(nullptr), + m_SetPartitionUUIDJob(nullptr), m_CreateFileSystemJob(nullptr), m_SetPartFlagsJob(nullptr), m_SetFileSystemLabelJob(nullptr), @@ -60,6 +62,11 @@ NewOperation::NewOperation(Device& d, Partition* p) : addJob(setPartitionLabelJob()); } + if (!p->uuid().isEmpty()) { + m_SetPartitionUUIDJob = new SetPartitionUUIDJob(targetDevice(), newPartition(), p->uuid()); + addJob(setPartitionUUIDJob()); + } + const FileSystem& fs = newPartition().fileSystem(); if (fs.type() != FileSystem::Type::Extended) { diff --git a/src/ops/newoperation.h b/src/ops/newoperation.h index 8f774ba..247999f 100644 --- a/src/ops/newoperation.h +++ b/src/ops/newoperation.h @@ -31,6 +31,7 @@ class OperationStack; class CreatePartitionJob; class SetPartitionLabelJob; +class SetPartitionUUIDJob; class CreateFileSystemJob; class SetFileSystemLabelJob; class SetPartFlagsJob; @@ -87,6 +88,9 @@ protected: SetPartitionLabelJob* setPartitionLabelJob() { return m_SetPartitionLabelJob; } + SetPartitionUUIDJob* setPartitionUUIDJob() { + return m_SetPartitionUUIDJob; + } CreateFileSystemJob* createFileSystemJob() { return m_CreateFileSystemJob; } @@ -105,6 +109,7 @@ private: Partition* m_NewPartition; CreatePartitionJob* m_CreatePartitionJob; SetPartitionLabelJob* m_SetPartitionLabelJob; + SetPartitionUUIDJob* m_SetPartitionUUIDJob; CreateFileSystemJob* m_CreateFileSystemJob; SetPartFlagsJob* m_SetPartFlagsJob; SetFileSystemLabelJob* m_SetFileSystemLabelJob; diff --git a/src/plugins/dummy/dummypartitiontable.cpp b/src/plugins/dummy/dummypartitiontable.cpp index dc4be94..0cb2718 100644 --- a/src/plugins/dummy/dummypartitiontable.cpp +++ b/src/plugins/dummy/dummypartitiontable.cpp @@ -117,6 +117,15 @@ bool DummyPartitionTable::setPartitionLabel(Report& report, const Partition& par return true; } +bool DummyPartitionTable::setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) +{ + Q_UNUSED(report) + Q_UNUSED(partition) + Q_UNUSED(uuid) + + return true; +} + bool DummyPartitionTable::setFlag(Report& report, const Partition& partition, PartitionTable::Flag partitionManagerFlag, bool state) { Q_UNUSED(report) diff --git a/src/plugins/dummy/dummypartitiontable.h b/src/plugins/dummy/dummypartitiontable.h index d5ba513..72639ed 100644 --- a/src/plugins/dummy/dummypartitiontable.h +++ b/src/plugins/dummy/dummypartitiontable.h @@ -47,6 +47,7 @@ public: bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) override; FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) override; bool setPartitionLabel(Report& report, const Partition& partition, const QString& label) override; + bool setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) override; bool setPartitionSystemType(Report& report, const Partition& partition) override; bool setFlag(Report& report, const Partition& partition, PartitionTable::Flag partitionManagerFlag, bool state) override; }; diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 4adcf65..224a135 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -222,6 +222,15 @@ bool SfdiskPartitionTable::setPartitionLabel(Report& report, const Partition& pa return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; } +bool SfdiskPartitionTable::setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) +{ + if (uuid.isEmpty()) + return true; + ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-uuid"), m_device->deviceNode(), QString::number(partition.number()), + uuid } ); + return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; +} + bool SfdiskPartitionTable::setPartitionSystemType(Report& report, const Partition& partition) { QString partitionType = partition.type(); diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.h b/src/plugins/sfdisk/sfdiskpartitiontable.h index b1dd08e..ee0d355 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.h +++ b/src/plugins/sfdisk/sfdiskpartitiontable.h @@ -47,6 +47,7 @@ public: bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) override; FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) override; bool setPartitionLabel(Report& report, const Partition& partition, const QString& label) override; + bool setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) override; bool setPartitionSystemType(Report& report, const Partition& partition) override; bool setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) override; From e12f0ec39144c9c6fa7b7d8f62d8ae2d8f798b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20PORTAY?= Date: Fri, 15 May 2020 11:22:57 -0400 Subject: [PATCH 2/2] Store the generated partitition UUID after creation The GPT UUID are generated by the backends automatically once a partition is created. This reads the UUID that was generated after the creation of a partition and stores it back to the object Partition. --- src/backend/corebackendpartitiontable.h | 10 ++++++++++ src/jobs/createpartitionjob.cpp | 4 ++++ src/plugins/dummy/dummypartitiontable.cpp | 8 ++++++++ src/plugins/dummy/dummypartitiontable.h | 1 + src/plugins/sfdisk/sfdiskpartitiontable.cpp | 15 +++++++++++++++ src/plugins/sfdisk/sfdiskpartitiontable.h | 1 + 6 files changed, 39 insertions(+) diff --git a/src/backend/corebackendpartitiontable.h b/src/backend/corebackendpartitiontable.h index 9302bf9..37b56f0 100644 --- a/src/backend/corebackendpartitiontable.h +++ b/src/backend/corebackendpartitiontable.h @@ -103,6 +103,16 @@ public: */ virtual bool updateGeometry(Report& report, const Partition& partition, qint64 sector_start, qint64 sector_end) = 0; + /** + * Get the UUID of a partition in the partition table (GPT only). + * The partition UUID is known as PARTUUID by several utilities. The device-manager links + * the device under /dev/disk/by-partuuid/. + * @param report the report to write information to + * @param partition the partition to get the UUID for + * @return the partition UUID + */ + virtual QString getPartitionUUID(Report& report, const Partition& partition) = 0; + /** * Set the label of a partition in the partition table (GPT only). * The label is set in the GPT partition name entry. The partition name is known as PARTLABEL by diff --git a/src/jobs/createpartitionjob.cpp b/src/jobs/createpartitionjob.cpp index 6f20ecd..96b1eb3 100644 --- a/src/jobs/createpartitionjob.cpp +++ b/src/jobs/createpartitionjob.cpp @@ -64,6 +64,10 @@ bool CreatePartitionJob::run(Report& parent) partition().setPartitionPath(partitionPath); partition().setState(Partition::State::None); backendPartitionTable->commit(); + // The UUID is supported by GPT only; it is generated automatically once the creation of a partition. + // Store the generated UUID to the partition object if no UUID is set. + if (m_Device.partitionTable()->type() == PartitionTable::gpt && partition().uuid().isEmpty()) + partition().setUUID(backendPartitionTable->getPartitionUUID(*report, partition())); } else report->line() << xi18nc("@info/plain", "Failed to add partition %1 to device %2.", partition().deviceNode(), device().deviceNode()); } else diff --git a/src/plugins/dummy/dummypartitiontable.cpp b/src/plugins/dummy/dummypartitiontable.cpp index 0cb2718..136806a 100644 --- a/src/plugins/dummy/dummypartitiontable.cpp +++ b/src/plugins/dummy/dummypartitiontable.cpp @@ -117,6 +117,14 @@ bool DummyPartitionTable::setPartitionLabel(Report& report, const Partition& par return true; } +QString DummyPartitionTable::getPartitionUUID(Report& report, const Partition& partition) +{ + Q_UNUSED(report) + Q_UNUSED(partition) + + return QString(); +} + bool DummyPartitionTable::setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) { Q_UNUSED(report) diff --git a/src/plugins/dummy/dummypartitiontable.h b/src/plugins/dummy/dummypartitiontable.h index 72639ed..55e9e8a 100644 --- a/src/plugins/dummy/dummypartitiontable.h +++ b/src/plugins/dummy/dummypartitiontable.h @@ -47,6 +47,7 @@ public: bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) override; FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) override; bool setPartitionLabel(Report& report, const Partition& partition, const QString& label) override; + QString getPartitionUUID(Report& report, const Partition& partition) override; bool setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) override; bool setPartitionSystemType(Report& report, const Partition& partition) override; bool setFlag(Report& report, const Partition& partition, PartitionTable::Flag partitionManagerFlag, bool state) override; diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 224a135..809cd71 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -222,6 +222,21 @@ bool SfdiskPartitionTable::setPartitionLabel(Report& report, const Partition& pa return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; } +QString SfdiskPartitionTable::getPartitionUUID(Report& report, const Partition& partition) +{ + ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--list"), QStringLiteral("--output"), QStringLiteral("Device,UUID"), + m_device->deviceNode() }); + if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) { + QRegularExpression re(m_device->deviceNode() + QString::number(partition.number()) + QStringLiteral(" +(.+)")); + QRegularExpressionMatch rem = re.match(sfdiskCommand.output()); + + if (rem.hasMatch()) + return rem.captured(1); + } + + return QString(); +} + bool SfdiskPartitionTable::setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) { if (uuid.isEmpty()) diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.h b/src/plugins/sfdisk/sfdiskpartitiontable.h index ee0d355..08dc46c 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.h +++ b/src/plugins/sfdisk/sfdiskpartitiontable.h @@ -47,6 +47,7 @@ public: bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) override; FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) override; bool setPartitionLabel(Report& report, const Partition& partition, const QString& label) override; + QString getPartitionUUID(Report& report, const Partition& partition) override; bool setPartitionUUID(Report& report, const Partition& partition, const QString& uuid) override; bool setPartitionSystemType(Report& report, const Partition& partition) override; bool setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) override;