|
|
@ -1,5 +1,5 @@
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2017-2020 Andrius Štikonas <andrius@stikonas.eu>
|
|
|
|
SPDX-FileCopyrightText: 2017-2022 Andrius Štikonas <andrius@stikonas.eu>
|
|
|
|
SPDX-FileCopyrightText: 2018 Huzaifa Faruqui <huzaifafaruqui@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2018 Huzaifa Faruqui <huzaifafaruqui@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2018-2019 Harald Sitter <sitter@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2018-2019 Harald Sitter <sitter@kde.org>
|
|
|
@ -156,19 +156,19 @@ bool ExternalCommandHelper::CreateFile(const QString &filePath, const QByteArray
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If targetDevice is empty then return QByteArray with data that was read from disk.
|
|
|
|
// If targetDevice is empty then return QByteArray with data that was read from disk.
|
|
|
|
QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, const qint64 sourceOffset, const qint64 sourceLength, const QString& targetDevice, const qint64 targetOffset, const qint64 blockSize)
|
|
|
|
QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, const qint64 sourceOffset, const qint64 sourceLength, const QString& targetDevice, const qint64 targetOffset, const qint64 chunkSize)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!isCallerAuthorized()) {
|
|
|
|
if (!isCallerAuthorized()) {
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Avoid division by zero further down
|
|
|
|
// Avoid division by zero further down
|
|
|
|
if (!blockSize) {
|
|
|
|
if (!chunkSize) {
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prevent some out of memory situations
|
|
|
|
// Prevent some out of memory situations
|
|
|
|
if (blockSize > 100 * MiB) {
|
|
|
|
if (chunkSize > 100 * MiB) {
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -187,38 +187,41 @@ QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, con
|
|
|
|
QVariantMap reply;
|
|
|
|
QVariantMap reply;
|
|
|
|
reply[QStringLiteral("success")] = true;
|
|
|
|
reply[QStringLiteral("success")] = true;
|
|
|
|
|
|
|
|
|
|
|
|
// This enum specified whether individual blocks are moved left or right
|
|
|
|
// This enum specified whether individual data chunks are moved left or right
|
|
|
|
// When partition is moved to the left, we start with the leftmost block,
|
|
|
|
// When source and target devices are the same we have to be careful not to overwrite
|
|
|
|
// and move it further left, then second leftmost block and so on.
|
|
|
|
// source data with newly written data. We don't have to do this if sourceDevice is not
|
|
|
|
// But when we move partition to the right, we start with rightmost block.
|
|
|
|
// targetDevice but there are no disadvantages in applying the same scheme.
|
|
|
|
|
|
|
|
// When partition is moved to the left, we start with the leftmost chunk,
|
|
|
|
|
|
|
|
// and move it further left, then second leftmost chunk and so on.
|
|
|
|
|
|
|
|
// But when we move partition to the right, we start with rightmost chunk.
|
|
|
|
// To account for this difference, we introduce CopyDirection variable which takes
|
|
|
|
// To account for this difference, we introduce CopyDirection variable which takes
|
|
|
|
// care of some of the differences between these two cases.
|
|
|
|
// care of some of the differences in offset calculation between these two cases.
|
|
|
|
enum CopyDirection : qint8 {
|
|
|
|
enum CopyDirection : qint8 {
|
|
|
|
Left = 1,
|
|
|
|
Left = 1,
|
|
|
|
Right = -1,
|
|
|
|
Right = -1,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
qint8 copyDirection = targetOffset > sourceOffset ? CopyDirection::Right : CopyDirection::Left;
|
|
|
|
qint8 copyDirection = targetOffset > sourceOffset ? CopyDirection::Right : CopyDirection::Left;
|
|
|
|
|
|
|
|
|
|
|
|
// Let readOffset (r) and writeOffset (w) be the offsets of the first block that we move.
|
|
|
|
// Let readOffset (r) and writeOffset (w) be the offsets of the first chunk that we move.
|
|
|
|
// When we move data to the left:
|
|
|
|
// When we move data to the left:
|
|
|
|
// ______target______ ______source______
|
|
|
|
// ______target______ ______source______
|
|
|
|
// r <- w=================
|
|
|
|
// r <- w=================
|
|
|
|
qint64 readOffset = sourceOffset;
|
|
|
|
qint64 readOffset = sourceOffset;
|
|
|
|
qint64 writeOffset = targetOffset;
|
|
|
|
qint64 writeOffset = targetOffset;
|
|
|
|
|
|
|
|
|
|
|
|
// When we move data to the right, we start moving data from the last block
|
|
|
|
// When we move data to the right, we start moving data from the last chunk
|
|
|
|
// ______source______ ______target______
|
|
|
|
// ______source______ ______target______
|
|
|
|
// =================r -> w
|
|
|
|
// =================r -> w
|
|
|
|
if (copyDirection == CopyDirection::Right) {
|
|
|
|
if (copyDirection == CopyDirection::Right) {
|
|
|
|
readOffset = sourceOffset + sourceLength - blockSize;
|
|
|
|
readOffset = sourceOffset + sourceLength - chunkSize;
|
|
|
|
writeOffset = targetOffset + sourceLength - blockSize;
|
|
|
|
writeOffset = targetOffset + sourceLength - chunkSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const qint64 blocksToCopy = sourceLength / blockSize;
|
|
|
|
const qint64 chunksToCopy = sourceLength / chunkSize;
|
|
|
|
const qint64 lastBlock = sourceLength % blockSize;
|
|
|
|
const qint64 lastBlock = sourceLength % chunkSize;
|
|
|
|
|
|
|
|
|
|
|
|
qint64 bytesWritten = 0;
|
|
|
|
qint64 bytesWritten = 0;
|
|
|
|
qint64 blocksCopied = 0;
|
|
|
|
qint64 chunksCopied = 0;
|
|
|
|
|
|
|
|
|
|
|
|
QByteArray buffer;
|
|
|
|
QByteArray buffer;
|
|
|
|
int percent = 0;
|
|
|
|
int percent = 0;
|
|
|
@ -226,7 +229,7 @@ QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, con
|
|
|
|
|
|
|
|
|
|
|
|
timer.start();
|
|
|
|
timer.start();
|
|
|
|
|
|
|
|
|
|
|
|
QString reportText = xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy,
|
|
|
|
QString reportText = xi18nc("@info:progress", "Copying %1 chunks (%2 bytes) from %3 to %4, direction: %5.", chunksToCopy,
|
|
|
|
sourceLength, readOffset, writeOffset, copyDirection == CopyDirection::Left ? i18nc("direction: left", "left")
|
|
|
|
sourceLength, readOffset, writeOffset, copyDirection == CopyDirection::Left ? i18nc("direction: left", "left")
|
|
|
|
: i18nc("direction: right", "right"));
|
|
|
|
: i18nc("direction: right", "right"));
|
|
|
|
Q_EMIT report(reportText);
|
|
|
|
Q_EMIT report(reportText);
|
|
|
@ -235,20 +238,20 @@ QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, con
|
|
|
|
|
|
|
|
|
|
|
|
QFile target(targetDevice);
|
|
|
|
QFile target(targetDevice);
|
|
|
|
QFile source(sourceDevice);
|
|
|
|
QFile source(sourceDevice);
|
|
|
|
while (blocksCopied < blocksToCopy) {
|
|
|
|
while (chunksCopied < chunksToCopy) {
|
|
|
|
if (!(rval = readData(source, buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize)))
|
|
|
|
if (!(rval = readData(source, buffer, readOffset + chunkSize * chunksCopied * copyDirection, chunkSize)))
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
if (!(rval = writeData(target, buffer, writeOffset + blockSize * blocksCopied * copyDirection)))
|
|
|
|
if (!(rval = writeData(target, buffer, writeOffset + chunkSize * chunksCopied * copyDirection)))
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
bytesWritten += buffer.size();
|
|
|
|
bytesWritten += buffer.size();
|
|
|
|
|
|
|
|
|
|
|
|
if (++blocksCopied * 100 / blocksToCopy != percent) {
|
|
|
|
if (++chunksCopied * 100 / chunksToCopy != percent) {
|
|
|
|
percent = blocksCopied * 100 / blocksToCopy;
|
|
|
|
percent = chunksCopied * 100 / chunksToCopy;
|
|
|
|
|
|
|
|
|
|
|
|
if (percent % 5 == 0 && timer.elapsed() > 1000) {
|
|
|
|
if (percent % 5 == 0 && timer.elapsed() > 1000) {
|
|
|
|
const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (timer.elapsed() / 1000);
|
|
|
|
const qint64 mibsPerSec = (chunksCopied * chunkSize / 1024 / 1024) / (timer.elapsed() / 1000);
|
|
|
|
const qint64 estSecsLeft = (100 - percent) * timer.elapsed() / percent / 1000;
|
|
|
|
const qint64 estSecsLeft = (100 - percent) * timer.elapsed() / percent / 1000;
|
|
|
|
reportText = xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString());
|
|
|
|
reportText = xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString());
|
|
|
|
Q_EMIT report(reportText);
|
|
|
|
Q_EMIT report(reportText);
|
|
|
@ -259,11 +262,11 @@ QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, con
|
|
|
|
|
|
|
|
|
|
|
|
// copy the remainder
|
|
|
|
// copy the remainder
|
|
|
|
if (rval && lastBlock > 0) {
|
|
|
|
if (rval && lastBlock > 0) {
|
|
|
|
Q_ASSERT(lastBlock < blockSize);
|
|
|
|
Q_ASSERT(lastBlock < chunkSize);
|
|
|
|
|
|
|
|
|
|
|
|
const qint64 lastBlockReadOffset = copyDirection == CopyDirection::Left ? readOffset + blockSize * blocksCopied : sourceOffset;
|
|
|
|
const qint64 lastBlockReadOffset = copyDirection == CopyDirection::Left ? readOffset + chunkSize * chunksCopied : sourceOffset;
|
|
|
|
const qint64 lastBlockWriteOffset = copyDirection == CopyDirection::Left ? writeOffset + blockSize * blocksCopied : targetOffset;
|
|
|
|
const qint64 lastBlockWriteOffset = copyDirection == CopyDirection::Left ? writeOffset + chunkSize * chunksCopied : targetOffset;
|
|
|
|
reportText = xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset);
|
|
|
|
reportText = xi18nc("@info:progress", "Copying remainder of chunk size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset);
|
|
|
|
Q_EMIT report(reportText);
|
|
|
|
Q_EMIT report(reportText);
|
|
|
|
rval = readData(source, buffer, lastBlockReadOffset, lastBlock);
|
|
|
|
rval = readData(source, buffer, lastBlockReadOffset, lastBlock);
|
|
|
|
|
|
|
|
|
|
|
@ -277,7 +280,7 @@ QVariantMap ExternalCommandHelper::CopyFileData(const QString& sourceDevice, con
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reportText = xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 block (%2) finished.", "Copying %1 blocks (%2) finished.", blocksCopied, i18np("1 byte", "%1 bytes", bytesWritten));
|
|
|
|
reportText = xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 chunk (%2) finished.", "Copying %1 chunks (%2) finished.", chunksCopied, i18np("1 byte", "%1 bytes", bytesWritten));
|
|
|
|
Q_EMIT report(reportText);
|
|
|
|
Q_EMIT report(reportText);
|
|
|
|
|
|
|
|
|
|
|
|
reply[QStringLiteral("success")] = rval;
|
|
|
|
reply[QStringLiteral("success")] = rval;
|
|
|
@ -339,10 +342,9 @@ QVariantMap ExternalCommandHelper::RunCommand(const QString& command, const QStr
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
QVariantMap reply;
|
|
|
|
QVariantMap reply;
|
|
|
|
reply[QStringLiteral("success")] = true;
|
|
|
|
reply[QStringLiteral("success")] = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (command.isEmpty()) {
|
|
|
|
if (command.isEmpty()) {
|
|
|
|
reply[QStringLiteral("success")] = false;
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -350,7 +352,6 @@ QVariantMap ExternalCommandHelper::RunCommand(const QString& command, const QStr
|
|
|
|
QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1);
|
|
|
|
QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1);
|
|
|
|
if (allowedCommands.find(basename) == allowedCommands.end()) { // TODO: C++20: replace with contains
|
|
|
|
if (allowedCommands.find(basename) == allowedCommands.end()) { // TODO: C++20: replace with contains
|
|
|
|
qInfo() << command <<" command is not one of the whitelisted command";
|
|
|
|
qInfo() << command <<" command is not one of the whitelisted command";
|
|
|
|
reply[QStringLiteral("success")] = false;
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -360,7 +361,6 @@ QVariantMap ExternalCommandHelper::RunCommand(const QString& command, const QStr
|
|
|
|
cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } );
|
|
|
|
cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } );
|
|
|
|
|
|
|
|
|
|
|
|
if((processChannelMode != QProcess::SeparateChannels) && (processChannelMode != QProcess::MergedChannels)) {
|
|
|
|
if((processChannelMode != QProcess::SeparateChannels) && (processChannelMode != QProcess::MergedChannels)) {
|
|
|
|
reply[QStringLiteral("success")] = false;
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannelMode));
|
|
|
|
cmd.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannelMode));
|
|
|
@ -372,6 +372,7 @@ QVariantMap ExternalCommandHelper::RunCommand(const QString& command, const QStr
|
|
|
|
reply[QStringLiteral("output")] = output;
|
|
|
|
reply[QStringLiteral("output")] = output;
|
|
|
|
reply[QStringLiteral("exitCode")] = cmd.exitCode();
|
|
|
|
reply[QStringLiteral("exitCode")] = cmd.exitCode();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reply[QStringLiteral("success")] = true;
|
|
|
|
return reply;
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|