diff --git a/CMakeLists.txt b/CMakeLists.txt index c91a4b3..30092cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,130 +1,130 @@ # Copyright (C) 2008 by Volker Lanz # Copyright (C) 2014-2019 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 . project(kpmcore) cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(CMAKE_USE_RELATIVE_PATHS OFF) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) # Dependencies set(QT_MIN_VERSION "5.10.0") set(KF5_MIN_VERSION "5.56") set(BLKID_MIN_VERSION "2.33.2") # Qca-qt5 (tested with botan and ossl backends) # Runtime # smartmontools 7.0 # Qca plugin (botan or ossl) set(VERSION_MAJOR "4") set(VERSION_MINOR "0") -set(VERSION_RELEASE "0") +set(VERSION_RELEASE "1") set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}) set(SOVERSION "8") add_definitions(-D'VERSION="${VERSION}"') #" set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") find_package(PolkitQt5-1 REQUIRED) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(GenerateExportHeader) include(ECMSetupVersion) ecm_setup_version(${VERSION} VARIABLE_PREFIX KPMCORE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpmcore_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfigVersion.cmake" SOVERSION ${SOVERSION}) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Gui Test Widgets ) # We do not link to KF5::AuthCore because we just have build-time dependency on it # Load the frameworks we need find_package(KF5 ${KF5_MIN_VERSION} REQUIRED Auth CoreAddons I18n WidgetsAddons ) find_package(Qca-qt5 REQUIRED) # use sane compile flags add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_CAST_TO_BYTEARRAY -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS ) kde_enable_exceptions() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") find_package(PkgConfig REQUIRED) pkg_check_modules(BLKID REQUIRED blkid>=${BLKID_MIN_VERSION}) endif() include_directories(${Qt5Core_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ${BLKID_INCLUDE_DIRS} lib/ src/) add_subdirectory(src) # create a Config.cmake and a ConfigVersion.cmake file and install them set(INCLUDE_INSTALL_DIR "include/kpmcore/") set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KPMcore") configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KPMcoreConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS INCLUDE_INSTALL_DIR ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KPMcoreTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPMcoreTargets.cmake ) ki18n_install(po) set_target_properties( kpmcore PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSION} ) message(STATUS "kpmcore ${VERSION} will be built for install into ${CMAKE_INSTALL_PREFIX}") feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) enable_testing() add_subdirectory(test) diff --git a/src/core/smartstatus.cpp b/src/core/smartstatus.cpp index b9e8e37..d640fcc 100644 --- a/src/core/smartstatus.cpp +++ b/src/core/smartstatus.cpp @@ -1,159 +1,159 @@ /************************************************************************* * Copyright (C) 2010 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 "core/smartstatus.h" #include "core/smartparser.h" #include "core/smartdiskinformation.h" #include "core/smartattributeparseddata.h" #include #include #include #include #include SmartStatus::SmartStatus(const QString &device_path) : m_DevicePath(device_path), m_InitSuccess(false), m_Status(false), m_ModelName(), m_Serial(), m_Firmware(), m_Overall(Overall::Bad), m_SelfTestStatus(SelfTestStatus::Success), m_Temp(0), m_BadSectors(0), m_PowerCycles(0), m_PoweredOn(0) { update(); } void SmartStatus::update() { SmartParser parser(devicePath()); if (!parser.init()) { qDebug() << "error during smart output parsing for " << devicePath() << ": " << strerror(errno); return; } SmartDiskInformation *disk; disk = parser.diskInformation(); if (!disk) return; setStatus(disk->smartStatus()); setModelName(disk->model()); setFirmware(disk->firmware()); setSerial(disk->serial()); setSelfTestStatus(disk->selfTestExecutionStatus()); setOverall(disk->overall()); setTemp(disk->temperature()); setBadSectors(disk->badSectors()); setPoweredOn(disk->poweredOn()); setPowerCycles(disk->powerCycles()); addAttributes(disk->attributes()); setInitSuccess(true); } QString SmartStatus::tempToString(quint64 mkelvin) { const double celsius = (mkelvin - 273150.0) / 1000.0; const double fahrenheit = 9.0 * celsius / 5.0 + 32; return xi18nc("@item:intable degrees in Celsius and Fahrenheit", "%1° C / %2° F", - QLocale().toString(celsius, 1), QLocale().toString(fahrenheit, 1)); + QLocale().toString(celsius, 'f', 0), QLocale().toString(fahrenheit, 'f', 0)); } QString SmartStatus::selfTestStatusToString(SmartStatus::SelfTestStatus s) { switch (s) { case SelfTestStatus::Aborted: return xi18nc("@item", "Aborted"); case SelfTestStatus::Interrupted: return xi18nc("@item", "Interrupted"); case SelfTestStatus::Fatal: return xi18nc("@item", "Fatal error"); case SelfTestStatus::ErrorUnknown: return xi18nc("@item", "Unknown error"); case SelfTestStatus::ErrorEletrical: return xi18nc("@item", "Electrical error"); case SelfTestStatus::ErrorServo: return xi18nc("@item", "Servo error"); case SelfTestStatus::ErrorRead: return xi18nc("@item", "Read error"); case SelfTestStatus::ErrorHandling: return xi18nc("@item", "Handling error"); case SelfTestStatus::InProgress: return xi18nc("@item", "Self test in progress"); case SelfTestStatus::Success: default: return xi18nc("@item", "Success"); } } QString SmartStatus::overallAssessmentToString(Overall o) { switch (o) { case Overall::Good: return xi18nc("@item", "Healthy"); case Overall::BadPast: return xi18nc("@item", "Has been used outside of its design parameters in the past."); case Overall::BadSectors: return xi18nc("@item", "Has some bad sectors."); case Overall::BadNow: return xi18nc("@item", "Is being used outside of its design parameters right now."); case Overall::BadSectorsMany: return xi18nc("@item", "Has many bad sectors."); case Overall::Bad: default: return xi18nc("@item", "Disk failure is imminent. Backup all data!"); } } void SmartStatus::addAttributes(QList attr) { m_Attributes.clear(); for (const SmartAttributeParsedData &at : qAsConst(attr)) { SmartAttribute sm(at); m_Attributes.append(sm); } } diff --git a/src/gui/partresizerwidget.h b/src/gui/partresizerwidget.h index 929223b..dbbd57d 100644 --- a/src/gui/partresizerwidget.h +++ b/src/gui/partresizerwidget.h @@ -1,215 +1,217 @@ /************************************************************************* * Copyright (C) 2008, 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_PARTRESIZERWIDGET_H) #define KPMCORE_PARTRESIZERWIDGET_H +#include "core/partition.h" #include "util/libpartitionmanagerexport.h" #include #include -class Partition; class PartWidget; class Device; class NewDialog; class QPaintEvent; class QResizeEvent; class QMouseEvent; /** Widget that allows the user to resize a Partition. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartResizerWidget : public QWidget { friend class NewDialog; Q_OBJECT Q_DISABLE_COPY(PartResizerWidget) public: PartResizerWidget(QWidget* parent); public: void init(Device& d, Partition& p, qint64 minFirst, qint64 maxLast, bool read_only = false, bool move_allowed = true); qint64 totalSectors() const { return maximumLastSector() - minimumFirstSector() + 1; /**< @return total sectors (free + Partition's length) */ } qint64 minimumFirstSector(bool aligned = false) const; /**< @return the lowest allowed first sector */ void setMinimumFirstSector(qint64 s) { m_MinimumFirstSector = s; /**< @param s the new lowest allowed first sector */ } qint64 maximumFirstSector(bool aligned = false) const; /**< @return the highest allowed first sector */ void setMaximumFirstSector(qint64 s) { m_MaximumFirstSector = s; /**< @param s the new highest allowed first sector */ } qint64 minimumLastSector(bool aligned = false) const; /**< @return the lowest allowed last sector */ void setMinimumLastSector(qint64 s) { m_MinimumLastSector = s; /**< @param s the new lowest allowed last sector */ } qint64 maximumLastSector(bool aligned = false) const; /**< @return the highest allowed last sector */ void setMaximumLastSector(qint64 s) { m_MaximumLastSector = s; /**< @param s the new highest allowed last sector */ } void setMinimumLength(qint64 s); qint64 minimumLength() const { return m_MinimumLength; /**< @return minimum length for Partition */ } void setMaximumLength(qint64 s); qint64 maximumLength() const { return m_MaximumLength; /**< @return maximum length for the Partition */ } void setMoveAllowed(bool b); bool moveAllowed() const { return m_MoveAllowed; /**< @return true if moving the Partition is allowed */ } bool readOnly() const { return m_ReadOnly; /**< @return true if the widget is read only */ } void setReadOnly(bool b) { m_ReadOnly = b; /**< @param b the new value for read only */ } bool align() const { + if (partition().isMounted()) + return false; return m_Align; /**< @return true if the Partition is to be aligned */ } void setAlign(bool b) { m_Align = b; /**< @param b the new value for aligning the Partition */ } qint32 handleWidth() const; /**< @return the handle width in pixels */ static qint32 handleHeight() { return m_HandleHeight; /**< @return the handle height in pixels */ } Q_SIGNALS: void firstSectorChanged(qint64); void lastSectorChanged(qint64); public: bool updateFirstSector(qint64 newFirstSector); bool updateLastSector(qint64 newLastSector); bool movePartition(qint64 newFirstSector); protected: Partition& partition() { Q_ASSERT(m_Partition); return *m_Partition; } const Partition& partition() const { Q_ASSERT(m_Partition); return *m_Partition; } void setPartition(Partition& p) { m_Partition = &p; } Device& device() { Q_ASSERT(m_Device); return *m_Device; } const Device& device() const { Q_ASSERT(m_Device); return *m_Device; } void setDevice(Device& d) { m_Device = &d; } void paintEvent(QPaintEvent* event) override; void resizeEvent(QResizeEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; PartWidget& partWidget() { Q_ASSERT(m_PartWidget); return *m_PartWidget; } const PartWidget& partWidget() const { Q_ASSERT(m_PartWidget); return *m_PartWidget; } void updatePositions(); int partWidgetStart() const; int partWidgetWidth() const; QLabel& leftHandle() { return m_LeftHandle; } QLabel& rightHandle() { return m_RightHandle; } long double sectorsPerPixel() const; void set(qint64 newCap, qint64 newFreeBefore, qint64 newFreeAfter); void resizeLogicals(qint64 deltaFirst, qint64 deltaLast, bool force = false); bool checkAlignment(const Partition& child, qint64 delta) const; QWidget* draggedWidget() { return m_DraggedWidget; } const QWidget* draggedWidget() const { return m_DraggedWidget; } bool checkConstraints(qint64 first, qint64 last) const; private: Device* m_Device; Partition* m_Partition; PartWidget* m_PartWidget; qint64 m_MinimumFirstSector; qint64 m_MaximumFirstSector; qint64 m_MinimumLastSector; qint64 m_MaximumLastSector; qint64 m_MinimumLength; qint64 m_MaximumLength; QLabel m_LeftHandle; QLabel m_RightHandle; QWidget* m_DraggedWidget; int m_Hotspot; bool m_MoveAllowed; bool m_ReadOnly; bool m_Align; static const qint32 m_HandleHeight; }; #endif diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index f740fe3..bf0ae6b 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -1,268 +1,269 @@ /************************************************************************* * 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 "plugins/sfdisk/sfdiskpartitiontable.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/partition.h" #include "core/device.h" #include "core/raid/softwareraid.h" #include "fs/filesystem.h" #include "util/report.h" #include "util/externalcommand.h" #include #include #include #include #include SfdiskPartitionTable::SfdiskPartitionTable(const Device* d) : CoreBackendPartitionTable(), m_device(d) { } SfdiskPartitionTable::~SfdiskPartitionTable() { } bool SfdiskPartitionTable::open() { return true; } bool SfdiskPartitionTable::commit(quint32 timeout) { if (m_device->type() == Device::Type::SoftwareRAID_Device) ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--stop-exec-queue") }).run(); ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); ExternalCommand(QStringLiteral("blockdev"), { QStringLiteral("--rereadpt"), m_device->deviceNode() }).run(); ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("trigger") }).run(); if (m_device->type() == Device::Type::SoftwareRAID_Device) ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--start-exec-queue") }).run(); + ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); return true; } QString SfdiskPartitionTable::createPartition(Report& report, const Partition& partition) { if ( !(partition.roles().has(PartitionRole::Extended) || partition.roles().has(PartitionRole::Logical) || partition.roles().has(PartitionRole::Primary) ) ) { report.line() << xi18nc("@info:progress", "Unknown partition role for new partition %1 (roles: %2)", partition.deviceNode(), partition.roles().toString()); return QString(); } QByteArray type = QByteArray(); if (partition.roles().has(PartitionRole::Extended)) type = QByteArrayLiteral(" type=5"); // NOTE: at least on GPT partition types "are" partition flags ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--append"), partition.devicePath() } ); if ( createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) + type + QByteArrayLiteral(" size=") + QByteArray::number(partition.length()) + QByteArrayLiteral("\nwrite\n")) && createCommand.start(-1) ) { QRegularExpression re(QStringLiteral("Created a new partition (\\d+)")); QRegularExpressionMatch rem = re.match(createCommand.output()); if (rem.hasMatch()) { if ( partition.devicePath().back().isDigit() ) return partition.devicePath() + QLatin1Char('p') + rem.captured(1); else return partition.devicePath() + rem.captured(1); } } report.line() << xi18nc("@info:progress", "Failed to add partition %1 to device %2.", partition.deviceNode(), m_device->deviceNode()); return QString(); } bool SfdiskPartitionTable::deletePartition(Report& report, const Partition& partition) { ExternalCommand deleteCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--delete"), partition.devicePath(), QString::number(partition.number()) } ); if (deleteCommand.run(-1) && deleteCommand.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Could not delete partition %1.", partition.devicePath()); return false; } bool SfdiskPartitionTable::updateGeometry(Report& report, const Partition& partition, qint64 sectorStart, qint64 sectorEnd) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), partition.devicePath(), QStringLiteral("-N"), QString::number(partition.number()) } ); if ( sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) + QByteArrayLiteral(" size=") + QByteArray::number(sectorEnd - sectorStart + 1) + QByteArrayLiteral("\nY\n")) && sfdiskCommand.start(-1) && sfdiskCommand.exitCode() == 0) { return true; } report.line() << xi18nc("@info:progress", "Could not set geometry for partition %1 while trying to resize/move it.", partition.devicePath()); return false; } bool SfdiskPartitionTable::clobberFileSystem(Report& report, const Partition& partition) { ExternalCommand wipeCommand(report, QStringLiteral("wipefs"), { QStringLiteral("--all"), partition.partitionPath() } ); if (wipeCommand.run(-1) && wipeCommand.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Failed to erase filesystem signature on partition %1.", partition.partitionPath()); return false; } bool SfdiskPartitionTable::resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) { // sfdisk does not have any partition resize capabilities Q_UNUSED(report) Q_UNUSED(partition) Q_UNUSED(newLength) return false; } FileSystem::Type SfdiskPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector) { FileSystem::Type type = FileSystem::Type::Unknown; ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), device.deviceNode() } ); if (jsonCommand.run(-1) && jsonCommand.exitCode() == 0) { const QJsonArray partitionTable = QJsonDocument::fromJson(jsonCommand.rawOutput()).object()[QLatin1String("partitiontable")].toObject()[QLatin1String("partitions")].toArray(); for (const auto &partition : partitionTable) { const QJsonObject partitionObject = partition.toObject(); const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); if (start == sector) { const QString deviceNode = partitionObject[QLatin1String("node")].toString(); type = CoreBackendManager::self()->backend()->detectFileSystem(deviceNode); return type; } } } report.line() << xi18nc("@info:progress", "Could not determine file system of partition at sector %1 on device %2.", sector, device.deviceNode()); return type; } static struct { FileSystem::Type type; QLatin1String partitionType[2]; // GPT, MBR } typemap[] = { { FileSystem::Type::Btrfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext3, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::LinuxSwap, { QLatin1String("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"), QLatin1String("82") } }, { FileSystem::Type::Fat12, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Type::Fat16, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Type::Fat32, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::Nilfs2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ntfs, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::Exfat, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::ReiserFS, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Reiser4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Xfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Jfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Hfs, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af")} }, { FileSystem::Type::HfsPlus, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af") } }, { FileSystem::Type::Udf, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } } // Add ZFS too }; static QLatin1String getPartitionType(FileSystem::Type t, PartitionTable::TableType tableType) { quint8 type; switch (tableType) { case PartitionTable::TableType::gpt: type = 0; break; case PartitionTable::TableType::msdos: case PartitionTable::TableType::msdos_sectorbased: type = 1; break; default:; return QLatin1String(); } for (quint32 i = 0; i < sizeof(typemap) / sizeof(typemap[0]); i++) if (typemap[i].type == t) return typemap[i].partitionType[type]; return QLatin1String(); } bool SfdiskPartitionTable::setPartitionSystemType(Report& report, const Partition& partition) { QString partitionType = getPartitionType(partition.fileSystem().type(), m_device->partitionTable()->type()); if (partitionType.isEmpty()) return true; ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), partitionType } ); return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; } bool SfdiskPartitionTable::setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) { if (m_device->partitionTable()->type() == PartitionTable::TableType::msdos || m_device->partitionTable()->type() == PartitionTable::TableType::msdos_sectorbased) { // We only allow setting one active partition per device if (flag == PartitionTable::Flag::Boot && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QString::number(partition.number()) } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } else if (flag == PartitionTable::Flag::Boot && state == false) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QStringLiteral("-") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } } if (flag == PartitionTable::Flag::Boot && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } if (flag == PartitionTable::Flag::Boot && state == false) setPartitionSystemType(report, partition); if (flag == PartitionTable::Flag::BiosGrub && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), QStringLiteral("21686148-6449-6E6F-744E-656564454649") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } if (flag == PartitionTable::Flag::BiosGrub && state == false) setPartitionSystemType(report, partition); return true; } diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index 61f49c5..96e0598 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,403 +1,396 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016-2018 by Andrius Štikonas * * Copyright (C) 2019 by Shubham * * * * 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/copytargetbytearray.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" #include "util/globallog.h" #include "util/externalcommand.h" #include "util/externalcommand_polkitbackend.h" #include "util/report.h" #include "externalcommandhelper_interface.h" #include #include #include #include #include #include #include #include #include #include #include struct ExternalCommandPrivate { Report *m_Report; QString m_Command; QStringList m_Args; int m_ExitCode; QByteArray m_Output; QByteArray m_Input; QProcess::ProcessChannelMode processChannelMode; }; Auth::PolkitQt1Backend* ExternalCommand::m_authJob; 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 */ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : d(std::make_unique()) { d->m_Report = nullptr; d->m_Command = cmd; d->m_Args = args; d->m_ExitCode = -1; d->m_Output = QByteArray(); m_authJob = new Auth::PolkitQt1Backend; if (!helperStarted) if(!startHelper()) Log(Log::Level::error) << xi18nc("@info:status", "Could not obtain administrator privileges."); d->processChannelMode = 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) : d(std::make_unique()) { d->m_Report = report.newChild(); d->m_Command = cmd; d->m_Args = args; d->m_ExitCode = -1; d->m_Output = QByteArray(); d->processChannelMode = processChannelMode; } ExternalCommand::~ExternalCommand() { } /* void ExternalCommand::setup() { connect(this, qOverload(&QProcess::finished), this, &ExternalCommand::onFinished); connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); } */ /** Executes the external command. @param timeout timeout to wait for the process to start @return true on success */ bool ExternalCommand::start(int timeout) { + Q_UNUSED(timeout) + if (command().isEmpty()) return false; if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); - QTimer::singleShot(timeout, this, &ExternalCommand::quit); return false; } if (report()) report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" )) qDebug() << 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/") }); auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days bool rval = false; QDBusPendingCall pcall = interface->start(cmd, args(), d->m_Input, d->processChannelMode); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; d->m_Output = reply.value()[QStringLiteral("output")].toByteArray(); setExitCode(reply.value()[QStringLiteral("exitCode")].toInt()); rval = reply.value()[QStringLiteral("success")].toBool(); } }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); - QTimer::singleShot(timeout, this, &ExternalCommand::quit); - return rval; } bool ExternalCommand::copyBlocks(const CopySource& source, CopyTarget& target) { bool rval = true; const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); return false; } auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days QDBusPendingCall pcall = interface->copyblocks(source.path(), source.firstByte(), source.length(), target.path(), target.firstByte(), blockSize); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; rval = reply.value()[QStringLiteral("success")].toBool(); CopyTargetByteArray *byteArrayTarget = dynamic_cast(&target); if (byteArrayTarget) byteArrayTarget->m_Array = reply.value()[QStringLiteral("targetByteArray")].toByteArray(); } setExitCode(!rval); }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); return rval; } bool ExternalCommand::writeData(Report& commandReport, const QByteArray& buffer, const QString& deviceNode, const quint64 firstByte) { d->m_Report = commandReport.newChild(); if (report()) report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); bool rval = true; if (!QDBusConnection::systemBus().isConnected()) { qWarning() << QDBusConnection::systemBus().lastError().message(); return false; } auto interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days QDBusPendingCall pcall = interface->writeData(buffer, deviceNode, firstByte); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; rval = reply.argumentAt<0>(); } setExitCode(!rval); }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); return rval; } bool ExternalCommand::write(const QByteArray& input) { if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" )) qDebug() << "Command input:" << QString::fromLocal8Bit(input); d->m_Input = input; 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) /* && 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::setCommand(const QString& cmd) { d->m_Command = cmd; } const QString& ExternalCommand::command() const { return d->m_Command; } const QStringList& ExternalCommand::args() const { return d->m_Args; } void ExternalCommand::addArg(const QString& s) { d->m_Args << s; } void ExternalCommand::setArgs(const QStringList& args) { d->m_Args = args; } int ExternalCommand::exitCode() const { return d->m_ExitCode; } const QString ExternalCommand::output() const { return QString::fromLocal8Bit(d->m_Output); } const QByteArray& ExternalCommand::rawOutput() const { return d->m_Output; } Report* ExternalCommand::report() { return d->m_Report; } void ExternalCommand::setExitCode(int i) { d->m_ExitCode = i; } -/**< Dummy function for QTimer */ -void ExternalCommand::quit() -{ - -} - bool ExternalCommand::startHelper() { if (!QDBusConnection::systemBus().isConnected()) { qWarning() << "Error starting the helper application!!"; 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); } /** Authorize using Polkit backend **/ // initialize KDE Polkit daemon m_authJob->initPolkitAgent(QStringLiteral("org.kde.kpmcore.externalcommand.init"), parent); // Kick start the helper application m_authJob->startHelper(QStringLiteral("org.kde.kpmcore.externalcommand.init"), QStringLiteral("org.kde.kpmcore.externalcommand")); bool isActionAuthorized = m_authJob->authorizeAction(QStringLiteral("org.kde.kpmcore.externalcommand.init"), m_authJob->callerID()); // Wait until ExternalCommand Helper is ready and sends signal(Connect to newData signal) QEventLoop loop; auto exitLoop = [&] () { loop.exit(); }; connect(this, &ExternalCommand::newData, exitLoop); loop.exec(); if (!isActionAuthorized) { qDebug() << "Unable to obtain Administrative privileges, the action can not be executed!!"; } 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 ExternalCommand::emitNewData(int percent) { Q_UNUSED(percent) emit newData(); } void ExternalCommand::emitNewData(QString message) { Q_UNUSED(message) emit newData(); } diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index 6a3686a..0cb685e 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -1,141 +1,138 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016-2018 by Andrius Štikonas * * Copyright (C) 2019 by Shubham * * * * 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_EXTERNALCOMMAND_H #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" #include #include #include #include #include #include namespace Auth { class PolkitQt1Backend; } class CopySource; class CopyTarget; class Report; class QDBusInterface; class KJob; struct ExternalCommandPrivate; /** An external command. Runs an external command as a child process. @author Volker Lanz @author Andrius Štikonas */ class LIBKPMCORE_EXPORT ExternalCommand : public QObject { 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") Q_DISABLE_COPY(ExternalCommand) 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); ~ExternalCommand(); 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 /**< @param cmd the command to run */ void setCommand(const QString& cmd); /**< @return the command to run */ const QString& command() const; /**< @return the arguments */ const QStringList& args() const; /**< @param s the argument to add */ void addArg(const QString& s); /**< @param args the new arguments */ void setArgs(const QStringList& args); bool write(const QByteArray& input); /**< @param input the input for the program */ bool startCopyBlocks(); bool start(int timeout = 30000); bool run(int timeout = 30000); /**< @return the exit code */ int exitCode() const; /**< @return the command output */ const QString output() const; /**< @return the command output */ const QByteArray& rawOutput() const; /**< @return pointer to the Report or nullptr */ Report* report(); void emitReport(const QVariantMap& report) { emit reportSignal(report); } - /**< Dummy function for QTimer when needed. */ - void quit(); - // 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&); void newData(); public Q_SLOTS: void emitProgress(KJob*, unsigned long percent) { emit progress(percent); } Q_SCRIPTABLE void emitNewData(int percent); Q_SCRIPTABLE void emitNewData(QString message); private: void setExitCode(int i); // void onReadOutput(); private: std::unique_ptr d; // Authorize using Polkit backend static Auth::PolkitQt1Backend *m_authJob; static bool helperStarted; static QWidget *parent; }; #endif // KPMCORE_EXTERNALCOMMAND_H diff --git a/src/util/helpers.cpp b/src/util/helpers.cpp index 859d060..b3d377c 100644 --- a/src/util/helpers.cpp +++ b/src/util/helpers.cpp @@ -1,73 +1,73 @@ /************************************************************************* * Copyright (C) 2008, 2009, 2010 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 "util/helpers.h" #include "util/externalcommand.h" #include "util/globallog.h" #include "ops/operation.h" #include #include void registerMetaTypes() { qRegisterMetaType("Operation*"); qRegisterMetaType("Log::Level"); } bool caseInsensitiveLessThan(const QString& s1, const QString& s2) { return s1.toLower() < s2.toLower(); } bool isMounted(const QString& deviceNode) { ExternalCommand cmd(QStringLiteral("lsblk"), { QStringLiteral("--noheadings"), QStringLiteral("--nodeps"), QStringLiteral("--output"), QStringLiteral("mountpoint"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { return !cmd.output().trimmed().isEmpty(); } return false; } KAboutData aboutKPMcore() { KAboutData aboutData( QStringLiteral("kpmcore"), xi18nc("@title", "KPMcore"), QStringLiteral(VERSION), xi18nc("@title", "Library for managing partitions"), KAboutLicense::GPL_V3, xi18nc("@info:credit", "© 2008-2019 KPMcore developers" ) ); aboutData.setOrganizationDomain(QByteArray("kde.org")); aboutData.setProductName(QByteArray("kpmcore")); aboutData.setHomepage(QStringLiteral("https://commits.kde.org/kpmcore")); aboutData.addAuthor(xi18nc("@info:credit", "Volker Lanz"), xi18nc("@info:credit", "Former maintainer")); aboutData.addAuthor(xi18nc("@info:credit", "Andrius Štikonas"), xi18nc("@info:credit", "Maintainer"), QStringLiteral("andrius@stikonas.eu")); aboutData.addCredit(xi18nc("@info:credit", "Teo Mrnjavac"), i18nc("@info:credit", "Former Calamares maintainer"), QStringLiteral("teo@kde.org")); aboutData.addCredit(xi18nc("@info:credit", "Chantara Tith"), i18nc("@info:credit", "LVM support"), QStringLiteral("tith.chantara@gmail.com")); 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 Carvalho"), i18nc("@info:credit", "Improved SMART support"), QStringLiteral("caiojcarvalho@gmail.com")); + aboutData.addCredit(xi18nc("@info:credit", "Caio Jordão Carvalho"), i18nc("@info:credit", "Improved SMART support"), QStringLiteral("caiojcarvalho@gmail.com")); return aboutData; }