diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bf10203..2b6365a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,63 +1,63 @@ # Copyright (C) 2008, 2012 by Volker Lanz # Copyright (C) 2015 by Teo Mrnjavac # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################ add_definitions(-DTRANSLATION_DOMAIN=\"kpmcore\") include(backend/CMakeLists.txt) include(core/CMakeLists.txt) include(util/CMakeLists.txt) include(ops/CMakeLists.txt) include(jobs/CMakeLists.txt) include(fs/CMakeLists.txt) include(gui/CMakeLists.txt) set(kpmcore_SRCS ${BACKEND_SRC} ${FS_SRC} ${CORE_SRC} ${OPS_SRC} ${JOBS_SRC} ${UTIL_SRC} ${GUI_SRC} ) ki18n_wrap_ui(kpmcore_SRCS ${gui_UIFILES}) add_library(kpmcore SHARED ${kpmcore_SRCS}) -target_link_libraries( kpmcore +target_link_libraries( kpmcore PUBLIC ${UUID_LIBRARIES} ${BLKID_LIBRARIES} Qt5::DBus KF5::Auth KF5::I18n KF5::CoreAddons KF5::WidgetsAddons ) install(TARGETS kpmcore EXPORT KPMcoreTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CORE_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/core/ COMPONENT Devel) install(FILES ${BACKEND_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/backend/ COMPONENT Devel) install(FILES ${FS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/fs/ COMPONENT Devel) install(FILES ${JOBS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/jobs/ COMPONENT Devel) install(FILES ${OPS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/ops/ COMPONENT Devel) install(FILES ${UTIL_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/util/ COMPONENT Devel) install(FILES ${GUI_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/gui/ COMPONENT Devel) ############################################ add_subdirectory(plugins) diff --git a/src/backend/corebackendmanager.cpp b/src/backend/corebackendmanager.cpp index bde70a6..219a899 100644 --- a/src/backend/corebackendmanager.cpp +++ b/src/backend/corebackendmanager.cpp @@ -1,118 +1,125 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "backend/corebackendmanager.h" #include "backend/corebackend.h" #include #include #include #include #include #include #include #include #include #include #include #include #include CoreBackendManager::CoreBackendManager() : m_Backend(nullptr) { m_KAuthThread = new QThread(); kauthThread()->start(); } CoreBackendManager* CoreBackendManager::self() { static CoreBackendManager* instance = nullptr; if (instance == nullptr) instance = new CoreBackendManager; return instance; } QVector CoreBackendManager::list() const { auto filter = [&](const KPluginMetaData &metaData) { return metaData.serviceTypes().contains(QStringLiteral("PartitionManager/Plugin")) && metaData.category().contains(QStringLiteral("BackendPlugin")); }; // find backend plugins in standard path (e.g. /usr/lib64/qt5/plugins) using filter from above return KPluginLoader::findPlugins(QString(), filter); } void CoreBackendManager::startExternalCommandHelper() { KAuth::Action action = KAuth::Action(QStringLiteral("org.kde.kpmcore.externalcommand.init")); action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); action.setTimeout(10 * 24 * 3600 * 1000); // 10 days QVariantMap arguments; m_Uuid = QUuid::createUuid().toString(); arguments.insert(QStringLiteral("callerUuid"), Uuid()); action.setArguments(arguments); - KAuth::ExecuteJob *job = action.execute(); - job->start(); + m_job = action.execute(); + job()->start(); QEventLoop loop; - QObject::connect(job, &KAuth::ExecuteJob::newData, [&] () {loop.exit();}); + auto exitLoop = [&] () {loop.exit();}; + auto conn = QObject::connect(job(), &KAuth::ExecuteJob::newData, exitLoop); loop.exec(); + QObject::disconnect(conn); + +} + +KAuth::ExecuteJob* CoreBackendManager::job() { + return m_job; } bool CoreBackendManager::load(const QString& name) { if (backend()) unload(); KPluginLoader loader(name); KPluginFactory* factory = loader.factory(); if (factory != nullptr) { m_Backend = factory->create(nullptr); QString id = loader.metaData().toVariantMap().value(QStringLiteral("MetaData")) .toMap().value(QStringLiteral("KPlugin")).toMap().value(QStringLiteral("Id")).toString(); QString version = loader.metaData().toVariantMap().value(QStringLiteral("MetaData")) .toMap().value(QStringLiteral("KPlugin")).toMap().value(QStringLiteral("Version")).toString(); if (id.isEmpty()) return false; backend()->setId(id); backend()->setVersion(version); qDebug() << "Loaded backend plugin: " << backend()->id(); startExternalCommandHelper(); return true; } qWarning() << "Could not load plugin for core backend " << name << ": " << loader.errorString(); return false; } void CoreBackendManager::unload() { delete m_Backend; m_Backend = nullptr; } diff --git a/src/backend/corebackendmanager.h b/src/backend/corebackendmanager.h index d4f1351..ed04bd7 100644 --- a/src/backend/corebackendmanager.h +++ b/src/backend/corebackendmanager.h @@ -1,106 +1,114 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_COREBACKENDMANAGER_H) #define KPMCORE_COREBACKENDMANAGER_H #include "util/libpartitionmanagerexport.h" +#include + #include #include class QThread; class QString; class QStringList; class KPluginMetaData; class CoreBackend; /** * The backend manager class. * * This is basically a singleton class to give the application access to the currently * selected backend and also to manage the available backend plugins. * @author Volker Lanz */ class LIBKPMCORE_EXPORT CoreBackendManager { private: CoreBackendManager(); public: /** * @return pointer to ourselves */ static CoreBackendManager* self(); /** * @return the name of the default backend plugin */ static QString defaultBackendName() { return QStringLiteral("pmsfdiskbackendplugin"); } /** * @return a list of available backend plugins */ QVector list() const; /** * Loads the given backend plugin into the application. * @param name the name of the plugin to load * @return true on success */ bool load(const QString& name); /** * Unload the current plugin. */ void unload(); /** * @return a pointer to the currently loaded backend */ CoreBackend* backend() { return m_Backend; } /** * @return a pointer to the thread where ExternalCommand will start KAuth job */ QThread* kauthThread() { return m_KAuthThread; } /** * @return a pointer to the currently loaded backend */ QString& Uuid() { return m_Uuid; } + /** + * @return a pointer to the KAuth job + */ + KAuth::ExecuteJob* job(); + private: void startExternalCommandHelper(); private: - CoreBackend* m_Backend; - QThread* m_KAuthThread; + CoreBackend *m_Backend; + QThread *m_KAuthThread; + KAuth::ExecuteJob *m_job; QString m_Uuid; }; #endif diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index ba95df0..790eb05 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,266 +1,235 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #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" #include #include #include #include #include #include #include #include #include #include #include #include ExternalCommand::ExternalCommand(CopySource& source, CopyTarget& target,const QProcess::ProcessChannelMode processChannelMode) : m_ExitCode(-1), m_Source(&source), m_Target(&target) { setup(processChannelMode); } /** Starts copyBlocks command. */ bool ExternalCommand::startCopyBlocks() { 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; + const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy - 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; + if (!QDBusConnection::systemBus().isConnected()) { + qWarning() << "Could not connect to DBus system bus"; + return false; } - 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/") }); - - 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); - arguments.insert(QStringLiteral("sourceLength"), m_Source->length()); - - 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))); - connect(job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); + connect(CoreBackendManager::self()->job(), SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); + connect(CoreBackendManager::self()->job(), &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); - if (!job->exec()) { - qWarning() << "KAuth returned an error code: " << job->errorString(); -// return false; - emit finished(); - return false; + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); + if (iface.isValid()) { + QDBusReply reply = iface.call(QStringLiteral("copyblocks"), CoreBackendManager::self()->Uuid(), m_Source->path(), m_Source->firstByte(), m_Source->length(), m_Target->path(), m_Target->firstByte(), blockSize); + if (reply.isValid()) { + rval = reply.value()[QStringLiteral("success")].toInt(); + qDebug() << rval; + } + else { + qWarning() << reply.error().message(); + } } - rval = job->data()[QStringLiteral("success")].toInt(); - setExitCode(!rval); - emit finished(); + setExitCode(!rval); return rval; } /** Creates a new ExternalCommand instance without Report. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : m_Report(nullptr), m_Command(cmd), m_Args(args), m_ExitCode(-1), m_Output() { setup(processChannelMode); } /** Creates a new ExternalCommand instance with Report. @param report the Report to write output to. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(Report& report, const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : m_Report(report.newChild()), m_Command(cmd), m_Args(args), m_ExitCode(-1), m_Output() { setup(processChannelMode); } void ExternalCommand::setup(const QProcess::ProcessChannelMode processChannelMode) { arguments.insert(QStringLiteral("environment"), QStringList() << QStringLiteral("LC_ALL=C") << QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1")); arguments.insert(QStringLiteral("processChannelMode"), processChannelMode); // connect(this, qOverload(&QProcess::finished), this, &ExternalCommand::onFinished); // connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); } /** Starts the external command. @param timeout timeout to wait for the process to start @return true on success */ bool ExternalCommand::start(int timeout) { // this->moveToThread(CoreBackendManager::self()->kauthThread()); // QTimer::singleShot(0, this, &ExternalCommand::execute); // QEventLoop loop; // connect(this, &ExternalCommand::finished, &loop, &QEventLoop::quit); // loop.exec(); // return true; execute(); return true; } /** Executes the external command in kauthThread() thread. */ void ExternalCommand::execute() { if (report()) { report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); } QString cmd = QStandardPaths::findExecutable(command()); if (cmd.isEmpty()) cmd = QStandardPaths::findExecutable(command(), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); if (!QDBusConnection::systemBus().isConnected()) { - qWarning() << "Could not connect to DBus session bus"; + qWarning() << "Could not connect to DBus system bus"; return; } QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); if (iface.isValid()) { QDBusReply reply = iface.call(QStringLiteral("start"), CoreBackendManager::self()->Uuid(), cmd, args(), m_Input, QStringList()); if (reply.isValid()) { m_Output = reply.value()[QStringLiteral("output")].toByteArray(); setExitCode(reply.value()[QStringLiteral("exitCode")].toInt()); } else { qWarning() << reply.error().message(); } } emit finished(); } bool ExternalCommand::write(const QByteArray& input) { m_Input = input; return true; } /** Waits for the external command to finish. @param timeout timeout to wait until the process finishes. @return true on success */ bool ExternalCommand::waitFor(int timeout) { // closeWriteChannel(); /* if (!waitForFinished(timeout)) { if (report()) report()->line() << xi18nc("@info:status", "(Command timeout while running)"); return false; }*/ // onReadOutput(); return true; } /** Runs the command. @param timeout timeout to use for waiting when starting and when waiting for the process to finish @return true on success */ bool ExternalCommand::run(int timeout) { return start(timeout) && waitFor(timeout)/* && exitStatus() == 0*/; } void ExternalCommand::onReadOutput() { // const QByteArray s = readAllStandardOutput(); // // if(m_Output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems // if (report()) // report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); // return; // } // // m_Output += s; // // if (report()) // *report() << QString::fromLocal8Bit(s); } void ExternalCommand::onFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus) setExitCode(exitCode); } diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp index ff3c72e..76cf992 100644 --- a/src/util/externalcommandhelper.cpp +++ b/src/util/externalcommandhelper.cpp @@ -1,244 +1,249 @@ /************************************************************************* * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "externalcommandhelper.h" #include #include #include #include #include #include #include #include /** Initialize ExternalCommandHelper Daemon and prepare DBus interface */ ActionReply ExternalCommandHelper::init(const QVariantMap& args) { ActionReply reply; if (!QDBusConnection::systemBus().isConnected()) { qWarning() << "Could not connect to DBus session bus"; reply.addData(QStringLiteral("success"), false); return reply; } m_callerUuid = args[QStringLiteral("callerUuid")].toString(); if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface"))) { qWarning() << QDBusConnection::systemBus().lastError().message(); reply.addData(QStringLiteral("success"), false); return reply; } QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots); HelperSupport::progressStep(QVariantMap()); m_loop.exec(); reply.addData(QStringLiteral("success"), true); return reply; } /** Reads the given number of bytes from the sourceDevice into the given buffer. @param sourceDevice device or file to read from @param buffer buffer to store the bytes read in @param offset offset where to begin reading @param size the number of bytes to read @return true on success */ -bool ExternalCommandHelper::readData(QString& sourceDevice, QByteArray& buffer, qint64 offset, qint64 size) +bool ExternalCommandHelper::readData(const QString& sourceDevice, QByteArray& buffer, qint64 offset, qint64 size) { QFile device(sourceDevice); if (!device.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) { qCritical() << xi18n("Could not open device %1 for reading.", sourceDevice); return false; } if (!device.seek(offset)) { qCritical() << xi18n("Could not seek position %1 on device %1.", sourceDevice); return false; } buffer = device.read(size); if (size != buffer.size()) { qCritical() << xi18n("Could not read from device %1.", sourceDevice); return false; } return true; } /** Writes the data from buffer to a given device or file. @param targetDevice device or file to write to @param buffer the data that we write @param offset offset where to begin writing @return true on success */ -bool ExternalCommandHelper::writeData(QString &targetDevice, QByteArray& buffer, qint64 offset) +bool ExternalCommandHelper::writeData(const QString &targetDevice, const QByteArray& buffer, qint64 offset) { QFile device(targetDevice); if (!device.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered)) { qCritical() << xi18n("Could not open device %1 for writing.", targetDevice); return false; } if (!device.seek(offset)) { qCritical() << xi18n("Could not seek position %1 on device %1.", targetDevice); return false; } if (device.write(buffer) != buffer.size()) { qCritical() << xi18n("Could not write to device %1.", targetDevice); return false; } return true; } -ActionReply ExternalCommandHelper::copyblockshelper(const QVariantMap& args) +QVariantMap ExternalCommandHelper::copyblocks(const QString& Uuid, const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize) { - m_command = args[QStringLiteral("command")].toString(); - qint64 blockSize = args[QStringLiteral("blockSize")].toLongLong(); - qint64 blocksToCopy = args[QStringLiteral("blocksToCopy")].toLongLong(); - qint64 readOffset = args[QStringLiteral("readOffset")].toLongLong(); - qint64 writeOffset = args[QStringLiteral("writeOffset")].toLongLong(); - qint32 copyDirection = args[QStringLiteral("copyDirection")].toLongLong(); - QString sourceDevice = args[QStringLiteral("sourceDevice")].toString(); - QString targetDevice = args[QStringLiteral("targetDevice")].toString(); - qint64 lastBlock = args[QStringLiteral("lastBlock")].toLongLong(); - qint64 sourceFirstByte = args[QStringLiteral("sourceFirstByte")].toLongLong(); - qint64 targetFirstByte = args[QStringLiteral("targetFirstByte")].toLongLong(); - qint64 sourceLength = args[QStringLiteral("sourceLength")].toLongLong(); - - QStringList environment = args[QStringLiteral("environment")].toStringList(); + isCallerAuthorized(Uuid); + QVariantMap reply; - ActionReply reply; + const qint64 blocksToCopy = sourceLength / blockSize; + qint64 readOffset = sourceFirstByte; + qint64 writeOffset = targetFirstByte; + qint32 copyDirection = 1; - m_cmd.setEnvironment(environment); + if (targetFirstByte > sourceFirstByte) { + readOffset = sourceFirstByte + sourceLength - blockSize; + writeOffset = targetFirstByte + sourceLength - blockSize; + copyDirection = -1; + } + + const qint64 lastBlock = sourceLength % blockSize; qint64 bytesWritten = 0; qint64 blocksCopied = 0; QByteArray buffer; int percent = 0; QTime t; t.start(); QVariantMap report; report[QStringLiteral("report")] = xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, sourceLength, readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") : i18nc("direction: right", "right")); HelperSupport::progressStep(report); bool rval = true; while (blocksCopied < blocksToCopy) { if (!(rval = readData(sourceDevice, buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize))) break; if (!(rval = writeData(targetDevice, buffer, writeOffset + blockSize * blocksCopied * copyDirection))) break; bytesWritten += buffer.size(); 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[QStringLiteral("report")]= xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString()); HelperSupport::progressStep(report); } HelperSupport::progressStep(percent); } } // 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[QStringLiteral("report")]= xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset); HelperSupport::progressStep(report); rval = readData(sourceDevice, buffer, lastBlockReadOffset, lastBlock); if (rval) rval = writeData(targetDevice, buffer, lastBlockWriteOffset); if (rval) { HelperSupport::progressStep(100); bytesWritten += buffer.size(); } } report[QStringLiteral("report")] = 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)); HelperSupport::progressStep(report); - reply.addData(QStringLiteral("success"), rval); + reply[QStringLiteral("success")] = rval; return reply; } QVariantMap ExternalCommandHelper::start(const QString& Uuid, const QString& command, const QStringList& arguments, const QByteArray& input, const QStringList& environment) { + isCallerAuthorized(Uuid); QVariantMap reply; - if (Uuid != m_callerUuid) { - qWarning() << "Caller is not authorized"; - return reply; - } // connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput); m_cmd.setEnvironment(environment); m_cmd.start(command, arguments); m_cmd.write(input); m_cmd.closeWriteChannel(); m_cmd.waitForFinished(-1); QByteArray output = m_cmd.readAllStandardOutput(); reply[QStringLiteral("output")] = output; reply[QStringLiteral("exitCode")] = m_cmd.exitCode(); return reply; } +bool ExternalCommandHelper::isCallerAuthorized(const QString& Uuid) +{ + if (Uuid != m_callerUuid) { + qWarning() << "Caller is not authorized"; + return false; + } + + return true; +} + void ExternalCommandHelper::exit(const QString& Uuid) { + isCallerAuthorized(Uuid); m_loop.exit(); } void ExternalCommandHelper::onReadOutput() { // const QByteArray s = cmd.readAllStandardOutput(); // if(output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems // if (report()) // report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); // return; // } // output += s; // if (report()) // *report() << QString::fromLocal8Bit(s); } KAUTH_HELPER_MAIN("org.kde.kpmcore.externalcommand", ExternalCommandHelper) diff --git a/src/util/externalcommandhelper.h b/src/util/externalcommandhelper.h index 0f05eec..7714de3 100644 --- a/src/util/externalcommandhelper.h +++ b/src/util/externalcommandhelper.h @@ -1,59 +1,60 @@ /************************************************************************* * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #ifndef KPMCORE_EXTERNALCOMMANDHELPER_H #define KPMCORE_EXTERNALCOMMANDHELPER_H #include #include #include #include using namespace KAuth; class ExternalCommandHelper : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.externalcommand") Q_SIGNALS: void progress(int); void quit(); public: - bool readData(QString& sourceDevice, QByteArray& buffer, qint64 offset, qint64 size); - bool writeData(QString& targetDevice, QByteArray& buffer, qint64 offset); + bool readData(const QString& sourceDevice, QByteArray& buffer, qint64 offset, qint64 size); + bool writeData(const QString& targetDevice, const QByteArray& buffer, qint64 offset); public Q_SLOTS: ActionReply init(const QVariantMap& args); Q_SCRIPTABLE QVariantMap start(const QString& Uuid, const QString& command, const QStringList& arguments, const QByteArray& input, const QStringList& environment); + Q_SCRIPTABLE QVariantMap copyblocks(const QString& Uuid, const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize); Q_SCRIPTABLE void exit(const QString& Uuid); - ActionReply copyblockshelper(const QVariantMap& args); private: void onReadOutput(); + bool isCallerAuthorized(const QString& Uuid); QEventLoop m_loop; QString m_callerUuid; QString m_command; QString m_sourceDevice; QProcess m_cmd; // QByteArray output; }; #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 66681e1..25151b8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,63 +1,64 @@ # Tests for KPMcore # # These are not so much "tests" as "small example programs". They illustrate # how to use the library, how to perform common tasks. set(CMAKE_SKIP_BUILD_RPATH FALSE) SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) include_directories(${CMAKE_SOURCE_DIR}/src) # To get at KPMcore headers add_compile_options(-fPIC) ### # # Helper macro to link to the helper (for initialization of kpmcore) # and to add a test with the given name. # -add_library(testhelpers OBJECT helpers.cpp) +add_library(testhelpers STATIC helpers.cpp) +target_link_libraries(testhelpers KF5::Auth) macro (kpm_test name) - add_executable(${name} ${ARGN} $) - target_link_libraries(${name} kpmcore) + add_executable(${name} ${ARGN}) + target_link_libraries(${name} testhelpers kpmcore) endmacro() ### # # Tests of initialization: try explicitly loading some backends kpm_test(testinit testinit.cpp) # Default backend if(TARGET pmdummybackendplugin) add_test(NAME testinit-dummy COMMAND testinit $) endif() -if(TARGET pmlibpartedbackendplugin) - add_test(NAME testinit-parted COMMAND testinit $) +if(TARGET pmsfdiskbackendplugin) + add_test(NAME testinit-sfdisk COMMAND testinit $) else() return() # All the rest really needs a working backend endif() -set(BACKEND $) +set(BACKEND $) ### # # Listing devices, partitions kpm_test(testlist testlist.cpp) add_test(NAME testlist COMMAND testlist ${BACKEND}) kpm_test(testdevicescanner testdevicescanner.cpp) add_test(NAME testdevicescanner COMMAND testdevicescanner ${BACKEND}) find_package (Threads) ### # # Execute external commands as root kpm_test(testexternalcommand testexternalcommand.cpp) add_test(NAME testexternalcommand COMMAND testexternalcommand ${BACKEND}) # Including SMART files reference set(SMARTPARSER ${CMAKE_SOURCE_DIR}/src/core/smartdiskinformation.cpp ${CMAKE_SOURCE_DIR}/src/core/smartattributeparseddata.cpp ${CMAKE_SOURCE_DIR}/src/core/smartparser.cpp) # Test SMART support kpm_test(testsmart testsmart.cpp ${SMARTPARSER}) add_test(NAME testsmart COMMAND testsmart ${BACKEND}) diff --git a/test/testdevicescanner.cpp b/test/testdevicescanner.cpp index 88b2451..d3a39b6 100644 --- a/test/testdevicescanner.cpp +++ b/test/testdevicescanner.cpp @@ -1,115 +1,117 @@ /************************************************************************* * Copyright 2017 by Adriaan de Groot * * Copyright 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ // SPDX-License-Identifier: GPL-3.0+ // Lists devices #include "helpers.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/device.h" #include "core/devicescanner.h" #include "core/operationstack.h" #include "core/partition.h" #include "util/capacity.h" +#include #include #include using PartitionList = QList; // Recursive helper for flatten(), adds partitions that // are children of @p n to the list @p l. void _flatten(PartitionList& l, PartitionNode *n) { for (const auto &p : n->children()) { l.append(p); if (p->roles().has(PartitionRole::Extended)) { _flatten(l, p); } } } /** * Recursively walk the partition table and collect all the partitions * in it (also in extended partitions). Produces a sorted list. */ PartitionList flatten(PartitionTable *table) { PartitionList l; _flatten(l, table); std::sort(l.begin(), l.end(), [](const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); }); return l; } int main( int argc, char **argv ) { + QCoreApplication app(argc, argv); if (argc != 2) { KPMCoreInitializer i; if (!i.isValid()) return 1; } else { KPMCoreInitializer i( argv[1] ); if (!i.isValid()) return 1; } auto backend = CoreBackendManager::self()->backend(); if (!backend) { qWarning() << "Could not get backend."; return 1; } // First create operationStack with another QObject as parent. OperationStack *operationStack = new OperationStack(); DeviceScanner *deviceScanner = new DeviceScanner(nullptr, *operationStack); deviceScanner->scan(); const auto devices = operationStack->previewDevices(); qDebug() << "Found" << devices.length() << "devices."; for (const auto pdev : devices) { qDebug() << "Device @" << (void *)pdev; qDebug() << " " << pdev->prettyName(); const auto partitiontable = pdev->partitionTable(); qDebug() << " Partition Table @" << (void *)partitiontable << '(' << (partitiontable ? partitiontable->typeName() : QLatin1String("null")) << ')'; const auto partitionlist = flatten(partitiontable); for (const auto &p : partitionlist) qDebug() << " " << p->number() << p->partitionPath() << p->label() << Capacity::formatByteSize(p->capacity()) << p->fileSystem().name(); } return 0; } diff --git a/test/testinit.cpp b/test/testinit.cpp index 276c0f8..fd97f56 100644 --- a/test/testinit.cpp +++ b/test/testinit.cpp @@ -1,39 +1,42 @@ /************************************************************************* * Copyright 2017 by Adriaan de Groot * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ // SPDX-License-Identifier: GPL-3.0+ // Initializes KPMcore, and either loads the default backend for // the current platform, or if one is named on the command line, // loads that one. Returns 0 on success. +#include + #include "helpers.h" int main( int argc, char** argv ) { + QCoreApplication app(argc, argv); if ( argc != 2 ) { KPMCoreInitializer i; return i.isValid() ? 0 : 1; } else { KPMCoreInitializer i( argv[1] ); return i.isValid() ? 0 : 1; } } diff --git a/test/testlist.cpp b/test/testlist.cpp index 91ff06a..6b1c9eb 100644 --- a/test/testlist.cpp +++ b/test/testlist.cpp @@ -1,107 +1,110 @@ /************************************************************************* * Copyright 2017 by Adriaan de Groot * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ // SPDX-License-Identifier: GPL-3.0+ // Lists devices #include "helpers.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/device.h" #include "core/partition.h" #include "util/capacity.h" +#include #include #include using PartitionList = QList; // Recursive helper for flatten(), adds partitions that // are children of @p n to the list @p l. void _flatten(PartitionList& l, PartitionNode *n) { for (const auto &p : n->children()) { l.append(p); if (p->roles().has(PartitionRole::Extended)) { _flatten(l, p); } } } /** * Recursively walk the partition table and collect all the partitions * in it (also in extended partitions). Produces a sorted list. */ PartitionList flatten(PartitionTable *table) { PartitionList l; _flatten(l, table); std::sort(l.begin(), l.end(), [](const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); }); return l; } int main( int argc, char **argv ) { + QCoreApplication app(argc, argv); + if (argc != 2) { KPMCoreInitializer i; if (!i.isValid()) return 1; } else { KPMCoreInitializer i( argv[1] ); if (!i.isValid()) return 1; } auto backend = CoreBackendManager::self()->backend(); if (!backend) { qWarning() << "Could not get backend."; return 1; } const auto devices = backend->scanDevices(); qDebug() << "Found" << devices.length() << "devices."; for (const auto pdev : devices) { qDebug() << "Device @" << (void *)pdev; qDebug() << " " << pdev->prettyName(); const auto partitiontable = pdev->partitionTable(); qDebug() << " Partition Table @" << (void *)partitiontable << '(' << (partitiontable ? partitiontable->typeName() : QLatin1String("null")) << ')'; const auto partitionlist = flatten(partitiontable); for (const auto &p : partitionlist) qDebug() << " " << p->number() << p->partitionPath() << p->label() << Capacity::formatByteSize(p->capacity()) << p->fileSystem().name(); } return 0; } diff --git a/test/testsmart.cpp b/test/testsmart.cpp index ea5aa72..ad8c098 100644 --- a/test/testsmart.cpp +++ b/test/testsmart.cpp @@ -1,102 +1,102 @@ #include "helpers.h" #include "util/externalcommand.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/smartstatus.h" #include "core/smartparser.h" -#include +#include #include static QString getDefaultDevicePath(); static bool testSmartStatus(); static bool testSmartParser(); int main(int argc, char **argv) { - QApplication app(argc, argv); + QCoreApplication app(argc, argv); KPMCoreInitializer i; if (argc == 2) i = KPMCoreInitializer(argv[1]); if (!i.isValid()) return 1; CoreBackend *backend = CoreBackendManager::self()->backend(); if (!backend) { qWarning() << "Couldn't get backend."; return 1; } if (!testSmartStatus() || !testSmartParser()) return 1; return app.exec(); } static QString getDefaultDevicePath() { // Getting default home partition using 'df -P /home | awk 'END{print $1}'' command ExternalCommand command(QStringLiteral("df"), { QStringLiteral("-P"), QStringLiteral("/home"), QStringLiteral("|"), QStringLiteral("awk"), QStringLiteral("\'END{print $1}\'") }); if (command.run() && command.exitCode() == 0) { QString output = command.output(); return output; } return QString(); } static bool testSmartStatus() { QString devicePath = getDefaultDevicePath(); SmartStatus smart(devicePath); if (smart.devicePath() != devicePath) return false; if (!smart.status()) return false; if (smart.modelName() == QString()) return false; if (smart.firmware() == QString()) return false; if (smart.serial() == QString()) return false; if (smart.selfTestStatus()) return false; if (!smart.isValid()) return false; return true; } static bool testSmartParser() { QString devicePath = getDefaultDevicePath(); SmartParser parser(devicePath); if (!parser.init()) return false; if (parser.devicePath() != devicePath) return false; if (!parser.diskInformation()) return false; return true; }