Merge pull request #2400 from ArrayBolt3/arraybolt3/restrict-fs

Allow restricting the filesystem's usable in manual partitioning
This commit is contained in:
Adriaan de Groot
2024-12-25 16:59:25 +01:00
committed by GitHub
15 changed files with 700 additions and 30 deletions

View File

@@ -60,6 +60,7 @@ if(KPMcore_FOUND)
core/DeviceList.cpp
core/DeviceModel.cpp
core/KPMHelpers.cpp
core/DirFSRestrictLayout.cpp
core/OsproberEntry.cpp
core/PartitionActions.cpp
core/PartitionCoreModule.cpp

View File

@@ -21,6 +21,7 @@
#include "gui/PartitionBarsView.h"
#include "gui/PartitionLabelsView.h"
#include "gui/PartitionPage.h"
#include "partition/FileSystem.h"
#include "Branding.h"
#include "GlobalStorage.h"
@@ -33,6 +34,7 @@
#include "widgets/TranslationFix.h"
#include "widgets/WaitingWidget.h"
#include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h>
#include <QFormLayout>
@@ -59,6 +61,18 @@ PartitionViewStep::PartitionViewStep( QObject* parent )
// We're not done loading, but we need the configuration map first.
}
PartitionViewStep::FSConflictEntry::FSConflictEntry() {}
PartitionViewStep::FSConflictEntry::FSConflictEntry( const QString& conflictingPathArg,
const QString& conflictingFilesystemArg,
const QString& conflictedPathArg,
QStringList allowableFilesystemsArg )
: conflictingPath( conflictingPathArg )
, conflictingFilesystem( conflictingFilesystemArg )
, conflictedPath( conflictedPathArg )
, allowableFilesystems( allowableFilesystemsArg )
{}
void
PartitionViewStep::initPartitionCoreModule()
{
@@ -459,6 +473,12 @@ PartitionViewStep::onActivate()
}
}
static QString
listItem( QString s )
{
return s.prepend( QStringLiteral( "<li>" ) ).append( QStringLiteral( "</li>" ) );
}
static bool
shouldWarnForGPTOnBIOS( const PartitionCoreModule* core )
{
@@ -522,6 +542,143 @@ shouldWarnForNotEncryptedBoot( const Config* config, const PartitionCoreModule*
return false;
}
static PartitionViewStep::FSConflictEntry
calcFSConflictEntry( PartitionCoreModule* core, PartitionModel* partModel, QModelIndex partFsIdx, QModelIndex partMountPointIdx, QStringList mountPointList )
{
PartitionViewStep::FSConflictEntry result;
QString partFs = partModel->data( partFsIdx ).toString().toLower();
QString partMountPoint = partModel->data( partMountPointIdx ).toString();
FileSystem::Type fsType;
PartUtils::canonicalFilesystemName( partFs, &fsType );
bool fsTypeIsAllowed = false;
if ( fsType == FileSystem::Type::Unknown )
{
fsTypeIsAllowed = true;
}
else
{
QList< FileSystem::Type > allowedFsTypes = core->dirFSRestrictLayout().allowedFSTypes( partMountPoint, mountPointList, true );
for ( const auto& allowedFsType : allowedFsTypes )
{
if ( fsType == allowedFsType )
{
fsTypeIsAllowed = true;
break;
}
}
}
if ( !fsTypeIsAllowed )
{
QString conflictedPath = core->dirFSRestrictLayout().diagnoseFSConflict( partMountPoint, fsType, mountPointList );
QList< FileSystem::Type > nonConflictingFilesystemTypes = core->dirFSRestrictLayout().allowedFSTypes( conflictedPath, mountPointList, true );
QStringList nonConflictingFilesystems;
for ( const auto& fsType : nonConflictingFilesystemTypes )
{
nonConflictingFilesystems.append( Calamares::Partition::prettyNameForFileSystemType( fsType ) );
}
result = PartitionViewStep::FSConflictEntry( partMountPoint, partFs, conflictedPath, nonConflictingFilesystems );
}
return result;
}
static QList< PartitionViewStep::FSConflictEntry >
checkForFilesystemConflicts( PartitionCoreModule* core )
{
QList< PartitionViewStep::FSConflictEntry > result;
DeviceModel* dm = core->deviceModel();
QStringList mountPointList;
// Walk the device and partition tree, extracting mountpoints from it
for ( int i = 0; i < dm->rowCount(); i++ )
{
Device* dev = dm->deviceForIndex( dm->index( i ) );
PartitionModel* pm = core->partitionModelForDevice( dev );
QModelIndex extPartMountPointIdx = QModelIndex();
bool extPartFound = false;
for ( int j = 0; j < pm->rowCount(); j++ )
{
QModelIndex partFsIdx = pm->index( j, PartitionModel::FileSystemColumn );
QModelIndex partMountPointIdx = pm->index( j, PartitionModel::MountPointColumn );
if ( pm->data( partFsIdx ).toString().toLower() == "extended" )
{
extPartFound = true;
extPartMountPointIdx = partMountPointIdx;
break;
}
QString mountPoint = pm->data( partMountPointIdx ).toString();
if ( !mountPoint.isEmpty() )
{
mountPointList.append( mountPoint );
}
}
if ( extPartFound )
{
for ( int j = 0; j < pm->rowCount( extPartMountPointIdx ); j++ )
{
QModelIndex partMountPointIdx = pm->index( j, PartitionModel::MountPointColumn, extPartMountPointIdx );
QString mountPoint = pm->data( partMountPointIdx ).toString();
if ( !mountPoint.isEmpty() )
{
mountPointList.append( mountPoint );
}
}
}
}
// Walk the device and partition tree again, validating it this time
for ( int i = 0; i < dm->rowCount(); i++ )
{
Device* dev = dm->deviceForIndex( dm->index( i ) );
PartitionModel* pm = core->partitionModelForDevice( dev );
QModelIndex extPartFsIdx = QModelIndex();
QModelIndex extPartMountPointIdx = QModelIndex();
bool extPartFound = false;
for ( int j = 0; j < pm->rowCount(); j++ )
{
QModelIndex partFsIdx = pm->index( j, PartitionModel::FileSystemColumn );
QModelIndex partMountPointIdx = pm->index( j, PartitionModel::MountPointColumn );
if ( pm->data( partFsIdx ).toString().toLower() == "extended" )
{
extPartFound = true;
extPartFsIdx = partFsIdx;
extPartMountPointIdx = partMountPointIdx;
break;
}
PartitionViewStep::FSConflictEntry conflictEntry = calcFSConflictEntry( core, pm, partFsIdx, partMountPointIdx, mountPointList );
if ( !conflictEntry.conflictedPath.isEmpty() )
{
result.append( conflictEntry );
}
}
if ( extPartFound )
{
for ( int j = 0; j < pm->rowCount( extPartFsIdx ); j++ )
{
QModelIndex partFsIdx = pm->index( j, PartitionModel::FileSystemColumn, extPartFsIdx );
QModelIndex partMountPointIdx = pm->index( j, PartitionModel::MountPointColumn, extPartMountPointIdx );
PartitionViewStep::FSConflictEntry conflictEntry = calcFSConflictEntry( core, pm, partFsIdx, partMountPointIdx, mountPointList );
if ( !conflictEntry.conflictedPath.isEmpty() )
{
result.append( conflictEntry );
}
}
}
}
return result;
}
void
PartitionViewStep::onLeave()
{
@@ -532,6 +689,10 @@ PartitionViewStep::onLeave()
}
const auto* branding = Calamares::Branding::instance();
const QString startList = QStringLiteral( "<br/><br/><ul>" );
const QString endList = QStringLiteral( "</ul><br/><br/>" );
if ( m_widget->currentWidget() == m_manualPartitionPage )
{
if ( PartUtils::isEfiSystem() )
@@ -587,12 +748,6 @@ PartitionViewStep::onLeave()
const QString possibleFail = tr( "You can continue with this EFI system "
"partition configuration but your system may fail to start." );
const QString startList = QStringLiteral( "<br/><br/><ul>" );
const QString endList = QStringLiteral( "</ul><br/><br/>" );
auto listItem = []( QString s ) -> QString
{ return s.prepend( QStringLiteral( "<li>" ) ).append( QStringLiteral( "</li>" ) ); };
if ( !esp )
{
cDebug() << o << "No ESP mounted";
@@ -684,6 +839,52 @@ PartitionViewStep::onLeave()
Calamares::fixButtonLabels( &mb );
mb.exec();
}
QList< FSConflictEntry > conflictMap = checkForFilesystemConflicts( m_core );
if ( !conflictMap.isEmpty() )
{
QString message = tr( "Filesystem conflicts found" );
const QString descHeader = tr( "The chosen manual partitioning layout does not "
"comply with the filesystem restrictions set by the "
"distro. The following issues were found:");
QStringList issueList;
for ( const auto& entry : conflictMap )
{
QString buildString;
if ( entry.conflictedPath == "any" )
{
buildString = tr( "The %1 directory uses filesystem %2, but this distro only allows the following filesystems: %3." )
.arg( entry.conflictingPath )
.arg( entry.conflictingFilesystem )
.arg( entry.allowableFilesystems.join( ", " ) );
issueList.append( buildString );
}
else
{
buildString = tr( "The %1 directory uses filesystem %2, but the %3 directory must use one of the following filesystems: %4." )
.arg( entry.conflictingPath )
.arg( entry.conflictingFilesystem )
.arg( entry.conflictedPath )
.arg( entry.allowableFilesystems.join( ", " ) );
issueList.append( buildString );
}
}
const QString descFooter = tr( "You can continue without setting up filesystems "
"properly, but your system may fail to start." );
QString description = descHeader + startList;
for ( const auto& item : issueList )
{
description += listItem( item );
}
description += endList + descFooter;
QMessageBox mb( QMessageBox::Warning, message, description, QMessageBox::Ok, m_manualPartitionPage );
Calamares::fixButtonLabels( &mb );
mb.exec();
}
}
}
@@ -740,6 +941,7 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
m_future->setFuture( future );
m_core->partitionLayout().init( m_config->defaultFsType(), configurationMap.value( "partitionLayout" ).toList() );
m_core->dirFSRestrictLayout().init( configurationMap.value( "directoryFilesystemRestrictions" ).toList() );
}
Calamares::JobList

