Implement copyblocks function in KAuth helper.

Switch BackupFileSystemJob to thenew copyblocks function.

Reviewed and cleaned up by: Andrius Štikonas
This commit is contained in:
Huzaifa Faruqui 2018-01-24 15:22:42 +00:00 committed by Andrius Štikonas
parent a3d43e159f
commit e42a5c9289
16 changed files with 289 additions and 8 deletions

View File

@ -22,6 +22,7 @@
#include <QtGlobal>
class CopyTarget;
class QString;
/** Base class for something to copy from.
@ -41,6 +42,7 @@ protected:
public:
virtual bool open() = 0;
virtual QString path() const = 0;
virtual bool readData(QByteArray& buffer, qint64 readOffset, qint64 size) = 0;
virtual qint64 length() const = 0;
virtual bool overlaps(const CopyTarget& target) const = 0;

View File

@ -103,3 +103,8 @@ bool CopySourceDevice::overlaps(const CopyTarget& target) const
return false;
}
QString CopySourceDevice::path() const
{
return m_Device.deviceNode();
}

View File

@ -27,6 +27,7 @@
class Device;
class CopyTarget;
class CoreBackendDevice;
class QString;
/** A Device to copy from.
@ -62,12 +63,13 @@ public:
return m_Device; /**< @return Device to copy from */
}
QString path() const override;
protected:
Device& m_Device;
const qint64 m_FirstByte;
const qint64 m_LastByte;
CoreBackendDevice* m_BackendDevice
;
CoreBackendDevice* m_BackendDevice;
};
#endif

View File

@ -52,6 +52,9 @@ public:
qint64 lastByte() const override {
return length(); /**< @return equal to length for file. @see length() */
}
QString path() const override {
return m_File.fileName();
}
protected:
QFile& file() {

View File

@ -24,6 +24,7 @@
#include <QFile>
class CopyTarget;
class QString;
/** A source for securely overwriting a partition (shredding).
@ -50,6 +51,9 @@ public:
qint64 lastByte() const override {
return length(); /**< @return equal to length for shred source. @see length() */
}
QString path() const override {
return m_SourceFile.fileName();
}
protected:
QFile& sourceFile() {

View File

@ -21,6 +21,7 @@
#include <QtGlobal>
class QString;
/** Base class for something to copy to.
@ -43,7 +44,7 @@ public:
virtual bool writeData(QByteArray& buffer, qint64 writeOffset) = 0;
virtual qint64 firstByte() const = 0;
virtual qint64 lastByte() const = 0;
virtual QString path() const = 0;
qint64 bytesWritten() const {
return m_BytesWritten;
}

View File

@ -23,7 +23,6 @@
#include "core/device.h"
/** Constructs a device to copy to.
@param d the Device to copy to
@param firstbyte the first byte on the Device to write to
@ -71,3 +70,8 @@ bool CopyTargetDevice::writeData(QByteArray& buffer, qint64 writeOffset)
return rval;
}
QString CopyTargetDevice::path() const
{
return m_Device.deviceNode();
}

View File

@ -60,6 +60,7 @@ public:
const Device& device() const {
return m_Device; /**< @return the Device to write to */
}
QString path() const override;
protected:
Device& m_Device;

View File

@ -49,6 +49,10 @@ public:
return bytesWritten(); /**< @return the number of bytes written so far */
}
QString path() const override {
return m_File.fileName();
}
protected:
QFile& file() {
return m_File;

View File

@ -24,7 +24,7 @@
#include "core/copytargetfile.h"
#include "fs/filesystem.h"
#include "util/externalcommand.h"
#include "util/report.h"
#include <KLocalizedString>
@ -63,8 +63,11 @@ bool BackupFileSystemJob::run(Report& parent)
report->line() << xi18nc("@info:progress", "Could not open file system on source partition <filename>%1</filename> for backup.", sourcePartition().deviceNode());
else if (!copyTarget.open())
report->line() << xi18nc("@info:progress", "Could not create backup file <filename>%1</filename>.", fileName());
else
rval = copyBlocks(*report, copyTarget, copySource);
else {
ExternalCommand copyCmd(copySource, copyTarget, QProcess::SeparateChannels);
connect(&copyCmd, &ExternalCommand::progress, this, [=] (int percent) { emit progress(percent); }, Qt::QueuedConnection);
rval = copyCmd.startCopyBlocks(-1);
}
}
jobFinished(*report, rval);

