Merge branch 'oem-resizer'

This commit is contained in:
Adriaan de Groot 2018-09-21 09:05:16 -04:00
commit 375024ee4c
12 changed files with 737 additions and 8 deletions

View File

@ -19,6 +19,9 @@ This release contains contributions from (alphabetically by first name):
is compiled with the newest KPMCore release. is compiled with the newest KPMCore release.
* The *keyboard* module now handles the (bogus) Austrian keymap for * The *keyboard* module now handles the (bogus) Austrian keymap for
the system console properly. the system console properly.
* New module *fsresizer* can be used to resize filesystems. It is intended
for use in OEM installs where an image of fixed size is created,
and then sized to the actual SD card the user has used.
# 3.2.2 (2018-09-04) # # 3.2.2 (2018-09-04) #

View File

@ -51,4 +51,4 @@ df -h
echo "# Install results" echo "# Install results"
install_debugging "$DESTDIR" install_debugging "$DESTDIR"
$result # Result of make install, above $result || { echo "! Install failed" ; exit 1 ; } # Result of make install, above

View File

@ -59,6 +59,7 @@ handle_args( QCoreApplication& a )
parser.addOption( debugLevelOption ); parser.addOption( debugLevelOption );
parser.addPositionalArgument( "module", "Path or name of module to run." ); parser.addPositionalArgument( "module", "Path or name of module to run." );
parser.addPositionalArgument( "config", "Path of job-config file to use.", "[config]");
parser.process( a ); parser.process( a );
@ -140,6 +141,8 @@ load_module( const ModuleConfig& moduleConfig )
? moduleDirectory + '/' + name + ".conf" ? moduleDirectory + '/' + name + ".conf"
: moduleConfig.configFile() ); : moduleConfig.configFile() );
cDebug() << "Module" << moduleName << "job-configuration:" << configFile;
Calamares::Module* module = Calamares::Module::fromDescriptor( Calamares::Module* module = Calamares::Module::fromDescriptor(
descriptor, name, configFile, moduleDirectory ); descriptor, name, configFile, moduleDirectory );
@ -158,7 +161,7 @@ main( int argc, char* argv[] )
std::unique_ptr< Calamares::Settings > settings_p( new Calamares::Settings( QString(), true ) ); std::unique_ptr< Calamares::Settings > settings_p( new Calamares::Settings( QString(), true ) );
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) ); std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
cDebug() << "Calamares test module-loader" << module.moduleName(); cDebug() << "Calamares module-loader testing" << module.moduleName();
Calamares::Module* m = load_module( module ); Calamares::Module* m = load_module( module );
if ( !m ) if ( !m )
{ {
@ -175,13 +178,19 @@ main( int argc, char* argv[] )
return 1; return 1;
} }
cDebug() << "Module" << m->name() << m->typeString() << m->interfaceString(); using TR = Logger::DebugRow<const char*, const QString&>;
cDebug() << "Module metadata"
<< TR( "name", m->name() )
<< TR( "type", m->typeString() )
<< TR( "interface", m->interfaceString() );
cDebug() << "Job outputs:";
Calamares::JobList jobList = m->jobs(); Calamares::JobList jobList = m->jobs();
unsigned int count = 1; unsigned int count = 1;
for ( const auto& p : jobList ) for ( const auto& p : jobList )
{ {
cDebug() << count << p->prettyName(); cDebug() << "Job #" << count << "name" << p->prettyName();
Calamares::JobResult r = p->exec(); Calamares::JobResult r = p->exec();
if ( !r ) if ( !r )
{ {

View File

@ -145,8 +145,15 @@ moduleConfigurationCandidates( bool assumeBuildDir, const QString& moduleName, c
paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) ); paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) );
else else
{ {
// If an absolute path is given, in debug mode, look for it
// first. The case contains('/'), below, will add the absolute
// path a second time, though.
if ( assumeBuildDir && configFileName.startsWith( '/' ) )
paths << configFileName;
if ( assumeBuildDir ) if ( assumeBuildDir )
paths << QDir().absoluteFilePath(QString( "src/modules/%1/%2" ).arg( moduleName ).arg( configFileName ) ); paths << QDir().absoluteFilePath(QString( "src/modules/%1/%2" ).arg( moduleName ).arg( configFileName ) );
if ( assumeBuildDir && configFileName.contains( '/' ) )
paths << QDir().absoluteFilePath( configFileName );
paths << QString( "/etc/calamares/modules/%1" ).arg( configFileName ); paths << QString( "/etc/calamares/modules/%1" ).arg( configFileName );
paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) ); paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) );
@ -168,6 +175,7 @@ Module::loadConfigurationFile( const QString& configFileName ) //throws YAML::Ex
YAML::Node doc = YAML::Load( ba.constData() ); YAML::Node doc = YAML::Load( ba.constData() );
if ( doc.IsNull() ) if ( doc.IsNull() )
{ {
cDebug() << "Found empty module configuration" << path;
// Special case: empty config files are valid, // Special case: empty config files are valid,
// but aren't a map. // but aren't a map.
return; return;
@ -178,14 +186,13 @@ Module::loadConfigurationFile( const QString& configFileName ) //throws YAML::Ex
return; return;
} }
cDebug() << "Loaded module configuration" << path;
m_configurationMap = CalamaresUtils::yamlMapToVariant( doc ).toMap(); m_configurationMap = CalamaresUtils::yamlMapToVariant( doc ).toMap();
m_emergency = m_maybe_emergency m_emergency = m_maybe_emergency
&& m_configurationMap.contains( EMERGENCY ) && m_configurationMap.contains( EMERGENCY )
&& m_configurationMap[ EMERGENCY ].toBool(); && m_configurationMap[ EMERGENCY ].toBool();
return; return;
} }
else
continue;
} }
} }

