diff --git a/CHANGES-3.3 b/CHANGES-3.3 index effc2ab91..79fb6fed1 100644 --- a/CHANGES-3.3 +++ b/CHANGES-3.3 @@ -10,6 +10,7 @@ the history of the 3.2 series (2018-05 - 2022-08). # 3.3.11 (unreleased) This release contains contributions from (alphabetically by given name): + - Adriaan de Groot - Jakob Petsovits - Simon Quigley @@ -19,6 +20,8 @@ This release contains contributions from (alphabetically by given name): ## Modules ## - *unpackfs* now supports a `condition` configuration option for conditional installation / unsquash. (thanks Simon) + - *unpackfsc* module imported from Calamares-extensions, and extended + with the same `condition` configuration. - *partition* crash fixed when swap was using the wrong end-sector in some GPT configurations. (thanks Jakob, #2367) diff --git a/src/modules/unpackfsc/CMakeLists.txt b/src/modules/unpackfsc/CMakeLists.txt new file mode 100644 index 000000000..a045253ff --- /dev/null +++ b/src/modules/unpackfsc/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2021 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later + +calamares_add_plugin( unpackfsc + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + UnpackFSCJob.cpp + # The workers for differently-packed filesystems + Runners.cpp + FSArchiverRunner.cpp + TarballRunner.cpp + UnsquashRunner.cpp + SHARED_LIB +) diff --git a/src/modules/unpackfsc/FSArchiverRunner.cpp b/src/modules/unpackfsc/FSArchiverRunner.cpp new file mode 100644 index 000000000..41b6b7122 --- /dev/null +++ b/src/modules/unpackfsc/FSArchiverRunner.cpp @@ -0,0 +1,117 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "FSArchiverRunner.h" + +#include "utils/Logger.h" +#include "utils/Runner.h" + +#include + +static constexpr const int chunk_size = 137; +static const QString& +toolName() +{ + static const QString name = QStringLiteral( "fsarchiver" ); + return name; +} + +void +FSArchiverRunner::fsarchiverProgress( QString line ) +{ + m_since++; + // Typical line of output is this: + // -[00][ 99%][REGFILEM] /boot/thing + // 5 9 ^21 + if ( m_since >= chunk_size && line.length() > 21 && line[ 5 ] == '[' && line[ 9 ] == '%' ) + { + m_since = 0; + double p = double( line.mid( 6, 3 ).toInt() ) / 100.0; + const QString filename = line.mid( 22 ); + Q_EMIT progress( p, filename ); + } +} + +Calamares::JobResult +FSArchiverRunner::checkPrerequisites( QString& fsarchiverExecutable ) const +{ + if ( !checkToolExists( toolName(), fsarchiverExecutable ) ) + { + return Calamares::JobResult::internalError( + tr( "Missing tools" ), + tr( "The %1 tool is not installed on the system." ).arg( toolName() ), + Calamares::JobResult::MissingRequirements ); + } + + if ( !checkSourceExists() ) + { + return Calamares::JobResult::internalError( + tr( "Invalid fsarchiver configuration" ), + tr( "The source archive %1 does not exist." ).arg( m_source ), + Calamares::JobResult::InvalidConfiguration ); + } + return Calamares::JobResult::ok(); +} + +Calamares::JobResult +FSArchiverRunner::checkDestination( QString& destinationPath ) const +{ + destinationPath = Calamares::System::instance()->targetPath( m_destination ); + if ( destinationPath.isEmpty() ) + { + return Calamares::JobResult::internalError( + tr( "Invalid fsarchiver configuration" ), + tr( "No destination could be found for %1." ).arg( m_destination ), + Calamares::JobResult::InvalidConfiguration ); + } + + return Calamares::JobResult::ok(); +} + +Calamares::JobResult +FSArchiverDirRunner::run() +{ + QString fsarchiverExecutable; + if ( auto res = checkPrerequisites( fsarchiverExecutable ); !res ) + { + return res; + } + QString destinationPath; + if ( auto res = checkDestination( destinationPath ); !res ) + { + return res; + } + + Calamares::Utils::Runner r( + { fsarchiverExecutable, QStringLiteral( "-v" ), QStringLiteral( "restdir" ), m_source, destinationPath } ); + r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); + connect( &r, &decltype( r )::output, this, &FSArchiverDirRunner::fsarchiverProgress ); + return r.run().explainProcess( toolName(), std::chrono::seconds( 0 ) ); +} + +Calamares::JobResult +FSArchiverFSRunner::run() +{ + QString fsarchiverExecutable; + if ( auto res = checkPrerequisites( fsarchiverExecutable ); !res ) + { + return res; + } + QString destinationPath; + if ( auto res = checkDestination( destinationPath ); !res ) + { + return res; + } + + Calamares::Utils::Runner r( + { fsarchiverExecutable, QStringLiteral( "-v" ), QStringLiteral( "restfs" ), m_source, destinationPath } ); + r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); + connect( &r, &decltype( r )::output, this, &FSArchiverFSRunner::fsarchiverProgress ); + return r.run().explainProcess( toolName(), std::chrono::seconds( 0 ) ); +} diff --git a/src/modules/unpackfsc/FSArchiverRunner.h b/src/modules/unpackfsc/FSArchiverRunner.h new file mode 100644 index 000000000..39be6233f --- /dev/null +++ b/src/modules/unpackfsc/FSArchiverRunner.h @@ -0,0 +1,59 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UNPACKFSC_FSARCHIVERRUNNER_H +#define UNPACKFSC_FSARCHIVERRUNNER_H + +#include "Runners.h" + +/** @brief Base class for runners of FSArchiver + * + */ +class FSArchiverRunner : public Runner +{ + Q_OBJECT +public: + using Runner::Runner; + +protected Q_SLOTS: + void fsarchiverProgress( QString line ); + +protected: + /** @brief Checks prerequisites, sets full path of fsarchiver in @p executable + */ + Calamares::JobResult checkPrerequisites( QString& executable ) const; + Calamares::JobResult checkDestination( QString& destinationPath ) const; + + int m_since = 0; +}; + +/** @brief Running FSArchiver in **dir** mode + * + */ +class FSArchiverDirRunner : public FSArchiverRunner +{ +public: + using FSArchiverRunner::FSArchiverRunner; + + Calamares::JobResult run() override; +}; + +/** @brief Running FSArchiver in **dir** mode + * + */ +class FSArchiverFSRunner : public FSArchiverRunner +{ +public: + using FSArchiverRunner::FSArchiverRunner; + + Calamares::JobResult run() override; +}; + + +#endif diff --git a/src/modules/unpackfsc/Runners.cpp b/src/modules/unpackfsc/Runners.cpp new file mode 100644 index 000000000..3b3862260 --- /dev/null +++ b/src/modules/unpackfsc/Runners.cpp @@ -0,0 +1,38 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "Runners.h" + +#include +#include + +#include +#include + +Runner::Runner( const QString& source, const QString& destination ) + : m_source( source ) + , m_destination( destination ) +{ +} + +Runner::~Runner() { } + +bool +Runner::checkSourceExists() const +{ + QFileInfo fi( m_source ); + return fi.exists() && fi.isReadable(); +} + +bool +Runner::checkToolExists( const QString& toolName, QString& fullPath ) +{ + fullPath = QStandardPaths::findExecutable( toolName ); + return !fullPath.isEmpty(); +} diff --git a/src/modules/unpackfsc/Runners.h b/src/modules/unpackfsc/Runners.h new file mode 100644 index 000000000..e8f385ca3 --- /dev/null +++ b/src/modules/unpackfsc/Runners.h @@ -0,0 +1,48 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UNPACKFSC_RUNNERS_H +#define UNPACKFSC_RUNNERS_H + +#include + +class Runner : public QObject +{ + Q_OBJECT + +public: + Runner( const QString& source, const QString& destination ); + ~Runner() override; + + virtual Calamares::JobResult run() = 0; + + /** @brief Check that the (configured) source file exists. + * + * Returns @c true if it's a file and readable. + */ + bool checkSourceExists() const; + + /** @brief Check that a named tool (executable) exists in the search path. + * + * Returns @c true if the tool is found and sets @p fullPath + * to the full path of that tool; returns @c false and clears + * @p fullPath otherwise. + */ + static bool checkToolExists( const QString& toolName, QString& fullPath ); + +Q_SIGNALS: + // See Calamares Job::progress + void progress( qreal percent, const QString& message ); + +protected: + QString m_source; + QString m_destination; +}; + +#endif diff --git a/src/modules/unpackfsc/TarballRunner.cpp b/src/modules/unpackfsc/TarballRunner.cpp new file mode 100644 index 000000000..79dbec9aa --- /dev/null +++ b/src/modules/unpackfsc/TarballRunner.cpp @@ -0,0 +1,86 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "TarballRunner.h" + +#include +#include +#include + +#include + +static constexpr const int chunk_size = 107; + +Calamares::JobResult +TarballRunner::run() +{ + if ( !checkSourceExists() ) + { + return Calamares::JobResult::internalError( + tr( "Invalid tarball configuration" ), + tr( "The source archive %1 does not exist." ).arg( m_source ), + Calamares::JobResult::InvalidConfiguration ); + } + + const QString toolName = QStringLiteral( "tar" ); + QString tarExecutable; + if ( !checkToolExists( toolName, tarExecutable ) ) + { + return Calamares::JobResult::internalError( + tr( "Missing tools" ), + tr( "The %1 tool is not installed on the system." ).arg( toolName ), + Calamares::JobResult::MissingRequirements ); + } + + const QString destinationPath = Calamares::System::instance()->targetPath( m_destination ); + if ( destinationPath.isEmpty() ) + { + return Calamares::JobResult::internalError( + tr( "Invalid tarball configuration" ), + tr( "No destination could be found for %1." ).arg( m_destination ), + Calamares::JobResult::InvalidConfiguration ); + } + + // Get the stats (number of inodes) from the FS + { + m_total = 0; + Calamares::Utils::Runner r( { tarExecutable, QStringLiteral( "-tf" ), m_source } ); + r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); + QObject::connect( &r, &decltype( r )::output, [ & ]( QString line ) { m_total++; } ); + /* ignored */ r.run(); + } + if ( m_total <= 0 ) + { + cWarning() << "No stats could be obtained from" << tarExecutable << "-tf" << m_source; + } + + // Now do the actual unpack + { + m_processed = 0; + m_since = 0; + Calamares::Utils::Runner r( + { tarExecutable, QStringLiteral( "-xpvf" ), m_source, QStringLiteral( "-C" ), destinationPath } ); + r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); + connect( &r, &decltype( r )::output, this, &TarballRunner::tarballProgress ); + return r.run().explainProcess( toolName, std::chrono::seconds( 0 ) ); + } +} + +void +TarballRunner::tarballProgress( QString line ) +{ + m_processed++; + m_since++; + if ( m_since > chunk_size ) + { + m_since = 0; + double p = m_total > 0 ? ( double( m_processed ) / double( m_total ) ) : 0.5; + Q_EMIT progress( p, tr( "Tarball extract file %1" ).arg( line ) ); + } +} diff --git a/src/modules/unpackfsc/TarballRunner.h b/src/modules/unpackfsc/TarballRunner.h new file mode 100644 index 000000000..8573cc3a7 --- /dev/null +++ b/src/modules/unpackfsc/TarballRunner.h @@ -0,0 +1,35 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UNPACKFSC_TARBALLRUNNER_H +#define UNPACKFSC_TARBALLRUNNER_H + +#include "Runners.h" + +/** @brief Use (GNU) tar for extracting a filesystem + * + */ +class TarballRunner : public Runner +{ +public: + using Runner::Runner; + + Calamares::JobResult run() override; + +protected Q_SLOTS: + void tarballProgress( QString line ); + +private: + // Progress reporting + int m_total = 0; + int m_processed = 0; + int m_since = 0; +}; + +#endif diff --git a/src/modules/unpackfsc/UnpackFSCJob.cpp b/src/modules/unpackfsc/UnpackFSCJob.cpp new file mode 100644 index 000000000..48b1e8456 --- /dev/null +++ b/src/modules/unpackfsc/UnpackFSCJob.cpp @@ -0,0 +1,194 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "UnpackFSCJob.h" + +#include "FSArchiverRunner.h" +#include "TarballRunner.h" +#include "UnsquashRunner.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "compat/Variant.h" +#include "utils/Logger.h" +#include "utils/NamedEnum.h" +#include "utils/RAII.h" +#include "utils/Variant.h" + +#include + +static const NamedEnumTable< UnpackFSCJob::Type > +typeNames() +{ + using T = UnpackFSCJob::Type; + // clang-format off + static const NamedEnumTable< T > names + { + { "none", T::None }, + { "fsarchiver", T::FSArchive }, + { "fsarchive", T::FSArchive }, + { "fsa", T::FSArchive }, + { "fsa-dir", T::FSArchive }, + { "fsa-block", T::FSArchiveFS }, + { "fsa-fs", T::FSArchiveFS }, + { "squashfs", T::Squashfs }, + { "squash", T::Squashfs }, + { "unsquash", T::Squashfs }, + { "tar", T::Tarball }, + { "tarball", T::Tarball }, + { "tgz", T::Tarball }, + }; + // clang-format on + return names; +} + +UnpackFSCJob::UnpackFSCJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + +UnpackFSCJob::~UnpackFSCJob() {} + +QString +UnpackFSCJob::prettyName() const +{ + return tr( "Unpack filesystems" ); +} + +QString +UnpackFSCJob::prettyStatusMessage() const +{ + return m_progressMessage; +} + +static bool +checkCondition( const QString& condition ) +{ + if ( condition.isEmpty() ) + { + return true; + } + + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + + bool ok = false; + const auto v = Calamares::lookup( gs, condition, ok ); + if ( !ok ) + { + cWarning() << "Item has condition '" << condition << "' which is not set at all (assuming 'true')."; + return true; + } + + if ( !v.canConvert< bool >() ) + { + cWarning() << "Item has condition '" << condition << "' with value" << v << "(assuming 'true')."; + return true; + } + + return v.toBool(); +} + +Calamares::JobResult +UnpackFSCJob::exec() +{ + if ( !checkCondition( m_condition ) ) + { + cDebug() << "Skipping item with condition '" << m_condition << "' which is set to false."; + return Calamares::JobResult::ok(); + } + + cScopedAssignment messageClearer( &m_progressMessage, QString() ); + std::unique_ptr< Runner > r; + switch ( m_type ) + { + case Type::FSArchive: + r = std::make_unique< FSArchiverDirRunner >( m_source, m_destination ); + break; + case Type::FSArchiveFS: + r = std::make_unique< FSArchiverFSRunner >( m_source, m_destination ); + break; + case Type::Squashfs: + r = std::make_unique< UnsquashRunner >( m_source, m_destination ); + break; + case Type::Tarball: + r = std::make_unique< TarballRunner >( m_source, m_destination ); + break; + case Type::None: + default: + cDebug() << "Nothing to do."; + return Calamares::JobResult::ok(); + } + + connect( r.get(), + &Runner::progress, + [ = ]( qreal percent, const QString& message ) + { + m_progressMessage = message; + Q_EMIT progress( percent ); + } ); + return r->run(); +} + +void +UnpackFSCJob::setConfigurationMap( const QVariantMap& map ) +{ + m_type = Type::None; + + const QString source = Calamares::getString( map, "source" ); + const QString sourceTypeName = Calamares::getString( map, "sourcefs" ); + if ( source.isEmpty() || sourceTypeName.isEmpty() ) + { + cWarning() << "Skipping item with bad source data:" << map; + return; + } + bool bogus = false; + Type sourceType = typeNames().find( sourceTypeName, bogus ); + if ( sourceType == Type::None ) + { + cWarning() << "Skipping item with source type None"; + return; + } + const QString destination = Calamares::getString( map, "destination" ); + if ( destination.isEmpty() ) + { + cWarning() << "Skipping item with empty destination"; + return; + } + const auto conditionKey = QStringLiteral( "condition" ); + if ( map.contains( conditionKey ) ) + { + const auto value = map[ conditionKey ]; + if ( Calamares::typeOf( value ) == Calamares::BoolVariantType ) + { + if ( !value.toBool() ) + { + cDebug() << "Skipping item with condition set to false."; + // Leave type set to None, which will be skipped later + return; + } + // Else the condition is true, and we're fine leaving the string empty because that defaults to true + } + else + { + const auto variable = value.toString(); + if ( variable.isEmpty() ) + { + cDebug() << "Skipping item with condition '" << value << "' that is empty (use 'true' instead)."; + return; + } + m_condition = variable; + } + } + + m_source = source; + m_destination = destination; + m_type = sourceType; +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( UnpackFSCFactory, registerPlugin< UnpackFSCJob >(); ) diff --git a/src/modules/unpackfsc/UnpackFSCJob.h b/src/modules/unpackfsc/UnpackFSCJob.h new file mode 100644 index 000000000..416efe1f9 --- /dev/null +++ b/src/modules/unpackfsc/UnpackFSCJob.h @@ -0,0 +1,51 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UNPACKFSC_UNPACKFSCJOB_H +#define UNPACKFSC_UNPACKFSCJOB_H + +#include +#include +#include + +class PLUGINDLLEXPORT UnpackFSCJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + enum class Type + { + None, /// << Invalid + FSArchive, + FSArchiveFS, + Squashfs, + Tarball, + }; + + explicit UnpackFSCJob( QObject* parent = nullptr ); + ~UnpackFSCJob() override; + + QString prettyName() const override; + QString prettyStatusMessage() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private: + QString m_source; + QString m_destination; + Type m_type = Type::None; + QString m_progressMessage; + QString m_condition; ///< May be empty to express condition "true" +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( UnpackFSCFactory ) + +#endif diff --git a/src/modules/unpackfsc/UnsquashRunner.cpp b/src/modules/unpackfsc/UnsquashRunner.cpp new file mode 100644 index 000000000..b3712b997 --- /dev/null +++ b/src/modules/unpackfsc/UnsquashRunner.cpp @@ -0,0 +1,101 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "UnsquashRunner.h" + +#include +#include +#include + +#include + +static constexpr const int chunk_size = 107; + +Calamares::JobResult +UnsquashRunner::run() +{ + if ( !checkSourceExists() ) + { + return Calamares::JobResult::internalError( + tr( "Invalid unsquash configuration" ), + tr( "The source archive %1 does not exist." ).arg( m_source ), + Calamares::JobResult::InvalidConfiguration ); + } + + const QString toolName = QStringLiteral( "unsquashfs" ); + QString unsquashExecutable; + if ( !checkToolExists( toolName, unsquashExecutable ) ) + { + return Calamares::JobResult::internalError( + tr( "Missing tools" ), + tr( "The %1 tool is not installed on the system." ).arg( toolName ), + Calamares::JobResult::MissingRequirements ); + } + + const QString destinationPath = Calamares::System::instance()->targetPath( m_destination ); + if ( destinationPath.isEmpty() ) + { + return Calamares::JobResult::internalError( + tr( "Invalid unsquash configuration" ), + tr( "No destination could be found for %1." ).arg( m_destination ), + Calamares::JobResult::InvalidConfiguration ); + } + + // Get the stats (number of inodes) from the FS + { + m_inodes = -1; + Calamares::Utils::Runner r( { unsquashExecutable, QStringLiteral( "-s" ), m_source } ); + r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); + QObject::connect( &r, + &decltype( r )::output, + [ & ]( QString line ) + { + if ( line.startsWith( "Number of inodes " ) ) + { + m_inodes = line.split( ' ', SplitSkipEmptyParts ).last().toInt(); + } + } ); + /* ignored */ r.run(); + } + if ( m_inodes <= 0 ) + { + cWarning() << "No stats could be obtained from" << unsquashExecutable << "-s"; + } + + // Now do the actual unpack + { + m_processed = 0; + Calamares::Utils::Runner r( { unsquashExecutable, + QStringLiteral( "-i" ), // List files + QStringLiteral( "-f" ), // Force-overwrite + QStringLiteral( "-d" ), + destinationPath, + m_source } ); + r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); + connect( &r, &decltype( r )::output, this, &UnsquashRunner::unsquashProgress ); + return r.run().explainProcess( toolName, std::chrono::seconds( 0 ) ); + } +} + +void +UnsquashRunner::unsquashProgress( QString line ) +{ + m_processed++; + m_since++; + if ( m_since > chunk_size && line.contains( '/' ) ) + { + const QString filename = line.split( '/', SplitSkipEmptyParts ).last().trimmed(); + if ( !filename.isEmpty() ) + { + m_since = 0; + double p = m_inodes > 0 ? ( double( m_processed ) / double( m_inodes ) ) : 0.5; + Q_EMIT progress( p, tr( "Unsquash file %1" ).arg( filename ) ); + } + } +} diff --git a/src/modules/unpackfsc/UnsquashRunner.h b/src/modules/unpackfsc/UnsquashRunner.h new file mode 100644 index 000000000..c9b57c0e6 --- /dev/null +++ b/src/modules/unpackfsc/UnsquashRunner.h @@ -0,0 +1,36 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UNPACKFSC_UNSQUASHRUNNER_H +#define UNPACKFSC_UNSQUASHRUNNER_H + +#include "Runners.h" + +/** @brief Use Unsquash for extracting a filesystem + * + */ +class UnsquashRunner : public Runner +{ +public: + using Runner::Runner; + + Calamares::JobResult run() override; + +protected Q_SLOTS: + void unsquashProgress( QString line ); + +private: + int m_inodes = 0; // Total in the FS + + // Progress reporting + int m_processed = 0; + int m_since = 0; +}; + +#endif diff --git a/src/modules/unpackfsc/tests/1.global b/src/modules/unpackfsc/tests/1.global new file mode 100644 index 000000000..064ac2a8b --- /dev/null +++ b/src/modules/unpackfsc/tests/1.global @@ -0,0 +1,2 @@ +--- +rootMountPoint: /tmp/fstest diff --git a/src/modules/unpackfsc/tests/1.job b/src/modules/unpackfsc/tests/1.job new file mode 100644 index 000000000..9c6e46dff --- /dev/null +++ b/src/modules/unpackfsc/tests/1.job @@ -0,0 +1,4 @@ +--- +source: /tmp/src.fsa +sourcefs: fsarchive +destination: "/calasrc" diff --git a/src/modules/unpackfsc/unpackfsc.conf b/src/modules/unpackfsc/unpackfsc.conf new file mode 100644 index 000000000..7cb4c3234 --- /dev/null +++ b/src/modules/unpackfsc/unpackfsc.conf @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Unpack a filesystem. Supported ways to "pack" the filesystem are: +# - fsarchiver in *savedir/restdir* mode (directories, not block devices) +# - squashfs +# +# Configuration: +# +# from globalstorage: rootMountPoint +# from job configuration: the item to unpack +# + +--- +# This module is configured a lot like the items in the *unpackfs* +# module, but with only **one** item. Use multiple instances for +# unpacking more than one filesystem. +# +# There are the following **mandatory** keys: +# - *source* path relative to the live / intstalling system to the image +# - *sourcefs* the type of the source files; valid entries are +# - `none` (this entry is ignored; kind of useless) +# - `fsarchiver` +# Aliases of this are `fsarchive`, `fsa` and `fsa-dir`. Uses +# fsarchiver in "restdir" mode. +# - `fsarchiver-block` +# Aliases of this are `fsa-block` and `fsa-fs`. Uses fsarchiver +# in "restfs" mode. +# - `squashfs` +# Aliases of this are `squash` and `unsquash`. +# - `tar` +# - *destination* path relative to rootMountPoint (so in the target +# system) where this filesystem is unpacked. It may be an +# empty string, which effectively is / (the root) of the target +# system. +# +# +# There are the following **optional** keys: +# - *condition* sets a dynamic condition on unpacking the item in +# this job. This may be true or false (constant) or name a globalstorage +# value. Use '.' to separate parts of a globalstorage name if it is nested. +# Remember to quote names. +# +# A condition is used in e.g. stacked squashfses, where the user can select +# a specific install type. The default value of *condition* is true. + +source: /data/rootfs.fsa +sourcefs: fsarchiver +destination: "/" +# condition: true diff --git a/src/modules/unpackfsc/unpackfsc.schema.yaml b/src/modules/unpackfsc/unpackfsc.schema.yaml new file mode 100644 index 000000000..14493b687 --- /dev/null +++ b/src/modules/unpackfsc/unpackfsc.schema.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/unpackfsc +additionalProperties: false +type: object +properties: + unpack: + type: array + items: + type: object + additionalProperties: false + properties: + source: { type: string } + sourcefs: { type: string } + destination: { type: string } + condition: + anyOf: + - type: boolean + - type: string + required: [ source , sourcefs, destination ]