View File

@ -22,7 +22,7 @@
#include "jobs/job.h"
#include <QString>
#include <QDebug>
class Partition;
class Device;
class Report;

View File

@ -17,6 +17,11 @@
*************************************************************************/
#include "backend/corebackendmanager.h"
#include "core/device.h"
#include "core/copysource.h"
#include "core/copytarget.h"
#include "core/copysourcedevice.h"
#include "core/copytargetdevice.h"
#include "util/externalcommand.h"
#include "util/report.h"
@ -32,6 +37,92 @@
#include <KAuth>
#include <KLocalizedString>
ExternalCommand::ExternalCommand(CopySource& source, CopyTarget& target,const QProcess::ProcessChannelMode processChannelMode) :
m_Source(&source),
m_Target(&target),
m_ExitCode(-1)
{
setup(processChannelMode);
}
/** Starts copyBlocks command.
@param timeout timeout to wait for the process to start
@return true on success
*/
bool ExternalCommand::startCopyBlocks(int timeout)
{
this->moveToThread(CoreBackendManager::self()->kauthThread());
QTimer::singleShot(0, this, &ExternalCommand::copyBlocks);
QEventLoop loop;
connect(this, &ExternalCommand::finished, &loop, &QEventLoop::quit);
loop.exec();
return true;
}
bool ExternalCommand::copyBlocks()
{
bool rval = true;
qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy
qint64 blocksToCopy = m_Source->length() / blockSize;
qint64 readOffset = m_Source->firstByte();
qint64 writeOffset = m_Target->firstByte();
qint32 copyDirection = 1;
if (m_Target->firstByte() > m_Source->firstByte()) {
readOffset = m_Source->firstByte() + m_Source->length() - blockSize;
writeOffset = m_Target->firstByte() + m_Source->length() - blockSize;
copyDirection = -1;
}
qint64 lastBlock = m_Source->length() % blockSize;
//report()->line() << xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, m_source.length(), readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") : i18nc("direction: right", "right"));
QString cmd = QStandardPaths::findExecutable(QStringLiteral("dd"));
if (cmd.isEmpty())
cmd = QStandardPaths::findExecutable(QStringLiteral("dd"), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") });
qDebug() << "ExternalCommand::copyBlocks\n";
KAuth::Action action(QStringLiteral("org.kde.kpmcore.externalcommand.copyblockshelper"));
action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand"));
arguments.insert(QStringLiteral("command"), cmd);
arguments.insert(QStringLiteral("sourceDevice"), m_Source->path() );
arguments.insert(QStringLiteral("targetDevice"), m_Target->path());
arguments.insert(QStringLiteral("blockSize"), blockSize);
arguments.insert(QStringLiteral("blocksToCopy"), blocksToCopy);
arguments.insert(QStringLiteral("readOffset"), readOffset);
arguments.insert(QStringLiteral("writeOffset"), writeOffset);
arguments.insert(QStringLiteral("copyDirection"), copyDirection);
arguments.insert(QStringLiteral("sourceFirstByte"), m_Source->firstByte());
arguments.insert(QStringLiteral("targetFirstByte"), m_Target->firstByte());
arguments.insert(QStringLiteral("lastBlock"), lastBlock);
action.setArguments(arguments);
action.setTimeout(24 * 3600 * 1000); // set 1 day DBus timeout
KAuth::ExecuteJob *job = action.execute();
// TODO KF6:Use new signal-slot syntax
connect(job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long)));
if (!job->exec()) {
qWarning() << "KAuth returned an error code: " << job->errorString();
// return false;
emit finished();
return false;
}
m_Output = job->data()[QStringLiteral("output")].toByteArray();
setExitCode(job->data()[QStringLiteral("exitCode")].toInt());
qDebug() << "ExternalCommand::copyBlocks finished";
emit finished();
return true;
}
/** Creates a new ExternalCommand instance without Report.
@param cmd the command to run
@param args the arguments to pass to the command
@ -74,6 +165,7 @@ void ExternalCommand::setup(const QProcess::ProcessChannelMode processChannelMod
@param timeout timeout to wait for the process to start
@return true on success
*/
bool ExternalCommand::start(int timeout)
{
this->moveToThread(CoreBackendManager::self()->kauthThread());
@ -149,6 +241,7 @@ bool ExternalCommand::waitFor(int timeout)
}*/
// onReadOutput();
return true;
}

