Implement copyblocks function in KAuth helper.
Switch BackupFileSystemJob to thenew copyblocks function. Reviewed and cleaned up by: Andrius Štikonas
This commit is contained in:
parent
a3d43e159f
commit
e42a5c9289
|
@ -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;
|
||||
|
|
|
@ -103,3 +103,8 @@ bool CopySourceDevice::overlaps(const CopyTarget& target) const
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString CopySourceDevice::path() const
|
||||
{
|
||||
return m_Device.deviceNode();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(©Cmd, &ExternalCommand::progress, this, [=] (int percent) { emit progress(percent); }, Qt::QueuedConnection);
|
||||
rval = copyCmd.startCopyBlocks(-1);
|
||||
}
|
||||
}
|
||||
|
||||
jobFinished(*report, rval);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "jobs/job.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <QDebug>
|
||||
class Partition;
|
||||
class Device;
|
||||
class Report;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue