Merge branch 'issue-2283' into calamares

This commit is contained in:
Adriaan de Groot 2024-02-18 22:45:29 +01:00
commit d7bbfd055c
6 changed files with 110 additions and 30 deletions

View File

@ -1,4 +1,4 @@
# Architecture
#Architecture
<!-- SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
@ -47,7 +47,7 @@ Calamares-specific properties to the Partition instances: setting the install
mount point is done with `PartitionInfo::setMountPoint(partition, "/")`,
retrieving it is done with `mountPoint = PartitionInfo::mountPoint(partition)`.
The rational behind this unusual design is simplicity: the alternative would
The rationale behind this unusual design is simplicity: the alternative would
have been to keep a separate PartitionInfo object and a map linking each
Partition to its PartitionInfo instance. Such a design makes things more
complicated. It complicates memory management: if a Partition goes away, its
@ -60,7 +60,7 @@ the real Partition object. This would have worked and would have made for a less
surprising API, but it would mean more Calamares-specific patches on KPMcore.
# Tests
#Tests
The module comes with unit tests for the partition jobs. Those tests need to
run on storage device which does not contain any data you care about.
@ -78,7 +78,7 @@ this:
sudo CALAMARES_TEST_DISK=/dev/sdb $top_build_dir/partitionjobtests
# TODO
#TODO
- Support resizing extended partitions. ResizePartitionJob should already
support this but the UI prevents editing of extended partitions for now.

View File

@ -166,6 +166,23 @@ struct PartitionCoreModule::DeviceInfo
return Calamares::job_ptr( nullptr );
}
/** @brief Take the jobs of any type that apply to @p partition */
void takeJobs( Partition* partition )
{
for ( auto it = m_jobs.begin(); it != m_jobs.end(); )
{
PartitionJob* job = qobject_cast< PartitionJob* >( it->data() );
if ( job && job->partition() == partition )
{
it = m_jobs.erase( it );
}
else
{
++it;
}
}
}
/** @brief Add a job of given type to the job list
*/
template < typename Job, typename... Args >
@ -557,15 +574,17 @@ PartitionCoreModule::formatPartition( Device* device, Partition* partition )
void
PartitionCoreModule::setFilesystemLabel( Device* device, Partition* partition, const QString& newLabel )
{
if ( newLabel.isEmpty() )
if ( newLabel == PartitionInfo::label( partition ) )
{
// Don't bother
return;
}
auto deviceInfo = infoForDevice( device );
Q_ASSERT( deviceInfo );
OperationHelper helper( partitionModelForDevice( device ), this );
PartitionInfo::setLabel( partition, newLabel );
deviceInfo->takeJob< ChangeFilesystemLabelJob >( partition );
deviceInfo->makeJob< ChangeFilesystemLabelJob >( partition, newLabel );
}
@ -1142,6 +1161,17 @@ PartitionCoreModule::clearJobs()
updateIsDirty();
}
void
PartitionCoreModule::clearJobs( Device* device, Partition* partition )
{
DeviceInfo* devInfo = infoForDevice( device );
if ( devInfo )
{
devInfo->takeJobs( partition );
}
}
bool
PartitionCoreModule::isDirty()

View File

@ -218,6 +218,7 @@ public:
void asyncRevertDevice( Device* dev, std::function< void() > callback ); //like revertDevice, but asynchronous
void clearJobs(); // only clear jobs, the Device* states are preserved
void clearJobs( Device* device, Partition* partition ); // clears all jobs changing @p partition
bool isDirty(); // true if there are pending changes, otherwise false

View File