View File

@ -20,6 +20,10 @@
#define KPMCORE_EXTERNALCOMMAND_H
#include "util/libpartitionmanagerexport.h"
#include "core/copysourcedevice.h"
#include "core/copytargetfile.h"
#include <KJob>
#include <QDebug>
#include <QtGlobal>
@ -45,8 +49,10 @@ class LIBKPMCORE_EXPORT ExternalCommand : public QObject
public:
explicit ExternalCommand(const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels);
explicit ExternalCommand(Report& report, const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels);
explicit ExternalCommand(CopySource& source, CopyTarget& target, QProcess::ProcessChannelMode processChannelMode = QProcess::MergedChannels);
public:
bool copyBlocks();
void setCommand(const QString& cmd) { m_Command = cmd; } /**< @param cmd the command to run */
const QString& command() const { return m_Command; } /**< @return the command to run */
@ -55,6 +61,7 @@ public:
void setArgs(const QStringList& args) { m_Args = args; } /**< @param args the new arguments */
bool write(const QByteArray& input); /**< @param input the input for the program */
bool startCopyBlocks(int timeout =30000);
bool start(int timeout = 30000);
bool waitFor(int timeout = 30000);
bool run(int timeout = 30000);
@ -76,8 +83,12 @@ public:
}
Q_SIGNALS:
void progress(int);
void finished();
public Q_SLOTS:
void emitProgress(KJob*, unsigned long percent) { emit progress(percent); };
protected:
void execute();
@ -98,6 +109,8 @@ private:
int m_ExitCode;
QByteArray m_Output;
QByteArray m_Input;
CopySource *m_Source;
CopyTarget *m_Target;
};
#endif

View File