View File

@ -0,0 +1,48 @@
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 )
if ( KPMcore_VERSION VERSION_GREATER "3.3.0")
add_definitions(-DWITH_KPMCOREGT33) # kpmcore greater than 3.3
endif()
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 )
find_package( Qt5 COMPONENTS Test REQUIRED )
include( ECMAddTests )
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()

View File

@ -0,0 +1,339 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ResizeFSJob.h"
#include <QProcess>
#include <QDateTime>
#include <QThread>
#include <kpmcore/backend/corebackend.h>
#include <kpmcore/backend/corebackendmanager.h>
#include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h>
#include <kpmcore/ops/resizeoperation.h>
#include <kpmcore/util/report.h>
#include "CalamaresVersion.h"
#include "JobQueue.h"
#include "GlobalStorage.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<int N>
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 )
{
}
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 can not 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 can not be resized." ).arg(m_fsname)
: tr( "The device %1 could not be found in this system, and can not 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 can not be resized." ).arg(m_fsname)
: tr( "The device %1 can not 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 can not be resized." ).arg(m_fsname)
: tr( "The device %1 can not be resized." ).arg(m_devicename) );
if ( new_end == 0 )
{
// TODO: is that a bad thing? is the resize required?
cWarning() << "Resize operation on" << m_fsname << m_devicename
<< "skipped as not-useful.";
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() );
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( ResizeFSJobFactory, registerPlugin<ResizeFSJob>(); )

View File

@ -0,0 +1,121 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef RESIZEFSJOB_H
#define RESIZEFSJOB_H
#include <QObject>
#include <QVariantMap>
#include <CppJob.h>
#include <utils/PluginFactory.h>
#include <PluginDllMacro.h>
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;
using PartitionMatch = QPair<Device*, Partition*>;
/** @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

View File

@ -0,0 +1,126 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "Tests.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "Settings.h"
#include "utils/Logger.h"
#include "utils/YamlUtils.h"
#include <yaml-cpp/yaml.h>
#include <QtTest/QtTest>
#include <QFileInfo>
#include <QStringList>
#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 );
}

View File

@ -0,0 +1,39 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TESTS_H
#define TESTS_H
#include <QObject>
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

View File

@ -0,0 +1,37 @@
# 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.
#
# Percentages apply to **total device size**.
atleast: 1000MiB

View File

@ -18,7 +18,7 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <core/PartitionIterator.h> #include "PartitionIterator.h"
// KPMcore // KPMcore
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>

View File

@ -87,7 +87,7 @@ int main(int argc, char** argv)
if ( !doc.IsMap() ) if ( !doc.IsMap() )
{ {
cerr << "WARNING:" << filename << '\n'; cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: not-a-YAML-map\n"; cerr << "WARNING: not-a-YAML-map (type=" << doc.Type() << ")\n";
return 1; return 1;
} }