View File

@@ -39,6 +39,21 @@ class PLUGINDLLEXPORT PartitionViewStep : public Calamares::ViewStep
Q_OBJECT
public:
struct FSConflictEntry
{
QString conflictingPath;
QString conflictingFilesystem;
QString conflictedPath;
QStringList allowableFilesystems;
FSConflictEntry();
FSConflictEntry( const QString& conflictingPathArg,
const QString& conflictingFilesystemArg,
const QString& conflictedPathArg,
QStringList allowableFilesystemsArg );
FSConflictEntry( const FSConflictEntry& e ) = default;
};
explicit PartitionViewStep( QObject* parent = nullptr );
~PartitionViewStep() override;

View File

@@ -0,0 +1,231 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014-2017 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2018-2019 Collabora Ltd <arnaud.ferraris@collabora.com>
* SPDX-FileCopyrightText: 2024 Aaron Rainbolt <arraybolt3@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "utils/Logger.h"
#include "core/DirFSRestrictLayout.h"
#include "core/KPMHelpers.h"
#include "core/PartUtils.h"
#include "utils/Variant.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include <kpmcore/fs/filesystem.h>
#include <kpmcore/fs/filesystemfactory.h>
#include <QSet>
DirFSRestrictLayout::DirFSRestrictLayout() {}
DirFSRestrictLayout::DirFSRestrictLayout( const DirFSRestrictLayout& layout )
: m_dirFSRestrictLayout( layout.m_dirFSRestrictLayout )
{
}
DirFSRestrictLayout::~DirFSRestrictLayout() {}
DirFSRestrictLayout::DirFSRestrictEntry::DirFSRestrictEntry( const QString& path,
QList< FileSystem::Type > allowedFSTypes,
bool onlyWhenMountpoint )
: dirPath( path )
, dirAllowedFSTypes( allowedFSTypes )
, useOnlyWhenMountpoint( onlyWhenMountpoint )
{
}
void
DirFSRestrictLayout::init( const QVariantList& config )
{
m_dirFSRestrictLayout.clear();
bool efiNeedsSet = true;
for ( const auto& r : config )
{
QVariantMap pentry = r.toMap();
if ( !pentry.contains( "directory" ) || !pentry.contains( "allowedFilesystemTypes" ) )
{
cError() << "Directory filesystem restriction layout entry #" << config.indexOf( r )
<< "lacks mandatory attributes, switching to default layout.";
m_dirFSRestrictLayout.clear();
break;
}
QString directory = Calamares::getString( pentry, "directory" );
QStringList allowedFSTypeStrings = Calamares::getStringList( pentry, "allowedFilesystemTypes" );
QList< FileSystem::Type > allowedFSTypes;
if ( allowedFSTypeStrings.length() == 1 && allowedFSTypeStrings[0] == "all" )
{
allowedFSTypes = fullFSList();
}
else
{
for ( const auto& fsStr : allowedFSTypeStrings )
{
FileSystem::Type allowedFSType;
PartUtils::canonicalFilesystemName( fsStr, &allowedFSType );
if ( allowedFSType == FileSystem::Type::Unknown )
{
continue;
}
allowedFSTypes.append( allowedFSType );
}
}
bool onlyWhenMountpoint = Calamares::getBool( pentry, "onlyWhenMountpoint", false );
if ( directory == "efi" )
{
efiNeedsSet = false;
}
DirFSRestrictEntry restrictEntry( directory, allowedFSTypes, onlyWhenMountpoint );
m_dirFSRestrictLayout.append( restrictEntry );
}
if ( efiNeedsSet )
{
QList< FileSystem::Type > efiAllowedFSTypes = { FileSystem::Fat32 };
DirFSRestrictEntry efiRestrictEntry( "efi", efiAllowedFSTypes, true );
m_dirFSRestrictLayout.append( efiRestrictEntry );
}
}
QList< FileSystem::Type >
DirFSRestrictLayout::allowedFSTypes( const QString& path, const QStringList& existingMountpoints, bool overlayDirs )
{
QSet< FileSystem::Type > typeSet;
bool foundTypeList = false;
for ( const auto& entry : m_dirFSRestrictLayout )
{
QString dirPath = entry.dirPath;
if ( dirPath == "efi" )
{
dirPath = Calamares::JobQueue::instance()->globalStorage()->value( "efiSystemPartition" ).toString();
}
if ( dirPath == path || ( !entry.useOnlyWhenMountpoint && overlayDirs && path.startsWith( QStringLiteral( "/" ) ) && dirPath.startsWith( path ) && !existingMountpoints.contains( dirPath ) ) )
{
QSet< FileSystem::Type > newTypeSet = QSet< FileSystem::Type >( entry.dirAllowedFSTypes.cbegin(), entry.dirAllowedFSTypes.cend() );
foundTypeList = true;
if ( typeSet.isEmpty() )
{
typeSet = newTypeSet;
if ( !overlayDirs )
{
break;
}
}
else
{
typeSet.intersect( newTypeSet );
}
}
}
if ( overlayDirs )
{
QList< FileSystem::Type > anyTypeList = anyAllowedFSTypes();
QSet< FileSystem::Type > anyTypeSet = QSet< FileSystem::Type >( anyTypeList.cbegin(), anyTypeList.cend() );
if ( !foundTypeList )
{
typeSet = anyTypeSet;
foundTypeList = true;
}
else
{
typeSet.intersect( anyTypeSet );
}
}
if ( foundTypeList )
{
return QList< FileSystem::Type >( typeSet.cbegin(), typeSet.cend() );
}
else
{
// This directory doesn't have any allowed filesystems explicitly
// configured, so all filesystems are valid.
return fullFSList();
}
}
QString
DirFSRestrictLayout::diagnoseFSConflict( const QString& path, const FileSystem::Type& fsType, const QStringList& existingMountpoints )
{
QSet< FileSystem::Type > typeSet;
bool foundTypeList = false;
for ( const auto& entry : m_dirFSRestrictLayout )
{
QString dirPath = entry.dirPath;
if ( dirPath == "efi" )
{
dirPath = Calamares::JobQueue::instance()->globalStorage()->value( "efiSystemPartition" ).toString();
}
if ( dirPath == path || ( !entry.useOnlyWhenMountpoint && path.startsWith( QStringLiteral( "/" ) ) && ( dirPath.startsWith( path ) || dirPath == "any" ) && !existingMountpoints.contains( dirPath ) ) )
{
QSet< FileSystem::Type > newTypeSet = QSet< FileSystem::Type >( entry.dirAllowedFSTypes.cbegin(), entry.dirAllowedFSTypes.cend() );
foundTypeList = true;
if ( typeSet.isEmpty() )
{
typeSet = newTypeSet;
}
else
{
typeSet.intersect( newTypeSet );
}
}
if ( foundTypeList && !typeSet.contains( fsType ) )
{
if ( typeSet.isEmpty() )
{
cWarning() << "no filesystems are valid for path '" << path << "', check directoryFilesystemRestrictions for issues";
}
// At this point, we've found the first mountpoint that, when
// taken into account, results in the currently chosen filesystem
// being invalid. Return that mountpoint.
return dirPath;
}
}
return QString();
}
QList< FileSystem::Type >
DirFSRestrictLayout::anyAllowedFSTypes()
{
for ( const auto& entry : m_dirFSRestrictLayout )
{
if ( entry.dirPath == "any" )
{
return entry.dirAllowedFSTypes;
}
}
// No global filesystem whitelist defined, so all filesystems are
// considered valid unless a mountpoint-specific whitelist is used to
// restrict the allowed filesystems.
return fullFSList();
}
QList< FileSystem::Type >
DirFSRestrictLayout::fullFSList()
{
QList< FileSystem::Type > typeList;
FileSystemFactory::init();
for ( auto fs : FileSystemFactory::map() )
{
typeList.append( fs->type() );
}
return typeList;
}

