Initial support for resizing LUKS2 volumes.

Does not yet work when LUKS2 is used with dm-integrity.
This commit is contained in:
Andrius Štikonas 2017-12-29 20:01:06 +00:00
parent 78a9ede36c
commit 4773f49edc
5 changed files with 111 additions and 9 deletions

View File

@ -143,6 +143,7 @@ bool luks::create(Report& report, const QString& deviceNode)
if (!( openCmd.start(-1) && openCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && openCmd.waitFor()))
return false;
setPayloadSize();
scan(deviceNode);
if (mapperName().isEmpty())
@ -259,6 +260,7 @@ bool luks::cryptOpen(QWidget* parent, const QString& deviceNode)
QString passphrase = dlg.password();
ExternalCommand openCmd(QStringLiteral("cryptsetup"),
{ QStringLiteral("open"),
QStringLiteral("--tries"), QStringLiteral("1"),
deviceNode,
suggestedMapperName(deviceNode) });
@ -490,19 +492,18 @@ bool luks::resize(Report& report, const QString& deviceNode, qint64 newLength) c
if (mapperName().isEmpty())
return false;
qint64 payloadLength = newLength - payloadOffset();
if ( newLength - length() * sectorSize() > 0 )
{
ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("resize"), mapperName() });
report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition <filename>%1</filename>.", deviceNode);
if (cryptResizeCmd.run(-1) && cryptResizeCmd.exitCode() == 0)
return m_innerFs->resize(report, mapperName(), payloadLength);
return m_innerFs->resize(report, mapperName(), m_PayloadSize);
}
else if (m_innerFs->resize(report, mapperName(), payloadLength))
else if (m_innerFs->resize(report, mapperName(), m_PayloadSize))
{
ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"),
{ QStringLiteral("--size"), QString::number(payloadLength / 512), // LUKS payload length is specified in multiples of 512 bytes
{ QStringLiteral("--size"), QString::number(m_PayloadSize / 512), // LUKS1 payload length is specified in multiples of 512 bytes
QStringLiteral("resize"), mapperName() });
report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition <filename>%1</filename>.", deviceNode);
if (cryptResizeCmd.run(-1) && cryptResizeCmd.exitCode() == 0)
@ -589,8 +590,7 @@ void luks::getMapperName(const QString& deviceNode)
void luks::getLuksInfo(const QString& deviceNode)
{
ExternalCommand cmd(QStringLiteral("cryptsetup"),
{ QStringLiteral("luksDump"), deviceNode });
ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("luksDump"), deviceNode });
if (cmd.run(-1) && cmd.exitCode() == 0) {
QRegularExpression re(QStringLiteral("Cipher name:\\s+(\\w+)"));
QRegularExpressionMatch rem = re.match(cmd.output());
@ -667,6 +667,7 @@ bool luks::canEncryptType(FileSystem::Type type)
void luks::initLUKS()
{
setPayloadSize();
QString mapperNode = mapperName();
bool isCryptOpen = !mapperNode.isEmpty();
setCryptOpen(isCryptOpen);
@ -676,4 +677,22 @@ void luks::initLUKS()
}
}
void luks::setPayloadSize()
{
ExternalCommand dmsetupCmd(QStringLiteral("dmsetup"), { QStringLiteral("table"), mapperName() });
dmsetupCmd.run();
QRegularExpression re(QStringLiteral("\\d+ (\\d+)"));
QRegularExpressionMatch rem = re.match(dmsetupCmd.output());
if (rem.hasMatch())
m_PayloadSize = rem.captured(1).toLongLong() * sectorSize();
}
bool luks::testPassphrase(const QString& deviceNode, const QString& passphrase) const {
ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("open"), QStringLiteral("--tries"), QStringLiteral("1"), QStringLiteral("--test-passphrase"), deviceNode });
if (cmd.start(-1) && cmd.write(passphrase.toLocal8Bit() + '\n') == passphrase.toLocal8Bit().length() + 1 && cmd.waitFor() && cmd.exitCode() == 0)
return true;
return false;
}
}

View File

