From e42a5c928964a79d11c46167cd73652bb50cece9 Mon Sep 17 00:00:00 2001 From: Huzaifa Faruqui Date: Wed, 24 Jan 2018 15:22:42 +0000 Subject: [PATCH] Implement copyblocks function in KAuth helper. Switch BackupFileSystemJob to thenew copyblocks function. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed and cleaned up by: Andrius Štikonas --- src/core/copysource.h | 2 + src/core/copysourcedevice.cpp | 5 + src/core/copysourcedevice.h | 6 +- src/core/copysourcefile.h | 3 + src/core/copysourceshred.h | 4 + src/core/copytarget.h | 3 +- src/core/copytargetdevice.cpp | 6 +- src/core/copytargetdevice.h | 1 + src/core/copytargetfile.h | 4 + src/jobs/backupfilesystemjob.cpp | 9 +- src/jobs/backupfilesystemjob.h | 2 +- src/util/externalcommand.cpp | 93 +++++++++++++ src/util/externalcommand.h | 13 ++ src/util/externalcommandhelper.cpp | 128 ++++++++++++++++++ src/util/externalcommandhelper.h | 12 ++ .../org.kde.kpmcore.externalcommand.actions | 6 + 16 files changed, 289 insertions(+), 8 deletions(-) diff --git a/src/core/copysource.h b/src/core/copysource.h index d1d9ae6..3f25e7f 100644 --- a/src/core/copysource.h +++ b/src/core/copysource.h @@ -22,6 +22,7 @@ #include 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; diff --git a/src/core/copysourcedevice.cpp b/src/core/copysourcedevice.cpp index a0b9d1b..3b91744 100644 --- a/src/core/copysourcedevice.cpp +++ b/src/core/copysourcedevice.cpp @@ -103,3 +103,8 @@ bool CopySourceDevice::overlaps(const CopyTarget& target) const return false; } + +QString CopySourceDevice::path() const +{ + return m_Device.deviceNode(); +} diff --git a/src/core/copysourcedevice.h b/src/core/copysourcedevice.h index 23f060f..04f1734 100644 --- a/src/core/copysourcedevice.h +++ b/src/core/copysourcedevice.h @@ -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 diff --git a/src/core/copysourcefile.h b/src/core/copysourcefile.h index bad5894..0ff8ff7 100644 --- a/src/core/copysourcefile.h +++ b/src/core/copysourcefile.h @@ -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() { diff --git a/src/core/copysourceshred.h b/src/core/copysourceshred.h index df83366..e293531 100644 --- a/src/core/copysourceshred.h +++ b/src/core/copysourceshred.h @@ -24,6 +24,7 @@ #include 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() { diff --git a/src/core/copytarget.h b/src/core/copytarget.h index e68d715..97385a1 100644 --- a/src/core/copytarget.h +++ b/src/core/copytarget.h @@ -21,6 +21,7 @@ #include +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; } diff --git a/src/core/copytargetdevice.cpp b/src/core/copytargetdevice.cpp index e80876c..92e67f2 100644 --- a/src/core/copytargetdevice.cpp +++ b/src/core/copytargetdevice.cpp @@ -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(); +} diff --git a/src/core/copytargetdevice.h b/src/core/copytargetdevice.h index 062acf7..556b75e 100644 --- a/src/core/copytargetdevice.h +++ b/src/core/copytargetdevice.h @@ -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; diff --git a/src/core/copytargetfile.h b/src/core/copytargetfile.h index 3e3ef3e..17ecff2 100644 --- a/src/core/copytargetfile.h +++ b/src/core/copytargetfile.h @@ -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; diff --git a/src/jobs/backupfilesystemjob.cpp b/src/jobs/backupfilesystemjob.cpp index bf72e2f..b1995f3 100644 --- a/src/jobs/backupfilesystemjob.cpp +++ b/src/jobs/backupfilesystemjob.cpp @@ -24,7 +24,7 @@ #include "core/copytargetfile.h" #include "fs/filesystem.h" - +#include "util/externalcommand.h" #include "util/report.h" #include @@ -63,8 +63,11 @@ bool BackupFileSystemJob::run(Report& parent) report->line() << xi18nc("@info:progress", "Could not open file system on source partition %1 for backup.", sourcePartition().deviceNode()); else if (!copyTarget.open()) report->line() << xi18nc("@info:progress", "Could not create backup file %1.", fileName()); - else - rval = copyBlocks(*report, copyTarget, copySource); + else { + ExternalCommand copyCmd(copySource, copyTarget, QProcess::SeparateChannels); + connect(©Cmd, &ExternalCommand::progress, this, [=] (int percent) { emit progress(percent); }, Qt::QueuedConnection); + rval = copyCmd.startCopyBlocks(-1); + } } jobFinished(*report, rval); diff --git a/src/jobs/backupfilesystemjob.h b/src/jobs/backupfilesystemjob.h index 67a9c92..89f4fb6 100644 --- a/src/jobs/backupfilesystemjob.h +++ b/src/jobs/backupfilesystemjob.h @@ -22,7 +22,7 @@ #include "jobs/job.h" #include - +#include class Partition; class Device; class Report; diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index 6ad4971..75e7e3e 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -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 #include + +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; } diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index ee9cea4..811b6f8 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -20,6 +20,10 @@ #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" +#include "core/copysourcedevice.h" +#include "core/copytargetfile.h" + +#include #include #include @@ -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 diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp index 30a719b..1735f0f 100644 --- a/src/util/externalcommandhelper.cpp +++ b/src/util/externalcommandhelper.cpp @@ -18,6 +18,134 @@ #include "externalcommandhelper.h" #include +#include + +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 "< 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) { diff --git a/src/util/externalcommandhelper.h b/src/util/externalcommandhelper.h index 371a3d9..5d2d28d 100644 --- a/src/util/externalcommandhelper.h +++ b/src/util/externalcommandhelper.h @@ -21,6 +21,7 @@ #include +#include #include 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; }; diff --git a/src/util/org.kde.kpmcore.externalcommand.actions b/src/util/org.kde.kpmcore.externalcommand.actions index 0a00710..4d935b2 100644 --- a/src/util/org.kde.kpmcore.externalcommand.actions +++ b/src/util/org.kde.kpmcore.externalcommand.actions @@ -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