View File

@@ -0,0 +1,87 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2018-2019 Collabora Ltd <arnaud.ferraris@collabora.com>
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2024 Aaron Rainbolt <arraybolt3@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef DIRFSRESTRICTLAYOUT_H
#define DIRFSRESTRICTLAYOUT_H
#include "Config.h"
// KPMcore
#include <kpmcore/fs/filesystem.h>
// Qt
#include <QList>
#include <QObject>
#include <QVariantMap>
class DirFSRestrictLayout
{
public:
struct DirFSRestrictEntry
{
QString dirPath;
QList< FileSystem::Type > dirAllowedFSTypes;
bool useOnlyWhenMountpoint = false;
/// @brief All-zeroes DirFSRestrictEntry
DirFSRestrictEntry() = default;
/** @brief Parse @p path, @p allowedFSTypes, and @p onlyWhenMountpoint to their respective member variables
*
* Sets a specific set of allowed filesystems for a mountpoint.
*/
DirFSRestrictEntry( const QString& path,
QList< FileSystem::Type > allowedFSTypes,
bool onlyWhenMountpoint );
/// @brief Copy DirFSRestrictEntry
DirFSRestrictEntry( const DirFSRestrictEntry& e ) = default;
};
DirFSRestrictLayout();
DirFSRestrictLayout( const DirFSRestrictLayout& layout );
~DirFSRestrictLayout();
/** @brief create the configuration from @p config
*
* @p config is a list of partition entries (in QVariant form,
* read from YAML). If no entries are given, the only restriction is that
* the EFI system partition must use fat32.
*
* Any unknown values in the config will be ignored.
*/
void init( const QVariantList& config );
/** @brief get a list of allowable filesystems for a path
*
* @p path is the path one wants to get the allowed FS types for.
* @p existingMountpoints is the list of all mountpoints that are
* currently configured to be placed on their own partition.
*/
QList< FileSystem::Type > allowedFSTypes( const QString& path, const QStringList& existingMountpoints, bool overlayDirs );
/** @brief determine which directory restriction rule makes a particular mountpoint + filesystem combination invalid
*
* @p path is the path with an improper filesystem chosen.
* @p fsType is the improper filesystem used on that path.
* @p existingMountpoints is the list of all mountpoints that are
* currently configured to be placed on their own partition.
*/
QString diagnoseFSConflict( const QString& path, const FileSystem::Type& fsType, const QStringList& existingMountpoints );
/// @brief get a global filesystem whitelist
QList< FileSystem::Type > anyAllowedFSTypes();
private:
QList< DirFSRestrictEntry > m_dirFSRestrictLayout;
QList< FileSystem::Type > fullFSList();
};
#endif /* DIRFSRESTRICTLAYOUT_H */

