diff --git a/src/modules/fsresizer/CMakeLists.txt b/src/modules/fsresizer/CMakeLists.txt new file mode 100644 index 000000000..e339b2799 --- /dev/null +++ b/src/modules/fsresizer/CMakeLists.txt @@ -0,0 +1,41 @@ +find_package( KPMcore 3.3 ) +find_package( Qt5 REQUIRED DBus ) # Needed for KPMCore +find_package( KF5 REQUIRED I18n WidgetsAddons ) # Needed for KPMCore + +if ( KPMcore_FOUND ) + include_directories( ${KPMCORE_INCLUDE_DIR} ) + include_directories( ${PROJECT_BINARY_DIR}/src/libcalamares ) + + # The PartitionIterator is a small class, and it's easiest -- but also a + # gross hack -- to just compile it again from the partition module tree. + calamares_add_plugin( fsresizer + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + ResizeFSJob.cpp + ${PROJECT_SOURCE_DIR}/src/modules/partition/core/PartitionIterator.cpp + LINK_PRIVATE_LIBRARIES + kpmcore + calamares + SHARED_LIB + ) + + if( ECM_FOUND AND BUILD_TESTING ) + ecm_add_test( + Tests.cpp + TEST_NAME + fsresizertest + LINK_LIBRARIES + ${CALAMARES_LIBRARIES} + calamares + calamares_job_fsresizer # From above + ${YAMLCPP_LIBRARY} + Qt5::Core + Qt5::Test + ) + set_target_properties( fsresizertest PROPERTIES AUTOMOC TRUE ) + target_include_directories(fsresizertest PRIVATE /usr/local/include ) + endif() +else() + calamares_skip_module( "fsresizer (missing suitable KPMcore)" ) +endif() diff --git a/src/modules/fsresizer/ResizeFSJob.cpp b/src/modules/fsresizer/ResizeFSJob.cpp new file mode 100644 index 000000000..4df41a7d4 --- /dev/null +++ b/src/modules/fsresizer/ResizeFSJob.cpp @@ -0,0 +1,347 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + */ + +#include "ResizeFSJob.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "CalamaresVersion.h" +#include "JobQueue.h" +#include "GlobalStorage.h" + +#include "utils/CalamaresUtils.h" +#include "utils/Logger.h" +#include "utils/Units.h" + +#include "modules/partition/core/PartitionIterator.h" + +ResizeFSJob::RelativeSize::RelativeSize() + : m_value( 0 ) + , m_unit( None ) +{ +} + + +template +void matchUnitSuffix( + const QString& s, + const char ( &suffix )[N], + ResizeFSJob::RelativeSize::Unit matchedUnit, + int& value, + ResizeFSJob::RelativeSize::Unit& unit +) +{ + if ( s.endsWith( suffix ) ) + { + value = s.left( s.length() - N + 1 ).toInt(); + unit = matchedUnit; + } +} + + +ResizeFSJob::RelativeSize::RelativeSize( const QString& s ) + : m_value( 0 ) + , m_unit( None ) +{ + matchUnitSuffix( s, "%", Percent, m_value, m_unit ); + matchUnitSuffix( s, "MiB", Absolute, m_value, m_unit ); + + if ( ( unit() == Percent ) && ( value() > 100 ) ) + { + cDebug() << "Percent value" << value() << "is not valid."; + m_value = 0; + m_unit = None; + } + + if ( !m_value ) + m_unit = None; +} + +qint64 +ResizeFSJob::RelativeSize::apply( qint64 totalSectors, qint64 sectorSize ) +{ + if ( !isValid() ) + return -1; + if ( sectorSize < 1 ) + return -1; + + switch ( m_unit ) + { + case None: + return -1; + case Absolute: + return CalamaresUtils::MiBtoBytes( value() ) / sectorSize; + case Percent: + if ( value() == 100 ) + return totalSectors; // Common-case, avoid futzing around + else + return totalSectors * value() / 100; + } + + // notreached + return -1; +} + +qint64 +ResizeFSJob::RelativeSize::apply( Device* d ) +{ + return apply( d->totalLogical(), d->logicalSize() ); +} + +ResizeFSJob::ResizeFSJob( QObject* parent ) + : Calamares::CppJob( parent ) + , m_required( false ) +{ +} + + +ResizeFSJob::~ResizeFSJob() +{ +} + + +QString +ResizeFSJob::prettyName() const +{ + return tr( "Resize Filesystem Job" ); +} + +ResizeFSJob::PartitionMatch +ResizeFSJob::findPartition( CoreBackend* backend ) +{ + using DeviceList = QList< Device* >; + DeviceList devices = backend->scanDevices( false ); + cDebug() << "ResizeFSJob found" << devices.count() << "devices."; + for ( DeviceList::iterator dev_it = devices.begin(); dev_it != devices.end(); ++dev_it ) + { + if ( ! ( *dev_it ) ) + continue; + cDebug() << "ResizeFSJob found" << ( *dev_it )->deviceNode(); + for ( auto part_it = PartitionIterator::begin( *dev_it ); part_it != PartitionIterator::end( *dev_it ); ++part_it ) + { + cDebug() << ".." << ( *part_it )->mountPoint() << "on" << ( *part_it )->deviceNode(); + if ( ( !m_fsname.isEmpty() && ( *part_it )->mountPoint() == m_fsname ) || + ( !m_devicename.isEmpty() && ( *part_it )->deviceNode() == m_devicename ) ) + { + cDebug() << ".. matched configuration dev=" << m_devicename << "fs=" << m_fsname; + return PartitionMatch( *dev_it, *part_it ); + } + } + } + + cDebug() << "No match for configuration dev=" << m_devicename << "fs=" << m_fsname; + return PartitionMatch( nullptr, nullptr ); +} + +/** @brief Returns the last sector the matched partition should occupy. + * + * Returns a sector number. Returns -1 if something is wrong (e.g. + * can't resize at all, or missing data). Returns 0 if the resize + * won't fit because it doesn't satisfy the settings for atleast + * and size (or won't grow at all because the partition is blocked + * by occupied space after it). + */ +qint64 +ResizeFSJob::findGrownEnd( ResizeFSJob::PartitionMatch m ) +{ + if ( !m.first || !m.second ) + return -1; // Missing device data + if ( !ResizeOperation::canGrow( m.second ) ) + return -1; // Operation is doomed + if ( !m_size.isValid() ) + return -1; // Must have a grow-size + + cDebug() << "Containing device size" << m.first->totalLogical(); + qint64 last_available = m.first->totalLogical() - 1; // Numbered from 0 + qint64 last_currently = m.second->lastSector(); + cDebug() << "Growing partition" << m.second->firstSector() << '-' << last_currently; + + for ( auto part_it = PartitionIterator::begin( m.first ); part_it != PartitionIterator::end( m.first ); ++part_it ) + { + qint64 next_start = ( *part_it )->firstSector(); + qint64 next_end = ( *part_it )->lastSector(); + if ( next_start > next_end ) + { + cWarning() << "Corrupt partition has end" << next_end << " < start" << next_start; + std::swap( next_start, next_end ); + } + if ( ( *part_it )->roles().has( PartitionRole::Unallocated ) ) + { + cDebug() << ".. ignoring unallocated" << next_start << '-' << next_end; + continue; + } + cDebug() << ".. comparing" << next_start << '-' << next_end; + if ( ( next_start > last_currently ) && ( next_start < last_available ) ) + { + cDebug() << " .. shrunk last available to" << next_start; + last_available = next_start - 1; // Before that one starts + } + } + + if ( !( last_available > last_currently ) ) + { + cDebug() << "Partition cannot grow larger."; + return 0; + } + + qint64 expand = last_available - last_currently; // number of sectors + if ( m_atleast.isValid() ) + { + qint64 required = m_atleast.apply( m.first ); + if ( expand < required ) + { + cDebug() << ".. need to expand by" << required << "but only" << expand << "is available."; + return 0; + } + } + + qint64 wanted = m_size.apply( expand, m.first->logicalSize() ); + if ( wanted < expand ) + { + cDebug() << ".. only growing by" << wanted << "instead of full" << expand; + last_available -= ( expand - wanted ); + } + + return last_available; +} + + +Calamares::JobResult +ResizeFSJob::exec() +{ + if ( !isValid() ) + return Calamares::JobResult::error( + tr( "Invalid configuration" ), + tr( "The file-system resize job has an invalid configuration and will not run." ) ); + + // Get KPMCore + auto backend_p = CoreBackendManager::self()->backend(); + if ( backend_p ) + cDebug() << "KPMCore backend @" << ( void* )backend_p << backend_p->id() << backend_p->version(); + else + { + cDebug() << "No KPMCore backend loaded yet"; + QByteArray backendName = qgetenv( "KPMCORE_BACKEND" ); + if ( !CoreBackendManager::self()->load( backendName.isEmpty() ? CoreBackendManager::defaultBackendName() : backendName ) ) + { + cWarning() << "Could not load KPMCore backend."; + return Calamares::JobResult::error( + tr( "KPMCore not Available" ), + tr( "Calamares cannot start KPMCore for the file-system resize job." ) ); + } + + backend_p = CoreBackendManager::self()->backend(); + } + if ( !backend_p ) + { + cWarning() << "Could not load KPMCore backend (2)."; + return Calamares::JobResult::error( + tr( "KPMCore not Available" ), + tr( "Calamares cannot start KPMCore for the file-system resize job." ) ); + } + backend_p->initFSSupport(); // Might not be enough, see below + + // Now get the partition and FS we want to work on + PartitionMatch m = findPartition( backend_p ); + if ( !m.first || !m.second ) + return Calamares::JobResult::error( + tr( "Resize Failed" ), + !m_fsname.isEmpty() ? tr( "The filesystem %1 could not be found in this system, and cannot be resized." ).arg( m_fsname ) + : tr( "The device %1 could not be found in this system, and cannot be resized." ).arg( m_devicename ) ); + + m.second->fileSystem().init(); // Initialize support for specific FS + if ( !ResizeOperation::canGrow( m.second ) ) + { + cDebug() << "canGrow() returned false."; + return Calamares::JobResult::error( + tr( "Resize Failed" ), + !m_fsname.isEmpty() ? tr( "The filesystem %1 cannot be resized." ).arg( m_fsname ) + : tr( "The device %1 cannot be resized." ).arg( m_devicename ) ); + } + + qint64 new_end = findGrownEnd( m ); + cDebug() << "Resize from" + << m.second->firstSector() << '-' << m.second->lastSector() + << '(' << m.second->length() << ')' + << "to -" << new_end; + + if ( new_end < 0 ) + return Calamares::JobResult::error( + tr( "Resize Failed" ), + !m_fsname.isEmpty() ? tr( "The filesystem %1 cannot be resized." ).arg( m_fsname ) + : tr( "The device %1 cannot be resized." ).arg( m_devicename ) ); + if ( new_end == 0 ) + { + cWarning() << "Resize operation on" << m_fsname << m_devicename + << "skipped as not-useful."; + if ( m_required ) + return Calamares::JobResult::error( + tr( "Resize Failed" ), + !m_fsname.isEmpty() ? tr( "The filesystem %1 must be resized, but cannot." ).arg( m_fsname ) + : tr( "The device %1 must be resized, but cannot" ).arg( m_fsname ) ); + + return Calamares::JobResult::ok(); + } + + if ( ( new_end > 0 ) && ( new_end > m.second->lastSector() ) ) + { + ResizeOperation op( *m.first, *m.second, m.second->firstSector(), new_end ); + Report op_report( nullptr ); + if ( op.execute( op_report ) ) + cDebug() << "Resize operation OK."; + else + { + cDebug() << "Resize failed." << op_report.output(); + return Calamares::JobResult::error( + tr( "Resize Failed" ), + op_report.toText() ); + } + } + + return Calamares::JobResult::ok(); +} + + +void +ResizeFSJob::setConfigurationMap( const QVariantMap& configurationMap ) +{ + m_fsname = configurationMap["fs"].toString(); + m_devicename = configurationMap["dev"].toString(); + + if ( m_fsname.isEmpty() && m_devicename.isEmpty() ) + { + cWarning() << "No fs or dev configured for resize."; + return; + } + + m_size = RelativeSize( configurationMap["size"].toString() ); + m_atleast = RelativeSize( configurationMap["atleast"].toString() ); + + m_required = CalamaresUtils::getBool( configurationMap, "required", false ); +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( ResizeFSJobFactory, registerPlugin(); ) diff --git a/src/modules/fsresizer/ResizeFSJob.h b/src/modules/fsresizer/ResizeFSJob.h new file mode 100644 index 000000000..c34ccb865 --- /dev/null +++ b/src/modules/fsresizer/ResizeFSJob.h @@ -0,0 +1,122 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + */ + +#ifndef RESIZEFSJOB_H +#define RESIZEFSJOB_H + +#include +#include + +#include + +#include + +#include + +class CoreBackend; // From KPMCore +class Device; // From KPMCore +class Partition; + +class PLUGINDLLEXPORT ResizeFSJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + /** @brief Size expressions + * + * Sizes can be specified in MiB or percent (of the device they + * are on). This class handles parsing of such strings from the + * config file. + */ + class RelativeSize + { + public: + RelativeSize(); + RelativeSize( const QString& ); + + enum Unit + { + None, + Percent, + Absolute + }; + + int value() const { return m_value; } + Unit unit() const { return m_unit; } + + bool isValid() const + { + return ( unit() != None ) && ( value() > 0 ); + } + + /** @brief Apply this size to the number of sectors @p totalSectors . + * + * Each sector has size @p sectorSize , for converting absolute + * sizes in MiB to sector counts. + * + * For invalid sizes, returns -1. + * For absolute sizes, returns the number of sectors needed. + * For percent sizes, returns that percent of the number of sectors. + */ + qint64 apply( qint64 totalSectors, qint64 sectorSize ); + + /** @brief Apply this size to the given device. + * + * Equivalent to apply( d->totalLogical(), d->logicalSize() ) + */ + qint64 apply( Device* d ); + + private: + int m_value; + Unit m_unit; + } ; + + explicit ResizeFSJob( QObject* parent = nullptr ); + virtual ~ResizeFSJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + + /** @brief Is the configuration of this job valid? */ + bool isValid() const + { + return ( !m_fsname.isEmpty() || !m_devicename.isEmpty() ) && + m_size.isValid(); + } + +private: + RelativeSize m_size; + RelativeSize m_atleast; + QString m_fsname; // Either this, or devicename, is set, not both + QString m_devicename; + bool m_required; + + using PartitionMatch = QPair; + /** @brief Find the configured FS using KPMCore @p backend */ + PartitionMatch findPartition( CoreBackend* backend ); + + /** @brief Return a new end-sector for the given dev-part pair. */ + qint64 findGrownEnd( PartitionMatch ); +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( ResizeFSJobFactory ) + +#endif // RESIZEFSJOB_H diff --git a/src/modules/fsresizer/Tests.cpp b/src/modules/fsresizer/Tests.cpp new file mode 100644 index 000000000..255153fa2 --- /dev/null +++ b/src/modules/fsresizer/Tests.cpp @@ -0,0 +1,126 @@ +/* === This file is part of Calamares - === + * + * Copyright 2017-2018, Adriaan de Groot + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + */ + +#include "Tests.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" + +#include "utils/Logger.h" +#include "utils/YamlUtils.h" + +#include + +#include + +#include +#include + +#define private public +#include "ResizeFSJob.h" +#undef private + +QTEST_GUILESS_MAIN( FSResizerTests ) + +FSResizerTests::FSResizerTests() +{ +} + +FSResizerTests::~FSResizerTests() +{ +} + +void +FSResizerTests::initTestCase() +{ +} + +void FSResizerTests::testConfigurationRobust() +{ + ResizeFSJob j; + + // Empty config + j.setConfigurationMap( QVariantMap() ); + QVERIFY( j.m_fsname.isEmpty() ); + QVERIFY( j.m_devicename.isEmpty() ); + QCOMPARE( j.m_size.unit(), ResizeFSJob::RelativeSize::None ); + QCOMPARE( j.m_atleast.unit(), ResizeFSJob::RelativeSize::None ); + + // Config is missing fs and dev, so it isn't valid + YAML::Node doc0 = YAML::Load( R"(--- +size: 100% +atleast: 600MiB +)" ); + j.setConfigurationMap( CalamaresUtils::yamlMapToVariant( doc0 ).toMap() ); + QVERIFY( j.m_fsname.isEmpty() ); + QVERIFY( j.m_devicename.isEmpty() ); + QCOMPARE( j.m_size.unit(), ResizeFSJob::RelativeSize::None ); + QCOMPARE( j.m_atleast.unit(), ResizeFSJob::RelativeSize::None ); + QCOMPARE( j.m_size.value(), 0 ); + QCOMPARE( j.m_atleast.value(), 0 ); +} + +void FSResizerTests::testConfigurationValues() +{ + ResizeFSJob j; + + // Check both + YAML::Node doc0 = YAML::Load( R"(--- +fs: / +size: 100% +atleast: 600MiB +)" ); + j.setConfigurationMap( CalamaresUtils::yamlMapToVariant( doc0 ).toMap() ); + QVERIFY( !j.m_fsname.isEmpty() ); + QVERIFY( j.m_devicename.isEmpty() ); + QCOMPARE( j.m_size.unit(), ResizeFSJob::RelativeSize::Percent ); + QCOMPARE( j.m_atleast.unit(), ResizeFSJob::RelativeSize::Absolute ); + QCOMPARE( j.m_size.value(), 100 ); + QCOMPARE( j.m_atleast.value(), 600 ); + + // Silly config + doc0 = YAML::Load( R"(--- +fs: / +dev: /dev/m00 +size: 72 MiB +atleast: 127 % +)" ); + j.setConfigurationMap( CalamaresUtils::yamlMapToVariant( doc0 ).toMap() ); + QVERIFY( !j.m_fsname.isEmpty() ); + QVERIFY( !j.m_devicename.isEmpty() ); + QCOMPARE( j.m_size.unit(), ResizeFSJob::RelativeSize::Absolute ); + QCOMPARE( j.m_atleast.unit(), ResizeFSJob::RelativeSize::Percent ); + QCOMPARE( j.m_size.value(), 72 ); + QCOMPARE( j.m_atleast.value(), 127 ); + + // Silly config + doc0 = YAML::Load( R"(--- +fs: / +# dev: /dev/m00 +size: 71MiB +# atleast: 127% +)" ); + j.setConfigurationMap( CalamaresUtils::yamlMapToVariant( doc0 ).toMap() ); + QVERIFY( !j.m_fsname.isEmpty() ); + QVERIFY( j.m_devicename.isEmpty() ); + QCOMPARE( j.m_size.unit(), ResizeFSJob::RelativeSize::Absolute ); + QCOMPARE( j.m_atleast.unit(), ResizeFSJob::RelativeSize::None ); + QCOMPARE( j.m_size.value(), 71 ); + QCOMPARE( j.m_atleast.value(), 0 ); +} diff --git a/src/modules/fsresizer/Tests.h b/src/modules/fsresizer/Tests.h new file mode 100644 index 000000000..958c0e655 --- /dev/null +++ b/src/modules/fsresizer/Tests.h @@ -0,0 +1,39 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + */ + +#ifndef TESTS_H +#define TESTS_H + +#include + +class FSResizerTests : public QObject +{ + Q_OBJECT +public: + FSResizerTests(); + ~FSResizerTests() override; + +private Q_SLOTS: + void initTestCase(); + // Can handle missing values + void testConfigurationRobust(); + // Can parse % and MiB values + void testConfigurationValues(); +}; + +#endif diff --git a/src/modules/fsresizer/fsresizer.conf b/src/modules/fsresizer/fsresizer.conf new file mode 100644 index 000000000..33329248d --- /dev/null +++ b/src/modules/fsresizer/fsresizer.conf @@ -0,0 +1,49 @@ +# Module that resizes a single FS to fill the entire (rest) of +# a device. This is used in OEM situations where an image is +# flashed onto an SD card (or similar) and used to boot a device, +# after which the FS should expand to fill the SD card. +# +# Example: a distro produces a 6GiB large image that is +# written to an 8GiB SD card; the FS should expand to take +# advantage of the unused 2GiB. The FS should expand much +# more if the same image is written to a 16GiB card. +--- + +# Which FS needs to be grown? Choose one way to identify it: +# - *fs* names a mount point which should already be mounted +# in the system. +# - *dev* names a device +fs: / +# dev: /dev/mmcblk0p1 + +# How much of the total remaining space should the FS use? +# The only sensible amount is "all of it". The value is +# in percent, so set it to 100. Perhaps a fixed size is +# needed (that would be weird though, since you don't know +# how big the card is), use MiB as suffix in that case. +# If missing, then it's assumed to be 0, and no resizing +# will happen. +# +# Percentages apply to **available space**. +size: 100% + +# Resizing might not be worth it, though. Set the minimum +# that it must grow; if it cannot grow that much, the +# resizing is skipped. Can be in percentage or absolute +# size, as above. If missing, then it's assumed to be 0, +# which means resizing is always worthwhile. +# +# If *atleast* is not zero, then the setting *required*, +# below, becomes relevant. +# +# Percentages apply to **total device size**. +#atleast: 1000MiB + +# When *atleast* is not zero, then the resize may be +# recommended (the default) or **required**. If the +# resize is required and cannot be carried out (because +# there's not enough space), then that is a fatal +# error for the installer. By default, resize is only +# recommended and it is not an error for no resize to be +# carried out. +required: false diff --git a/src/modules/partition/core/DeviceList.cpp b/src/modules/partition/core/DeviceList.cpp index 4a353efb4..f51eec047 100644 --- a/src/modules/partition/core/DeviceList.cpp +++ b/src/modules/partition/core/DeviceList.cpp @@ -113,28 +113,32 @@ QList< Device* > getDevices( DeviceType which, qint64 minimumSize ) // Remove the device which contains / from the list for ( DeviceList::iterator it = devices.begin(); it != devices.end(); ) - if ( ! ( *it ) || - ( *it )->deviceNode().startsWith( "/dev/zram" ) + if ( !( *it ) ) + { + cDebug() << " .. Skipping nullptr device"; + it = erase( devices, it); + } + else if ( ( *it )->deviceNode().startsWith( "/dev/zram" ) ) { cDebug() << " .. Removing zram" << it; - it = erase(devices, it ); + it = erase( devices, it ); } else if ( writableOnly && hasRootPartition( *it ) ) { cDebug() << " .. Removing device with root filesystem (/) on it" << it; - it = erase(devices, it ); + it = erase( devices, it ); } else if ( writableOnly && isIso9660( *it ) ) { cDebug() << " .. Removing device with iso9660 filesystem (probably a CD) on it" << it; - it = erase(devices, it ); + it = erase( devices, it ); } else if ( (minimumSize >= 0) && !( (*it)->capacity() > minimumSize ) ) { cDebug() << " .. Removing too-small" << it; - it = erase(devices, it ); + it = erase( devices, it ); } else ++it; diff --git a/src/modules/partition/core/KPMHelpers.h b/src/modules/partition/core/KPMHelpers.h index c9d9a30f9..0bcc533fb 100644 --- a/src/modules/partition/core/KPMHelpers.h +++ b/src/modules/partition/core/KPMHelpers.h @@ -89,7 +89,7 @@ Partition* createNewPartition( PartitionNode* parent, FileSystem::Type fsType, qint64 firstSector, qint64 lastSector, - PartitionTable::Flags flags = PartitionTable::FlagNone ); + PartitionTable::Flags flags ); Partition* createNewEncryptedPartition( PartitionNode* parent, const Device& device, @@ -98,7 +98,7 @@ Partition* createNewEncryptedPartition( PartitionNode* parent, qint64 firstSector, qint64 lastSector, const QString& passphrase, - PartitionTable::Flags flags = PartitionTable::FlagNone ); + PartitionTable::Flags flags ); Partition* clonePartition( Device* device, Partition* partition ); diff --git a/src/modules/partition/core/OsproberEntry.h b/src/modules/partition/core/OsproberEntry.h index 792f22b29..e8c7895f0 100644 --- a/src/modules/partition/core/OsproberEntry.h +++ b/src/modules/partition/core/OsproberEntry.h @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2016, Teo Mrnjavac + * Copyright 2018, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +30,18 @@ struct FstabEntry QString options; int dump; int pass; + + /// Does this entry make sense and is it complete? + bool isValid() const; // implemented in Partutils.cpp + + /** @brief Create an entry from a live of /etc/fstab + * + * Splits the given string (which ought to follow the format + * of /etc/fstab) and returns a corresponding Fstab entry. + * If the string isn't valid (e.g. comment-line, or broken + * fstab entry) then the entry that is returned is invalid. + */ + static FstabEntry fromEtcFstab( const QString& ); // implemented in Partutils.cpp }; typedef QList< FstabEntry > FstabEntryList; diff --git a/src/modules/partition/core/PartUtils.cpp b/src/modules/partition/core/PartUtils.cpp index f080d97a4..7dddec414 100644 --- a/src/modules/partition/core/PartUtils.cpp +++ b/src/modules/partition/core/PartUtils.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -161,11 +162,26 @@ canBeResized( PartitionCoreModule* core, const QString& partitionPath ) static FstabEntryList lookForFstabEntries( const QString& partitionPath ) { + QStringList mountOptions{ "ro" }; + + auto r = CalamaresUtils::System::runCommand( + CalamaresUtils::System::RunLocation::RunInHost, + { "blkid", "-s", "TYPE", "-o", "value", partitionPath } + ); + if ( r.getExitCode() ) + cWarning() << "blkid on" << partitionPath << "failed."; + else + { + QString fstype = r.getOutput().trimmed(); + if ( ( fstype == "ext3" ) || ( fstype == "ext4" ) ) + mountOptions.append( "noload" ); + } + FstabEntryList fstabEntries; QTemporaryDir mountsDir; mountsDir.setAutoRemove( false ); - int exit = QProcess::execute( "mount", { partitionPath, mountsDir.path() } ); + int exit = QProcess::execute( "mount", { "-o", mountOptions.join(','), partitionPath, mountsDir.path() } ); if ( !exit ) // if all is well { QFile fstabFile( mountsDir.path() + "/etc/fstab" ); @@ -175,28 +191,13 @@ lookForFstabEntries( const QString& partitionPath ) .split( '\n' ); for ( const QString& rawLine : fstabLines ) - { - QString line = rawLine.simplified(); - if ( line.startsWith( '#' ) ) - continue; - - QStringList splitLine = line.split( ' ' ); - if ( splitLine.length() != 6 ) - continue; - - fstabEntries.append( { splitLine.at( 0 ), // path, or UUID, or LABEL, etc. - splitLine.at( 1 ), // mount point - splitLine.at( 2 ), // fs type - splitLine.at( 3 ), // options - splitLine.at( 4 ).toInt(), //dump - splitLine.at( 5 ).toInt() //pass - } ); - } - + fstabEntries.append( FstabEntry::fromEtcFstab( rawLine ) ); fstabFile.close(); + std::remove_if( fstabEntries.begin(), fstabEntries.end(), [](const FstabEntry& x) { return !x.isValid(); } ); } - QProcess::execute( "umount", { "-R", mountsDir.path() } ); + if ( QProcess::execute( "umount", { "-R", mountsDir.path() } ) ) + cWarning() << "Could not unmount" << mountsDir.path(); } return fstabEntries; @@ -296,7 +297,6 @@ runOsprober( PartitionCoreModule* core ) osprober.readAllStandardOutput() ).trimmed() ); } - QString osProberReport( "Osprober lines, clean:\n" ); QStringList osproberCleanLines; OsproberEntryList osproberEntries; const auto lines = osproberOutput.split( '\n' ); @@ -328,8 +328,11 @@ runOsprober( PartitionCoreModule* core ) osproberCleanLines.append( line ); } } - osProberReport.append( osproberCleanLines.join( '\n' ) ); - cDebug() << osProberReport; + + if ( osproberCleanLines.count() > 0 ) + cDebug() << "os-prober lines after cleanup:" << Logger::DebugList( osproberCleanLines ); + else + cDebug() << "os-prober gave no output."; Calamares::JobQueue::instance()->globalStorage()->insert( "osproberLines", osproberCleanLines ); @@ -373,3 +376,31 @@ isEfiBootable( const Partition* candidate ) } } // nmamespace PartUtils + +/* Implementation of methods for FstabEntry, from OsproberEntry.h */ + +bool +FstabEntry::isValid() const +{ + return !partitionNode.isEmpty() && !mountPoint.isEmpty() && !fsType.isEmpty(); +} + +FstabEntry +FstabEntry::fromEtcFstab( const QString& rawLine ) +{ + QString line = rawLine.simplified(); + if ( line.startsWith( '#' ) ) + return FstabEntry{ QString(), QString(), QString(), QString(), 0, 0 }; + + QStringList splitLine = line.split( ' ' ); + if ( splitLine.length() != 6 ) + return FstabEntry{ QString(), QString(), QString(), QString(), 0, 0 }; + + return FstabEntry{ splitLine.at( 0 ), // path, or UUID, or LABEL, etc. + splitLine.at( 1 ), // mount point + splitLine.at( 2 ), // fs type + splitLine.at( 3 ), // options + splitLine.at( 4 ).toInt(), //dump + splitLine.at( 5 ).toInt() //pass + }; + } diff --git a/src/modules/partition/core/PartitionActions.cpp b/src/modules/partition/core/PartitionActions.cpp index d35345424..fc4914696 100644 --- a/src/modules/partition/core/PartitionActions.cpp +++ b/src/modules/partition/core/PartitionActions.cpp @@ -104,10 +104,7 @@ swapSuggestion( const qint64 availableSpaceB ) constexpr qint64 alignBytesToBlockSize( qint64 bytes, qint64 blocksize ) { - Q_ASSERT( bytes >= 0 ); - Q_ASSERT( blocksize > 0 ); qint64 blocks = bytes / blocksize; - Q_ASSERT( blocks >= 0 ); if ( blocks * blocksize != bytes ) ++blocks; @@ -135,17 +132,17 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass // the logical sector size (usually 512B). EFI starts with 2MiB // empty and a 300MiB EFI boot partition, while BIOS starts at // the 1MiB boundary (usually sector 2048). - int uefisys_part_size = isEfi ? 300 : 0; - int empty_space_size = isEfi ? 2 : 1; + int uefisys_part_sizeB = isEfi ? 300_MiB : 0_MiB; + int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB; // Since sectors count from 0, if the space is 2048 sectors in size, // the first free sector has number 2048 (and there are 2048 sectors // before that one, numbered 0..2047). - qint64 firstFreeSector = bytesToSectors( MiBtoBytes(empty_space_size), dev->logicalSize() ); + qint64 firstFreeSector = bytesToSectors( empty_space_sizeB, dev->logicalSize() ); if ( isEfi ) { - qint64 efiSectorCount = bytesToSectors( MiBtoBytes(uefisys_part_size), dev->logicalSize() ); + qint64 efiSectorCount = bytesToSectors( uefisys_part_sizeB, dev->logicalSize() ); Q_ASSERT( efiSectorCount > 0 ); // Since sectors count from 0, and this partition is created starting @@ -207,7 +204,8 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass PartitionRole( PartitionRole::Primary ), FileSystem::typeForName( defaultFsType ), firstFreeSector, - lastSectorForRoot + lastSectorForRoot, + PartitionTable::FlagNone ); } else @@ -219,12 +217,17 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass FileSystem::typeForName( defaultFsType ), firstFreeSector, lastSectorForRoot, - luksPassphrase + luksPassphrase, + PartitionTable::FlagNone ); } PartitionInfo::setFormat( rootPartition, true ); PartitionInfo::setMountPoint( rootPartition, "/" ); - core->createPartition( dev, rootPartition ); + // Some buggy (legacy) BIOSes test if the bootflag of at least one partition is set. + // Otherwise they ignore the device in boot-order, so add it here. + core->createPartition( dev, rootPartition, + rootPartition->activeFlags() | ( isEfi ? PartitionTable::FlagNone : PartitionTable::FlagBoot ) + ); if ( shouldCreateSwap ) { @@ -237,7 +240,8 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass PartitionRole( PartitionRole::Primary ), FileSystem::LinuxSwap, lastSectorForRoot + 1, - dev->totalLogical() - 1 + dev->totalLogical() - 1, + PartitionTable::FlagNone ); } else @@ -249,7 +253,8 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass FileSystem::LinuxSwap, lastSectorForRoot + 1, dev->totalLogical() - 1, - luksPassphrase + luksPassphrase, + PartitionTable::FlagNone ); } PartitionInfo::setFormat( swapPartition, true ); @@ -299,7 +304,8 @@ doReplacePartition( PartitionCoreModule* core, newRoles, FileSystem::typeForName( defaultFsType ), partition->firstSector(), - partition->lastSector() + partition->lastSector(), + PartitionTable::FlagNone ); } else @@ -311,7 +317,8 @@ doReplacePartition( PartitionCoreModule* core, FileSystem::typeForName( defaultFsType ), partition->firstSector(), partition->lastSector(), - luksPassphrase + luksPassphrase, + PartitionTable::FlagNone ); } PartitionInfo::setMountPoint( newPartition, "/" ); diff --git a/src/modules/partition/core/PartitionCoreModule.cpp b/src/modules/partition/core/PartitionCoreModule.cpp index b8011f066..f41142b6a 100644 --- a/src/modules/partition/core/PartitionCoreModule.cpp +++ b/src/modules/partition/core/PartitionCoreModule.cpp @@ -509,17 +509,8 @@ PartitionCoreModule::jobs() const lst << info->jobs; devices << info->device.data(); } - cDebug() << "Creating FillGlobalStorageJob with bootLoader path" << m_bootLoaderInstallPath; lst << Calamares::job_ptr( new FillGlobalStorageJob( devices, m_bootLoaderInstallPath ) ); - - QStringList jobsDebug; - foreach ( auto job, lst ) - jobsDebug.append( job->prettyName() ); - - cDebug() << "PartitionCodeModule has been asked for jobs. About to return:" - << jobsDebug.join( "\n" ); - return lst; } diff --git a/src/modules/partition/core/PartitionCoreModule.h b/src/modules/partition/core/PartitionCoreModule.h index 704fff322..15dadc7f5 100644 --- a/src/modules/partition/core/PartitionCoreModule.h +++ b/src/modules/partition/core/PartitionCoreModule.h @@ -128,6 +128,12 @@ public: void createPartitionTable( Device* device, PartitionTable::TableType type ); + /** + * @brief Add a job to do the actual partition-creation. + * + * If @p flags is not FlagNone, then the given flags are + * applied to the newly-created partition. + */ void createPartition( Device* device, Partition* partition, PartitionTable::Flags flags = PartitionTable::FlagNone ); diff --git a/src/modules/partition/core/PartitionIterator.cpp b/src/modules/partition/core/PartitionIterator.cpp index 5ed48fd91..8301835c6 100644 --- a/src/modules/partition/core/PartitionIterator.cpp +++ b/src/modules/partition/core/PartitionIterator.cpp @@ -18,7 +18,7 @@ * along with Calamares. If not, see . */ -#include +#include "PartitionIterator.h" // KPMcore #include diff --git a/src/modules/partition/gui/ChoicePage.cpp b/src/modules/partition/gui/ChoicePage.cpp index bef6e4966..74410ee80 100644 --- a/src/modules/partition/gui/ChoicePage.cpp +++ b/src/modules/partition/gui/ChoicePage.cpp @@ -48,6 +48,9 @@ #include #include +#ifdef WITH_KPMCOREGT33 +#include +#endif #include #include @@ -632,7 +635,8 @@ ChoicePage::doAlongsideApply() candidate->roles(), FileSystem::typeForName( m_defaultFsType ), newLastSector + 2, // * - oldLastSector + oldLastSector, + PartitionTable::FlagNone ); } else @@ -644,7 +648,8 @@ ChoicePage::doAlongsideApply() FileSystem::typeForName( m_defaultFsType ), newLastSector + 2, // * oldLastSector, - luksPassphrase + luksPassphrase, + PartitionTable::FlagNone ); } PartitionInfo::setMountPoint( newPartition, "/" ); @@ -732,7 +737,9 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current ) FileSystem::typeForName( m_defaultFsType ), selectedPartition->firstSector(), selectedPartition->lastSector(), - m_encryptWidget->passphrase() ); + m_encryptWidget->passphrase(), + PartitionTable::FlagNone + ); } else { @@ -742,7 +749,9 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current ) newRoles, FileSystem::typeForName( m_defaultFsType ), selectedPartition->firstSector(), - selectedPartition->lastSector() ); + selectedPartition->lastSector(), + PartitionTable::FlagNone + ); } PartitionInfo::setMountPoint( newPartition, "/" ); @@ -1158,6 +1167,13 @@ force_uncheck(QButtonGroup* grp, PrettyRadioButton* button) grp->setExclusive( true ); } +static inline QDebug& +operator <<( QDebug& s, PartitionIterator& it ) +{ + s << ( ( *it ) ? ( *it )->deviceNode() : QString( "" ) ); + return s; +} + /** * @brief ChoicePage::setupActions happens every time a new Device* is selected in the * device picker. Sets up the text and visibility of the partitioning actions based @@ -1171,6 +1187,9 @@ ChoicePage::setupActions() OsproberEntryList osproberEntriesForCurrentDevice = getOsproberEntriesForDevice( currentDevice ); + cDebug() << "Setting up actions for" << currentDevice->deviceNode() + << "with" << osproberEntriesForCurrentDevice.count() << "entries."; + if ( currentDevice->partitionTable() ) m_deviceInfoWidget->setPartitionTableType( currentDevice->partitionTable()->type() ); else @@ -1182,16 +1201,35 @@ ChoicePage::setupActions() bool atLeastOneCanBeResized = false; bool atLeastOneCanBeReplaced = false; bool atLeastOneIsMounted = false; // Suppress 'erase' if so + bool isInactiveRAID = false; + +#ifdef WITH_KPMCOREGT33 + if ( currentDevice->type() == Device::Type::SoftwareRAID_Device && + static_cast< SoftwareRAID* >(currentDevice)->status() == SoftwareRAID::Status::Inactive ) + { + cDebug() << ".. part of an inactive RAID device"; + isInactiveRAID = true; + } +#endif for ( auto it = PartitionIterator::begin( currentDevice ); it != PartitionIterator::end( currentDevice ); ++it ) { if ( PartUtils::canBeResized( *it ) ) + { + cDebug() << ".. contains resizable" << it; atLeastOneCanBeResized = true; + } if ( PartUtils::canBeReplaced( *it ) ) + { + cDebug() << ".. contains replacable" << it; atLeastOneCanBeReplaced = true; + } if ( (*it)->isMounted() ) + { + cDebug() << ".. contains mounted" << it; atLeastOneIsMounted = true; + } } if ( osproberEntriesForCurrentDevice.count() == 0 ) @@ -1305,10 +1343,15 @@ ChoicePage::setupActions() else force_uncheck( m_grp, m_alongsideButton ); - if ( !atLeastOneIsMounted ) + if ( !atLeastOneIsMounted && !isInactiveRAID ) m_eraseButton->show(); // None mounted else + { + cDebug() << "Erase button suppressed" + << "mount?" << atLeastOneIsMounted + << "raid?" << isInactiveRAID; force_uncheck( m_grp, m_eraseButton ); + } bool isEfi = PartUtils::isEfiSystem(); bool efiSystemPartitionFound = !m_core->efiSystemPartitions().isEmpty(); diff --git a/src/modules/partition/gui/CreatePartitionDialog.cpp b/src/modules/partition/gui/CreatePartitionDialog.cpp index 0e7602c08..7823d743d 100644 --- a/src/modules/partition/gui/CreatePartitionDialog.cpp +++ b/src/modules/partition/gui/CreatePartitionDialog.cpp @@ -72,7 +72,7 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device, PartitionNode* par m_ui->encryptWidget->setText( tr( "En&crypt" ) ); m_ui->encryptWidget->hide(); - if (m_device->type() == Device::Type::Disk_Device) { + if (m_device->type() != Device::Type::LVM_Device) { m_ui->lvNameLabel->hide(); m_ui->lvNameLineEdit->hide(); } diff --git a/src/modules/partition/gui/PartitionPage.cpp b/src/modules/partition/gui/PartitionPage.cpp index 994adc3e8..9d972c8fd 100644 --- a/src/modules/partition/gui/PartitionPage.cpp +++ b/src/modules/partition/gui/PartitionPage.cpp @@ -4,6 +4,7 @@ * Copyright 2015-2016, Teo Mrnjavac * Copyright 2018, Adriaan de Groot * Copyright 2018, Andrius Štikonas + * Copyright 2018, Caio Jordão Carvalho * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,6 +48,9 @@ // KPMcore #include #include +#ifdef WITH_KPMCOREGT33 +#include +#endif #include #include @@ -146,6 +150,7 @@ PartitionPage::updateButtons() bool isInVG = m_core->isInVG( partition ); create = isFree; + // Keep it simple for now: do not support editing extended partitions as // it does not work with our current edit implementation which is // actually remove + add. This would not work with extended partitions @@ -160,8 +165,20 @@ PartitionPage::updateButtons() if ( m_ui->deviceComboBox->currentIndex() >= 0 ) { QModelIndex deviceIndex = m_core->deviceModel()->index( m_ui->deviceComboBox->currentIndex(), 0 ); - if ( m_core->deviceModel()->deviceForIndex( deviceIndex )->type() != Device::Type::LVM_Device ) + auto device = m_core->deviceModel()->deviceForIndex( deviceIndex ); + if ( device->type() != Device::Type::LVM_Device ) + { createTable = true; + +#ifdef WITH_KPMCOREGT33 + if ( device->type() == Device::Type::SoftwareRAID_Device && + static_cast< SoftwareRAID* >(device)->status() == SoftwareRAID::Status::Inactive ) + { + createTable = false; + create = false; + } +#endif + } else { currentDeviceIsVG = true; diff --git a/src/modules/partition/jobs/CreatePartitionTableJob.cpp b/src/modules/partition/jobs/CreatePartitionTableJob.cpp index 2aec4a5fc..937b8437d 100644 --- a/src/modules/partition/jobs/CreatePartitionTableJob.cpp +++ b/src/modules/partition/jobs/CreatePartitionTableJob.cpp @@ -20,6 +20,8 @@ #include "jobs/CreatePartitionTableJob.h" +#include "core/PartitionIterator.h" + #include "utils/Logger.h" // KPMcore @@ -65,6 +67,14 @@ CreatePartitionTableJob::prettyStatusMessage() const } +static inline QDebug& +operator <<( QDebug& s, PartitionIterator& it ) +{ + s << ( ( *it ) ? ( *it )->deviceNode() : QString( "" ) ); + return s; +} + + Calamares::JobResult CreatePartitionTableJob::exec() { @@ -73,21 +83,28 @@ CreatePartitionTableJob::exec() PartitionTable* table = m_device->partitionTable(); cDebug() << "Creating new partition table of type" << table->typeName() - << ", uncommitted yet:\n" << table; + << ", uncommitted yet:"; - QProcess lsblk; - lsblk.setProgram( "lsblk" ); - lsblk.setProcessChannelMode( QProcess::MergedChannels ); - lsblk.start(); - lsblk.waitForFinished(); - cDebug() << "lsblk:\n" << lsblk.readAllStandardOutput(); + if ( Logger::logLevelEnabled( Logger::LOGDEBUG ) ) + { + for ( auto it = PartitionIterator::begin( table ); + it != PartitionIterator::end( table ); ++it ) + cDebug() << *it; - QProcess mount; - mount.setProgram( "mount" ); - mount.setProcessChannelMode( QProcess::MergedChannels ); - mount.start(); - mount.waitForFinished(); - cDebug() << "mount:\n" << mount.readAllStandardOutput(); + QProcess lsblk; + lsblk.setProgram( "lsblk" ); + lsblk.setProcessChannelMode( QProcess::MergedChannels ); + lsblk.start(); + lsblk.waitForFinished(); + cDebug() << "lsblk:\n" << lsblk.readAllStandardOutput(); + + QProcess mount; + mount.setProgram( "mount" ); + mount.setProcessChannelMode( QProcess::MergedChannels ); + mount.start(); + mount.waitForFinished(); + cDebug() << "mount:\n" << mount.readAllStandardOutput(); + } CreatePartitionTableOperation op(*m_device, table); op.setStatus(Operation::StatusRunning); diff --git a/src/modules/partition/jobs/FillGlobalStorageJob.cpp b/src/modules/partition/jobs/FillGlobalStorageJob.cpp index 43a5f3904..597d62a82 100644 --- a/src/modules/partition/jobs/FillGlobalStorageJob.cpp +++ b/src/modules/partition/jobs/FillGlobalStorageJob.cpp @@ -56,9 +56,12 @@ findPartitionUuids( QList < Device* > devices ) QString path = p->partitionPath(); QString uuid = p->fileSystem().readUUID( p->partitionPath() ); hash.insert( path, uuid ); + cDebug() << ".. added path=" << path << "UUID=" << uuid; } } - cDebug() << hash; + + if ( hash.isEmpty() ) + cDebug() << ".. no UUIDs found."; return hash; } @@ -90,10 +93,16 @@ mapForPartition( Partition* partition, const QString& uuid ) dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() ) map[ "fs" ] = dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS()->name(); map[ "uuid" ] = uuid; - cDebug() << partition->partitionPath() - << "mtpoint:" << PartitionInfo::mountPoint( partition ) - << "fs:" << map[ "fs" ] << '(' << map[ "fsName" ] << ')' - << uuid; + + // Debugging for inside the loop in createPartitionList(), + // so indent a bit + Logger::CLog deb = cDebug(); + using TR = Logger::DebugRow; + deb << " .. mapping for" << partition->partitionPath() << partition->deviceNode() + << TR( "mtpoint:", PartitionInfo::mountPoint( partition ) ) + << TR( "fs:", map[ "fs" ].toString() ) + << TR( "fsname", map[ "fsName" ].toString() ) + << TR( "uuid", uuid ); if ( partition->roles().has( PartitionRole::Luks ) ) { @@ -104,7 +113,7 @@ mapForPartition( Partition* partition, const QString& uuid ) map[ "luksMapperName" ] = luksFs->mapperName().split( "/" ).last(); map[ "luksUuid" ] = getLuksUuid( partition->partitionPath() ); map[ "luksPassphrase" ] = luksFs->passphrase(); - cDebug() << "luksMapperName:" << map[ "luksMapperName" ]; + deb << TR( "luksMapperName:", map[ "luksMapperName" ].toString() ); } } @@ -215,9 +224,11 @@ FillGlobalStorageJob::createPartitionList() const cDebug() << "Writing to GlobalStorage[\"partitions\"]"; for ( auto device : m_devices ) { + cDebug() << ".. partitions on" << device->deviceNode(); for ( auto it = PartitionIterator::begin( device ); it != PartitionIterator::end( device ); ++it ) { + // Debug-logging is done when creating the map lst << mapForPartition( *it, hash.value( ( *it )->partitionPath() ) ); } } diff --git a/src/modules/partition/jobs/SetPartitionFlagsJob.cpp b/src/modules/partition/jobs/SetPartitionFlagsJob.cpp index 7f6169bbe..fee987479 100644 --- a/src/modules/partition/jobs/SetPartitionFlagsJob.cpp +++ b/src/modules/partition/jobs/SetPartitionFlagsJob.cpp @@ -132,6 +132,10 @@ SetPartFlagsJob::prettyStatusMessage() const Calamares::JobResult SetPartFlagsJob::exec() { + cDebug() << "Setting flags on" << m_device->deviceNode() + << "partition" << partition()->deviceNode() + << "to" << m_flags; + Report report ( nullptr ); SetPartFlagsOperation op( *m_device, *partition(), m_flags ); op.setStatus( Operation::StatusRunning ); diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index 68474287e..7b40c34a5 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -1,6 +1,4 @@ -find_package( Qt5 COMPONENTS Gui Test REQUIRED ) - -include( ECMAddTests ) +find_package( Qt5 COMPONENTS Gui REQUIRED ) set( PartitionModule_SOURCE_DIR .. ) @@ -23,13 +21,15 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -ecm_add_test( ${partitionjobtests_SRCS} - TEST_NAME partitionjobtests - LINK_LIBRARIES - ${CALAMARES_LIBRARIES} - kpmcore - Qt5::Core - Qt5::Test -) +if( ECM_FOUND AND BUILD_TESTING ) + ecm_add_test( ${partitionjobtests_SRCS} + TEST_NAME partitionjobtests + LINK_LIBRARIES + ${CALAMARES_LIBRARIES} + kpmcore + Qt5::Core + Qt5::Test + ) -set_target_properties( partitionjobtests PROPERTIES AUTOMOC TRUE ) + set_target_properties( partitionjobtests PROPERTIES AUTOMOC TRUE ) +endif() diff --git a/src/modules/partition/tests/PartitionJobTests.cpp b/src/modules/partition/tests/PartitionJobTests.cpp index de10631a0..f261b12ed 100644 --- a/src/modules/partition/tests/PartitionJobTests.cpp +++ b/src/modules/partition/tests/PartitionJobTests.cpp @@ -2,6 +2,7 @@ * * Copyright 2014, Aurélien Gâteau * Copyright 2017, Adriaan de Groot + * Copyright 2018, Philip Müller * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -358,7 +359,15 @@ PartitionJobTests::testResizePartition() Partition* freePartition = firstFreePartition( m_device->partitionTable() ); QVERIFY( freePartition ); - Partition* partition = KPMHelpers::createNewPartition( freePartition->parent(), *m_device, PartitionRole( PartitionRole::Primary ), FileSystem::Ext4, oldFirst, oldLast ); + Partition* partition = KPMHelpers::createNewPartition( + freePartition->parent(), + *m_device, + PartitionRole( PartitionRole::Primary ), + FileSystem::Ext4, + oldFirst, + oldLast, + PartitionTable::FlagNone + ); CreatePartitionJob* job = new CreatePartitionJob( m_device.data(), partition ); job->updatePreview(); m_queue.enqueue( job_ptr( job ) );