[unpackfsc] Import unpackfsc module from Calamares-extensions

This does some of the same things that unpackfs does, but
with nicer progress reporting, possibly it is faster.
It does not use rsync, so it might not be a drop-in replacement
for unpackfs.

Instead of having one configuration file with multiple items,
each unpackfsc instance unpacks just one thing.
This commit is contained in:
Adriaan de Groot 2024-11-05 15:35:10 +01:00
parent c6f429e310
commit 3008260bb2
16 changed files with 861 additions and 0 deletions

View File

@ -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)

View File

@ -0,0 +1,15 @@
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
# 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
)

View File

@ -0,0 +1,117 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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 <QProcess>
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 <i>%1</i> 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 <i>%1</i> 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 <i>%1</i>." ).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 ) );
}

View File

@ -0,0 +1,59 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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

View File

@ -0,0 +1,38 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "Runners.h"
#include <utils/System.h>
#include <utils/Logger.h>
#include <QFileInfo>
#include <QStandardPaths>
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();
}

View File

@ -0,0 +1,48 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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 <Job.h>
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

View File

@ -0,0 +1,86 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "TarballRunner.h"
#include <utils/Logger.h>
#include <utils/Runner.h>
#include <utils/String.h>
#include <QString>
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 <i>%1</i> 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 <i>%1</i> 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 <i>%1</i>." ).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 ) );
}
}

View File

@ -0,0 +1,35 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* 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

View File

@ -0,0 +1,194 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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 <memory>
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 >(); )

View File

@ -0,0 +1,51 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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 <CppJob.h>
#include <DllMacro.h>
#include <utils/PluginFactory.h>
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

View File

@ -0,0 +1,101 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "UnsquashRunner.h"
#include <utils/Logger.h>
#include <utils/Runner.h>
#include <utils/String.h>
#include <QString>
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 <i>%1</i> 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 <i>%1</i> 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 <i>%1</i>." ).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 ) );
}
}
}

View File

@ -0,0 +1,36 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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

View File

@ -0,0 +1,2 @@
---
rootMountPoint: /tmp/fstest

View File

@ -0,0 +1,4 @@
---
source: /tmp/src.fsa
sourcefs: fsarchive
destination: "/calasrc"

View File

@ -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

View File

@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# 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 ]