@ -43,6 +43,12 @@ public:
luks(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Luks);
~luks() override;
enum KeyLocation {
unknown,
dmcrypt,
keyring
};
public:
void init() override;
void scan(const QString& deviceNode) override;
@ -167,9 +173,9 @@ public:
QString suggestedMapperName(const QString& deviceNode) const;
void getMapperName(const QString& deviceNode);
void getLuksInfo(const QString& deviceNode);
virtual void getLuksInfo(const QString& deviceNode);
FileSystem* innerFS() const { return m_innerFs; } // avoid calling this unless necessary
FileSystem* innerFS() const { return m_innerFs; }
QString outerUuid() const;
QString mapperName() const { return m_MapperName; }
@ -182,8 +188,12 @@ public:
static bool canEncryptType(FileSystem::Type type);
void initLUKS();
virtual luks::KeyLocation keyLocation() { return luks::dmcrypt; };
bool testPassphrase(const QString& deviceNode, const QString& passphrase) const;
protected:
virtual QString readOuterUUID(const QString& deviceNode) const;
void setPayloadSize();
public:
static CommandSupportType m_GetUsed;
@ -213,7 +223,10 @@ protected:
QString m_HashName;
qint64 m_KeySize;
qint64 m_PayloadOffset;
qint64 m_PayloadSize;
QString m_outerUuid;
luks::KeyLocation m_KeyLocation = unknown;
};
}

View File

@ -17,6 +17,13 @@
#include "fs/luks2.h"
#include "util/externalcommand.h"
#include "util/report.h"
#include <QRegularExpression>
#include <KLocalizedString>
namespace FS
{
@ -36,4 +43,64 @@ FileSystem::Type luks2::type() const
return FileSystem::Luks2;
}
bool luks2::resize(Report& report, const QString& deviceNode, qint64 newLength) const
{
Q_ASSERT(m_innerFs);
if (mapperName().isEmpty())
return false;
if ( newLength - length() * sectorSize() > 0 )
{
ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("resize"), mapperName() });
report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition <filename>%1</filename>.", deviceNode);
cryptResizeCmd.start(-1);
if (m_KeyLocation == keyring) {
if (m_passphrase.isEmpty())
return false;
cryptResizeCmd.write(m_passphrase.toLocal8Bit() + '\n');
}
cryptResizeCmd.waitFor();
if ( cryptResizeCmd.exitCode() == 0 )
return m_innerFs->resize(report, mapperName(), m_PayloadSize);
}
else if (m_innerFs->resize(report, mapperName(), m_PayloadSize))
{
ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"),
{ QStringLiteral("--size"), QString::number(m_PayloadSize / 512), // FIXME, LUKS2 can have different sector sizes
QStringLiteral("resize"), mapperName() });
report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition <filename>%1</filename>.", deviceNode);
cryptResizeCmd.start(-1);
if (m_KeyLocation == keyring) {
if (m_passphrase.isEmpty())
return false;
cryptResizeCmd.write(m_passphrase.toLocal8Bit() + '\n');
}
cryptResizeCmd.waitFor();
if ( cryptResizeCmd.exitCode() == 0 )
return true;
}
report.line() << xi18nc("@info:progress", "Resizing encrypted file system on partition <filename>%1</filename> failed.", deviceNode);
return false;
}
luks::KeyLocation luks2::keyLocation()
{
m_KeyLocation = unknown;
ExternalCommand statusCmd(QStringLiteral("cryptsetup"), { QStringLiteral("status"), mapperName() });
if (statusCmd.run(-1) && statusCmd.exitCode() == 0) {
QRegularExpression re(QStringLiteral("key location:\\s+(\\w+)"));
QRegularExpressionMatch rem = re.match(statusCmd.output());
if (rem.hasMatch()) {
if (rem.captured(1) == QStringLiteral("keyring"))
m_KeyLocation = keyring;
else if (rem.captured(1) == QStringLiteral("dm-crypt"))
m_KeyLocation = dmcrypt;
}
}
return m_KeyLocation;
}
}

View File

@ -38,7 +38,11 @@ public:
luks2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label);
~luks2() override;
bool resize(Report& report, const QString& deviceNode, qint64 length) const override;
FileSystem::Type type() const override;
luks::KeyLocation keyLocation() override;
};
}

View File

@ -73,7 +73,6 @@ bool SetPartGeometryJob::run(Report& parent)
delete backendPartitionTable;
}
delete backendDevice;
} else
report->line() << xi18nc("@info:progress", "Could not open device <filename>%1</filename> while trying to resize/move partition <filename>%2</filename>.", device().deviceNode(), partition().deviceNode());