@ -14,6 +14,7 @@
// KPMcore
#include <kpmcore/core/lvmdevice.h>
#include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystemfactory.h>
// Qt
#include <QVariant>
@ -24,6 +25,7 @@ namespace PartitionInfo
static const char MOUNT_POINT_PROPERTY[] = "_calamares_mountPoint";
static const char FORMAT_PROPERTY[] = "_calamares_format";
static const char FLAGS_PROPERTY[] = "_calamares_flags";
static const char LABEL_PROPERTY[] = "_calamares_label";
QString
mountPoint( const Partition* partition )
@ -74,12 +76,32 @@ setFlags( Partition* partition, PartitionTable::Flags f )
partition->setProperty( FLAGS_PROPERTY, PartitionTable::Flags::Int( f ) );
}
QString
label( const Partition* partition )
{
auto v = partition->property( LABEL_PROPERTY );
if ( !v.isValid() )
{
return partition->fileSystem().label();
}
return v.toString();
}
void
setLabel( Partition* partition, const QString& value )
{
partition->setProperty( LABEL_PROPERTY, value );
}
void
reset( Partition* partition )
{
// Setting a property to an invalid QVariant is equal to removing it
partition->setProperty( MOUNT_POINT_PROPERTY, QVariant() );
partition->setProperty( FORMAT_PROPERTY, QVariant() );
partition->setProperty( FLAGS_PROPERTY, QVariant() );
partition->setProperty( LABEL_PROPERTY, QVariant() );
}
bool

View File