@ -18,6 +18,134 @@
#include "externalcommandhelper.h"
#include <QDebug>
#include <QString>
bool ExternalCommandHelper::readData(QByteArray& buffer, qint64 offset, qint64 size)
{
QStringList arguments = {
QStringLiteral("skip=") + QString::number(offset),
QStringLiteral("bs=") + QString::number(size),
QStringLiteral("count=1"),
QStringLiteral("iflag=skip_bytes"),
QStringLiteral("if=") + sourceDevice
};
cmd.start(command, arguments);
cmd.waitForFinished(-1);
if (cmd.exitCode() == 0) {
buffer = cmd.readAllStandardOutput();
return true;
}
qDebug() << sourceDevice << " " << offset << " " << size << " cmd exitCode " << cmd.exitCode() << "\n";
return false;
}
bool ExternalCommandHelper::writeData(QByteArray& buffer, qint64 offset)
{
QStringList arguments = {
QStringLiteral("of=") + targetDevice,
QStringLiteral("seek=") + QString::number(offset),
QStringLiteral("bs=1M"),
QStringLiteral("oflag=seek_bytes"),
QStringLiteral("conv=fsync") };
cmd.start(command, arguments);
cmd.write(buffer);
cmd.closeWriteChannel();
cmd.waitForFinished(-1);
if (cmd.exitCode() == 0 ) {
return true;
}
qDebug() << "cmd exitCode "<<cmd.exitCode() << "\n";
return false;
}
ActionReply ExternalCommandHelper::copyblockshelper(const QVariantMap& args)
{
qDebug() << "ExternalCommandHelper::copyBlocksHelper\n";
command = args[QStringLiteral("command")].toString();
qint64 blockSize = args[QStringLiteral("blockSize")].toInt();
qint64 blocksToCopy = args[QStringLiteral("blocksToCopy")].toInt();
qint64 readOffset = args[QStringLiteral("readOffset")].toInt();
qint64 writeOffset = args[QStringLiteral("writeOffset")].toInt();
qint32 copyDirection = args[QStringLiteral("copyDirection")].toInt();
sourceDevice = args[QStringLiteral("sourceDevice")].toString();
targetDevice = args[QStringLiteral("targetDevice")].toString();
qint64 lastBlock = args[QStringLiteral("lastBlock")].toInt();
qint64 sourceFirstByte = args[QStringLiteral("sourceFirstByte")].toInt();
qint64 targetFirstByte = args[QStringLiteral("targetFirstByte")].toInt();
QStringList environment = args[QStringLiteral("environment")].toStringList();
//qDebug() << command<< " " << sourceDevice <<" " << targetDevice << " blocksToCopy: " << blocksToCopy << " blockSize" << blockSize << " " << readOffset<<" "<<writeOffset<<" "<<environment<<"\n";
ActionReply reply;
//connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput);
QByteArray buffer;
cmd.setEnvironment(environment);
qint64 blocksCopied = 0;
//QByteArray buffer;
int percent = 0;
//QTime t;
bool rval = true;
//qDebug() << command <<"\n";
while (blocksCopied < blocksToCopy) {
if (!(rval = readData(buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize)))
break;
if (!(rval = writeData(buffer, writeOffset + blockSize * blocksCopied * copyDirection)))
break;
//qDebug() << "Exit code" <<rval <<"\n";
if (++blocksCopied * 100 / blocksToCopy != percent) {
percent = blocksCopied * 100 / blocksToCopy;
//if (percent % 5 == 0 && t.elapsed() > 1000) {
// const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (t.elapsed() / 1000);
// const qint64 estSecsLeft = (100 - percent) * t.elapsed() / percent / 1000;
// report.line() << xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString());
//}
HelperSupport::progressStep(percent);
}
}
//const qint64 lastBlock = args[QStringLiteral("lastBlock")];
// copy the remainder
if (rval && lastBlock > 0) {
Q_ASSERT(lastBlock < blockSize);
const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : sourceFirstByte;
const qint64 lastBlockWriteOffset = copyDirection > 0 ? writeOffset + blockSize * blocksCopied : targetFirstByte;
//report.line() << xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset);
rval = readData(buffer, lastBlockReadOffset, lastBlock);
if (rval)
rval = writeData(buffer, lastBlockWriteOffset);
if (rval){
qDebug() << "Percent 100"<<"\n";
emit progress(100);
}
}
//report.line() << 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", target.bytesWritten()));
return reply;
}
ActionReply ExternalCommandHelper::start(const QVariantMap& args)
{

View File

@ -21,6 +21,7 @@
#include <KAuth>
#include <QString>
#include <QProcess>
using namespace KAuth;
@ -29,12 +30,23 @@ class ExternalCommandHelper : public QObject
{
Q_OBJECT
Q_SIGNALS:
void progress(int);
public:
bool readData(QByteArray& buffer, qint64 offset, qint64 size);
bool writeData(QByteArray& buffer, qint64 offset);
public Q_SLOTS:
ActionReply start(const QVariantMap& args);
ActionReply copyblockshelper(const QVariantMap& args);
private:
void onReadOutput();
QString command;
QString sourceDevice;
QString targetDevice;
QProcess cmd;
// QByteArray output;
};

View File

@ -3,3 +3,9 @@ Name=Run external command action
Description=Run external command as privileged user
Policy=auth_admin
Persistence=session
[org.kde.kpmcore.externalcommand.copyblockshelper]
Name=copy blocks external command action
Description=copy blocks external command as privileged user
Policy=auth_admin
Persistence=session