View File

@@ -16,6 +16,7 @@
#include "core/KPMHelpers.h"
#include "core/PartitionLayout.h"
#include "core/PartitionModel.h"
#include "core/DirFSRestrictLayout.h"
#include "jobs/PartitionJob.h"
#include "Job.h"
@@ -167,6 +168,9 @@ public:
*/
PartitionLayout& partitionLayout() { return m_partLayout; }
/// @brief Get the directory filesystem restriction layout.
DirFSRestrictLayout& dirFSRestrictLayout() { return m_dirFSRestrictLayout; }
void layoutApply( Device* dev,
qint64 firstSector,
qint64 lastSector,
@@ -270,6 +274,7 @@ private:
bool m_isDirty = false;
QString m_bootLoaderInstallPath;
PartitionLayout m_partLayout;
DirFSRestrictLayout m_dirFSRestrictLayout;
OsproberEntryList m_osproberLines;

View File

@@ -18,6 +18,7 @@
#include "core/KPMHelpers.h"
#include "core/PartUtils.h"
#include "core/PartitionInfo.h"
#include "core/PartitionCoreModule.h"
#include "gui/PartitionDialogHelpers.h"
#include "gui/PartitionSizeController.h"
@@ -46,18 +47,14 @@
using Calamares::Partition::untranslatedFS;
using Calamares::Partition::userVisibleFS;
static QSet< FileSystem::Type > s_unmountableFS( { FileSystem::Unformatted,
FileSystem::LinuxSwap,
FileSystem::Extended,
FileSystem::Unknown,
FileSystem::Lvm2_PV } );
CreatePartitionDialog::CreatePartitionDialog( Device* device,
CreatePartitionDialog::CreatePartitionDialog( PartitionCoreModule* core,
Device* device,
PartitionNode* parentPartition,
const QStringList& usedMountPoints,
QWidget* parentWidget )
: QDialog( parentWidget )
, m_ui( new Ui_CreatePartitionDialog )
, m_core( core )
, m_partitionSizeController( new PartitionSizeController( this ) )
, m_device( device )
, m_parent( parentPartition )
@@ -128,17 +125,23 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
this,
&CreatePartitionDialog::checkMountPointSelection );
connect( m_ui->fsComboBox,
&QComboBox::currentTextChanged,
this,
&CreatePartitionDialog::checkMountPointSelection );
// Select a default
m_ui->fsComboBox->setCurrentIndex( defaultFsIndex );
updateMountPointUi();
checkMountPointSelection();
}
CreatePartitionDialog::CreatePartitionDialog( Device* device,
CreatePartitionDialog::CreatePartitionDialog( PartitionCoreModule* core,
Device* device,
const FreeSpace& freeSpacePartition,
const QStringList& usedMountPoints,
QWidget* parentWidget )
: CreatePartitionDialog( device, freeSpacePartition.p->parent(), usedMountPoints, parentWidget )
: CreatePartitionDialog( core, device, freeSpacePartition.p->parent(), usedMountPoints, parentWidget )
{
standardMountPoints( *( m_ui->mountPointComboBox ), QString() );
setFlagList( *( m_ui->m_listFlags ),
@@ -147,11 +150,12 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
initPartResizerWidget( freeSpacePartition.p );
}
CreatePartitionDialog::CreatePartitionDialog( Device* device,
CreatePartitionDialog::CreatePartitionDialog( PartitionCoreModule* core,
Device* device,
const FreshPartition& existingNewPartition,
const QStringList& usedMountPoints,
QWidget* parentWidget )
: CreatePartitionDialog( device, existingNewPartition.p->parent(), usedMountPoints, parentWidget )
: CreatePartitionDialog( core, device, existingNewPartition.p->parent(), usedMountPoints, parentWidget )
{
standardMountPoints( *( m_ui->mountPointComboBox ), PartitionInfo::mountPoint( existingNewPartition.p ) );
setFlagList( *( m_ui->m_listFlags ),
@@ -343,8 +347,10 @@ CreatePartitionDialog::updateMountPointUi()
void
CreatePartitionDialog::checkMountPointSelection()
{
validateMountPoint( selectedMountPoint( m_ui->mountPointComboBox ),
validateMountPoint( m_core,
selectedMountPoint( m_ui->mountPointComboBox ),
m_usedMountPoints,
m_ui->fsComboBox->currentText(),
m_ui->mountPointExplanation,
m_ui->buttonBox->button( QDialogButtonBox::Ok ) );
}

View File

@@ -19,7 +19,7 @@
#include <QDialog>
#include <QScopedPointer>
class PartitionCoreModule;
class Device;
class Partition;
class PartitionNode;
@@ -39,7 +39,8 @@ private:
*
* This does all the shared UI setup.
*/
CreatePartitionDialog( Device* device,
CreatePartitionDialog( PartitionCoreModule* core,
Device* device,
PartitionNode* parentPartition,
const QStringList& usedMountPoints,
QWidget* parentWidget );
@@ -59,7 +60,8 @@ public:
* Creating from free space makes a wholly new partition with
* no flags set at all.
*/
CreatePartitionDialog( Device* device,
CreatePartitionDialog( PartitionCoreModule* core,
Device* device,
const FreeSpace& freeSpacePartition,
const QStringList& usedMountPoints,
QWidget* parentWidget = nullptr );
@@ -68,7 +70,8 @@ public:
* A partition previously newly created (e.g. via this dialog
* and the constructor above) can be re-edited.
*/
CreatePartitionDialog( Device* device,
CreatePartitionDialog( PartitionCoreModule* core,
Device* device,
const FreshPartition& existingNewPartition,
const QStringList& usedMountPoints,
QWidget* parentWidget = nullptr );
@@ -84,6 +87,7 @@ private Q_SLOTS:
private:
QScopedPointer< Ui_CreatePartitionDialog > m_ui;
PartitionCoreModule* m_core;
PartitionSizeController* m_partitionSizeController;
Device* m_device;
PartitionNode* m_parent;

View File

@@ -57,12 +57,14 @@ updateLabel( PartitionCoreModule* core, Device* device, Partition* partition, co
}
}
EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
EditExistingPartitionDialog::EditExistingPartitionDialog( PartitionCoreModule* core,
Device* device,
Partition* partition,
const QStringList& usedMountPoints,
QWidget* parentWidget )
: QDialog( parentWidget )
, m_ui( new Ui_EditExistingPartitionDialog )
, m_core( core )
, m_device( device )
, m_partition( partition )
, m_partitionSizeController( new PartitionSizeController( this ) )
@@ -81,6 +83,11 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
this,
&EditExistingPartitionDialog::checkMountPointSelection );
connect( m_ui->fileSystemComboBox,
&QComboBox::currentTextChanged,
this,
&EditExistingPartitionDialog::checkMountPointSelection );
// 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( PartitionInfo::label( m_partition ) );
@@ -345,8 +352,10 @@ EditExistingPartitionDialog::updateMountPointPicker()
void
EditExistingPartitionDialog::checkMountPointSelection()
{
if ( validateMountPoint( selectedMountPoint( m_ui->mountPointComboBox ),
if ( validateMountPoint( m_core,
selectedMountPoint( m_ui->mountPointComboBox ),
m_usedMountPoints,
m_ui->fileSystemComboBox->currentText(),
m_ui->mountPointExplanation,
m_ui->buttonBox->button( QDialogButtonBox::Ok ) ) )
{

View File

@@ -37,7 +37,8 @@ public:
Partition* p;
};
EditExistingPartitionDialog( Device* device,
EditExistingPartitionDialog( PartitionCoreModule* core,
Device* device,
Partition* partition,
const QStringList& usedMountPoints,
QWidget* parentWidget = nullptr );
@@ -50,6 +51,7 @@ private slots:
private:
QScopedPointer< Ui_EditExistingPartitionDialog > m_ui;
PartitionCoreModule* m_core;
Device* m_device;
Partition* m_partition;
PartitionSizeController* m_partitionSizeController;

View File

@@ -12,12 +12,15 @@
#include "PartitionDialogHelpers.h"
#include "core/PartUtils.h"
#include "core/PartitionCoreModule.h"
#include "gui/CreatePartitionDialog.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/Logger.h"
#include <kpmcore/fs/filesystem.h>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
@@ -81,7 +84,7 @@ setSelectedMountPoint( QComboBox& combo, const QString& selected )
}
bool
validateMountPoint( const QString& mountPoint, const QStringList& inUse, QLabel* label, QPushButton* button )
validateMountPoint( PartitionCoreModule* core, const QString& mountPoint, const QStringList& inUse, const QString& fileSystem, QLabel* label, QPushButton* button )
{
QString msg;
bool ok = true;
@@ -95,6 +98,58 @@ validateMountPoint( const QString& mountPoint, const QStringList& inUse, QLabel*
{
msg = CreatePartitionDialog::tr( "Mountpoint must start with a <tt>/</tt>.", "@info" );
ok = false;
} else {
// Validate the chosen filesystem + mountpoint combination.
FileSystem::Type selectedFsType;
PartUtils::canonicalFilesystemName( fileSystem, &selectedFsType );
bool fsTypeIsAllowed = false;
if ( selectedFsType == FileSystem::Type::Unknown )
{
fsTypeIsAllowed = true;
}
else
{
QList< FileSystem::Type > anyAllowedFsTypes = core->dirFSRestrictLayout().anyAllowedFSTypes();
for ( auto& anyAllowedFsType : anyAllowedFsTypes )
{
if ( selectedFsType == anyAllowedFsType )
{
fsTypeIsAllowed = true;
break;
}
}
}
bool fsTypeIsAllowedForMountPoint = false;
// We allow arbitrary unmountable filesystems here since an
// unmountable filesystem has no mount point associated with it, thus
// any filesystem restriction we'd find at this point would be
// irrelevant.
if ( selectedFsType == FileSystem::Type::Unknown || s_unmountableFS.contains( selectedFsType ) )
{
fsTypeIsAllowedForMountPoint = true;
}
else
{
QList< FileSystem::Type > allowedFsTypes = core->dirFSRestrictLayout().allowedFSTypes( mountPoint, inUse, false );
for ( auto& allowedFsType : allowedFsTypes )
{
if ( selectedFsType == allowedFsType )
{
fsTypeIsAllowedForMountPoint = true;
break;
}
}
}
if ( !fsTypeIsAllowed ) {
msg = CreatePartitionDialog::tr( "Filesystem is prohibited by this distro. Consider selecting another one.", "@info" );
ok = true;
}
else if ( !fsTypeIsAllowedForMountPoint ) {
msg = CreatePartitionDialog::tr( "Filesystem is prohibited for use on this mountpoint. Consider selecting a different filesystem or mountpoint.", "@info" );
ok = true;
}
}
if ( label )

View File

@@ -13,14 +13,23 @@
#define PARTITION_GUI_PARTITIONDIALOGHELPERS
#include <kpmcore/core/partitiontable.h>
#include <kpmcore/fs/filesystem.h>
#include <QStringList>
#include <QSet>
class PartitionCoreModule;
class QPushButton;
class QComboBox;
class QLabel;
class QListWidget;
static QSet< FileSystem::Type > s_unmountableFS( { FileSystem::Unformatted,
FileSystem::LinuxSwap,
FileSystem::Extended,
FileSystem::Unknown,
FileSystem::Lvm2_PV } );
/**
* Returns a list of standard mount points (e.g. /, /usr, ...).
* This also includes the EFI mount point if that is necessary
@@ -68,7 +77,7 @@ setSelectedMountPoint( QComboBox* combo, const QString& selected )
* If it is not valid, returns @c false and sets the UI
* to explain why.
*/
bool validateMountPoint( const QString& mountPoint, const QStringList& inUse, QLabel* label, QPushButton* button );
bool validateMountPoint( PartitionCoreModule* core, const QString& mountPoint, const QStringList& inUse, const QString& fileSystem, QLabel* label, QPushButton* button );
/**
* Get the flags that have been checked in the list widget.

View File

@@ -411,7 +411,7 @@ PartitionPage::onCreateClicked()
}
QPointer< CreatePartitionDialog > dlg = new CreatePartitionDialog(
model->device(), CreatePartitionDialog::FreeSpace { partition }, getCurrentUsedMountpoints(), this );
m_core, model->device(), CreatePartitionDialog::FreeSpace { partition }, getCurrentUsedMountpoints(), this );
if ( dlg->exec() == QDialog::Accepted )
{
Partition* newPart = dlg->getNewlyCreatedPartition();
@@ -515,7 +515,7 @@ PartitionPage::updatePartitionToCreate( Device* device, Partition* partition )
mountPoints.removeOne( PartitionInfo::mountPoint( partition ) );
QPointer< CreatePartitionDialog > dlg
= new CreatePartitionDialog( device, CreatePartitionDialog::FreshPartition { partition }, mountPoints, this );
= new CreatePartitionDialog( m_core, device, CreatePartitionDialog::FreshPartition { partition }, mountPoints, this );
if ( dlg->exec() == QDialog::Accepted )
{
Partition* newPartition = dlg->getNewlyCreatedPartition();
@@ -532,7 +532,7 @@ PartitionPage::editExistingPartition( Device* device, Partition* partition )
mountPoints.removeOne( PartitionInfo::mountPoint( partition ) );
QPointer< EditExistingPartitionDialog > dlg
= new EditExistingPartitionDialog( device, partition, mountPoints, this );
= new EditExistingPartitionDialog( m_core, device, partition, mountPoints, this );
if ( dlg->exec() == QDialog::Accepted )
{
dlg->applyChanges( m_core );

View File

@@ -223,6 +223,49 @@ defaultFileSystemType: "ext4"
# warning (this matches traditional no-choice-available behavior best).
# availableFileSystemTypes: ["ext4","f2fs"]
# Per-directory filesystem restrictions.
#
# This optional setting specifies what filesystems the user can and cannot use
# for various directories and mountpoints when using manual partitioning.
#
# If nothing is specified, the only restriction enforced by default is that
# the EFI system partition must use the fat32 filesystem.
#
# Otherwise, the filesystem restrictions are defined as follow:
#
# directoryFilesystemRestrictions:
# - directory: "any"
# allowedFilesystemTypes: ["all"]
# - directory: "/"
# allowedFilesystemTypes: ["ext4","xfs","btrfs","jfs","f2fs"]
# - mountpoint: "efi"
# allowedFilesystemTypes: ["fat32"]
# onlyWhenMountpoint: true
#
# There can be any number of mountpoints listed, each entry having the
# following attributes:
# - mountpoint: mountpoint's full path
# or
# "any" to specify a global whitelist that applies to all
# mountpoints
# or
# "efi" to specify a whitelist specific to the EFI system
# partition, wherever that partition is located
# - allowedFilesystemTypes: the list of all filesystems valid for this
# mountpoint. If the list contains exactly one
# element, and that element is the special value
# "any", all filesystem types recognized by
# Calamares will be allowed.
# - onlyWhenMountpoint: Whether the restriction should apply only when the
# specified directory is a mountpoint. When set to
# true, Calamares will only enforce the listed
# restrictions when the user makes a separate partition
# for this directory and assigns the mountpoint
# accordingly. When set to false, Calamares will
# ensure this directory uses the specified filesystem
# even if the directory is part of a filesystem on a
# different mountpoint. Defaults to false.
# Show/hide LUKS related functionality in automated partitioning modes.
# Disable this if you choose not to deploy early unlocking support in GRUB2
# and/or your distribution's initramfs solution.

View File

@@ -37,6 +37,7 @@ properties:
defaultFileSystemType: { type: string }
availableFileSystemTypes: { type: array, items: { type: string } }
mountpointFilesystemRestrictions: { type: array } # TODO: specify items
luksGeneration: { type: string, enum: [luks1, luks2] } # Also allows "luks" as alias of "luks1"
enableLuksAutomatedPartitioning: { type: boolean, default: false }