@ -21,7 +21,9 @@ class Partition;
* Functions to store Calamares-specific information in the Qt properties of a
* Partition object.
*
* See README.md for the rational behind this design.
* See README.md for the rationale behind this design. Roughly, these
* functions access **intent** while the existing Partition methods
* access current state.
*
* Properties:
* - mountPoint: which directory will a partition be mounted on the installed
@ -29,6 +31,7 @@ class Partition;
* directory on which a partition is *currently* mounted while the installer
* is running.
* - format: whether this partition should be formatted at install time.
* - label: label to apply to the filesystem in the partition
*/
namespace PartitionInfo
{
@ -42,6 +45,9 @@ void setFormat( Partition* partition, bool value );
PartitionTable::Flags flags( const Partition* partition );
void setFlags( Partition* partition, PartitionTable::Flags f );
QString label( const Partition* partition );
void setLabel( Partition* partition, const QString& value );
void reset( Partition* partition );
/**

View File

@ -45,6 +45,18 @@
using Calamares::Partition::untranslatedFS;
using Calamares::Partition::userVisibleFS;
static void
updateLabel( PartitionCoreModule* core, Device* device, Partition* partition, const QString& fsLabel )
{
// In this case, we are not formatting the partition, but we are setting the
// label on the current filesystem, if any. We only create the job if the
// label actually changed.
if ( partition->fileSystem().type() != FileSystem::Type::Unformatted && fsLabel != partition->fileSystem().label() )
{
core->setFilesystemLabel( device, partition, fsLabel );
}
}
EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
Partition* partition,
const QStringList& usedMountPoints,
@ -69,9 +81,10 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
this,
&EditExistingPartitionDialog::checkMountPointSelection );
// The filesystem label dialog is always enabled, because we may want to change
// The filesystem label field is always enabled, because we may want to change
// the label on the current filesystem without formatting.
m_ui->fileSystemLabelEdit->setText( m_partition->fileSystem().label() );
m_ui->fileSystemLabelEdit->setText( PartitionInfo::label( m_partition ) );
m_ui->fileSystemLabel->setEnabled( true );
replacePartResizerWidget();
@ -81,7 +94,6 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
{
replacePartResizerWidget();
m_ui->fileSystemLabel->setEnabled( doFormat );
m_ui->fileSystemComboBox->setEnabled( doFormat );
if ( !doFormat )
@ -126,14 +138,17 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
m_ui->fileSystemComboBox->setCurrentText( FileSystem::nameForType( defaultFSType ) );
}
m_ui->fileSystemLabel->setEnabled( m_ui->formatRadioButton->isChecked() );
m_ui->fileSystemComboBox->setEnabled( m_ui->formatRadioButton->isChecked() );
// Force a format if the existing device is a zfs device since reusing a
// zpool isn't currently supported; disable the radio buttons then.
const bool partitionIsZFS = m_partition->fileSystem().type() == FileSystem::Type::Zfs;
m_ui->formatRadioButton->setEnabled( !partitionIsZFS );
m_ui->keepRadioButton->setEnabled( !partitionIsZFS );
// Force a format if the existing device is a zfs device since reusing a zpool isn't currently supported
m_ui->formatRadioButton->setChecked( m_partition->fileSystem().type() == FileSystem::Type::Zfs );
m_ui->formatRadioButton->setEnabled( !( m_partition->fileSystem().type() == FileSystem::Type::Zfs ) );
m_ui->keepRadioButton->setChecked( !( m_partition->fileSystem().type() == FileSystem::Type::Zfs ) );
m_ui->keepRadioButton->setEnabled( !( m_partition->fileSystem().type() == FileSystem::Type::Zfs ) );
const bool formatChecked = partitionIsZFS || PartitionInfo::format( m_partition );
m_ui->formatRadioButton->setChecked( formatChecked );
m_ui->keepRadioButton->setChecked( !formatChecked );
m_ui->fileSystemComboBox->setEnabled( m_ui->formatRadioButton->isChecked() );
setFlagList( *( m_ui->m_listFlags ), m_partition->availableFlags(), PartitionInfo::flags( m_partition ) );
}
@ -149,16 +164,18 @@ EditExistingPartitionDialog::newFlags() const
void
EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
{
PartitionInfo::setMountPoint( m_partition, selectedMountPoint( m_ui->mountPointComboBox ) );
// Remove jobs that we might have created for this partition already,
// and also clear intentions so that we set the current ones unconditionally.
core->clearJobs( m_device, m_partition );
PartitionInfo::reset( m_partition );
const QString mountPoint = selectedMountPoint( m_ui->mountPointComboBox );
PartitionInfo::setMountPoint( m_partition, mountPoint );
qint64 newFirstSector = m_partitionSizeController->firstSector();
qint64 newLastSector = m_partitionSizeController->lastSector();
bool partResizedMoved = newFirstSector != m_partition->firstSector() || newLastSector != m_partition->lastSector();
cDebug() << "old boundaries:" << m_partition->firstSector() << m_partition->lastSector() << m_partition->length();
cDebug() << Logger::SubEntry << "new boundaries:" << newFirstSector << newLastSector;
cDebug() << Logger::SubEntry << "dirty status:" << m_partitionSizeController->isDirty();
FileSystem::Type fsType = FileSystem::Unknown;
if ( m_ui->formatRadioButton->isChecked() )
{
@ -171,8 +188,15 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
const auto resultFlags = newFlags();
const auto currentFlags = PartitionInfo::flags( m_partition );
cDebug() << m_partition->partitionPath() << "format?" << m_ui->formatRadioButton->isChecked() << "label=" << fsLabel
<< "mount=" << mountPoint;
if ( partResizedMoved )
{
cDebug() << "old boundaries:" << m_partition->firstSector() << m_partition->lastSector()
<< m_partition->length();
cDebug() << Logger::SubEntry << "new boundaries:" << newFirstSector << newLastSector;
if ( m_ui->formatRadioButton->isChecked() )
{
Partition* newPartition = KPMHelpers::createNewPartition( m_partition->parent(),
@ -197,6 +221,8 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
{
core->setPartitionFlags( m_device, m_partition, resultFlags );
}
updateLabel( core, m_device, m_partition, fsLabel );
PartitionInfo::setFormat( m_partition, false );
}
}
else
@ -213,6 +239,7 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
core->setPartitionFlags( m_device, m_partition, resultFlags );
}
core->setFilesystemLabel( m_device, m_partition, fsLabel );
PartitionInfo::setFormat( m_partition, true );
}
else // otherwise, we delete and recreate the partition with new fs type
{
@ -238,14 +265,8 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
{
core->setPartitionFlags( m_device, m_partition, resultFlags );
}
// In this case, we are not formatting the partition, but we are setting the
// label on the current filesystem, if any. We only create the job if the
// label actually changed.
if ( m_partition->fileSystem().type() != FileSystem::Type::Unformatted
&& fsLabel != m_partition->fileSystem().label() )
{
core->setFilesystemLabel( m_device, m_partition, fsLabel );
}
updateLabel( core, m_device, m_partition, fsLabel );
PartitionInfo::setFormat( m_partition, false );
core->refreshPartition( m_device, m_partition );
}