diff --git a/.gitignore b/.gitignore index 39e5dd6..5838c04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# SPDX-FileCopyrightText: 2020 Andrius Štikonas -# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: none +# SPDX-License-Identifier: CC0-1.0 build .kdev4/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4408e97..17a9f9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2008 Volker Lanz # SPDX-FileCopyrightText: 2015 Teo Mrnjavac # SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas +# SPDX-FileCopyrightText: 2020 David Edmundson # SPDX-License-Identifier: GPL-3.0-or-later @@ -39,6 +40,7 @@ include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(GenerateExportHeader) include(ECMSetupVersion) +include(ECMConfiguredInstall) ecm_setup_version(${VERSION} VARIABLE_PREFIX KPMCORE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpmcore_version.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce33f9d..fda245d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,7 +37,6 @@ target_link_libraries( kpmcore PUBLIC KF5::I18n KF5::CoreAddons KF5::WidgetsAddons - KF5::AuthCore ) install(TARGETS kpmcore EXPORT KPMcoreTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/core/fstab.cpp b/src/core/fstab.cpp index 2f18200..16b94fc 100644 --- a/src/core/fstab.cpp +++ b/src/core/fstab.cpp @@ -10,6 +10,7 @@ #include "util/report.h" #include +#include #if defined(Q_OS_LINUX) #include diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index f5f2b7a..60ad8bb 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -1,7 +1,7 @@ /* SPDX-FileCopyrightText: 2008-2012 Volker Lanz SPDX-FileCopyrightText: 2009 Andrew Coles - SPDX-FileCopyrightText: 2013-2019 Andrius Štikonas + SPDX-FileCopyrightText: 2013-2020 Andrius Štikonas SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac SPDX-FileCopyrightText: 2016 Chantara Tith SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho @@ -439,7 +439,7 @@ void PartitionTable::updateUnallocated(const Device& d) qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t) { Q_UNUSED(t) - if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { + if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device || t == PartitionTable::TableType::none) { return 0; } @@ -462,21 +462,21 @@ static struct { bool isReadOnly; /**< does KDE Partition Manager support this only in read only mode */ PartitionTable::TableType type; /**< enum type */ } tableTypes[] = { - { QLatin1String("aix"), 4, false, true, PartitionTable::aix }, - { QLatin1String("bsd"), 8, false, true, PartitionTable::bsd }, - { QLatin1String("dasd"), 1, false, true, PartitionTable::dasd }, - { QLatin1String("msdos"), 4, true, false, PartitionTable::msdos }, - { QLatin1String("msdos"), 4, true, false, PartitionTable::msdos_sectorbased }, - { QLatin1String("dos"), 4, true, false, PartitionTable::msdos_sectorbased }, - { QLatin1String("dvh"), 16, true, true, PartitionTable::dvh }, - { QLatin1String("gpt"), 128, false, false, PartitionTable::gpt }, - { QLatin1String("loop"), 1, false, true, PartitionTable::loop }, - { QLatin1String("mac"), 0xffff, false, true, PartitionTable::mac }, - { QLatin1String("pc98"), 16, false, true, PartitionTable::pc98 }, - { QLatin1String("amiga"), 128, false, true, PartitionTable::amiga }, - { QLatin1String("sun"), 8, false, true, PartitionTable::sun }, - { QLatin1String("vmd"), 0xffff, false, false, PartitionTable::vmd }, - { QLatin1String("none"), 1, false, false, PartitionTable::none }, + { QLatin1String("aix"), 4, false, true, PartitionTable::TableType::aix }, + { QLatin1String("bsd"), 8, false, true, PartitionTable::TableType::bsd }, + { QLatin1String("dasd"), 1, false, true, PartitionTable::TableType::dasd }, + { QLatin1String("msdos"), 4, true, false, PartitionTable::TableType::msdos }, + { QLatin1String("msdos"), 4, true, false, PartitionTable::TableType::msdos_sectorbased }, + { QLatin1String("dos"), 4, true, false, PartitionTable::TableType::msdos_sectorbased }, + { QLatin1String("dvh"), 16, true, true, PartitionTable::TableType::dvh }, + { QLatin1String("gpt"), 128, false, false, PartitionTable::TableType::gpt }, + { QLatin1String("loop"), 1, false, true, PartitionTable::TableType::loop }, + { QLatin1String("mac"), 0xffff, false, true, PartitionTable::TableType::mac }, + { QLatin1String("pc98"), 16, false, true, PartitionTable::TableType::pc98 }, + { QLatin1String("amiga"), 128, false, true, PartitionTable::TableType::amiga }, + { QLatin1String("sun"), 8, false, true, PartitionTable::TableType::sun }, + { QLatin1String("vmd"), 0xffff, false, false, PartitionTable::TableType::vmd }, + { QLatin1String("none"), 1, false, false, PartitionTable::TableType::none }, }; PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) @@ -485,7 +485,7 @@ PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) if (n == type.name) return type.type; - return PartitionTable::unknownTableType; + return PartitionTable::TableType::unknownTableType; } QString PartitionTable::tableTypeToName(TableType l) diff --git a/src/fs/fat16.cpp b/src/fs/fat16.cpp index e225178..7f79b2c 100644 --- a/src/fs/fat16.cpp +++ b/src/fs/fat16.cpp @@ -81,7 +81,7 @@ bool fat16::create(Report& report, const QString& deviceNode) bool fat16::resize(Report& report, const QString& deviceNode, qint64 length) const { - ExternalCommand cmd(report, QStringLiteral("fatresize"), { QStringLiteral("--verbose"), QStringLiteral("--size"), QString::number(length), deviceNode }); + ExternalCommand cmd(report, QStringLiteral("fatresize"), { QStringLiteral("--verbose"), QStringLiteral("--size"), QString::number(length - 1), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } diff --git a/src/fs/unknown.cpp b/src/fs/unknown.cpp index 6df09f7..156862c 100644 --- a/src/fs/unknown.cpp +++ b/src/fs/unknown.cpp @@ -12,7 +12,7 @@ namespace FS { -FileSystem::CommandSupportType unknown::m_Move = FileSystem::cmdSupportNone; +FileSystem::CommandSupportType unknown::m_Move = FileSystem::cmdSupportCore; unknown::unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, const QVariantMap& features) : FileSystem(firstsector, lastsector, sectorsused, label, features, FileSystem::Type::Unknown) diff --git a/src/jobs/createpartitionjob.cpp b/src/jobs/createpartitionjob.cpp index 15c4008..435ff88 100644 --- a/src/jobs/createpartitionjob.cpp +++ b/src/jobs/createpartitionjob.cpp @@ -1,6 +1,6 @@ /* SPDX-FileCopyrightText: 2008-2010 Volker Lanz - SPDX-FileCopyrightText: 2013-2018 Andrius Štikonas + SPDX-FileCopyrightText: 2013-2020 Andrius Štikonas SPDX-FileCopyrightText: 2016 Chantara Tith SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho SPDX-FileCopyrightText: 2019 Yuri Chornoivan @@ -40,9 +40,16 @@ bool CreatePartitionJob::run(Report& parent) Q_ASSERT(partition().devicePath() == device().deviceNode()); bool rval = false; - Report* report = jobStarted(parent); + if (device().partitionTable()->type() == PartitionTable::TableType::none) { + partition().setPartitionPath(device().deviceNode()); + partition().setState(Partition::State::None); + rval = true; + jobFinished(*report, rval); + return rval; + } + if (device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); diff --git a/src/jobs/createpartitiontablejob.cpp b/src/jobs/createpartitiontablejob.cpp index bfb18ca..41153b4 100644 --- a/src/jobs/createpartitiontablejob.cpp +++ b/src/jobs/createpartitiontablejob.cpp @@ -1,6 +1,6 @@ /* SPDX-FileCopyrightText: 2008-2010 Volker Lanz - SPDX-FileCopyrightText: 2014-2018 Andrius Štikonas + SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas SPDX-FileCopyrightText: 2016 Chantara Tith SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho @@ -35,6 +35,9 @@ bool CreatePartitionTableJob::run(Report& parent) Report* report = jobStarted(parent); + if (device().partitionTable()->type() == PartitionTable::TableType::none) + return true; + if (device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); diff --git a/src/jobs/job.cpp b/src/jobs/job.cpp index f46f409..4e2878b 100644 --- a/src/jobs/job.cpp +++ b/src/jobs/job.cpp @@ -94,9 +94,9 @@ void Job::emitProgress(int i) Q_EMIT progress(i); } -void Job::updateReport(const QVariantMap& reportString) +void Job::updateReport(const QString& report) { - m_Report->line() << reportString[QStringLiteral("report")].toString(); + m_Report->line() << report; } Report* Job::jobStarted(Report& parent) diff --git a/src/jobs/job.h b/src/jobs/job.h index 8e74128..cc32c4d 100644 --- a/src/jobs/job.h +++ b/src/jobs/job.h @@ -71,7 +71,7 @@ public: } void emitProgress(int i); - void updateReport(const QVariantMap& reportString); + void updateReport(const QString& report); protected: bool copyBlocks(Report& report, CopyTarget& target, CopySource& source); diff --git a/src/plugins/sfdisk/sfdiskbackend.cpp b/src/plugins/sfdisk/sfdiskbackend.cpp index 5d8cd6c..8dccc4f 100644 --- a/src/plugins/sfdisk/sfdiskbackend.cpp +++ b/src/plugins/sfdisk/sfdiskbackend.cpp @@ -560,7 +560,7 @@ FileSystem::Type SfdiskBackend::fileSystemNameToType(const QString& name, const else if (name == QStringLiteral("vfat")) { if (version == QStringLiteral("FAT32")) rval = FileSystem::Type::Fat32; - else if (version == QStringLiteral("FAT16")) + else if (version == QStringLiteral("FAT16") || version == QStringLiteral("msdos")) // blkid uses msdos for both FAT16 and FAT12 rval = FileSystem::Type::Fat16; else if (version == QStringLiteral("FAT12")) rval = FileSystem::Type::Fat12; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 0eeadbb..0f606e8 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -5,6 +5,7 @@ # SPDX-FileCopyrightText: 2018 Huzaifa Faruqui # SPDX-FileCopyrightText: 2019 Albert Astals Cid # SPDX-FileCopyrightText: 2019 Antonio Rojas +# SPDX-FileCopyrightText: 2020 David Edmundson # SPDX-License-Identifier: GPL-3.0-or-later @@ -23,6 +24,9 @@ qt5_generate_dbus_interface( OPTIONS -a ) + +find_package(PolkitQt5-1 REQUIRED) + qt5_add_dbus_interface(ApplicationInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${application_interface_xml} externalcommand_interface) qt5_add_dbus_interface(HelperInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${helper_interface_xml} externalcommandhelper_interface) @@ -65,12 +69,16 @@ target_link_libraries(kpmcore_externalcommand Qt5::DBus KF5::AuthCore KF5::I18n + PolkitQt5-1::Core ) install(TARGETS kpmcore_mdadmupdateconf DESTINATION ${LIBEXEC_INSTALL_DIR}) -install(TARGETS kpmcore_externalcommand DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) +install(TARGETS kpmcore_externalcommand DESTINATION ${KDE_INSTALL_LIBEXECDIR}) install( FILES util/org.kde.kpmcore.helperinterface.conf DESTINATION ${KDE_INSTALL_DBUSDIR}/system.d ) install( FILES util/org.kde.kpmcore.applicationinterface.conf DESTINATION ${KDE_INSTALL_DBUSDIR}/system.d ) -kauth_install_helper_files(kpmcore_externalcommand org.kde.kpmcore.externalcommand root) kauth_install_actions(org.kde.kpmcore.externalcommand util/org.kde.kpmcore.externalcommand.actions) +ecm_install_configured_files( + INPUT util/org.kde.kpmcore.helperinterface.service.in + DESTINATION ${KDE_INSTALL_DBUSDIR}/system-services +) diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index ef3de03..54996bb 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -7,6 +7,7 @@ SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho SPDX-FileCopyrightText: 2019 Shubham Jangra SPDX-FileCopyrightText: 2019 Yuri Chornoivan + SPDX-FileCopyrightText: 2020 David Edmundson SPDX-License-Identifier: GPL-3.0-or-later */ @@ -36,8 +37,6 @@ #include #include #include - -#include #include #include @@ -49,15 +48,9 @@ struct ExternalCommandPrivate int m_ExitCode; QByteArray m_Output; QByteArray m_Input; - DBusThread *m_thread; QProcess::ProcessChannelMode processChannelMode; }; -KAuth::ExecuteJob* ExternalCommand::m_job; -bool ExternalCommand::helperStarted = false; -QWidget* ExternalCommand::parent; - - /** Creates a new ExternalCommand instance without Report. @param cmd the command to run @param args the arguments to pass to the command @@ -70,11 +63,6 @@ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, co d->m_Args = args; d->m_ExitCode = -1; d->m_Output = QByteArray(); - - if (!helperStarted) - if(!startHelper()) - Log(Log::Level::error) << xi18nc("@info:status", "Could not obtain administrator privileges."); - d->processChannelMode = processChannelMode; } @@ -135,7 +123,7 @@ bool ExternalCommand::start(int timeout) bool rval = false; - QDBusPendingCall pcall = interface->start(cmd, args(), d->m_Input, d->processChannelMode); + QDBusPendingCall pcall = interface->RunCommand(cmd, args(), d->m_Input, d->processChannelMode); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; @@ -165,15 +153,14 @@ bool ExternalCommand::copyBlocks(const CopySource& source, CopyTarget& target) bool rval = true; const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy - // TODO KF6:Use new signal-slot syntax - connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); - connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); - auto interface = helperInterface(); if (!interface) return false; - QDBusPendingCall pcall = interface->copyblocks(source.path(), source.firstByte(), source.length(), + connect(interface, &OrgKdeKpmcoreExternalcommandInterface::progress, this, &ExternalCommand::progress); + connect(interface, &OrgKdeKpmcoreExternalcommandInterface::report, this, &ExternalCommand::reportSignal); + + QDBusPendingCall pcall = interface->CopyBlocks(source.path(), source.firstByte(), source.length(), target.path(), target.firstByte(), blockSize); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); @@ -210,17 +197,17 @@ bool ExternalCommand::writeData(Report& commandReport, const QByteArray& buffer, if (!interface) return false; - QDBusPendingCall pcall = interface->writeData(buffer, deviceNode, firstByte); + QDBusPendingCall pcall = interface->WriteData(buffer, deviceNode, firstByte); return waitForDbusReply(pcall); } -bool ExternalCommand::createFile(const QByteArray& buffer, const QString& deviceNode) +bool ExternalCommand::createFile(const QByteArray& fileContents, const QString& filePath) { auto interface = helperInterface(); if (!interface) return false; - QDBusPendingCall pcall = interface->createFile(buffer, deviceNode); + QDBusPendingCall pcall = interface->CreateFile(filePath, fileContents); return waitForDbusReply(pcall); } @@ -231,7 +218,7 @@ OrgKdeKpmcoreExternalcommandInterface* ExternalCommand::helperInterface() return nullptr; } - auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), + auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days return interface; @@ -342,59 +329,3 @@ void ExternalCommand::setExitCode(int i) { d->m_ExitCode = i; } - -bool ExternalCommand::startHelper() -{ - if (!QDBusConnection::systemBus().isConnected()) { - qWarning() << QDBusConnection::systemBus().lastError().message(); - return false; - } - - QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); - if (iface.isValid()) { - exit(0); - } - - d->m_thread = new DBusThread; - d->m_thread->start(); - - 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 - action.setParentWidget(parent); - QVariantMap arguments; - action.setArguments(arguments); - m_job = action.execute(); - m_job->start(); - - // Wait until ExternalCommand Helper is ready (helper sends newData signal just before it enters event loop) - QEventLoop loop; - auto exitLoop = [&] () { loop.exit(); }; - auto conn = QObject::connect(m_job, &KAuth::ExecuteJob::newData, exitLoop); - QObject::connect(m_job, &KJob::finished, [=] () { if(m_job->error()) exitLoop(); } ); - loop.exec(); - QObject::disconnect(conn); - - helperStarted = true; - return true; -} - -void ExternalCommand::stopHelper() -{ - auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), - QStringLiteral("/Helper"), QDBusConnection::systemBus()); - interface->exit(); - -} - -void DBusThread::run() -{ - if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface")) || - !QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots)) { - qWarning() << QDBusConnection::systemBus().lastError().message(); - return; - } - - QEventLoop loop; - loop.exec(); -} diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index 8bbdaa4..516c160 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -24,8 +24,6 @@ #include -namespace KAuth { class ExecuteJob; } - class KJob; class Report; class CopySource; @@ -36,15 +34,6 @@ class OrgKdeKpmcoreExternalcommandInterface; struct ExternalCommandPrivate; -class DBusThread : public QThread -{ - Q_OBJECT - // We register on DBus so the helper can monitor us and terminate if we - // terminate. - Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.applicationinterface") - void run() override; -}; - /** An external command. Runs an external command as a child process. @@ -66,7 +55,7 @@ public: public: bool copyBlocks(const CopySource& source, CopyTarget& target); bool writeData(Report& commandReport, const QByteArray& buffer, const QString& deviceNode, const quint64 firstByte); // same as copyBlocks but from QByteArray - bool createFile(const QByteArray& buffer, const QString& deviceNode); // similar to writeData but creates a new file + bool createFile(const QByteArray& filePath, const QString& fileContents); // similar to writeData but creates a new file /**< @param cmd the command to run */ void setCommand(const QString& cmd); @@ -98,28 +87,9 @@ public: /**< @return pointer to the Report or nullptr */ Report* report(); - void emitReport(const QVariantMap& report) { Q_EMIT reportSignal(report); } - - // KAuth - /**< start ExternalCommand Helper */ - bool startHelper(); - - /**< stop ExternalCommand Helper */ - static void stopHelper(); - - /**< Sets a parent widget for the authentication dialog. - * @param p parent widget - */ - static void setParentWidget(QWidget *p) { - parent = p; - } - Q_SIGNALS: void progress(int); - void reportSignal(const QVariantMap&); - -public Q_SLOTS: - void emitProgress(KJob*, unsigned long percent) { Q_EMIT progress(percent); } + void reportSignal(const QString&); private: void setExitCode(int i); @@ -130,10 +100,6 @@ private: private: std::unique_ptr d; - // KAuth - static KAuth::ExecuteJob *m_job; - static bool helperStarted; - static QWidget *parent; }; #endif diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp index b84fc7c..9c68226 100644 --- a/src/util/externalcommandhelper.cpp +++ b/src/util/externalcommandhelper.cpp @@ -5,6 +5,7 @@ SPDX-FileCopyrightText: 2018-2019 Harald Sitter SPDX-FileCopyrightText: 2018 Simon Depiets SPDX-FileCopyrightText: 2019 Shubham Jangra + SPDX-FileCopyrightText: 2020 David Edmundson SPDX-License-Identifier: GPL-3.0-or-later */ @@ -13,6 +14,7 @@ #include "externalcommand_whitelist.h" #include + #include #include #include @@ -21,66 +23,45 @@ #include #include +#include +#include + +#include /** Initialize ExternalCommandHelper Daemon and prepare DBus interface * - * KAuth helper runs in the background until application exits. - * To avoid forever running helper in case of application crash - * ExternalCommand class opens a DBus service that we monitor for changes. + * This helper runs in the background until all applications using it exit. * If helper is not busy then it exits when the client services gets - * unregistered. Otherwise, - * we wait for the current job to finish before exiting, so even in case - * of main application crash, we do not leave partially moved data. + * unregistered. In case the client crashes, the helper waits + * for the current job to finish before exiting, to avoid leaving partially moved data. * - * This helper also starts another DBus interface where it listens to - * command execution requests from the application that started the helper. - * + * This helper starts DBus interface where it listens to command execution requests. + * New clients connecting to the helper have to authenticate using Polkit. */ -ActionReply ExternalCommandHelper::init(const QVariantMap& args) + +ExternalCommandHelper::ExternalCommandHelper() { - Q_UNUSED(args) - - ActionReply reply; - - if (!QDBusConnection::systemBus().isConnected() || !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface")) || - !QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) { - qWarning() << QDBusConnection::systemBus().lastError().message(); - reply.addData(QStringLiteral("success"), false); - - // Also end the application loop started by KAuth's main() code. Our loop - // exits when our client disappears. Without client we have no reason to - // live. - qApp->quit(); - - return reply; + if (!QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) { + ::exit(-1); } - - m_loop = std::make_unique(); - HelperSupport::progressStep(QVariantMap()); - // End the loop and return only once the client is done using us. - auto serviceWatcher = - new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"), - QDBusConnection::systemBus(), - QDBusServiceWatcher::WatchForUnregistration, - this); - connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, - [this]() { - m_loop->exit(); + if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface"))) { + ::exit(-1); + } + + // we know this service must be registered already as DBus policy blocks calls from anyone else + m_serviceWatcher = new QDBusServiceWatcher(this); + m_serviceWatcher->setConnection(QDBusConnection ::systemBus()); + m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + + connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, qApp, [this](const QString &service) { + m_serviceWatcher->removeWatchedService(service); + if (m_serviceWatcher->watchedServices().isEmpty()) { + qApp->quit(); + } }); - - m_loop->exec(); - reply.addData(QStringLiteral("success"), true); - - // Also end the application loop started by KAuth's main() code. Our loop - // exits when our client disappears. Without client we have no reason to - // live. - qApp->quit(); - - 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 @@ -146,8 +127,15 @@ bool ExternalCommandHelper::writeData(const QString &targetDevice, const QByteAr @param fileContents the data that we write @return true on success */ -bool ExternalCommandHelper::createFile(const QString &filePath, const QByteArray& fileContents) +bool ExternalCommandHelper::CreateFile(const QString &filePath, const QByteArray& fileContents) { + if (!isCallerAuthorized()) { + return false; + } + // Do not allow using this helper for writing to arbitrary location + if ( !filePath.contains(QStringLiteral("/etc/fstab")) ) + return false; + QFile device(filePath); auto flags = QIODevice::WriteOnly | QIODevice::Unbuffered; @@ -165,8 +153,11 @@ bool ExternalCommandHelper::createFile(const QString &filePath, const QByteArray } // If targetDevice is empty then return QByteArray with data that was read from disk. -QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize) +QVariantMap ExternalCommandHelper::CopyBlocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize) { + if (!isCallerAuthorized()) { + return QVariantMap(); + } QVariantMap reply; reply[QStringLiteral("success")] = true; @@ -192,13 +183,10 @@ QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const timer.start(); - QVariantMap report; - - report[QStringLiteral("report")] = xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, + QString reportText = 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); + Q_EMIT report(reportText); bool rval = true; @@ -217,10 +205,10 @@ QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const if (percent % 5 == 0 && timer.elapsed() > 1000) { const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (timer.elapsed() / 1000); const qint64 estSecsLeft = (100 - percent) * timer.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); + reportText = xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString()); + Q_EMIT report(reportText); } - HelperSupport::progressStep(percent); + Q_EMIT progress(percent); } } @@ -230,8 +218,8 @@ QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const 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); + reportText = xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset); + Q_EMIT report(reportText); rval = readData(sourceDevice, buffer, lastBlockReadOffset, lastBlock); if (rval) { @@ -242,20 +230,23 @@ QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const } if (rval) { - HelperSupport::progressStep(100); + Q_EMIT progress(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); + reportText = xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 block (%2) finished.", "Copying %1 blocks (%2) finished.", blocksCopied, i18np("1 byte", "%1 bytes", bytesWritten)); + Q_EMIT report(reportText); reply[QStringLiteral("success")] = rval; return reply; } -bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) +bool ExternalCommandHelper::WriteData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) { + if (!isCallerAuthorized()) { + return false; + } // Do not allow using this helper for writing to arbitrary location if ( targetDevice.left(5) != QStringLiteral("/dev/") ) return false; @@ -263,17 +254,11 @@ bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& t return writeData(targetDevice, buffer, targetFirstByte); } -bool ExternalCommandHelper::createFile(const QByteArray& fileContents, const QString& filePath) -{ - // Do not allow using this helper for writing to arbitrary location - if ( !filePath.contains(QStringLiteral("/etc/fstab")) ) - return false; - - return createFile(filePath, fileContents); -} - -QVariantMap ExternalCommandHelper::start(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode) +QVariantMap ExternalCommandHelper::RunCommand(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode) { + if (!isCallerAuthorized()) { + return QVariantMap(); + } QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QVariantMap reply; reply[QStringLiteral("success")] = true; @@ -287,7 +272,7 @@ QVariantMap ExternalCommandHelper::start(const QString& command, const QStringLi QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1); if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) { qInfo() << command <<" command is not one of the whitelisted command"; - m_loop->exit(); + qApp->quit(); reply[QStringLiteral("success")] = false; return reply; } @@ -307,14 +292,6 @@ QVariantMap ExternalCommandHelper::start(const QString& command, const QStringLi return reply; } -void ExternalCommandHelper::exit() -{ - m_loop->exit(); - - QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper")); - QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface")); -} - void ExternalCommandHelper::onReadOutput() { /* const QByteArray s = cmd.readAllStandardOutput(); @@ -331,4 +308,52 @@ void ExternalCommandHelper::onReadOutput() *report() << QString::fromLocal8Bit(s);*/ } -KAUTH_HELPER_MAIN("org.kde.kpmcore.externalcommand", ExternalCommandHelper) +bool ExternalCommandHelper::isCallerAuthorized() +{ + if (!calledFromDBus()) { + return false; + } + + // Cache successful authentication requests, so that clients don't need + // to authenticate multiple times during long partitioning operations. + if (m_serviceWatcher->watchedServices().contains(message().service())) { + return true; + } + + PolkitQt1::SystemBusNameSubject subject(message().service()); + PolkitQt1::Authority *authority = PolkitQt1::Authority::instance(); + + PolkitQt1::Authority::Result result; + QEventLoop e; + connect(authority, &PolkitQt1::Authority::checkAuthorizationFinished, &e, [&e, &result](PolkitQt1::Authority::Result _result) { + result = _result; + e.quit(); + }); + + authority->checkAuthorization(QStringLiteral("org.kde.kpmcore.externalcommand.init"), subject, PolkitQt1::Authority::AllowUserInteraction); + e.exec(); + + if (authority->hasError()) { + qDebug() << "Encountered error while checking authorization, error code:" << authority->lastError() << authority->errorDetails(); + authority->clearError(); + } + + switch (result) { + case PolkitQt1::Authority::Yes: + // track who called into us so we can close when all callers have gone away + m_serviceWatcher->addWatchedService(message().service()); + return true; + default: + sendErrorReply(QDBusError::AccessDenied); + if (m_serviceWatcher->watchedServices().isEmpty()) + qApp->quit(); + return false; + } +} + +int main(int argc, char ** argv) +{ + QCoreApplication app(argc, argv); + ExternalCommandHelper helper; + app.exec(); +} diff --git a/src/util/externalcommandhelper.h b/src/util/externalcommandhelper.h index b910cf1..c86a8d6 100644 --- a/src/util/externalcommandhelper.h +++ b/src/util/externalcommandhelper.h @@ -3,6 +3,7 @@ SPDX-FileCopyrightText: 2018 Huzaifa Faruqui SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho SPDX-FileCopyrightText: 2019 Shubham Jangra + SPDX-FileCopyrightText: 2020 David Edmundson SPDX-License-Identifier: GPL-3.0-or-later */ @@ -13,42 +14,40 @@ #include #include -#include - #include #include #include +#include -using namespace KAuth; +class QDBusServiceWatcher; -class ExternalCommandHelper : public QObject +class ExternalCommandHelper : public QObject, public QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.externalcommand") Q_SIGNALS: - void progress(int); - void quit(); + Q_SCRIPTABLE void progress(int); + Q_SCRIPTABLE void report(QString); public: + ExternalCommandHelper(); bool readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const qint64 size); bool writeData(const QString& targetDevice, const QByteArray& buffer, const qint64 offset); - bool createFile(const QString& filePath, const QByteArray& fileContents); public Q_SLOTS: - ActionReply init(const QVariantMap& args); - Q_SCRIPTABLE QVariantMap start(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode); - Q_SCRIPTABLE QVariantMap copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize); - Q_SCRIPTABLE bool writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte); - Q_SCRIPTABLE bool createFile(const QByteArray& fileContents, const QString& filePath); - Q_SCRIPTABLE void exit(); + Q_SCRIPTABLE QVariantMap RunCommand(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode); + Q_SCRIPTABLE QVariantMap CopyBlocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize); + Q_SCRIPTABLE bool WriteData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte); + Q_SCRIPTABLE bool CreateFile(const QString& filePath, const QByteArray& fileContents); private: - void onReadOutput(); - std::unique_ptr m_loop; + bool isCallerAuthorized(); + + void onReadOutput(); QProcess m_cmd; -// QByteArray output; + QDBusServiceWatcher *m_serviceWatcher = nullptr; }; #endif diff --git a/src/util/helpers.cpp b/src/util/helpers.cpp index 365327d..a531c0c 100644 --- a/src/util/helpers.cpp +++ b/src/util/helpers.cpp @@ -60,6 +60,7 @@ KAboutData aboutKPMcore() aboutData.addCredit(xi18nc("@info:credit", "Pali Rohár"), i18nc("@info:credit", "UDF support"), QStringLiteral("pali.rohar@gmail.com")); aboutData.addCredit(xi18nc("@info:credit", "Adriaan de Groot"), i18nc("@info:credit", "Calamares maintainer"), QStringLiteral("groot@kde.org")); aboutData.addCredit(xi18nc("@info:credit", "Caio Jordão Carvalho"), i18nc("@info:credit", "Improved SMART support"), QStringLiteral("caiojcarvalho@gmail.com")); + aboutData.addCredit(xi18nc("@info:credit", "David Edmundson"), i18nc("@info:credit", "Port from KAuth to Polkit"), QStringLiteral("kde@davidedmundson.co.uk")); return aboutData; } diff --git a/src/util/org.kde.kpmcore.helperinterface.conf b/src/util/org.kde.kpmcore.helperinterface.conf index 990dadf..069015e 100644 --- a/src/util/org.kde.kpmcore.helperinterface.conf +++ b/src/util/org.kde.kpmcore.helperinterface.conf @@ -9,7 +9,7 @@ - diff --git a/src/util/org.kde.kpmcore.helperinterface.service.in b/src/util/org.kde.kpmcore.helperinterface.service.in new file mode 100644 index 0000000..c3da3f1 --- /dev/null +++ b/src/util/org.kde.kpmcore.helperinterface.service.in @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2020 David Edmundson +# SPDX-License-Identifier: GPL-3.0-or-later +[D-BUS Service] +Name=org.kde.kpmcore.helperinterface +Exec=@KDE_INSTALL_FULL_LIBEXECDIR@/kpmcore_externalcommand +User=root diff --git a/test/helpers.cpp b/test/helpers.cpp index 0187cd8..1bd2f8d 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -46,7 +46,3 @@ KPMCoreInitializer::KPMCoreInitializer( const char* backend ) : KPMCoreInitializ { } -KPMCoreInitializer::~KPMCoreInitializer() -{ - ExternalCommand::stopHelper(); -} diff --git a/test/helpers.h b/test/helpers.h index 9e762af..82b1341 100644 --- a/test/helpers.h +++ b/test/helpers.h @@ -20,7 +20,6 @@ public: KPMCoreInitializer(); /// Default backend KPMCoreInitializer( const QString& backend ); /// Use named backend KPMCoreInitializer( const char* backend ); /// Use named backend - ~KPMCoreInitializer(); bool isValid() const {