diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f863a7..c3ac673 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,14 +16,22 @@ project(kpmcore) -cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(CMAKE_USE_RELATIVE_PATHS OFF) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) -set(QT_MIN_VERSION "5.7.0") +# Dependencies +set(QT_MIN_VERSION "5.10.0") +set(KF5_MIN_VERSION "5.25") +set(BLKID_MIN_VERSION "2.32") +# Qca-qt5 (tested with botan and ossl backends) + +# Runtime +# smartmontools 6.7 + set(VERSION_MAJOR "3") -set(VERSION_MINOR "3") +set(VERSION_MINOR "50") set(VERSION_RELEASE "0") set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}) set(SOVERSION "8") @@ -32,7 +40,7 @@ add_definitions(-D'VERSION="${VERSION}"') #" set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(ECM 1.0.0 REQUIRED NO_MODULE) +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/") include(KDEInstallDirs) @@ -41,7 +49,6 @@ include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(GenerateExportHeader) include(ECMSetupVersion) -include(ECMPackageConfigHelpers) ecm_setup_version(${VERSION} VARIABLE_PREFIX KPMCORE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpmcore_version.h" @@ -56,12 +63,15 @@ find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS ) # Load the frameworks we need -find_package(KF5 REQUIRED +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED + Auth I18n CoreAddons WidgetsAddons ) +find_package(Qca-qt5 REQUIRED) + # use sane compile flags add_definitions( -DQT_USE_QSTRINGBUILDER @@ -76,10 +86,10 @@ add_definitions( ) kde_enable_exceptions() - -find_package(PkgConfig REQUIRED) -pkg_check_modules(BLKID REQUIRED blkid>=2.30) -pkg_check_modules(LIBATASMART REQUIRED libatasmart) +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/) @@ -89,7 +99,7 @@ add_subdirectory(src) set(INCLUDE_INSTALL_DIR "include/kpmcore/") set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KPMcore") -ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KPMcoreConfig.cmake.in" +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 diff --git a/INSTALL b/INSTALL index 7abd18e..a2f232e 100644 --- a/INSTALL +++ b/INSTALL @@ -4,24 +4,16 @@ Building and installing KDE Partition Manager Core Library from source 1. Dependencies -libparted: Either get it from http://www.gnu.org/software/parted/download.shtml -and build it yourself or, preferably, install your distribution's packages -(don't forget the dev-package). - -libblkid: Part of the util-linux project available at -https://github.com/karelzak/util-linux - -libatasmart: Available from http://0pointer.de/blog/projects/being-smart.html +util-linux 2.32: available at https://github.com/karelzak/util-linux KDE Frameworks: The minimum required version is 5.0. - 2. Configure KPMcore is built with cmake. It is recommended to build out of tree: After unpacking the source, create a separate build directory and run cmake there: -$ tar xfj kpmcore-x.y.z.tar.bz2 +$ tar xf kpmcore-x.y.z.tar.xz $ cd kpmcore-x.y.z $ mkdir build $ cd build diff --git a/README.md b/README.md index 15e4127..9b16d66 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ of storage devices on a system: There are multiple backends so that KPMcore can support different operating systems, although the only functional backend is the one for Linux systems: -* libparted backend (Linux) +* sfdisk backend (Linux) * null backend ## Using KPMcore diff --git a/cmake/modules/FindLIBPARTED.cmake b/cmake/modules/FindLIBPARTED.cmake deleted file mode 100644 index b689b40..0000000 --- a/cmake/modules/FindLIBPARTED.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) 2008,2010,2011 by Volker Lanz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -include(CheckCSourceCompiles) -include(CheckFunctionExists) - -if (LIBPARTED_INCLUDE_DIR AND LIBPARTED_LIBRARY) - # Already in cache, be silent - set(LIBPARTED_FIND_QUIETLY TRUE) -endif (LIBPARTED_INCLUDE_DIR AND LIBPARTED_LIBRARY) - - -FIND_PATH(LIBPARTED_INCLUDE_DIR parted.h PATH_SUFFIXES parted ) - -FIND_LIBRARY(LIBPARTED_LIBRARY NAMES parted) -FIND_LIBRARY(LIBPARTED_FS_RESIZE_LIBRARY NAMES parted-fs-resize) - -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPARTED DEFAULT_MSG LIBPARTED_LIBRARY LIBPARTED_INCLUDE_DIR) - -if (LIBPARTED_FS_RESIZE_LIBRARY) - set(LIBPARTED_LIBS ${LIBPARTED_FS_RESIZE_LIBRARY} ${LIBPARTED_LIBRARY}) -else (LIBPARTED_FS_RESIZE_LIBRARY) - set(LIBPARTED_LIBS ${LIBPARTED_LIBRARY}) -endif (LIBPARTED_FS_RESIZE_LIBRARY) - -# KDE adds -ansi to the C make flags, parted headers use GNU extensions, so -# undo that -unset(CMAKE_C_FLAGS) - -set(CMAKE_REQUIRED_INCLUDES ${LIBPARTED_INCLUDE_DIR}) -set(CMAKE_REQUIRED_LIBRARIES ${LIBPARTED_LIBS}) - -CHECK_FUNCTION_EXISTS("ped_file_system_clobber" LIBPARTED_FILESYSTEM_SUPPORT) # parted < 3.0 -CHECK_FUNCTION_EXISTS("ped_file_system_resize" LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) # parted != 3.0 - -MARK_AS_ADVANCED(LIBPARTED_LIBRARY LIBPARTED_INCLUDE_DIR LIBPARTED_FILESYSTEM_SUPPORT LIBPARTED_FS_RESIZE_LIBRARY LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6708a64..500b9a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,14 +39,17 @@ set(kpmcore_SRCS ki18n_wrap_ui(kpmcore_SRCS ${gui_UIFILES}) add_library(kpmcore SHARED ${kpmcore_SRCS}) -target_link_libraries( kpmcore - ${UUID_LIBRARIES} +target_link_libraries( kpmcore PUBLIC + Qt5::Core + PRIVATE ${BLKID_LIBRARIES} - ${LIBATASMART_LIBRARIES} Qt5::DBus + Qt5::Gui + qca-qt5 KF5::I18n KF5::CoreAddons KF5::WidgetsAddons + KF5::Auth ) install(TARGETS kpmcore EXPORT KPMcoreTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/backend/corebackend.cpp b/src/backend/corebackend.cpp index 9ce45c0..fbd3410 100644 --- a/src/backend/corebackend.cpp +++ b/src/backend/corebackend.cpp @@ -24,20 +24,18 @@ #include -class CoreBackend::CoreBackendPrivate +struct CoreBackendPrivate { -public: - CoreBackendPrivate() {} + QString m_id, m_version; }; CoreBackend::CoreBackend() : - d(new CoreBackendPrivate()) + d(std::make_unique()) { } CoreBackend::~CoreBackend() { - delete d; } void CoreBackend::emitProgress(int i) @@ -59,3 +57,23 @@ void CoreBackend::setPartitionTableMaxPrimaries(PartitionTable& p, qint32 max_pr { p.setMaxPrimaries(max_primaries); } + +QString CoreBackend::id() +{ + return d->m_id; +} + +QString CoreBackend::version() +{ + return d->m_version; +} + +void CoreBackend::setId(const QString& id) +{ + d->m_id = id; +} + +void CoreBackend::setVersion(const QString& version) +{ + d->m_version = version; +} diff --git a/src/backend/corebackend.h b/src/backend/corebackend.h index 7829ce2..386d67f 100644 --- a/src/backend/corebackend.h +++ b/src/backend/corebackend.h @@ -23,11 +23,14 @@ #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" +#include + #include #include class CoreBackendManager; class CoreBackendDevice; +struct CoreBackendPrivate; class Device; class PartitionTable; @@ -68,17 +71,13 @@ public: * Return the plugin's unique Id from JSON metadata * @return the plugin's unique Id from JSON metadata */ - QString id() { - return m_id; - } + QString id(); /** * Return the plugin's version from JSON metadata * @return the plugin's version from JSON metadata */ - QString version() { - return m_version; - } + QString version(); /** * Initialize the plugin's FileSystem support @@ -130,29 +129,23 @@ public: /** * Open a device for reading. * @param deviceNode The path of the device that is to be opened (e.g. /dev/sda) - * @return a pointer to a CoreBackendDevice or nullptr if the open failed. If a pointer to - * an instance is returned, it's the caller's responsibility to delete the - * object. + * @return a pointer to a CoreBackendDevice or nullptr if the open failed. */ - virtual CoreBackendDevice* openDevice(const Device& d) = 0; + virtual std::unique_ptr openDevice(const Device& d) = 0; /** * Open a device in exclusive mode for writing. * @param deviceNode The path of the device that is to be opened (e.g. /dev/sda) - * @return a pointer to a CoreBackendDevice or nullptr if the open failed. If a pointer to - * an instance is returned, it's the caller's responsibility to delete the - * object. + * @return a pointer to a CoreBackendDevice or nullptr if the open failed. */ - virtual CoreBackendDevice* openDeviceExclusive(const Device& d) = 0; + virtual std::unique_ptr openDeviceExclusive(const Device& d) = 0; /** * Close a CoreBackendDevice that has previously been opened. * @param core_device Pointer to the CoreBackendDevice to be closed. Must not be nullptr. * @return true if closing the CoreBackendDevice succeeded, otherwise false. - * - * This method does not delete the object. */ - virtual bool closeDevice(CoreBackendDevice* core_device) = 0; + virtual bool closeDevice(std::unique_ptr coreDevice) = 0; /** * Emit progress. @@ -176,18 +169,11 @@ protected: static void setPartitionTableMaxPrimaries(PartitionTable& p, qint32 max_primaries); private: - void setId(const QString& id) { - m_id = id; - } - void setVersion(const QString& version) { - m_version = version; - } + void setId(const QString& id); + void setVersion(const QString& version); private: - QString m_id, m_version; - - class CoreBackendPrivate; - CoreBackendPrivate* d; + std::unique_ptr d; }; #endif diff --git a/src/backend/corebackenddevice.h b/src/backend/corebackenddevice.h index 600adc6..0629f33 100644 --- a/src/backend/corebackenddevice.h +++ b/src/backend/corebackenddevice.h @@ -19,6 +19,7 @@ #define KPMCORE_COREBACKENDDEVICE_H +#include #include class CoreBackendPartition; @@ -81,7 +82,7 @@ public: * @return a pointer to the CoreBackendPartitionTable for this device or nullptr in case * of errors */ - virtual CoreBackendPartitionTable* openPartitionTable() = 0; + virtual std::unique_ptr openPartitionTable() = 0; /** * Create a new partition table on this device. @@ -91,23 +92,6 @@ public: */ virtual bool createPartitionTable(Report& report, const PartitionTable& ptable) = 0; - /** - * Read data from an opened device into a buffer. - * @param buffer the buffer to write the read data to - * @param offset offset byte where to start reading on the device - * @param size the number of bytes to read - * @return true on success - */ - virtual bool readData(QByteArray& buffer, qint64 offset, qint64 size) = 0; - - /** - * Write data from a buffer to an exclusively opened device. - * @param buffer the buffer with the data - * @param offset offset byte where to start writing to the device - * @return true on success - */ - virtual bool writeData(QByteArray& buffer, qint64 offset) = 0; - protected: void setExclusive(bool b) { m_Exclusive = b; diff --git a/src/backend/corebackendmanager.cpp b/src/backend/corebackendmanager.cpp index 7ddef4c..bb1fdd2 100644 --- a/src/backend/corebackendmanager.cpp +++ b/src/backend/corebackendmanager.cpp @@ -1,7 +1,7 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -20,8 +20,8 @@ #include "backend/corebackendmanager.h" #include "backend/corebackend.h" +#include #include -#include #include #include @@ -30,8 +30,17 @@ #include #include +struct CoreBackendManagerPrivate +{ + CoreBackend *m_Backend; +}; + CoreBackendManager::CoreBackendManager() : - m_Backend(nullptr) + d(std::make_unique()) +{ +} + +CoreBackendManager::~CoreBackendManager() { } @@ -45,6 +54,11 @@ CoreBackendManager* CoreBackendManager::self() return instance; } +CoreBackend* CoreBackendManager::backend() +{ + return d->m_Backend; +} + QVector CoreBackendManager::list() const { auto filter = [&](const KPluginMetaData &metaData) { @@ -66,7 +80,7 @@ bool CoreBackendManager::load(const QString& name) KPluginFactory* factory = loader.factory(); if (factory != nullptr) { - m_Backend = factory->create(nullptr); + d->m_Backend = factory->create(nullptr); QString id = loader.metaData().toVariantMap().value(QStringLiteral("MetaData")) .toMap().value(QStringLiteral("KPlugin")).toMap().value(QStringLiteral("Id")).toString(); @@ -78,6 +92,7 @@ bool CoreBackendManager::load(const QString& name) backend()->setId(id); backend()->setVersion(version); qDebug() << "Loaded backend plugin: " << backend()->id(); + return true; } @@ -87,6 +102,4 @@ bool CoreBackendManager::load(const QString& name) void CoreBackendManager::unload() { - delete m_Backend; - m_Backend = nullptr; } diff --git a/src/backend/corebackendmanager.h b/src/backend/corebackendmanager.h index 9946292..45ceaae 100644 --- a/src/backend/corebackendmanager.h +++ b/src/backend/corebackendmanager.h @@ -1,5 +1,6 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * + * Copyright (C) 2018 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 * @@ -15,18 +16,20 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_COREBACKENDMANAGER_H) - +#ifndef KPMCORE_COREBACKENDMANAGER_H #define KPMCORE_COREBACKENDMANAGER_H #include "util/libpartitionmanagerexport.h" +#include + #include class QString; class QStringList; class KPluginMetaData; class CoreBackend; +struct CoreBackendManagerPrivate; /** * The backend manager class. @@ -37,8 +40,8 @@ class CoreBackend; */ class LIBKPMCORE_EXPORT CoreBackendManager { -private: CoreBackendManager(); + ~CoreBackendManager(); public: /** @@ -50,7 +53,7 @@ public: * @return the name of the default backend plugin */ static QString defaultBackendName() { - return QStringLiteral("pmlibpartedbackendplugin"); + return QStringLiteral("pmsfdiskbackendplugin"); } /** @@ -73,12 +76,10 @@ public: /** * @return a pointer to the currently loaded backend */ - CoreBackend* backend() { - return m_Backend; - } + CoreBackend* backend(); private: - CoreBackend* m_Backend; + std::unique_ptr d; }; #endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bb5fb6b..719ec3a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,3 +1,5 @@ +include(core/raid/CMakeLists.txt) + set(CORE_SRC core/copysource.cpp core/copysourcedevice.cpp @@ -20,7 +22,11 @@ set(CORE_SRC core/partitiontable.cpp core/smartstatus.cpp core/smartattribute.cpp + core/smartparser.cpp + core/smartattributeparseddata.cpp + core/smartdiskinformation.cpp core/volumemanagerdevice.cpp + ${RAID_SRC} ) set(CORE_LIB_HDRS @@ -39,5 +45,5 @@ set(CORE_LIB_HDRS core/smartattribute.h core/smartstatus.h core/volumemanagerdevice.h + ${RAID_LIB_HDRS} ) - diff --git a/src/core/copysource.h b/src/core/copysource.h index d1d9ae6..4e4f447 100644 --- a/src/core/copysource.h +++ b/src/core/copysource.h @@ -22,6 +22,7 @@ #include class CopyTarget; +class QString; /** Base class for something to copy from. @@ -41,14 +42,12 @@ protected: public: virtual bool open() = 0; - virtual bool readData(QByteArray& buffer, qint64 readOffset, qint64 size) = 0; + virtual QString path() const = 0; virtual qint64 length() const = 0; virtual bool overlaps(const CopyTarget& target) const = 0; virtual qint64 firstByte() const = 0; virtual qint64 lastByte() const = 0; - -private: }; #endif diff --git a/src/core/copysourcedevice.cpp b/src/core/copysourcedevice.cpp index a0b9d1b..18fcb85 100644 --- a/src/core/copysourcedevice.cpp +++ b/src/core/copysourcedevice.cpp @@ -20,7 +20,6 @@ #include "backend/corebackend.h" #include "backend/corebackendmanager.h" -#include "backend/corebackenddevice.h" #include "core/copytarget.h" #include "core/copytargetdevice.h" @@ -40,12 +39,6 @@ CopySourceDevice::CopySourceDevice(Device& d, qint64 firstbyte, qint64 lastbyte) { } -/** Destructs a CopySourceDevice */ -CopySourceDevice::~CopySourceDevice() -{ - delete m_BackendDevice; -} - /** Opens the Device @return true if the Device could be successfully opened */ @@ -63,22 +56,6 @@ qint64 CopySourceDevice::length() const return lastByte() - firstByte() + 1; } -/** Reads a given number of bytes from the Device into the given buffer. - - Note that @p readOffset must be greater or equal than zero. - - @param buffer the buffer to store the read bytes in - @param readOffset the offset to begin reading - @param size the number of bytes to read - - @return true if successful -*/ -bool CopySourceDevice::readData(QByteArray& buffer, qint64 readOffset, qint64 size) -{ - Q_ASSERT(readOffset >= 0); - return m_BackendDevice->readData(buffer, readOffset, size); -} - /** Checks if this CopySourceDevice overlaps with the given CopyTarget @param target the CopyTarget to check overlapping with @return true if overlaps @@ -103,3 +80,8 @@ bool CopySourceDevice::overlaps(const CopyTarget& target) const return false; } + +QString CopySourceDevice::path() const +{ + return m_Device.deviceNode(); +} diff --git a/src/core/copysourcedevice.h b/src/core/copysourcedevice.h index 23f060f..07aee9d 100644 --- a/src/core/copysourcedevice.h +++ b/src/core/copysourcedevice.h @@ -19,14 +19,18 @@ #define KPMCORE_COPYSOURCEDEVICE_H +#include "backend/corebackenddevice.h" #include "core/copysource.h" #include "util/libpartitionmanagerexport.h" +#include + #include class Device; class CopyTarget; class CoreBackendDevice; +class QString; /** A Device to copy from. @@ -40,11 +44,9 @@ class CopySourceDevice : public CopySource public: CopySourceDevice(Device& d, qint64 firstbyte, qint64 lastbyte); - ~CopySourceDevice(); public: bool open() override; - bool readData(QByteArray& buffer, qint64 readOffset, qint64 size) override; qint64 length() const override; bool overlaps(const CopyTarget& target) const override; @@ -62,12 +64,13 @@ public: return m_Device; /**< @return Device to copy from */ } + QString path() const override; + protected: Device& m_Device; const qint64 m_FirstByte; const qint64 m_LastByte; - CoreBackendDevice* m_BackendDevice - ; + std::unique_ptr m_BackendDevice; }; #endif diff --git a/src/core/copysourcefile.cpp b/src/core/copysourcefile.cpp index 936aec0..40c3a15 100644 --- a/src/core/copysourcefile.cpp +++ b/src/core/copysourcefile.cpp @@ -45,18 +45,3 @@ qint64 CopySourceFile::length() const { return QFileInfo(file()).size(); } - -/** Reads the given number of bytes from the file into the given buffer. - @param buffer buffer to store the bytes read in - @param readOffset offset where to begin reading - @param size the number of bytes to read - @return true on success -*/ -bool CopySourceFile::readData(QByteArray& buffer, qint64 readOffset, qint64 size) -{ - if (!file().seek(readOffset)) - return false; - - buffer = file().read(size); - return !buffer.isEmpty(); -} diff --git a/src/core/copysourcefile.h b/src/core/copysourcefile.h index bad5894..3a5cebd 100644 --- a/src/core/copysourcefile.h +++ b/src/core/copysourcefile.h @@ -40,7 +40,6 @@ public: public: bool open() override; - bool readData(QByteArray& buffer, qint64 readOffset, qint64 size) override; qint64 length() const override; bool overlaps(const CopyTarget&) const override { @@ -52,6 +51,9 @@ public: qint64 lastByte() const override { return length(); /**< @return equal to length for file. @see length() */ } + QString path() const override { + return m_File.fileName(); + } protected: QFile& file() { diff --git a/src/core/copysourceshred.cpp b/src/core/copysourceshred.cpp index b8d708e..91ab354 100644 --- a/src/core/copysourceshred.cpp +++ b/src/core/copysourceshred.cpp @@ -43,17 +43,3 @@ qint64 CopySourceShred::length() const { return size(); } - -/** Reads the given number of bytes from the source into the given buffer. - @param buffer buffer to store the data read in - @param readOffset offset where to begin reading (unused) - @param size the number of bytes to read - @return true on success -*/ -bool CopySourceShred::readData(QByteArray& buffer, qint64 readOffset, qint64 size) -{ - Q_UNUSED(readOffset); - - buffer = sourceFile().read(size); - return !buffer.isEmpty(); -} diff --git a/src/core/copysourceshred.h b/src/core/copysourceshred.h index df83366..a181100 100644 --- a/src/core/copysourceshred.h +++ b/src/core/copysourceshred.h @@ -24,6 +24,7 @@ #include class CopyTarget; +class QString; /** A source for securely overwriting a partition (shredding). @@ -38,7 +39,6 @@ public: public: bool open() override; - bool readData(QByteArray& buffer, qint64 readOffset, qint64 size) override; qint64 length() const override; bool overlaps(const CopyTarget&) const override { @@ -50,6 +50,9 @@ public: qint64 lastByte() const override { return length(); /**< @return equal to length for shred source. @see length() */ } + QString path() const override { + return m_SourceFile.fileName(); + } protected: QFile& sourceFile() { diff --git a/src/core/copytarget.h b/src/core/copytarget.h index e68d715..92324f6 100644 --- a/src/core/copytarget.h +++ b/src/core/copytarget.h @@ -21,6 +21,7 @@ #include +class QString; /** Base class for something to copy to. @@ -40,10 +41,9 @@ protected: public: virtual bool open() = 0; - virtual bool writeData(QByteArray& buffer, qint64 writeOffset) = 0; virtual qint64 firstByte() const = 0; virtual qint64 lastByte() const = 0; - + virtual QString path() const = 0; qint64 bytesWritten() const { return m_BytesWritten; } diff --git a/src/core/copytargetdevice.cpp b/src/core/copytargetdevice.cpp index e80876c..b5dd671 100644 --- a/src/core/copytargetdevice.cpp +++ b/src/core/copytargetdevice.cpp @@ -19,11 +19,9 @@ #include "backend/corebackend.h" #include "backend/corebackendmanager.h" -#include "backend/corebackenddevice.h" #include "core/device.h" - /** Constructs a device to copy to. @param d the Device to copy to @param firstbyte the first byte on the Device to write to @@ -38,12 +36,6 @@ CopyTargetDevice::CopyTargetDevice(Device& d, qint64 firstbyte, qint64 lastbyte) { } -/** Destructs a CopyTargetDevice */ -CopyTargetDevice::~CopyTargetDevice() -{ - delete m_BackendDevice; -} - /** Opens a CopyTargetDevice for writing to. @return true on success */ @@ -53,21 +45,7 @@ bool CopyTargetDevice::open() return m_BackendDevice != nullptr; } -/** Writes the given number of bytes to the Device. - - Note that @p writeOffset must be greater or equal than zero. - - @param buffer the data to write - @param writeOffset where to start writing on the Device - @return true on success -*/ -bool CopyTargetDevice::writeData(QByteArray& buffer, qint64 writeOffset) +QString CopyTargetDevice::path() const { - Q_ASSERT(writeOffset >= 0); - bool rval = m_BackendDevice->writeData(buffer, writeOffset); - - if (rval) - setBytesWritten(bytesWritten() + buffer.size()); - - return rval; + return m_Device.deviceNode(); } diff --git a/src/core/copytargetdevice.h b/src/core/copytargetdevice.h index 062acf7..3306b47 100644 --- a/src/core/copytargetdevice.h +++ b/src/core/copytargetdevice.h @@ -19,9 +19,12 @@ #define KPMCORE_COPYTARGETDEVICE_H +#include "backend/corebackenddevice.h" #include "core/copytarget.h" #include "util/libpartitionmanagerexport.h" +#include + #include class Device; @@ -42,11 +45,9 @@ class CopyTargetDevice : public CopyTarget public: CopyTargetDevice(Device& d, qint64 firstbyte, qint64 lastbyte); - ~CopyTargetDevice(); public: bool open() override; - bool writeData(QByteArray& buffer, qint64 writeOffset) override; qint64 firstByte() const override { return m_FirstByte; /**< @return the first byte to write to */ } @@ -60,10 +61,11 @@ public: const Device& device() const { return m_Device; /**< @return the Device to write to */ } + QString path() const override; protected: Device& m_Device; - CoreBackendDevice* m_BackendDevice; + std::unique_ptr m_BackendDevice; const qint64 m_FirstByte; const qint64 m_LastByte; }; diff --git a/src/core/copytargetfile.cpp b/src/core/copytargetfile.cpp index 508f9b2..bd87637 100644 --- a/src/core/copytargetfile.cpp +++ b/src/core/copytargetfile.cpp @@ -33,21 +33,3 @@ bool CopyTargetFile::open() { return file().open(QIODevice::WriteOnly | QIODevice::Truncate); } - -/** Writes the given number of bytes from the given buffer to the file. - @param buffer the data to write - @param writeOffset where in the file to start writing - @return true on success -*/ -bool CopyTargetFile::writeData(QByteArray& buffer, qint64 writeOffset) -{ - if (!file().seek(writeOffset)) - return false; - - bool rval = file().write(buffer) == buffer.size(); - - if (rval) - setBytesWritten(bytesWritten() + buffer.size()); - - return rval; -} diff --git a/src/core/copytargetfile.h b/src/core/copytargetfile.h index 3e3ef3e..258a403 100644 --- a/src/core/copytargetfile.h +++ b/src/core/copytargetfile.h @@ -40,7 +40,6 @@ public: public: bool open() override; - bool writeData(QByteArray& buffer, qint64 writeOffset) override; qint64 firstByte() const override { return 0; /**< @return always 0 for a file */ @@ -49,6 +48,10 @@ public: return bytesWritten(); /**< @return the number of bytes written so far */ } + QString path() const override { + return m_File.fileName(); + } + protected: QFile& file() { return m_File; diff --git a/src/core/device.cpp b/src/core/device.cpp index 7e2c4a5..8368633 100644 --- a/src/core/device.cpp +++ b/src/core/device.cpp @@ -1,6 +1,6 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -17,6 +17,7 @@ *************************************************************************/ #include "core/device.h" +#include "core/device_p.h" #include "core/partitiontable.h" #include "core/smartstatus.h" @@ -28,22 +29,24 @@ @param name the Device's name, usually some string defined by the manufacturer @param deviceNode the Device's node, for example "/dev/sda" */ -Device::Device(const QString& name, +Device::Device(std::shared_ptr d_ptr, + const QString& name, const QString& deviceNode, - const qint64 logicalSize, - const qint64 totalLogical, + const qint64 logicalSectorSize, + const qint64 totalLogicalSectors, const QString& iconName, Device::Type type) : QObject() - , m_Name(name.length() > 0 ? name : i18n("Unknown Device")) - , m_DeviceNode(deviceNode) - , m_LogicalSize(logicalSize) - , m_TotalLogical(totalLogical) - , m_PartitionTable(nullptr) - , m_IconName(iconName.isEmpty() ? QStringLiteral("drive-harddisk") : iconName) - , m_SmartStatus(type == Device::Disk_Device ? new SmartStatus(deviceNode) : nullptr) - , m_Type(type) + , d(d_ptr) { + d->m_Name = name.length() > 0 ? name : i18n("Unknown Device"); + d->m_DeviceNode = deviceNode; + d->m_LogicalSectorSize = logicalSectorSize; + d->m_TotalLogical = totalLogicalSectors; + d->m_PartitionTable = nullptr; + d->m_IconName = iconName.isEmpty() ? QStringLiteral("drive-harddisk") : iconName; + d->m_SmartStatus = type == Device::Type::Disk_Device ? std::make_shared(deviceNode) : nullptr; + d->m_Type = type; } /** Copy constructor for Device. @@ -51,30 +54,31 @@ Device::Device(const QString& name, */ Device::Device(const Device& other) : QObject() - , m_Name(other.m_Name) - , m_DeviceNode(other.m_DeviceNode) - , m_LogicalSize(other.m_LogicalSize) - , m_TotalLogical(other.m_TotalLogical) - , m_PartitionTable(nullptr) - , m_IconName(other.m_IconName) - , m_SmartStatus(nullptr) - , m_Type(other.m_Type) + , d(std::make_shared()) { - if (other.m_PartitionTable) - m_PartitionTable = new PartitionTable(*other.m_PartitionTable); - if (other.m_SmartStatus) - m_SmartStatus = new SmartStatus(*other.m_SmartStatus); + d->m_Name = other.d->m_Name; + d->m_DeviceNode = other.d->m_DeviceNode; + d->m_LogicalSectorSize = other.d->m_LogicalSectorSize; + d->m_TotalLogical = other.d->m_TotalLogical; + d->m_PartitionTable = nullptr; + d->m_IconName = other.d->m_IconName; + d->m_SmartStatus = nullptr; + d->m_Type = other.d->m_Type; + d->m_SmartStatus = other.d->m_SmartStatus; + + if (other.d->m_PartitionTable) + d->m_PartitionTable = new PartitionTable(*other.d->m_PartitionTable); } /** Destructs a Device. */ Device::~Device() { - delete m_PartitionTable; + delete d->m_PartitionTable; } bool Device::operator==(const Device& other) const { - return m_DeviceNode == other.m_DeviceNode; + return d->m_DeviceNode == other.d->m_DeviceNode; } bool Device::operator!=(const Device& other) const @@ -86,3 +90,68 @@ QString Device::prettyName() const { return xi18nc("@item:inlistbox Device name – Capacity (device node)", "%1 – %2 (%3)", name(), Capacity::formatByteSize(capacity()), deviceNode()); } + +QString& Device::name() +{ + return d->m_Name; +} + +const QString& Device::name() const +{ + return d->m_Name; +} + +const QString& Device::deviceNode() const +{ + return d->m_DeviceNode; +} + +qint64 Device::logicalSize() const +{ + return d->m_LogicalSectorSize; +} + +qint64 Device::totalLogical() const +{ + return d->m_TotalLogical; +} + +PartitionTable* Device::partitionTable() +{ + return d->m_PartitionTable; +} + +const PartitionTable* Device::partitionTable() const +{ + return d->m_PartitionTable; +} + +void Device::setPartitionTable(PartitionTable* ptable) +{ + d->m_PartitionTable = ptable; +} + +const QString& Device::iconName() const +{ + return d->m_IconName; +} + +void Device::setIconName(const QString& name) +{ + d->m_IconName = name; +} + +SmartStatus& Device::smartStatus() +{ + return *(d->m_SmartStatus); +} + +const SmartStatus& Device::smartStatus() const +{ + return *(d->m_SmartStatus); +} + +Device::Type Device::type() const +{ + return d->m_Type; +} diff --git a/src/core/device.h b/src/core/device.h index a32f77b..68c40e3 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_DEVICE_H) - +#ifndef KPMCORE_DEVICE_H #define KPMCORE_DEVICE_H #include "util/libpartitionmanagerexport.h" @@ -24,10 +23,13 @@ #include #include +#include + class PartitionTable; class CreatePartitionTableOperation; class CoreBackend; class SmartStatus; +class DevicePrivate; /** A device description. @@ -48,96 +50,66 @@ class LIBKPMCORE_EXPORT Device : public QObject friend class CoreBackend; public: - enum Type { - Disk_Device = 0, - LVM_Device = 1, /* VG */ - RAID_Device = 2, /* software RAID device */ - Unknown_Device = 4 + enum class Type { + Unknown_Device, + Disk_Device, + LVM_Device, /* VG */ + SoftwareRAID_Device, /* software RAID device, i.e. mdraid */ + FakeRAID_Device, /* fake RAID device, i.e. dmraid */ }; -protected: - explicit Device(const QString& name, const QString& deviceNode, const qint64 logicalSize, const qint64 totalLogical, const QString& iconName = QString(), Device::Type type = Device::Disk_Device); + explicit Device(std::shared_ptr d_ptr, const QString& name, const QString& deviceNode, const qint64 logicalSectorSize, const qint64 totalLogicalSectors, const QString& iconName = QString(), Device::Type type = Device::Type::Disk_Device); public: explicit Device(const Device& other); virtual ~Device(); -public: virtual bool operator==(const Device& other) const; virtual bool operator!=(const Device& other) const; - virtual QString& name() { - return m_Name; /**< @return the Device's name, usually some manufacturer string */ - } + /**< @return the Device's name, usually some manufacturer string */ + virtual QString& name(); + virtual const QString& name() const; - virtual const QString& name() const { - return m_Name; /**< @return the Device's name, usually some manufacturer string */ - } + /**< @return the Device's node, for example "/dev/sda" */ + virtual const QString& deviceNode() const; - virtual const QString& deviceNode() const { - return m_DeviceNode; /**< @return the Device's node, for example "/dev/sda" */ - } + /**< @return the logical sector size the Device uses (would be extent size for e.g. LVM devices) */ + virtual qint64 logicalSize() const; - virtual PartitionTable* partitionTable() { - return m_PartitionTable; /**< @return the Device's PartitionTable */ - } + /**< @return the total number of logical sectors on the device */ + virtual qint64 totalLogical() const; - virtual const PartitionTable* partitionTable() const { - return m_PartitionTable; /**< @return the Device's PartitionTable */ - } - - virtual qint64 capacity() const { /**< @return the Device's capacity in bytes */ - return logicalSize() * totalLogical(); - } - - virtual void setIconName(const QString& name) { - m_IconName = name; - } - - virtual const QString& iconName() const { - return m_IconName; /**< @return suggested icon name for this Device */ - } - - virtual SmartStatus& smartStatus() { - return *m_SmartStatus; - } - - virtual const SmartStatus& smartStatus() const { - return *m_SmartStatus; - } + /**< @return the Device's PartitionTable */ + virtual PartitionTable* partitionTable(); + virtual const PartitionTable* partitionTable() const; /** * Change the description of the partition table for different one. * The device itself is not changed; use CreatePartitionTableOperation * for that. The Device instance becomes the owner of @p ptable . */ - virtual void setPartitionTable(PartitionTable* ptable) { - m_PartitionTable = ptable; + virtual void setPartitionTable(PartitionTable* ptable); + + virtual qint64 capacity() const { /**< @return the Device's capacity in bytes */ + return logicalSize() * totalLogical(); } - virtual qint64 logicalSize() const { - return m_LogicalSize; - } + /**< @return suggested icon name for this Device */ + virtual const QString& iconName() const; - virtual qint64 totalLogical() const { - return m_TotalLogical; - } + /**< @param name set the new Icon for this Device */ + virtual void setIconName(const QString& name); - virtual Device::Type type() const { - return m_Type; - } + virtual SmartStatus& smartStatus(); + virtual const SmartStatus& smartStatus() const; + + virtual Device::Type type() const; virtual QString prettyName() const; protected: - QString m_Name; - QString m_DeviceNode; - qint64 m_LogicalSize; - qint64 m_TotalLogical; - PartitionTable* m_PartitionTable; - QString m_IconName; - SmartStatus* m_SmartStatus; - Device::Type m_Type; + std::shared_ptr d; }; #endif diff --git a/src/core/device_p.h b/src/core/device_p.h new file mode 100644 index 0000000..97d152c --- /dev/null +++ b/src/core/device_p.h @@ -0,0 +1,43 @@ +/************************************************************************* + * Copyright (C) 2018 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_DEVICE_P_H +#define KPMCORE_DEVICE_P_H + +#include "core/device.h" + +#include + +#include + +class PartitionTable; +class SmartStatus; + +class DevicePrivate +{ +public: + QString m_Name; + QString m_DeviceNode; + qint64 m_LogicalSectorSize; + qint64 m_TotalLogical; + PartitionTable* m_PartitionTable; + QString m_IconName; + std::shared_ptr m_SmartStatus; + Device::Type m_Type; +}; + +#endif diff --git a/src/core/diskdevice.cpp b/src/core/diskdevice.cpp index 6e6fac4..38c8175 100644 --- a/src/core/diskdevice.cpp +++ b/src/core/diskdevice.cpp @@ -1,6 +1,6 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -17,6 +17,7 @@ *************************************************************************/ #include "core/diskdevice.h" +#include "core/device_p.h" #include "core/partitiontable.h" #include "core/smartstatus.h" @@ -39,14 +40,25 @@ #define BLKPBSZGET _IO(0x12,123)/* get block physical sector size */ #endif +#define d_ptr std::static_pointer_cast(d) + +class DiskDevicePrivate : public DevicePrivate +{ +public: + qint32 m_Heads; + qint32 m_SectorsPerTrack; + qint32 m_Cylinders; + qint64 m_LogicalSectorSize; + qint64 m_PhysicalSectorSize; +}; + static qint64 getPhysicalSectorSize(const QString& device_node) { /* * possible ways of getting the physical sector size for a drive: * - ioctl(BLKPBSZGET) -- supported with Linux 2.6.32 and later * - /sys/block/sda/queue/physical_block_size - * - libblkid from util-linux-ng 2.17 or later - * TODO: implement the blkid method + * - libblkid from util-linux 2.17 or later (not implemented) */ #if defined(BLKPBSZGET) @@ -87,11 +99,46 @@ DiskDevice::DiskDevice(const QString& name, qint32 cylinders, qint64 sectorSize, const QString& iconName) - : Device(name, deviceNode, sectorSize, (static_cast(heads) * cylinders * numSectors), iconName, Device::Disk_Device) - , m_Heads(heads) - , m_SectorsPerTrack(numSectors) - , m_Cylinders(cylinders) - , m_LogicalSectorSize(sectorSize) - , m_PhysicalSectorSize(getPhysicalSectorSize(deviceNode)) + : Device(std::make_shared(), name, deviceNode, sectorSize, (static_cast(heads) * cylinders * numSectors), iconName, Device::Type::Disk_Device) { + d_ptr->m_Heads = heads; + d_ptr->m_SectorsPerTrack = numSectors; + d_ptr->m_Cylinders = cylinders; + d_ptr->m_LogicalSectorSize = sectorSize; + d_ptr->m_PhysicalSectorSize = getPhysicalSectorSize(deviceNode); +} + +qint32 DiskDevice::heads() const +{ + return d_ptr->m_Heads; +} + +qint32 DiskDevice::cylinders() const +{ + return d_ptr->m_Cylinders; +} + +qint32 DiskDevice::sectorsPerTrack() const +{ + return d_ptr->m_SectorsPerTrack; +} + +qint64 DiskDevice::physicalSectorSize() const +{ + return d_ptr->m_PhysicalSectorSize; +} + +qint64 DiskDevice::logicalSectorSize() const +{ + return d_ptr->m_LogicalSectorSize; +} + +qint64 DiskDevice::totalSectors() const +{ + return static_cast(d_ptr->m_Heads) * d_ptr->m_Cylinders * d_ptr->m_SectorsPerTrack; +} + +qint64 DiskDevice::cylinderSize() const +{ + return static_cast(d_ptr->m_Heads) * d_ptr->m_SectorsPerTrack; } diff --git a/src/core/diskdevice.h b/src/core/diskdevice.h index 9c4a359..1205b88 100644 --- a/src/core/diskdevice.h +++ b/src/core/diskdevice.h @@ -1,5 +1,6 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2018 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 * @@ -15,13 +16,14 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_DISKDEVICE_H) - +#ifndef KPMCORE_DISKDEVICE_H #define KPMCORE_DISKDEVICE_H #include "util/libpartitionmanagerexport.h" #include "core/device.h" +#include + #include #include #include @@ -30,6 +32,7 @@ class PartitionTable; class CreatePartitionTableOperation; class CoreBackend; class SmartStatus; +class DiskDevicePrivate; /** A disk device. @@ -40,6 +43,7 @@ class SmartStatus; @see PartitionTable, Partition @author Volker Lanz */ + class LIBKPMCORE_EXPORT DiskDevice : public Device { Q_DISABLE_COPY(DiskDevice) @@ -51,34 +55,42 @@ public: DiskDevice(const QString& name, const QString& deviceNode, qint32 heads, qint32 numSectors, qint32 cylinders, qint64 sectorSize, const QString& iconName = QString()); public: - qint32 heads() const { - return m_Heads; /**< @return the number of heads on the Device in CHS notation */ - } - qint32 cylinders() const { - return m_Cylinders; /**< @return the number of cylinders on the Device in CHS notation */ - } - qint32 sectorsPerTrack() const { - return m_SectorsPerTrack; /**< @return the number of sectors on the Device in CHS notation */ - } - qint64 physicalSectorSize() const { - return m_PhysicalSectorSize; /**< @return the physical sector size the Device uses or -1 if unknown */ - } - qint64 logicalSectorSize() const { - return m_LogicalSectorSize; /**< @return the logical sector size the Device uses */ - } - qint64 totalSectors() const { - return static_cast(heads()) * cylinders() * sectorsPerTrack(); /**< @return the total number of sectors on the device */ - } - qint64 cylinderSize() const { - return static_cast(heads()) * sectorsPerTrack(); /**< @return the size of a cylinder on this Device in sectors */ - } + /** + * @return the number of heads on the Device in CHS notation + */ + [[deprecated]] + qint32 heads() const; -private: - qint32 m_Heads; - qint32 m_SectorsPerTrack; - qint32 m_Cylinders; - qint64 m_LogicalSectorSize; - qint64 m_PhysicalSectorSize; + /** + * @return the number of cylinders on the Device in CHS notation + */ + [[deprecated]] + qint32 cylinders() const; + + /** + * @return the number of sectors on the Device in CHS notation + */ + qint32 sectorsPerTrack() const; + + /** + * @return the physical sector size the Device uses or -1 if unknown + */ + qint64 physicalSectorSize() const; + + /** + * @return the logical sector size the Device uses + */ + qint64 logicalSectorSize() const; + + /** + * @return the total number of sectors on the device + */ + qint64 totalSectors() const; + + /** + * @return the size of a cylinder on this Device in sectors + */ + qint64 cylinderSize() const; }; #endif diff --git a/src/core/fstab.cpp b/src/core/fstab.cpp index 6b88562..6bdc7f2 100644 --- a/src/core/fstab.cpp +++ b/src/core/fstab.cpp @@ -18,38 +18,47 @@ *************************************************************************/ #include "core/fstab.h" +#include "util/externalcommand.h" -#include +#if defined(Q_OS_LINUX) + #include +#endif #include #include -#include #include #include +#include #include -static void parseFsSpec(const QString& m_fsSpec, FstabEntryType& m_entryType, QString& m_deviceNode); -static QString findBlkIdDevice(const QString& token, const QString& value); +static void parseFsSpec(const QString& m_fsSpec, FstabEntry::Type& m_entryType, QString& m_deviceNode); +static QString findBlkIdDevice(const char *token, const QString& value); -FstabEntry::FstabEntry(const QString& fsSpec, const QString& mountPoint, const QString& type, const QString& options, int dumpFreq, int passNumber, const QString& comment) - : m_fsSpec(fsSpec) - , m_mountPoint(mountPoint) - , m_type(type) - , m_dumpFreq(dumpFreq) - , m_passNumber(passNumber) - , m_comment(comment) +struct FstabEntryPrivate { - m_options = options.split(QLatin1Char(',')); - parseFsSpec(m_fsSpec, m_entryType, m_deviceNode); -} + QString m_fsSpec; + QString m_deviceNode; + QString m_mountPoint; + QString m_type; + QStringList m_options; + int m_dumpFreq; + int m_passNumber; + QString m_comment; + FstabEntry::Type m_entryType; +}; -/** - @param s the new value for the fs_spec field of fstab entry -*/ -void FstabEntry::setFsSpec(const QString& s) +FstabEntry::FstabEntry(const QString& fsSpec, const QString& mountPoint, const QString& type, const QString& options, int dumpFreq, int passNumber, const QString& comment) : + d(std::make_unique()) { - m_fsSpec = s; - parseFsSpec(m_fsSpec, m_entryType, m_deviceNode); + d->m_fsSpec = fsSpec; + d->m_mountPoint = mountPoint; + d->m_type = type; + d->m_dumpFreq = dumpFreq; + d->m_passNumber = passNumber; + d->m_comment = comment; + + d->m_options = options.split(QLatin1Char(',')); + parseFsSpec(d->m_fsSpec, d->m_entryType, d->m_deviceNode); } FstabEntryList readFstabEntries( const QString& fstabPath ) @@ -63,7 +72,7 @@ FstabEntryList readFstabEntries( const QString& fstabPath ) { QString line = rawLine.trimmed(); if ( line.startsWith( QLatin1Char('#') ) || line.isEmpty()) { - fstabEntries.append( { {}, {}, {}, {}, {}, {}, line } ); + fstabEntries.push_back( { {}, {}, {}, {}, {}, {}, line } ); continue; } @@ -80,27 +89,98 @@ FstabEntryList readFstabEntries( const QString& fstabPath ) // (#) comment (optional). switch (splitLine.length()) { case 4: - fstabEntries.append( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3) } ); + fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3) } ); break; case 5: - fstabEntries.append( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt() } ); + fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt() } ); break; case 6: - fstabEntries.append( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt(), splitLine.at(5).toInt(), comment.isEmpty() ? QString() : QLatin1Char('#') + comment } ); + fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt(), splitLine.at(5).toInt(), comment.isEmpty() ? QString() : QLatin1Char('#') + comment } ); break; default: - fstabEntries.append( { {}, {}, {}, {}, {}, {}, QLatin1Char('#') + line } ); + fstabEntries.push_back( { {}, {}, {}, {}, {}, {}, QLatin1Char('#') + line } ); } } fstabFile.close(); - if (fstabEntries.last().entryType() == comment && fstabEntries.last().comment().isEmpty()) - fstabEntries.removeLast(); + if (fstabEntries.back().entryType() == FstabEntry::Type::comment && fstabEntries.back().comment().isEmpty()) + fstabEntries.pop_back(); } return fstabEntries; } +void FstabEntry::setFsSpec(const QString& s) +{ + d->m_fsSpec = s; + parseFsSpec(d->m_fsSpec, d->m_entryType, d->m_deviceNode); +} + +const QString& FstabEntry::fsSpec() const +{ + return d->m_fsSpec; +} + +const QString& FstabEntry::deviceNode() const +{ + return d->m_deviceNode; +} + +const QString& FstabEntry::mountPoint() const +{ + return d->m_mountPoint; +} + +const QString& FstabEntry::type() const +{ + return d->m_type; +} + +const QStringList& FstabEntry::options() const +{ + return d->m_options; +} + +int FstabEntry::dumpFreq() const +{ + return d->m_dumpFreq; +} + +int FstabEntry::passNumber() const +{ + return d->m_passNumber; +} + +const QString& FstabEntry::comment() const +{ + return d->m_comment; +} + +FstabEntry::Type FstabEntry::entryType() const +{ + return d->m_entryType; +} + +void FstabEntry::setMountPoint(const QString& s) +{ + d->m_mountPoint = s; +} + +void FstabEntry::setOptions(const QStringList& s) +{ + d->m_options = s; +} + +void FstabEntry::setDumpFreq(int s) +{ + d->m_dumpFreq = s; +} + +void FstabEntry::setPassNumber(int s) +{ + d->m_passNumber = s; +} + QStringList possibleMountPoints(const QString& deviceNode, const QString& fstabPath) { QStringList mountPoints; @@ -113,40 +193,37 @@ QStringList possibleMountPoints(const QString& deviceNode, const QString& fstabP return mountPoints; } -static QString findBlkIdDevice(const QString& token, const QString& value) +static QString findBlkIdDevice(const char *token, const QString& value) { - blkid_cache cache; QString rval; - if (blkid_get_cache(&cache, nullptr) == 0) { - if (char* c = blkid_evaluate_tag(token.toLocal8Bit().constData(), value.toLocal8Bit().constData(), &cache)) { - rval = QString::fromLocal8Bit(c); - free(c); - } - - blkid_put_cache(cache); +#if defined(Q_OS_LINUX) + if (char* c = blkid_evaluate_tag(token, value.toLocal8Bit().constData(), nullptr)) { + rval = QString::fromLocal8Bit(c); + free(c); } +#endif return rval; } -static void parseFsSpec(const QString& m_fsSpec, FstabEntryType& m_entryType, QString& m_deviceNode) +static void parseFsSpec(const QString& m_fsSpec, FstabEntry::Type& m_entryType, QString& m_deviceNode) { - m_entryType = FstabEntryType::comment; + m_entryType = FstabEntry::Type::comment; if (m_fsSpec.startsWith(QStringLiteral("UUID="))) { - m_entryType = FstabEntryType::uuid; - m_deviceNode = findBlkIdDevice(QStringLiteral("UUID"), QString(m_fsSpec).remove(QStringLiteral("UUID="))); + m_entryType = FstabEntry::Type::uuid; + m_deviceNode = findBlkIdDevice("UUID", QString(m_fsSpec).remove(QStringLiteral("UUID="))); } else if (m_fsSpec.startsWith(QStringLiteral("LABEL="))) { - m_entryType = FstabEntryType::label; - m_deviceNode = findBlkIdDevice(QStringLiteral("LABEL"), QString(m_fsSpec).remove(QStringLiteral("LABEL="))); + m_entryType = FstabEntry::Type::label; + m_deviceNode = findBlkIdDevice("LABEL", QString(m_fsSpec).remove(QStringLiteral("LABEL="))); } else if (m_fsSpec.startsWith(QStringLiteral("PARTUUID="))) { - m_entryType = FstabEntryType::uuid; - m_deviceNode = findBlkIdDevice(QStringLiteral("PARTUUID"), QString(m_fsSpec).remove(QStringLiteral("PARTUUID="))); + m_entryType = FstabEntry::Type::uuid; + m_deviceNode = findBlkIdDevice("PARTUUID", QString(m_fsSpec).remove(QStringLiteral("PARTUUID="))); } else if (m_fsSpec.startsWith(QStringLiteral("PARTLABEL="))) { - m_entryType = FstabEntryType::label; - m_deviceNode = findBlkIdDevice(QStringLiteral("PARTLABEL"), QString(m_fsSpec).remove(QStringLiteral("PARTLABEL="))); + m_entryType = FstabEntry::Type::label; + m_deviceNode = findBlkIdDevice("PARTLABEL", QString(m_fsSpec).remove(QStringLiteral("PARTLABEL="))); } else if (m_fsSpec.startsWith(QStringLiteral("/"))) { - m_entryType = FstabEntryType::deviceNode; + m_entryType = FstabEntry::Type::deviceNode; m_deviceNode = m_fsSpec; } } @@ -154,7 +231,7 @@ static void parseFsSpec(const QString& m_fsSpec, FstabEntryType& m_entryType, QS static void writeEntry(QFile& output, const FstabEntry& entry) { QTextStream s(&output); - if (entry.entryType() == FstabEntryType::comment) { + if (entry.entryType() == FstabEntry::Type::comment) { s << entry.comment() << "\n"; return; } @@ -177,34 +254,34 @@ static void writeEntry(QFile& output, const FstabEntry& entry) << entry.comment() << "\n"; } -bool writeMountpoints(const FstabEntryList fstabEntries, const QString& filename) +bool writeMountpoints(const FstabEntryList& fstabEntries, const QString& filename) { - bool rval = true; - const QString newFilename = QStringLiteral("%1.new").arg(filename); - QFile out(newFilename); + QTemporaryFile out; + out.setAutoRemove(false); - if (!out.open(QFile::ReadWrite | QFile::Truncate)) { - qWarning() << "could not open output file " << newFilename; - rval = false; + if (!out.open()) { + qWarning() << "could not open output file " << out.fileName(); + return false; } else { for (const auto &e : fstabEntries) writeEntry(out, e); out.close(); - const QString bakFilename = QStringLiteral("%1.bak").arg(filename); - QFile::remove(bakFilename); + ExternalCommand mvCmd(QStringLiteral("mv"), { filename, bakFilename } ); - if (QFile::exists(filename) && !QFile::rename(filename, bakFilename)) { - qWarning() << "could not rename " << filename << " to " << bakFilename; - rval = false; + if ( !(mvCmd.run(-1) && mvCmd.exitCode() == 0) ) { + qWarning() << "could not backup " << filename << " to " << bakFilename; + return false; } - if (rval && !QFile::rename(newFilename, filename)) { - qWarning() << "could not rename " << newFilename << " to " << filename; - rval = false; + ExternalCommand mvCmd2(QStringLiteral("mv"), { out.fileName(), filename } ); + + if ( !(mvCmd2.run(-1) && mvCmd2.exitCode() == 0) ) { + qWarning() << "could not move " << out.fileName() << " to " << filename; + return false; } } - return rval; + return true; } diff --git a/src/core/fstab.h b/src/core/fstab.h index b813c64..d158813 100644 --- a/src/core/fstab.h +++ b/src/core/fstab.h @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 2017 by Andrius Štikonas * + * Copyright (C) 2017-2018 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 * @@ -20,10 +20,12 @@ #include "util/libpartitionmanagerexport.h" +#include + #include #include -enum FstabEntryType { deviceNode, uuid, label, partlabel, partuuid, comment }; +struct FstabEntryPrivate; /** Base class for fstab handling. @@ -35,65 +37,88 @@ enum FstabEntryType { deviceNode, uuid, label, partlabel, partuuid, comment }; class LIBKPMCORE_EXPORT FstabEntry { public: + enum class Type { deviceNode, uuid, label, partlabel, partuuid, comment }; + FstabEntry(const QString& fsSpec, const QString& mountPoint, const QString& type, const QString& options, int dumpFreq = 0, int passNumber = 0, const QString& comment = QString()); - const QString& fsSpec() const { - return m_fsSpec; /**< @return the fs_spec field of fstab entry */ - } - const QString& deviceNode() const { - return m_deviceNode; /**< @return the device node corresponding to fs_spec entry */ - } - const QString& mountPoint() const { - return m_mountPoint; /**< @return the mount point (target) for the file system */ - } - const QString& type() const { - return m_type; /**< @return the type of the file system */ - } - const QStringList& options() const { - return m_options; /**< @return the mount options associated with the file system */ - } - int dumpFreq() const { - return m_dumpFreq; /**< @return the fs_freq field of fstab entry */ - } - int passNumber() const { - return m_passNumber; /**< @return the fs_passno field of fstab entry */ - } - const QString& comment() const { - return m_comment; /**< @return commented part of the line in fstab file */ - } - FstabEntryType entryType() const { - return m_entryType; /**< @return the type of fstab entry, e.g. device node or UUID or comment only */ - } + /** + * @return the fs_spec field of fstab entry + */ + const QString& fsSpec() const; + /** + * @return the device node corresponding to fs_spec entry + */ + const QString& deviceNode() const; + + /** + * @return the mount point (target) for the file system + */ + const QString& mountPoint() const; + + /** + * @return the type of the file system + */ + const QString& type() const; + + /** + * @return the mount options associated with the file system + */ + const QStringList& options() const; + + /** + * @return the fs_freq field of fstab entry + */ + int dumpFreq() const; + + /** + * @return the fs_passno field of fstab entry + */ + int passNumber() const; + + /** + * @return commented part of the line in fstab file + */ + const QString& comment() const; + + /** + * @return the type of fstab entry, e.g. device node or UUID or comment only + */ + Type entryType() const; + + /** + * @param s the new value for the fs_spec field of fstab entry + */ void setFsSpec(const QString& s); - void setMountPoint(const QString& s) { - m_mountPoint = s; /**< @param s the new value for the mount point */ - } - void setOptions(const QStringList& s) { - m_options = s; /**< @param s the new list with the mount options */ - } - void setDumpFreq(int s) { - m_dumpFreq = s; /**< @param s the new value for the dump frequency */ - } - void setPassNumber(int s) { - m_passNumber = s; /**< @param s the new value for the pass number */ - } + + /** + * @param s the new value for the mount point + */ + void setMountPoint(const QString& s); + + /** + * @param s the new list with the mount options + */ + void setOptions(const QStringList& s); + + /** + * @param s the new value for the dump frequency + */ + void setDumpFreq(int s); + + /** + * @param s the new value for the pass number + */ + void setPassNumber(int s); + private: - QString m_fsSpec; - QString m_deviceNode; - QString m_mountPoint; - QString m_type; - QStringList m_options; - int m_dumpFreq; - int m_passNumber; - QString m_comment; - FstabEntryType m_entryType; + std::shared_ptr d; }; typedef QList FstabEntryList; LIBKPMCORE_EXPORT FstabEntryList readFstabEntries(const QString& fstabPath = QStringLiteral("/etc/fstab")); LIBKPMCORE_EXPORT QStringList possibleMountPoints(const QString& deviceNode, const QString& fstabPath = QStringLiteral("/etc/fstab")); -LIBKPMCORE_EXPORT bool writeMountpoints(const FstabEntryList fstabEntries, const QString& filename = QStringLiteral("/etc/fstab")); +LIBKPMCORE_EXPORT bool writeMountpoints(const FstabEntryList& fstabEntries, const QString& filename = QStringLiteral("/etc/fstab")); #endif diff --git a/src/core/lvmdevice.cpp b/src/core/lvmdevice.cpp index 7c0ea0b..203fb0b 100644 --- a/src/core/lvmdevice.cpp +++ b/src/core/lvmdevice.cpp @@ -18,14 +18,16 @@ #include "core/lvmdevice.h" #include "core/partition.h" +#include "core/partitiontable.h" +#include "core/volumemanagerdevice_p.h" #include "fs/filesystem.h" #include "fs/lvm2_pv.h" #include "fs/luks.h" #include "fs/filesystemfactory.h" -#include "core/partitiontable.h" #include "util/externalcommand.h" #include "util/helpers.h" +#include "util/globallog.h" #include "util/report.h" #include @@ -34,26 +36,43 @@ #include +#define d_ptr std::static_pointer_cast(d) + +class LvmDevicePrivate : public VolumeManagerDevicePrivate +{ +public: + qint64 m_peSize; + qint64 m_totalPE; + qint64 m_allocPE; + qint64 m_freePE; + QString m_UUID; + + mutable QStringList m_LVPathList; + QVector m_PVs; + mutable std::unique_ptr> m_LVSizeMap; +}; + /** Constructs a representation of LVM device with initialized LV as Partitions * * @param vgName Volume Group name * @param iconName Icon representing LVM Volume group */ LvmDevice::LvmDevice(const QString& vgName, const QString& iconName) - : VolumeManagerDevice(vgName, + : VolumeManagerDevice(std::make_shared(), + vgName, (QStringLiteral("/dev/") + vgName), getPeSize(vgName), getTotalPE(vgName), iconName, - Device::LVM_Device) + Device::Type::LVM_Device) { - m_peSize = logicalSize(); - m_totalPE = totalLogical(); - m_freePE = getFreePE(vgName); - m_allocPE = m_totalPE - m_freePE; - m_UUID = getUUID(vgName); - m_LVPathList = new QStringList(getLVs(vgName)); - m_LVSizeMap = new QHash(); + d_ptr->m_peSize = logicalSize(); + d_ptr->m_totalPE = totalLogical(); + d_ptr->m_freePE = getFreePE(vgName); + d_ptr->m_allocPE = d_ptr->m_totalPE - d_ptr->m_freePE; + d_ptr->m_UUID = getUUID(vgName); + d_ptr->m_LVPathList = getLVs(vgName); + d_ptr->m_LVSizeMap = std::make_unique>(); initPartitions(); } @@ -64,36 +83,47 @@ LvmDevice::LvmDevice(const QString& vgName, const QString& iconName) */ QVector LvmDevice::s_DirtyPVs; + +/** + * shared list of PVs paths that are member of VGs that will be deleted soon. + */ +QVector LvmDevice::s_OrphanPVs; + LvmDevice::~LvmDevice() { - delete m_LVPathList; - delete m_LVSizeMap; } void LvmDevice::initPartitions() { qint64 firstUsable = 0; - qint64 lastusable = totalPE() - 1; - PartitionTable* pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastusable); + qint64 lastUsable = totalPE() - 1; + PartitionTable* pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastUsable); for (const auto &p : scanPartitions(pTable)) { LVSizeMap()->insert(p->partitionPath(), p->length()); pTable->append(p); } - pTable->updateUnallocated(*this); + if (pTable) + pTable->updateUnallocated(*this); + else + pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastUsable); setPartitionTable(pTable); } /** + * Scan LVM LV Partitions + * + * @param pTable Virtual PartitionTable of LVM device * @return an initialized Partition(LV) list */ const QList LvmDevice::scanPartitions(PartitionTable* pTable) const { QList pList; for (const auto &lvPath : partitionNodes()) { - pList.append(scanPartition(lvPath, pTable)); + Partition *p = scanPartition(lvPath, pTable); + pList.append(p); } return pList; } @@ -129,7 +159,7 @@ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTabl bool mounted; // Handle LUKS partition - if (fs->type() == FileSystem::Luks) { + if (fs->type() == FileSystem::Type::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = static_cast(fs); luksFs->initLUKS(); @@ -141,9 +171,9 @@ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTabl mountPoint = FileSystem::detectMountPoint(fs, lvPath); mounted = FileSystem::detectMountStatus(fs, lvPath); - if (mountPoint != QString() && fs->type() != FileSystem::LinuxSwap) { + if (mountPoint != QString() && fs->type() != FileSystem::Type::LinuxSwap) { const QStorageInfo storage = QStorageInfo(mountPoint); - if (logicalSize() > 0 && fs->type() != FileSystem::Luks && mounted && storage.isValid()) + if (logicalSize() > 0 && fs->type() != FileSystem::Type::Luks && mounted && storage.isValid()) fs->setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / logicalSize() ); } else if (fs->supportGetUsed() == FileSystem::cmdSupportFileSystem) @@ -175,23 +205,26 @@ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTabl */ void LvmDevice::scanSystemLVM(QList& devices) { + LvmDevice::s_OrphanPVs.clear(); + QList lvmList; for (const auto &vgName : getVGs()) { lvmList.append(new LvmDevice(vgName)); } - // Some LVM operations require additional information about LVM physical volumes which we store in LVM::pvList - LVM::pvList = FS::lvm2_pv::getPVs(devices); + // Some LVM operations require additional information about LVM physical volumes which we store in LVM::pvList::list() + LVM::pvList::list().clear(); + LVM::pvList::list().append(FS::lvm2_pv::getPVs(devices)); // Look for LVM physical volumes in LVM VGs for (const auto &d : lvmList) { devices.append(d); - LVM::pvList.append(FS::lvm2_pv::getPVinNode(d->partitionTable())); + LVM::pvList::list().append(FS::lvm2_pv::getPVinNode(d->partitionTable())); } // Inform LvmDevice about which physical volumes form that particular LvmDevice for (const auto &d : lvmList) - for (const auto &p : qAsConst(LVM::pvList)) + for (const auto &p : qAsConst(LVM::pvList::list())) if (p.vgName() == d->name()) d->physicalVolumes().append(p.partition()); @@ -225,9 +258,9 @@ const QStringList LvmDevice::deviceNodes() const return pvList; } -const QStringList LvmDevice::partitionNodes() const +const QStringList& LvmDevice::partitionNodes() const { - return *LVPathList(); + return d_ptr->m_LVPathList; } qint64 LvmDevice::partitionSize(QString& partitionPath) const @@ -240,7 +273,7 @@ const QStringList LvmDevice::getVGs() QStringList vgList; QString output = getField(QStringLiteral("vg_name")); if (!output.isEmpty()) { - const QStringList vgNameList = output.split(QStringLiteral("\n"), QString::SkipEmptyParts); + const QStringList vgNameList = output.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const auto &vgName : vgNameList) { vgList.append(vgName.trimmed()); } @@ -254,7 +287,7 @@ const QStringList LvmDevice::getLVs(const QString& vgName) QString cmdOutput = getField(QStringLiteral("lv_path"), vgName); if (cmdOutput.size()) { - const QStringList tempPathList = cmdOutput.split(QStringLiteral("\n"), QString::SkipEmptyParts); + const QStringList tempPathList = cmdOutput.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const auto &lvPath : tempPathList) { lvPathList.append(lvPath.trimmed()); } @@ -333,6 +366,7 @@ qint64 LvmDevice::getTotalLE(const QString& lvPath) return match.captured(1).toInt(); } } + Log(Log::Level::error) << xi18nc("@info:status", "An error occurred while running lvdisplay."); return -1; } @@ -419,8 +453,7 @@ bool LvmDevice::movePV(Report& report, const QString& pvPath, const QStringList& if (FS::lvm2_pv::getAllocatedPE(pvPath) <= 0) return true; - QStringList args = QStringList(); - args << QStringLiteral("pvmove"); + QStringList args = { QStringLiteral("pvmove") }; args << pvPath; if (!destinations.isEmpty()) for (const auto &destPath : destinations) @@ -432,8 +465,7 @@ bool LvmDevice::movePV(Report& report, const QString& pvPath, const QStringList& bool LvmDevice::createVG(Report& report, const QString vgName, const QVector& pvList, const qint32 peSize) { - QStringList args = QStringList(); - args << QStringLiteral("vgcreate") << QStringLiteral("--physicalextentsize") << QString::number(peSize); + QStringList args = { QStringLiteral("vgcreate"), QStringLiteral("--physicalextentsize"), QString::number(peSize) }; args << vgName; for (const auto &p : pvList) { if (p->roles().has(PartitionRole::Luks)) @@ -492,3 +524,43 @@ bool LvmDevice::activateLV(const QString& lvPath) lvPath }); return deactivate.run(-1) && deactivate.exitCode() == 0; } + +qint64 LvmDevice::peSize() const +{ + return d_ptr->m_peSize; +} + +qint64 LvmDevice::totalPE() const +{ + return d_ptr->m_totalPE; +} + +qint64 LvmDevice::allocatedPE() const +{ + return d_ptr->m_allocPE; +} + +qint64 LvmDevice::freePE() const +{ + return d_ptr->m_freePE; +} + +QString LvmDevice::UUID() const +{ + return d_ptr->m_UUID; +} + +QVector & LvmDevice::physicalVolumes() +{ + return d_ptr->m_PVs; +} + +const QVector & LvmDevice::physicalVolumes() const +{ + return d_ptr->m_PVs; +} + +std::unique_ptr>& LvmDevice::LVSizeMap() const +{ + return d_ptr->m_LVSizeMap; +} diff --git a/src/core/lvmdevice.h b/src/core/lvmdevice.h index 8a49263..089719b 100644 --- a/src/core/lvmdevice.h +++ b/src/core/lvmdevice.h @@ -16,8 +16,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_LVMDEVICE_H) - +#ifndef KPMCORE_LVMDEVICE_H #define KPMCORE_LVMDEVICE_H #include "core/device.h" @@ -52,12 +51,12 @@ public: public: const QStringList deviceNodes() const override; - const QStringList partitionNodes() const override; + const QStringList& partitionNodes() const override; qint64 partitionSize(QString& partitionPath) const override; static QVector s_DirtyPVs; + static QVector s_OrphanPVs; -public: static void scanSystemLVM(QList& devices); static const QStringList getVGs(); @@ -89,54 +88,22 @@ public: static bool activateVG(Report& report, const LvmDevice& d); protected: - void initPartitions() override; const QList scanPartitions(PartitionTable* pTable) const; Partition* scanPartition(const QString& lvPath, PartitionTable* pTable) const; qint64 mappedSector(const QString& lvPath, qint64 sector) const override; public: - qint64 peSize() const { - return m_peSize; - } - qint64 totalPE() const { - return m_totalPE; - } - qint64 allocatedPE() const { - return m_allocPE; - } - qint64 freePE() const { - return m_freePE; - } - QString UUID() const { - return m_UUID; - } - QStringList* LVPathList() const { - return m_LVPathList; - } - QVector & physicalVolumes() { - return m_PVs; - } - const QVector & physicalVolumes() const { - return m_PVs; - } + qint64 peSize() const; + qint64 totalPE() const; + qint64 allocatedPE() const; + qint64 freePE() const; + QString UUID() const; + QVector & physicalVolumes(); + const QVector & physicalVolumes() const; protected: - QHash* LVSizeMap() const { - return m_LVSizeMap; - } - -private: - qint64 m_peSize; - qint64 m_totalPE; - qint64 m_allocPE; - qint64 m_freePE; - QString m_UUID; - - mutable QStringList* m_LVPathList; - QVector m_PVs; - mutable QHash* m_LVSizeMap; - + std::unique_ptr>& LVSizeMap() const; }; #endif diff --git a/src/core/operationrunner.cpp b/src/core/operationrunner.cpp index 7906172..1880bf6 100644 --- a/src/core/operationrunner.cpp +++ b/src/core/operationrunner.cpp @@ -25,9 +25,6 @@ #include #include -#include -#include - /** Constructs an OperationRunner. @param ostack the OperationStack to act on */ @@ -50,15 +47,6 @@ void OperationRunner::run() bool status = true; // Disable Plasma removable device automounting - unsigned int currentUid = getuid(); // 0 if running as root - char *login = getlogin(); - if (login != nullptr){ - passwd* pwnam = getpwnam(login); - if (pwnam != nullptr) { - unsigned int userId = pwnam->pw_uid; // uid of original user before sudo - seteuid(userId); - } - } QStringList modules; QDBusConnection bus = QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("sessionBus")); QDBusInterface kdedInterface( QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QStringLiteral("org.kde.kded5"), bus ); @@ -69,7 +57,6 @@ void OperationRunner::run() bool automounter = modules.contains(automounterService); if (automounter) kdedInterface.call( QStringLiteral("unloadModule"), automounterService ); - seteuid(currentUid); for (int i = 0; i < numOperations(); i++) { suspendMutex().lock(); diff --git a/src/core/operationstack.cpp b/src/core/operationstack.cpp index 8e7bd57..0a9b9f5 100644 --- a/src/core/operationstack.cpp +++ b/src/core/operationstack.cpp @@ -555,7 +555,7 @@ void OperationStack::addDevice(Device* d) static bool deviceLessThan(const Device* d1, const Device* d2) { // Display alphabetically sorted disk devices above LVM VGs - if (d1->type() == Device::LVM_Device && d2->type() == Device::Disk_Device ) + if (d1->type() == Device::Type::LVM_Device && d2->type() == Device::Type::Disk_Device ) return false; return d1->deviceNode() <= d2->deviceNode(); diff --git a/src/core/partition.h b/src/core/partition.h index c675c10..63f604d 100644 --- a/src/core/partition.h +++ b/src/core/partition.h @@ -80,10 +80,10 @@ public: New, /**< from a NewOperation */ Copy, /**< from a CopyOperation */ Restore, /**< from a RestoreOperation */ - StateNone = None, - StateNew = New, - StateCopy = Copy, - StateRestore = Restore + StateNone __attribute__((deprecated("Use Partition::State::None"))) = None, + StateNew __attribute__((deprecated("Use Partition::State::New"))) = New, + StateCopy __attribute__((deprecated("Use Partition::State::Copy"))) = Copy, + StateRestore __attribute__((deprecated("Use Partition::State::Restore"))) = Restore }; Partition(PartitionNode* parent, const Device& device, const PartitionRole& role, FileSystem* fs, qint64 sectorStart, qint64 sectorEnd, QString partitionPath, PartitionTable::Flags availableFlags = PartitionTable::FlagNone, const QString& mountPoint = QString(), bool mounted = false, PartitionTable::Flags activeFlags = PartitionTable::FlagNone, State state = State::None); diff --git a/src/core/partitionalignment.cpp b/src/core/partitionalignment.cpp index e38c569..d32da6d 100644 --- a/src/core/partitionalignment.cpp +++ b/src/core/partitionalignment.cpp @@ -85,10 +85,10 @@ bool PartitionAlignment::isAligned(const Device& d, const Partition& p, bool qui bool PartitionAlignment::isAligned(const Device& d, const Partition& p, qint64 newFirst, qint64 newLast, bool quiet) { if (firstDelta(d, p, newFirst) && !quiet) - Log(Log::warning) << xi18nc("@info:status", "Partition %1 is not properly aligned (first sector: %2, modulo: %3).", p.deviceNode(), newFirst, firstDelta(d, p, newFirst)); + Log(Log::Level::warning) << xi18nc("@info:status", "Partition %1 is not properly aligned (first sector: %2, modulo: %3).", p.deviceNode(), newFirst, firstDelta(d, p, newFirst)); if (lastDelta(d, p, newLast) && !quiet) - Log(Log::warning) << xi18nc("@info:status", "Partition %1 is not properly aligned (last sector: %2, modulo: %3).", p.deviceNode(), newLast, lastDelta(d, p, newLast)); + Log(Log::Level::warning) << xi18nc("@info:status", "Partition %1 is not properly aligned (last sector: %2, modulo: %3).", p.deviceNode(), newLast, lastDelta(d, p, newLast)); return firstDelta(d, p, newFirst) == 0 && lastDelta(d, p, newLast) == 0; } diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index 98c1d06..72c0693 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -274,7 +274,7 @@ QStringList PartitionTable::flagNames(Flags flags) bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end) { - if (d.type() == Device::Disk_Device) { + if (d.type() == Device::Type::Disk_Device) { const DiskDevice& device = dynamic_cast(d); if (!parent.isRoot()) { Partition* extended = dynamic_cast(&parent); @@ -295,7 +295,7 @@ bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, } return end - start + 1 >= PartitionAlignment::sectorAlignment(device); - } else if (d.type() == Device::LVM_Device) { + } else if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { if (end - start + 1 > 0) { return true; } @@ -319,13 +319,13 @@ Partition* createUnallocated(const Device& device, PartitionNode& parent, qint64 r |= PartitionRole::Logical; // Mark unallocated space in LVM VG as LVM LV so that pasting can be easily disabled (it does not work yet) - if (device.type() == Device::LVM_Device) + if (device.type() == Device::Type::LVM_Device) r |= PartitionRole::Lvm_Lv; if (!PartitionTable::getUnallocatedRange(device, parent, start, end)) return nullptr; - return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Unknown, start, end, device.logicalSize()), start, end, QString()); + return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Type::Unknown, start, end, device.logicalSize()), start, end, QString()); } /** Removes all unallocated children from a PartitionNode @@ -379,7 +379,7 @@ void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 qint64 lastEnd = start; - if (d.type() == Device::LVM_Device && !p->children().isEmpty()) { + if (d.type() == Device::Type::LVM_Device && !p->children().isEmpty()) { // rearranging the sectors of all partitions to keep unallocated space at the end lastEnd = 0; std::sort(children().begin(), children().end(), [](const Partition* p1, const Partition* p2) { return p1->deviceNode() < p2->deviceNode(); }); @@ -402,18 +402,26 @@ void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 } } - // Take care of the free space between the end of the last child and the end - // of the device or the extended partition. - qint64 parentEnd = lastUsable(); - - if (!p->isRoot()) { - Partition* extended = dynamic_cast(p); - parentEnd = extended ? extended->lastSector() : -1; - Q_ASSERT(extended); + if (d.type() == Device::Type::LVM_Device) + { + const LvmDevice& lvm = static_cast(d); + p->insert(createUnallocated(d, *p, lastEnd, lastEnd + lvm.freePE() - 1)); } + else + { + // Take care of the free space between the end of the last child and the end + // of the device or the extended partition. + qint64 parentEnd = lastUsable(); - if (parentEnd >= firstUsable() && parentEnd >= lastEnd) - p->insert(createUnallocated(d, *p, lastEnd, parentEnd)); + if (!p->isRoot()) { + Partition* extended = dynamic_cast(p); + parentEnd = extended ? extended->lastSector() : -1; + Q_ASSERT(extended); + } + + if (parentEnd >= firstUsable() && parentEnd >= lastEnd) + p->insert(createUnallocated(d, *p, lastEnd, parentEnd)); + } } /** Updates the unallocated Partitions for this PartitionTable. @@ -428,7 +436,7 @@ void PartitionTable::updateUnallocated(const Device& d) qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t) { Q_UNUSED(t) - if (d.type() == Device::LVM_Device) { + if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { return 0; } @@ -518,7 +526,7 @@ bool PartitionTable::tableTypeIsReadOnly(TableType l) */ bool PartitionTable::isSectorBased(const Device& d) const { - if (d.type() == Device::Disk_Device) { + if (d.type() == Device::Type::Disk_Device) { const DiskDevice& diskDevice = dynamic_cast(d); if (type() == PartitionTable::msdos) { diff --git a/src/core/raid/CMakeLists.txt b/src/core/raid/CMakeLists.txt new file mode 100644 index 0000000..d2e18f1 --- /dev/null +++ b/src/core/raid/CMakeLists.txt @@ -0,0 +1,7 @@ +set(RAID_SRC + core/raid/softwareraid.cpp +) + +set(RAID_LIB_HDRS + core/raid/softwareraid.h +) diff --git a/src/core/raid/softwareraid.cpp b/src/core/raid/softwareraid.cpp new file mode 100644 index 0000000..21aebd9 --- /dev/null +++ b/src/core/raid/softwareraid.cpp @@ -0,0 +1,480 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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 "softwareraid.h" + +#include "backend/corebackend.h" +#include "backend/corebackendmanager.h" +#include "core/partition.h" +#include "core/volumemanagerdevice_p.h" +#include "fs/filesystem.h" +#include "fs/filesystemfactory.h" +#include "util/externalcommand.h" + +#include +#include +#include + +#define d_ptr std::static_pointer_cast(d) + +class SoftwareRAIDPrivate : public VolumeManagerDevicePrivate +{ +public: + qint32 m_raidLevel; + qint64 m_chunkSize; + qint64 m_totalChunk; + qint64 m_arraySize; + QString m_UUID; + QStringList m_devicePathList; + SoftwareRAID::Status m_status; +}; + +SoftwareRAID::SoftwareRAID(const QString& name, SoftwareRAID::Status status, const QString& iconName) + : VolumeManagerDevice(std::make_shared(), + name, + (QStringLiteral("/dev/") + name), + getChunkSize(QStringLiteral("/dev/") + name), + getTotalChunk(QStringLiteral("/dev/") + name), + iconName, + Device::Type::SoftwareRAID_Device) +{ + d_ptr->m_raidLevel = getRaidLevel(deviceNode()); + d_ptr->m_chunkSize = logicalSize(); + d_ptr->m_totalChunk = totalLogical(); + d_ptr->m_arraySize = getArraySize(deviceNode()); + d_ptr->m_UUID = getUUID(deviceNode()); + d_ptr->m_devicePathList = getDevicePathList(deviceNode()); + d_ptr->m_status = status; + + initPartitions(); +} + +const QStringList SoftwareRAID::deviceNodes() const +{ + return d_ptr->m_devicePathList; +} + +const QStringList& SoftwareRAID::partitionNodes() const +{ + return {}; +} + +qint64 SoftwareRAID::partitionSize(QString &partitionPath) const +{ + Q_UNUSED(partitionPath) + return 0; +} + +bool SoftwareRAID::growArray(Report &report, const QStringList &devices) +{ + Q_UNUSED(report) + Q_UNUSED(devices) + return false; +} + +bool SoftwareRAID::shrinkArray(Report &report, const QStringList &devices) +{ + Q_UNUSED(report) + Q_UNUSED(devices) + return false; +} + +QString SoftwareRAID::prettyName() const +{ + QString raidInfo; + + if (status() == SoftwareRAID::Status::Active) + raidInfo = xi18nc("@item:inlistbox [RAID level]", " [RAID %1]", raidLevel()); + else if (status() == SoftwareRAID::Status::Recovery) + raidInfo = xi18nc("@item:inlistbox [RAID level - Recovering]", " [RAID %1 - Recovering]", raidLevel()); + else if (status() == SoftwareRAID::Status::Resync) + raidInfo = xi18nc("@item:inlistbox [RAID level - Resyncing]", " [RAID %1 - Resyncing]", raidLevel()); + else + raidInfo = QStringLiteral(" [RAID]"); + + return VolumeManagerDevice::prettyName() + raidInfo; +} + +bool SoftwareRAID::operator ==(const Device& other) const +{ + bool equalDeviceNode = Device::operator ==(other); + + if (other.type() == Device::Type::SoftwareRAID_Device) { + const SoftwareRAID& raid = static_cast(other); + + if (!equalDeviceNode) + return raid.uuid() == uuid(); + } + + return equalDeviceNode; +} + +qint32 SoftwareRAID::raidLevel() const +{ + return d_ptr->m_raidLevel; +} + +qint64 SoftwareRAID::chunkSize() const +{ + return d_ptr->m_chunkSize; +} + +qint64 SoftwareRAID::totalChunk() const +{ + return d_ptr->m_totalChunk; +} + +qint64 SoftwareRAID::arraySize() const +{ + return d_ptr->m_arraySize; +} + +QString SoftwareRAID::uuid() const +{ + return d_ptr->m_UUID; +} + +QStringList SoftwareRAID::devicePathList() const +{ + return d_ptr->m_devicePathList; +} + +SoftwareRAID::Status SoftwareRAID::status() const +{ + return d_ptr->m_status; +} + +void SoftwareRAID::setStatus(SoftwareRAID::Status status) +{ + d_ptr->m_status = status; +} + +void SoftwareRAID::scanSoftwareRAID(QList& devices) +{ + QStringList availableInConf; + + // TODO: Support custom config files. + QString config = getRAIDConfiguration(QStringLiteral("/etc/mdadm.conf")); + + if (!config.isEmpty()) { + QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/([\\/\\w-]+)")); + QRegularExpressionMatchIterator i = re.globalMatch(config); + + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + QString deviceName = reMatch.captured(2).trimmed(); + + availableInConf << deviceName; + } + } + + QFile mdstat(QStringLiteral("/proc/mdstat")); + + if (mdstat.open(QIODevice::ReadOnly)) { + QTextStream stream(&mdstat); + + QString content = stream.readAll(); + + mdstat.close(); + + QRegularExpression re(QStringLiteral("md([\\/\\w]+)\\s+:\\s+([\\w]+)")); + QRegularExpressionMatchIterator i = re.globalMatch(content); + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + + QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(1).trimmed(); + QString status = reMatch.captured(2).trimmed(); + + SoftwareRAID* d = static_cast(CoreBackendManager::self()->backend()->scanDevice(deviceNode)); + + // Just to prevent segfault in some case + if (d == nullptr) + continue; + + const QStringList constAvailableInConf = availableInConf; + + for (const QString& path : constAvailableInConf) + if (getUUID(QStringLiteral("/dev/") + path) == d->uuid()) + availableInConf.removeAll(path); + + devices << d; + + if (status == QStringLiteral("inactive")) + d->setStatus(SoftwareRAID::Status::Inactive); + + if (d->raidLevel() > 0) { + QRegularExpression reMirrorStatus(d->name() + QStringLiteral("\\s+:\\s+(.*\\n\\s+)+\\[[=>.]+\\]\\s+(resync|recovery)")); + + QRegularExpressionMatch reMirrorStatusMatch = reMirrorStatus.match(content); + + if (reMirrorStatusMatch.hasMatch()) { + if (reMirrorStatusMatch.captured(2) == QStringLiteral("resync")) + d->setStatus(SoftwareRAID::Status::Resync); + else if (reMirrorStatusMatch.captured(2) == QStringLiteral("recovery")) + d->setStatus(SoftwareRAID::Status::Recovery); + } + } + } + } + + for (const QString& name : qAsConst(availableInConf)) { + SoftwareRAID *raidDevice = new SoftwareRAID(name, SoftwareRAID::Status::Inactive); + devices << raidDevice; + } +} + +qint32 SoftwareRAID::getRaidLevel(const QString &path) +{ + QString output = getDetail(path); + + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("Raid Level :\\s+\\w+(\\d+)")); + QRegularExpressionMatch reMatch = re.match(output); + if (reMatch.hasMatch()) + return reMatch.captured(1).toLongLong(); + } + + return -1; +} + +qint64 SoftwareRAID::getChunkSize(const QString &path) +{ + if (getRaidLevel(path) == 1) { + QStringList devices = getDevicePathList(path); + + if (!devices.isEmpty()) { + QString device = devices[0]; + // Look sector size for the first device/partition on the list, as RAID 1 is composed by mirrored devices + ExternalCommand sectorSize(QStringLiteral("blockdev"), { QStringLiteral("--getss"), device }); + + if (sectorSize.run(-1) && sectorSize.exitCode() == 0) { + int sectors = sectorSize.output().trimmed().toLongLong(); + return sectors; + } + } + } + else { + QString output = getDetail(path); + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("Chunk Size :\\s+(\\d+)")); + QRegularExpressionMatch reMatch = re.match(output); + if (reMatch.hasMatch()) + return reMatch.captured(1).toLongLong(); + } + } + return -1; + +} + +qint64 SoftwareRAID::getTotalChunk(const QString &path) +{ + return getArraySize(path) / getChunkSize(path); +} + +qint64 SoftwareRAID::getArraySize(const QString &path) +{ + QString output = getDetail(path); + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("Array Size :\\s+(\\d+)")); + QRegularExpressionMatch reMatch = re.match(output); + if (reMatch.hasMatch()) + return reMatch.captured(1).toLongLong() * 1024; + } + return -1; + +} + +QString SoftwareRAID::getUUID(const QString &path) +{ + QString output = getDetail(path); + + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("UUID :\\s+([\\w:]+)")); + QRegularExpressionMatch reMatch = re.match(output); + + if (reMatch.hasMatch()) + return reMatch.captured(1); + } + + // If UUID was not found in detail output, it should be searched in config file + + // TODO: Support custom config files. + QString config = getRAIDConfiguration(QStringLiteral("/etc/mdadm.conf")); + + if (!config.isEmpty()) { + QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/md([\\/\\w-]+)(.*)")); + QRegularExpressionMatchIterator i = re.globalMatch(config); + + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(2).trimmed(); + QString otherInfo = reMatch.captured(3).trimmed(); + + // Consider device node as name=host:deviceNode when the captured device node string has '-' character + // It happens when user have included the device to config file using 'mdadm --examine --scan' + if (deviceNode.contains(QLatin1Char('-'))) { + QRegularExpression reName(QStringLiteral("name=[\\w:]+\\/dev\\/md\\/([\\/\\w]+)")); + QRegularExpressionMatch nameMatch = reName.match(otherInfo); + + if (nameMatch.hasMatch()) + deviceNode = nameMatch.captured(1); + } + + if (deviceNode == path) { + QRegularExpression reUUID(QStringLiteral("(UUID=|uuid=)([\\w:]+)")); + QRegularExpressionMatch uuidMatch = reUUID.match(otherInfo); + + if (uuidMatch.hasMatch()) + return uuidMatch.captured(2); + } + } + } + + return QString(); +} + +QStringList SoftwareRAID::getDevicePathList(const QString &path) +{ + QStringList result; + + QString detail = getDetail(path); + + if (!detail.isEmpty()) { + QRegularExpression re(QStringLiteral("\\s+\\/dev\\/(\\w+)")); + QRegularExpressionMatchIterator i = re.globalMatch(detail); + + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + + QString device = QStringLiteral("/dev/") + match.captured(1); + if (device != path) + result << device; + } + } + + return result; +} + +bool SoftwareRAID::isRaidPath(const QString &path) +{ + return !getDetail(path).isEmpty(); +} + +bool SoftwareRAID::createSoftwareRAID(Report &report, + const QString &name, + const QStringList devicePathList, + const qint32 raidLevel, + const qint32 chunkSize) +{ + return false; +} + +bool SoftwareRAID::deleteSoftwareRAID(Report &report, + SoftwareRAID &raidDevice) +{ + Q_UNUSED(report) + Q_UNUSED(raidDevice) + return false; +} + +bool SoftwareRAID::assembleSoftwareRAID(const QString& deviceNode) +{ + if (!isRaidPath(deviceNode)) + return false; + + ExternalCommand cmd(QStringLiteral("mdadm"), + { QStringLiteral("--assemble"), QStringLiteral("--scan"), deviceNode }); + + return cmd.run(-1) && cmd.exitCode() == 0; +} + +bool SoftwareRAID::stopSoftwareRAID(const QString& deviceNode) +{ + if (!isRaidPath(deviceNode)) + return false; + + ExternalCommand cmd(QStringLiteral("mdadm"), + { QStringLiteral("--manage"), QStringLiteral("--stop"), deviceNode }); + + return cmd.run(-1) && cmd.exitCode() == 0; +} + +bool SoftwareRAID::reassembleSoftwareRAID(const QString &deviceNode) +{ + return stopSoftwareRAID(deviceNode) && assembleSoftwareRAID(deviceNode); +} + +bool SoftwareRAID::isRaidMember(const QString &path) +{ + QFile mdstat(QStringLiteral("/proc/mdstat")); + + if (!mdstat.open(QIODevice::ReadOnly)) + return false; + + QTextStream stream(&mdstat); + + QString content = stream.readAll(); + + mdstat.close(); + + QRegularExpression re(QStringLiteral("(\\w+)\\[\\d+\\]")); + QRegularExpressionMatchIterator i = re.globalMatch(content); + + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + + QString match = QStringLiteral("/dev/") + reMatch.captured(1); + + if (match == path) + return true; + } + + return false; +} + +void SoftwareRAID::initPartitions() +{ + +} + +qint64 SoftwareRAID::mappedSector(const QString &partitionPath, qint64 sector) const +{ + return -1; +} + +QString SoftwareRAID::getDetail(const QString &path) +{ + ExternalCommand cmd(QStringLiteral("mdadm"), + { QStringLiteral("--misc"), QStringLiteral("--detail"), path }); + return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); +} + +QString SoftwareRAID::getRAIDConfiguration(const QString &configurationPath) +{ + QFile config(configurationPath); + + if (!config.open(QIODevice::ReadOnly)) + return QString(); + + QTextStream stream(&config); + + QString result = stream.readAll(); + + config.close(); + + return result; +} diff --git a/src/core/raid/softwareraid.h b/src/core/raid/softwareraid.h new file mode 100644 index 0000000..803ec54 --- /dev/null +++ b/src/core/raid/softwareraid.h @@ -0,0 +1,103 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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_SOFTWARERAID_H) +#define KPMCORE_SOFTWARERAID_H + +#include "core/volumemanagerdevice.h" +#include "util/libpartitionmanagerexport.h" +#include "util/report.h" + +class LIBKPMCORE_EXPORT SoftwareRAID : public VolumeManagerDevice +{ + Q_DISABLE_COPY(SoftwareRAID) + +public: + enum class Status { + Active, + Inactive, + Resync, + Recovery, + }; + + SoftwareRAID(const QString& name, + SoftwareRAID::Status status = SoftwareRAID::Status::Active, + const QString& iconName = QString()); + + const QStringList deviceNodes() const override; + const QStringList& partitionNodes() const override; + qint64 partitionSize(QString &partitionPath) const override; + + virtual bool growArray(Report& report, const QStringList& devices); + + virtual bool shrinkArray(Report& report, const QStringList& devices); + + virtual QString prettyName() const override; + + virtual bool operator==(const Device& other) const override; + + qint32 raidLevel() const; + qint64 chunkSize() const; + qint64 totalChunk() const; + qint64 arraySize() const; + QString uuid() const; + QStringList devicePathList() const; + SoftwareRAID::Status status() const; + + void setStatus(SoftwareRAID::Status status); + +public: + static void scanSoftwareRAID(QList& devices); + + static qint32 getRaidLevel(const QString& path); + static qint64 getChunkSize(const QString& path); + static qint64 getTotalChunk(const QString& path); + static qint64 getArraySize(const QString& path); + static QString getUUID(const QString& path); + static QStringList getDevicePathList(const QString& path); + + static bool isRaidPath(const QString& path); + + static bool createSoftwareRAID(Report& report, + const QString& name, + const QStringList devicePathList, + const qint32 raidLevel, + const qint32 chunkSize); + + static bool deleteSoftwareRAID(Report& report, + SoftwareRAID& raidDevice); + + static bool assembleSoftwareRAID(const QString& deviceNode); + + static bool stopSoftwareRAID(const QString& deviceNode); + + static bool reassembleSoftwareRAID(const QString& deviceNode); + + static bool isRaidMember(const QString& path); + +protected: + void initPartitions() override; + + qint64 mappedSector(const QString &partitionPath, qint64 sector) const override; + +private: + static QString getDetail(const QString& path); + + static QString getRAIDConfiguration(const QString& configurationPath); +}; + +#endif // SOFTWARERAID_H diff --git a/src/core/smartattribute.cpp b/src/core/smartattribute.cpp index 0346bcb..f27bb07 100644 --- a/src/core/smartattribute.cpp +++ b/src/core/smartattribute.cpp @@ -18,78 +18,78 @@ #include "core/smartattribute.h" #include "core/smartstatus.h" +#include "core/smartattributeparseddata.h" #include #include #include -#include - static QString getAttrName(qint32 id); static QString getAttrDescription(qint32 id); -static QString getPrettyValue(quint64 value, qint64 unit); -static SmartAttribute::Assessment getAssessment(const SkSmartAttributeParsedData* a); -static QString getRaw(const uint8_t*); +static QString getPrettyValue(quint64 value, SmartAttributeUnit unit); +static SmartAttribute::Assessment getAssessment(const SmartAttributeParsedData& a); +static QString getRaw(quint64 raw); -SmartAttribute::SmartAttribute(const SkSmartAttributeParsedData* a) : - m_Id(a->id), - m_Name(getAttrName(a->id)), - m_Desc(getAttrDescription(a->id)), - m_FailureType(a->prefailure ? PreFailure : OldAge), - m_UpdateType(a->online ? Online : Offline), - m_Current(a->current_value_valid ? a->current_value : -1), - m_Worst(a->worst_value_valid ? a->worst_value : -1), - m_Threshold(a->threshold_valid ? a->threshold : -1), - m_Raw(getRaw(a->raw)), +SmartAttribute::SmartAttribute(const SmartAttributeParsedData& a) : + m_Id(a.id()), + m_Name(getAttrName(a.id())), + m_Desc(getAttrDescription(a.id())), + m_FailureType(a.prefailure() ? FailureType::PreFailure : FailureType::OldAge), + m_UpdateType(a.online() ? UpdateType::Online : UpdateType::Offline), + m_Current(a.currentValueValid() ? a.currentValue() : -1), + m_Worst(a.worstValueValid() ? a.worstValue() : -1), + m_Threshold(a.thresholdValid() ? a.threshold() : -1), + m_Raw(getRaw(a.raw())), m_Assessment(getAssessment(a)), - m_Value(getPrettyValue(a->pretty_value, a->pretty_unit)) + m_Value(getPrettyValue(a.prettyValue(), a.prettyUnit())) { + } QString SmartAttribute::assessmentToString(Assessment a) { switch (a) { - case Failing: + case Assessment::Failing: return xi18nc("@item:intable", "failing"); - case HasFailed: + case Assessment::HasFailed: return xi18nc("@item:intable", "has failed"); - case Warning: + case Assessment::Warning: return xi18nc("@item:intable", "warning"); - case Good: + case Assessment::Good: return xi18nc("@item:intable", "good"); - case NotApplicable: + case Assessment::NotApplicable: default: return xi18nc("@item:intable not applicable", "N/A"); } } -static QString getPrettyValue(quint64 value, qint64 unit) +static QString getPrettyValue(quint64 value, SmartAttributeUnit unit) { QString rval; switch (unit) { - case SK_SMART_ATTRIBUTE_UNIT_MSECONDS: + case SmartAttributeUnit::Miliseconds: rval = KFormat().formatDuration(value); break; - case SK_SMART_ATTRIBUTE_UNIT_SECTORS: + case SmartAttributeUnit::Sectors: rval = xi18ncp("@item:intable", "%1 sector", "%1 sectors", value); break; - case SK_SMART_ATTRIBUTE_UNIT_MKELVIN: + case SmartAttributeUnit::Milikelvin: rval = SmartStatus::tempToString(value); break; - case SK_SMART_ATTRIBUTE_UNIT_NONE: + case SmartAttributeUnit::None: rval = QLocale().toString(value); break; - case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN: + case SmartAttributeUnit::Unknown: default: rval = xi18nc("@item:intable not applicable", "N/A"); break; @@ -214,43 +214,41 @@ static QString getAttrDescription(qint32 id) return QString(); } -static SmartAttribute::Assessment getAssessment(const SkSmartAttributeParsedData* a) +static SmartAttribute::Assessment getAssessment(const SmartAttributeParsedData& a) { - SmartAttribute::Assessment rval = SmartAttribute::NotApplicable; + SmartAttribute::Assessment rval = SmartAttribute::Assessment::NotApplicable; bool failed = false; bool hasFailed = false; - if (a->prefailure) { - if (a->good_now_valid && !a->good_now) + if (a.prefailure()) { + if (a.goodNowValid() && !a.goodNow()) failed = true; - if (a->good_in_the_past_valid && !a->good_in_the_past) + if (a.goodInThePastValid() && !a.goodInThePast()) hasFailed = true; - } else if (a->threshold_valid) { - if (a->current_value_valid && a->current_value <= a->threshold) + } else if (a.thresholdValid()) { + if (a.currentValueValid() && a.currentValue() <= a.threshold()) failed = true; - else if (a->worst_value_valid && a->worst_value <= a->threshold) + else if (a.worstValueValid() && a.worstValue() <= a.threshold()) hasFailed = true; } if (failed) - rval = SmartAttribute::Failing; + rval = SmartAttribute::Assessment::Failing; else if (hasFailed) - rval = SmartAttribute::HasFailed; - else if (a->warn) - rval = SmartAttribute::Warning; - else if (a->good_now_valid) - rval = SmartAttribute::Good; + rval = SmartAttribute::Assessment::HasFailed; + else if (a.warn()) + rval = SmartAttribute::Assessment::Warning; + else if (a.goodNowValid()) + rval = SmartAttribute::Assessment::Good; return rval; } -static QString getRaw(const uint8_t* raw) +static QString getRaw(quint64 raw) { QString rval = QStringLiteral("0x"); - for (qint32 i = 5; i >= 0; i--) - rval += QStringLiteral("%1").arg(raw[i], 2, 16, QLatin1Char('0')); - + rval += QStringLiteral("%1").arg(raw, 12, 16, QLatin1Char('0')); return rval; } diff --git a/src/core/smartattribute.h b/src/core/smartattribute.h index 226e6eb..2df9ae9 100644 --- a/src/core/smartattribute.h +++ b/src/core/smartattribute.h @@ -15,30 +15,29 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_SMARTATTRIBUTE_H) - +#ifndef KPMCORE_SMARTATTRIBUTE_H #define KPMCORE_SMARTATTRIBUTE_H #include "util/libpartitionmanagerexport.h" #include -struct SkSmartAttributeParsedData; +class SmartAttributeParsedData; class LIBKPMCORE_EXPORT SmartAttribute { public: - enum FailureType { + enum class FailureType { PreFailure, OldAge }; - enum UpdateType { + enum class UpdateType { Online, Offline }; - enum Assessment { + enum class Assessment { NotApplicable, Failing, HasFailed, @@ -47,7 +46,7 @@ public: }; public: - SmartAttribute(const SkSmartAttributeParsedData* a); + SmartAttribute(const SmartAttributeParsedData& a); public: qint32 id() const { @@ -104,4 +103,3 @@ private: }; #endif - diff --git a/src/core/smartattributeparseddata.cpp b/src/core/smartattributeparseddata.cpp new file mode 100644 index 0000000..b310a7b --- /dev/null +++ b/src/core/smartattributeparseddata.cpp @@ -0,0 +1,644 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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 "smartattributeparseddata.h" +#include "core/smartdiskinformation.h" + +#include +#include +#include +#include +#include + +#define MKELVIN_VALID_MIN ((qint64) ((-15LL*1000LL) + 273150LL)) +#define MKELVIN_VALID_MAX ((qint64) ((100LL*1000LL) + 273150LL)) + +#define MSECOND_VALID_MIN 1ULL +#define MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL) +#define MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL) + +static QMap tableUnit(); +static SmartQuirk getQuirk(QString model, QString firmware); + +/** Creates a new SmartAttributeParsedData object. + @param disk the reference to the disk that this attribute is allocated to + @param jsonAttribute JSON attribute data +*/ +SmartAttributeParsedData::SmartAttributeParsedData(SmartDiskInformation *disk, + QJsonObject jsonAttribute) : + m_Id(0), + m_CurrentValue(0), + m_WorstValue(0), + m_Threshold(0), + m_Raw(0), + m_PrettyValue(0), + m_CurrentValueValid(false), + m_WorstValueValid(false), + m_ThresholdValid(false), + m_Prefailure(false), + m_Online(false), + m_GoodNow(true), + m_GoodNowValid(false), + m_GoodInThePast(true), + m_GoodInThePastValid(false), + m_Warn(false), + m_PrettyUnit(SmartAttributeUnit::Unknown), + m_Disk(disk), + m_Quirk(SmartQuirk::None) +{ + if (disk) + m_Quirk = getQuirk(disk->model(), disk->firmware()); + + if (!jsonAttribute.isEmpty()) { + QString id = QStringLiteral("id"); + QString value = QStringLiteral("value"); + QString worst = QStringLiteral("worst"); + QString thresh = QStringLiteral("thresh"); + QString raw = QStringLiteral("raw"); + QString flags = QStringLiteral("flags"); + QString prefailure = QStringLiteral("prefailure"); + QString online = QStringLiteral("updated_online"); + + m_Id = jsonAttribute[id].toInt(); + m_CurrentValue = jsonAttribute[value].toInt(); + m_WorstValue = jsonAttribute[worst].toInt(); + m_Threshold = jsonAttribute[thresh].toInt(); + + QJsonObject rawObj = jsonAttribute[raw].toObject(); + + m_Raw = rawObj[value].toVariant().toULongLong(); + + QJsonObject flagsObj = jsonAttribute[flags].toObject(); + + m_Prefailure = flagsObj[prefailure].toBool(); + m_Online = flagsObj[online].toBool(); + + if (!updateUnit()) + m_PrettyUnit = SmartAttributeUnit::Unknown; + + makePretty(); + + validateValues(); + + verifyAttribute(); + } +} + +/** @param other SmartAttributeParsedData to copy +*/ +SmartAttributeParsedData::SmartAttributeParsedData(const SmartAttributeParsedData &other) : + m_Id(other.id()), + m_CurrentValue(other.currentValue()), + m_WorstValue(other.worstValue()), + m_Threshold(other.threshold()), + m_Raw(other.raw()), + m_PrettyValue(other.prettyValue()), + m_CurrentValueValid(other.currentValueValid()), + m_WorstValueValid(other.worstValueValid()), + m_ThresholdValid(other.thresholdValid()), + m_Prefailure(other.prefailure()), + m_Online(other.online()), + m_GoodNow(other.goodNow()), + m_GoodNowValid(other.goodNowValid()), + m_GoodInThePast(other.goodInThePast()), + m_GoodInThePastValid(other.goodInThePastValid()), + m_Warn(other.warn()), + m_PrettyUnit(other.prettyUnit()), + m_Disk(other.disk()), + m_Quirk(other.m_Quirk) +{ + +} + +/** Validate values from the current attribute */ +void SmartAttributeParsedData::validateValues() +{ + m_CurrentValueValid = m_CurrentValue >= 1 && m_CurrentValue <= 0xFD; + m_WorstValueValid = m_WorstValue >= 1 && m_WorstValue <= 0xFD; + m_ThresholdValid = m_Threshold != 0xFE; + + if (m_Threshold >= 1 && m_Threshold <= 0xFD) { + if (m_WorstValueValid) { + m_GoodInThePast = m_GoodInThePast && (m_WorstValue > m_Threshold); + m_GoodInThePastValid = true; + } + if (m_CurrentValueValid) { + m_GoodNow = m_GoodNow && (m_CurrentValue > m_Threshold); + m_GoodNowValid = true; + } + } + + m_Warn = (m_GoodNowValid && !m_GoodNow) || (m_GoodInThePastValid && !m_GoodInThePast); +} + +/** Make a pretty value from raw based on attribute's id */ +void SmartAttributeParsedData::makePretty() +{ + if (m_PrettyUnit == SmartAttributeUnit::Unknown) + return; + + switch (id()) { + case 3: + m_PrettyValue = raw() & 0xFFFF; + break; + + case 5: + m_PrettyValue = raw() & 0xFFFFFFFFU; + break; + + case 9: + m_PrettyValue = (raw() & 0xFFFFFFFFU) * 60 * 60 * 1000; + break; + + case 170: + m_PrettyValue = currentValue(); + break; + + case 190: + m_PrettyValue = (raw() & 0xFFFF) * 1000 + 273150; + break; + + case 194: + m_PrettyValue = (raw() & 0xFFFF) * 1000 + 273150; + break; + + case 197: + m_PrettyValue = (raw() & 0xFFFFFFFFU); + break; + + case 222: + m_PrettyValue = (raw() & 0xFFFFFFFFU) * 60 * 60 * 1000; + break; + + case 228: + m_PrettyValue = raw() * 60 * 1000; + break; + + case 231: + m_PrettyValue = (raw() & 0xFFFF) * 1000 + 273150; + break; + + case 232: + m_PrettyValue = currentValue(); + break; + + case 240: + m_PrettyValue = (raw() & 0xFFFFFFFFU) * 60 * 60 * 1000; + break; + + case 241: + m_PrettyValue = raw() * 65536ULL * 512ULL / 1000000ULL; + break; + + case 242: + m_PrettyValue = raw() * 65536ULL * 512ULL / 1000000ULL; + break; + + default: + m_PrettyValue = raw(); + break; + + } +} + +/** Verify attribute's unit */ +void SmartAttributeParsedData::verifyAttribute() +{ + if (id() == 3 || id() == 226) + verifyShortTime(); + else if (id() == 5 || id() == 187 || id() == 197 || id() == 198) + verifySectors(); + else if (id() == 9 || id() == 222 || id() == 240) + verifyLongTime(); + else if (id() == 190 || id() == 194 || id() == 231) + verifyTemperature(); +} + +void SmartAttributeParsedData::verifyTemperature() +{ + if (prettyUnit() != SmartAttributeUnit::Milikelvin) + return; + + if (prettyValue() < MKELVIN_VALID_MIN || prettyValue() > MKELVIN_VALID_MAX) + m_PrettyUnit = SmartAttributeUnit::Unknown; +} + +void SmartAttributeParsedData::verifyShortTime() +{ + if (prettyUnit() != SmartAttributeUnit::Miliseconds) + return; + + if (prettyValue() < MSECOND_VALID_MIN || prettyValue() > MSECOND_VALID_SHORT_MAX) + m_PrettyUnit = SmartAttributeUnit::Unknown; +} + +void SmartAttributeParsedData::verifyLongTime() +{ + if (prettyUnit() != SmartAttributeUnit::Miliseconds) + return; + + if (prettyValue() < MSECOND_VALID_MIN || prettyValue() > MSECOND_VALID_LONG_MAX) + m_PrettyUnit = SmartAttributeUnit::Unknown; +} + +void SmartAttributeParsedData::verifySectors() +{ + if (prettyUnit() != SmartAttributeUnit::Sectors) + return; + + quint64 maxSectors = disk()->size() / 512ULL; + + if (prettyValue() == 0xFFFFFFFFULL || prettyValue() == 0xFFFFFFFFFFFFULL || (maxSectors > 0 + && prettyValue() > maxSectors)) + m_PrettyUnit = SmartAttributeUnit::Unknown; + else if ((id() == 5 || id() == 197) && prettyValue() > 0) + m_Warn = true; +} + +bool SmartAttributeParsedData::updateUnit() +{ + if (m_Quirk) { + switch (id()) { + case 3: + if (m_Quirk & SmartQuirk::SMART_QUIRK_3_UNUSED) { + m_PrettyUnit = SmartAttributeUnit::Unknown; + return true; + } + break; + + case 4: + if (m_Quirk & SmartQuirk::SMART_QUIRK_4_UNUSED) { + m_PrettyUnit = SmartAttributeUnit::Unknown; + return true; + } + break; + + case 5: + if (m_Quirk & SmartQuirk::SMART_QUIRK_5_UNKNOWN) + return false; + break; + + case 9: + if (m_Quirk & SmartQuirk::SMART_QUIRK_9_POWERONMINUTES) { + m_PrettyUnit = SmartAttributeUnit::Miliseconds; + return true; + } else if (m_Quirk & SmartQuirk::SMART_QUIRK_9_POWERONSECONDS) { + m_PrettyUnit = SmartAttributeUnit::Miliseconds; + return true; + } else if (m_Quirk & SmartQuirk::SMART_QUIRK_9_POWERONHALFMINUTES) { + m_PrettyUnit = SmartAttributeUnit::Miliseconds; + return true; + } else if (m_Quirk & SmartQuirk::SMART_QUIRK_9_UNKNOWN) + return false; + break; + + case 190: + if (m_Quirk & SmartQuirk::SMART_QUIRK_190_UNKNOWN) + return false; + break; + + case 192: + if (m_Quirk & SmartQuirk::SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) { + m_PrettyUnit = SmartAttributeUnit::None; + return true; + } + break; + + case 194: + if (m_Quirk & SmartQuirk::SMART_QUIRK_194_10XCELSIUS) { + m_PrettyUnit = SmartAttributeUnit::Milikelvin; + return true; + } else if (m_Quirk & SmartQuirk::SMART_QUIRK_194_UNKNOWN) + return false; + break; + + case 197: + if (m_Quirk & SmartQuirk::SMART_QUIRK_197_UNKNOWN) + return false; + break; + + case 198: + if (m_Quirk & SmartQuirk::SMART_QUIRK_198_UNKNOWN) + return false; + break; + + case 200: + if (m_Quirk & SmartQuirk::SMART_QUIRK_200_WRITEERRORCOUNT) { + m_PrettyUnit = SmartAttributeUnit::None; + return true; + } + break; + + case 201: + if (m_Quirk & SmartQuirk::SMART_QUIRK_201_DETECTEDTACOUNT) { + m_PrettyUnit = SmartAttributeUnit::None; + return true; + } + break; + + case 225: + if (m_Quirk & SmartQuirk::SMART_QUIRK_225_TOTALLBASWRITTEN) { + m_PrettyUnit = SmartAttributeUnit::MB; + return true; + } + break; + + case 226: + if (m_Quirk & SmartQuirk::SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) { + m_PrettyUnit = SmartAttributeUnit::SmallPercent; + return true; + } + break; + + case 227: + if (m_Quirk & SmartQuirk::SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) { + m_PrettyUnit = SmartAttributeUnit::SmallPercent; + return true; + } + break; + + case 228: + if (m_Quirk & SmartQuirk::SMART_QUIRK_228_WORKLOADTIMER) { + m_PrettyUnit = SmartAttributeUnit::Miliseconds; + return true; + } + break; + + case 232: + if (m_Quirk & SmartQuirk::SMART_QUIRK_232_AVAILABLERESERVEDSPACE) { + m_PrettyUnit = SmartAttributeUnit::Percent; + return true; + } + break; + + case 233: + if (m_Quirk & SmartQuirk::SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) { + m_PrettyUnit = SmartAttributeUnit::Percent; + return true; + } + break; + + } + } + + if (tableUnit().contains(id())) { + m_PrettyUnit = tableUnit()[id()]; + return true; + } + + return false; +} + +static QMap tableUnit() +{ + QMap table; + table.insert(1, SmartAttributeUnit::None); + table.insert(2, SmartAttributeUnit::Unknown); + table.insert(3, SmartAttributeUnit::Miliseconds); + table.insert(4, SmartAttributeUnit::None); + table.insert(5, SmartAttributeUnit::Sectors); + table.insert(6, SmartAttributeUnit::Unknown); + table.insert(7, SmartAttributeUnit::None); + table.insert(8, SmartAttributeUnit::Unknown); + table.insert(9, SmartAttributeUnit::Miliseconds); + table.insert(10, SmartAttributeUnit::None); + table.insert(11, SmartAttributeUnit::None); + table.insert(12, SmartAttributeUnit::None); + table.insert(13, SmartAttributeUnit::None); + table.insert(170, SmartAttributeUnit::Percent); + table.insert(171, SmartAttributeUnit::None); + table.insert(172, SmartAttributeUnit::None); + table.insert(175, SmartAttributeUnit::None); + table.insert(176, SmartAttributeUnit::None); + table.insert(177, SmartAttributeUnit::None); + table.insert(178, SmartAttributeUnit::None); + table.insert(179, SmartAttributeUnit::None); + table.insert(180, SmartAttributeUnit::None); + table.insert(181, SmartAttributeUnit::None); + table.insert(182, SmartAttributeUnit::None); + table.insert(183, SmartAttributeUnit::None); + table.insert(184, SmartAttributeUnit::None); + table.insert(187, SmartAttributeUnit::Sectors); + table.insert(188, SmartAttributeUnit::None); + table.insert(189, SmartAttributeUnit::None); + table.insert(190, SmartAttributeUnit::Milikelvin); + table.insert(191, SmartAttributeUnit::None); + table.insert(192, SmartAttributeUnit::None); + table.insert(193, SmartAttributeUnit::None); + table.insert(194, SmartAttributeUnit::Milikelvin); + table.insert(195, SmartAttributeUnit::None); + table.insert(196, SmartAttributeUnit::None); + table.insert(197, SmartAttributeUnit::Sectors); + table.insert(198, SmartAttributeUnit::Sectors); + table.insert(199, SmartAttributeUnit::None); + table.insert(200, SmartAttributeUnit::None); + table.insert(201, SmartAttributeUnit::None); + table.insert(202, SmartAttributeUnit::None); + table.insert(203, SmartAttributeUnit::Unknown); + table.insert(204, SmartAttributeUnit::None); + table.insert(205, SmartAttributeUnit::None); + table.insert(206, SmartAttributeUnit::Unknown); + table.insert(207, SmartAttributeUnit::Unknown); + table.insert(208, SmartAttributeUnit::Unknown); + table.insert(209, SmartAttributeUnit::Unknown); + table.insert(220, SmartAttributeUnit::Unknown); + table.insert(221, SmartAttributeUnit::None); + table.insert(222, SmartAttributeUnit::Miliseconds); + table.insert(223, SmartAttributeUnit::None); + table.insert(224, SmartAttributeUnit::Unknown); + table.insert(225, SmartAttributeUnit::None); + table.insert(226, SmartAttributeUnit::Miliseconds); + table.insert(227, SmartAttributeUnit::None); + table.insert(228, SmartAttributeUnit::None); + table.insert(230, SmartAttributeUnit::Unknown); + table.insert(231, SmartAttributeUnit::Milikelvin); + table.insert(232, SmartAttributeUnit::Percent); + table.insert(233, SmartAttributeUnit::Unknown); + table.insert(234, SmartAttributeUnit::Sectors); + table.insert(235, SmartAttributeUnit::Unknown); + table.insert(240, SmartAttributeUnit::Miliseconds); + table.insert(241, SmartAttributeUnit::MB); + table.insert(242, SmartAttributeUnit::MB); + table.insert(250, SmartAttributeUnit::None); + + return table; +} + +static const QVector quirkDatabase() +{ + typedef SmartAttributeParsedData::SmartQuirkDataBase QuirkDatabase; + + QVector quirkDb; + + quirkDb << QuirkDatabase(QStringLiteral("^(FUJITSU MHY2120BH|FUJITSU MHY2250BH)$"), QStringLiteral("^0085000B$"), + static_cast(SmartQuirk::SMART_QUIRK_9_POWERONMINUTES | + SmartQuirk::SMART_QUIRK_197_UNKNOWN | + SmartQuirk::SMART_QUIRK_198_UNKNOWN)); + + quirkDb << QuirkDatabase(QStringLiteral("^FUJITSU MHR2040AT$"), QStringLiteral(), + static_cast(SmartQuirk::SMART_QUIRK_9_POWERONSECONDS | + SmartQuirk::SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT | + SmartQuirk::SMART_QUIRK_200_WRITEERRORCOUNT)); + + quirkDb << QuirkDatabase(QStringLiteral("^FUJITSU MHS20[6432]0AT( .)?$"), QStringLiteral(), + static_cast(SmartQuirk::SMART_QUIRK_9_POWERONSECONDS | + SmartQuirk::SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT | + SmartQuirk::SMART_QUIRK_200_WRITEERRORCOUNT | + SmartQuirk::SMART_QUIRK_201_DETECTEDTACOUNT)); + + quirkDb << QuirkDatabase(QStringLiteral("^(" + "FUJITSU M1623TAU|" + "FUJITSU MHG2...ATU?.*|" + "FUJITSU MHH2...ATU?.*|" + "FUJITSU MHJ2...ATU?.*|" + "FUJITSU MHK2...ATU?.*|" + "FUJITSU MHL2300AT|" + "FUJITSU MHM2(20|15|10|06)0AT|" + "FUJITSU MHN2...AT|" + "FUJITSU MHR2020AT|" + "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|" + "FUJITSU MHU2...ATU?.*|" + "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|" + "FUJITSU MP[A-G]3...A[HTEV]U?.*" + ")$"), + QStringLiteral(), + SmartQuirk::SMART_QUIRK_9_POWERONSECONDS); + + quirkDb << QuirkDatabase(QStringLiteral("^(" + "SAMSUNG SV4012H|" + "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]" + ")$"), + QStringLiteral(), + SmartQuirk::SMART_QUIRK_9_POWERONHALFMINUTES); + + quirkDb << QuirkDatabase(QStringLiteral("^(" + "SAMSUNG SV0412H|" + "SAMSUNG SV1204H" + ")$"), + QStringLiteral(), + static_cast(SmartQuirk::SMART_QUIRK_9_POWERONHALFMINUTES | SmartQuirk::SMART_QUIRK_194_10XCELSIUS)); + + quirkDb << QuirkDatabase(QStringLiteral("^SAMSUNG SP40A2H$"), + QStringLiteral("^RR100-07$"), + SmartQuirk::SMART_QUIRK_9_POWERONHALFMINUTES); + + quirkDb << QuirkDatabase(QStringLiteral("^SAMSUNG SP80A4H$"), + QStringLiteral("^RT100-06$"), + SmartQuirk::SMART_QUIRK_9_POWERONHALFMINUTES); + + quirkDb << QuirkDatabase(QStringLiteral("^SAMSUNG SP8004H$"), + QStringLiteral("^QW100-61$"), + SmartQuirk::SMART_QUIRK_9_POWERONHALFMINUTES); + + quirkDb << QuirkDatabase(QStringLiteral("^(" + "Maxtor 2B0(0[468]|1[05]|20)H1|" + "Maxtor 4G(120J6|160J[68])|" + "Maxtor 4D0(20H1|40H2|60H3|80H4)" + ")$"), + QStringLiteral(), + static_cast(SmartQuirk::SMART_QUIRK_9_POWERONMINUTES | SmartQuirk::SMART_QUIRK_194_UNKNOWN)); + + quirkDb << QuirkDatabase(QStringLiteral("^(" + "Maxtor 2F0[234]0[JL]0|" + "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|" + "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|" + "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|" + "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|" + "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|" + "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|" + "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|" + "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|" + "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|" + "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|" + "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|" + "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|" + "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|" + "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|" + "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|" + "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|" + "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|" + "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|" + "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|" + "Maxtor (98196H8|96147H6)|" + "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|" + "Maxtor 6(E0[234]|K04)0L0|" + "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0|" + "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|" + "Maxtor 7Y250[PM]0|" + "Maxtor [45]A(25|30|32)0[JN]0|" + "Maxtor 7L(25|30)0[SR]0" + ")$"), + QStringLiteral(), + SmartQuirk::SMART_QUIRK_9_POWERONMINUTES); + + quirkDb << QuirkDatabase(QStringLiteral("^(" + "HITACHI_DK14FA-20B|" + "HITACHI_DK23..-..B?|" + "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|" + "HE[JN]4230[23]0F9AT00|" + "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00" + ")$"), + QStringLiteral(), + static_cast(SmartQuirk::SMART_QUIRK_9_POWERONMINUTES | SmartQuirk::SMART_QUIRK_193_LOADUNLOAD)); + + quirkDb << QuirkDatabase(QStringLiteral("^HTS541010G9SA00$"), + QStringLiteral("^MBZOC60P$"), + SmartQuirk::SMART_QUIRK_5_UNKNOWN); + + quirkDb << QuirkDatabase(QStringLiteral("^MCCOE64GEMPP$"), + QStringLiteral("^2.9.0[3-9]$"), + static_cast(SmartQuirk::SMART_QUIRK_5_UNKNOWN | SmartQuirk::SMART_QUIRK_190_UNKNOWN)); + + quirkDb << QuirkDatabase(QStringLiteral("^INTEL SSDSA2(CT|BT|CW|BW)[0-9]{3}G3.*$"), + QStringLiteral(), + static_cast(SmartQuirk::SMART_QUIRK_3_UNUSED | + SmartQuirk::SMART_QUIRK_4_UNUSED | + SmartQuirk::SMART_QUIRK_225_TOTALLBASWRITTEN | + SmartQuirk::SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR | + SmartQuirk::SMART_QUIRK_227_TIMEWORKLOADHOSTREADS | + SmartQuirk::SMART_QUIRK_228_WORKLOADTIMER | + SmartQuirk::SMART_QUIRK_232_AVAILABLERESERVEDSPACE | + SmartQuirk::SMART_QUIRK_233_MEDIAWEAROUTINDICATOR)); + + return quirkDb; +} + +static SmartQuirk getQuirk(QString model, QString firmware) +{ + const QVector db = quirkDatabase(); + + QRegularExpression modelRegex; + QRegularExpression firmwareRegex; + + for (const SmartAttributeParsedData::SmartQuirkDataBase &item : qAsConst(db)) { + if (!item.model.isEmpty()) { + modelRegex.setPattern(item.model); + QRegularExpressionMatch match = modelRegex.match(model); + if (!match.hasMatch()) + continue; + } + if (!item.firmware.isEmpty()) { + firmwareRegex.setPattern(item.firmware); + QRegularExpressionMatch match = firmwareRegex.match(firmware); + if (!match.hasMatch()) + continue; + } + return item.quirk; + } + + return SmartQuirk::None; +} diff --git a/src/core/smartattributeparseddata.h b/src/core/smartattributeparseddata.h new file mode 100644 index 0000000..014c277 --- /dev/null +++ b/src/core/smartattributeparseddata.h @@ -0,0 +1,225 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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_SMARTATTRIBUTEPARSEDDATA_H +#define KPMCORE_SMARTATTRIBUTEPARSEDDATA_H + +#include +#include + +class SmartDiskInformation; + +/** SMART Quirk */ +enum SmartQuirk { + None = 0x000000, + SMART_QUIRK_9_POWERONMINUTES = 0x000001, + SMART_QUIRK_9_POWERONSECONDS = 0x000002, + SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004, + SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008, + SMART_QUIRK_193_LOADUNLOAD = 0x000010, + SMART_QUIRK_194_10XCELSIUS = 0x000020, + SMART_QUIRK_194_UNKNOWN = 0x000040, + SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080, + SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100, + SMART_QUIRK_5_UNKNOWN = 0x000200, + SMART_QUIRK_9_UNKNOWN = 0x000400, + SMART_QUIRK_197_UNKNOWN = 0x000800, + SMART_QUIRK_198_UNKNOWN = 0x001000, + SMART_QUIRK_190_UNKNOWN = 0x002000, + SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000, + SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000, + SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000, + SMART_QUIRK_4_UNUSED = 0x020000, + SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000, + SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000, + SMART_QUIRK_228_WORKLOADTIMER = 0x100000, + SMART_QUIRK_3_UNUSED = 0x200000, +}; + +/** A unit for SMART attributes */ +enum class SmartAttributeUnit { + Unknown, + None, + Miliseconds, + Sectors, + Milikelvin, + SmallPercent, /* percentage with 3 decimal points */ + Percent, + MB, +}; + +/** A SMART parsed attribute. + + It receives the attribute data from JSON, retrieve its data and validates its values. + + @author Caio Carvalho +*/ +class SmartAttributeParsedData +{ +public: + /** SMART Quirk to some particular model and firmware */ + struct SmartQuirkDataBase { + QString model; + QString firmware; + SmartQuirk quirk; + + SmartQuirkDataBase(const QString &m = QString(), + const QString &f = QString(), + SmartQuirk q = SmartQuirk::None) : + model(m), + firmware(f), + quirk(q) + { + }; + }; + +public: + SmartAttributeParsedData(SmartDiskInformation *disk, QJsonObject jsonAttribute); + + SmartAttributeParsedData(const SmartAttributeParsedData &other); + +public: + quint32 id() const + { + return m_Id; /**< @return attribute id */ + } + + qint32 currentValue() const + { + return m_CurrentValue; /**< @return attribute current value */ + } + + qint32 worstValue() const + { + return m_WorstValue; /**< @return attribute worst value */ + } + + qint32 threshold() const + { + return m_Threshold; /**< @return attribute threshold value */ + } + + bool prefailure() const + { + return m_Prefailure; /**< @return attribute prefailure status */ + } + + bool online() const + { + return m_Online; /**< @return attribute online status */ + } + + quint64 raw() const + { + return m_Raw; /**< @return attribute raw value */ + } + + quint64 prettyValue() const + { + return m_PrettyValue; /**< @return attribute pretty value */ + } + + SmartAttributeUnit prettyUnit() const + { + return m_PrettyUnit; /**< @return pretty unit value */ + } + + bool goodNowValid() const + { + return m_GoodNowValid; /**< @return good now attribute status validation */ + } + + bool goodNow() const + { + return m_GoodNow; /**< @return good now attribute status */ + } + + bool goodInThePastValid() const + { + return m_GoodInThePastValid; /**< @return good in the past attribute status validation */ + } + + bool goodInThePast() const + { + return m_GoodInThePast; /**< @return good in the past attribute status */ + } + + bool thresholdValid() const + { + return m_ThresholdValid; /**< @return threshold value validation */ + } + + bool currentValueValid() const + { + return m_CurrentValueValid; /**< @return current value validation */ + } + + bool worstValueValid() const + { + return m_WorstValueValid; /**< @return worst value validation */ + } + + bool warn() const + { + return m_Warn; /**< @return warn status */ + } + + SmartDiskInformation *disk() const + { + return m_Disk; /**< @return attribute's disk reference */ + } + +protected: + void validateValues(); + + bool updateUnit(); + + void makePretty(); + + void verifyAttribute(); + + void verifyTemperature(); + + void verifyShortTime(); + + void verifyLongTime(); + + void verifySectors(); + +private: + quint32 m_Id; + qint32 m_CurrentValue; + qint32 m_WorstValue; + qint32 m_Threshold; + quint64 m_Raw; + quint64 m_PrettyValue; + bool m_CurrentValueValid; + bool m_WorstValueValid; + bool m_ThresholdValid; + bool m_Prefailure; + bool m_Online; + bool m_GoodNow; + bool m_GoodNowValid; + bool m_GoodInThePast; + bool m_GoodInThePastValid; + bool m_Warn; + SmartAttributeUnit m_PrettyUnit; + SmartDiskInformation *m_Disk; + SmartQuirk m_Quirk; +}; + +#endif // SMARTATTRIBUTEPARSEDDATA_H diff --git a/src/core/smartdiskinformation.cpp b/src/core/smartdiskinformation.cpp new file mode 100644 index 0000000..85a5dbb --- /dev/null +++ b/src/core/smartdiskinformation.cpp @@ -0,0 +1,198 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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/smartdiskinformation.h" +#include "core/smartattributeparseddata.h" + +#include + +static quint64 u64log2(quint64 n); + +/** Creates a new SmartDiskInformationObject */ +SmartDiskInformation::SmartDiskInformation() : + m_ModelName(QString()), + m_FirmwareVersion(QString()), + m_SerialNumber(QString()), + m_Size(0), + m_Temperature(0), + m_BadSectors(0), + m_PoweredOn(0), + m_PowerCycles(0), + m_SmartStatus(false), + m_BadAttributeNow(false), + m_BadAttributeInThePast(false), + m_SelfTestExecutionStatus(SmartStatus::SelfTestStatus::Success), + m_Overall(SmartStatus::Overall::Bad) +{ +} + +/** Update the number of bad sectors based on reallocated sector count and current pending sector attributes data */ +void SmartDiskInformation::updateBadSectors() +{ + std::unique_ptr reallocatedSectorCt(findAttribute(5)); + std::unique_ptr currentPendingSector(findAttribute(197)); + + if (!reallocatedSectorCt && !currentPendingSector) + m_BadSectors = 0; + else if (reallocatedSectorCt && currentPendingSector) + m_BadSectors = reallocatedSectorCt->prettyValue() + currentPendingSector->prettyValue(); + else if (reallocatedSectorCt) + m_BadSectors = reallocatedSectorCt->prettyValue(); + else + m_BadSectors = currentPendingSector->prettyValue(); +} + +/** Update SMART overall data based on the quantity of bad sectors and the status of SMART attributes */ +void SmartDiskInformation::updateOverall() +{ + if (!smartStatus()) { + m_Overall = SmartStatus::Overall::Bad; + return; + } + + quint64 sector_threshold = u64log2(size() / 512) * 1024; + + if (badSectors() >= sector_threshold) { + m_Overall = SmartStatus::Overall::BadSectorsMany; + return; + } + + validateBadAttributes(); + + if (m_BadAttributeNow) { + m_Overall = SmartStatus::Overall::BadNow; + return; + } + + if (badSectors() > 0) { + m_Overall = SmartStatus::Overall::BadSectors; + return; + } + + if (m_BadAttributeInThePast) { + m_Overall = SmartStatus::Overall::BadPast; + return; + } + + m_Overall = SmartStatus::Overall::Good; +} + +/** Update the temperature value based on SMART attributes + @return a boolean representing the status of the operation +*/ +bool SmartDiskInformation::updateTemperature() +{ + std::unique_ptr temperatureCelsius(findAttribute(231)); + std::unique_ptr temperatureCelsius2(findAttribute(194)); + std::unique_ptr airflowTemperatureCelsius(findAttribute(190)); + + if (temperatureCelsius != nullptr + && temperatureCelsius->prettyUnit() == SmartAttributeUnit::Milikelvin) { + m_Temperature = temperatureCelsius->prettyValue(); + return true; + } else if (temperatureCelsius2 != nullptr + && temperatureCelsius2->prettyUnit() == SmartAttributeUnit::Milikelvin) { + m_Temperature = temperatureCelsius2->prettyValue(); + return true; + } else if (airflowTemperatureCelsius != nullptr + && airflowTemperatureCelsius->prettyUnit() == + SmartAttributeUnit::Milikelvin) { + m_Temperature = airflowTemperatureCelsius->prettyValue(); + return true; + } + return false; +} + +/** Update the powered on value based on SMART attributes + @return a boolean representing the status of the operation +*/ +bool SmartDiskInformation::updatePowerOn() +{ + std::unique_ptr powerOnHours(findAttribute(9)); + std::unique_ptr powerOnSeconds(findAttribute(233)); + + if (powerOnHours != nullptr + && powerOnHours->prettyUnit() == SmartAttributeUnit::Miliseconds) { + m_PoweredOn = powerOnHours->prettyValue(); + return true; + } else if (powerOnSeconds != nullptr + && powerOnSeconds->prettyUnit() == SmartAttributeUnit::Miliseconds) { + m_PoweredOn = powerOnSeconds->prettyValue(); + return true; + } + return false; +} + +/** Update the power cycles value based on SMART attributes + @return a boolean representing the status of the operation +*/ +bool SmartDiskInformation::updatePowerCycle() +{ + std::unique_ptr powerCycleCount(findAttribute(12)); + + if (powerCycleCount != nullptr + && powerCycleCount->prettyUnit() == SmartAttributeUnit::None) { + m_PowerCycles = powerCycleCount->prettyValue(); + return true; + } + return false; +} + +/** Validate disk attributes status */ +void SmartDiskInformation::validateBadAttributes() +{ + for (const SmartAttributeParsedData &attribute : qAsConst(m_Attributes)) { + if (attribute.prefailure()) { + if (attribute.goodNowValid() && !attribute.goodNow()) + m_BadAttributeNow = true; + if (attribute.goodInThePastValid() && !attribute.goodInThePast()) + m_BadAttributeInThePast = true; + } + } +} + +/** Search for a attribute based on its id number + @return a reference to the attribute +*/ +SmartAttributeParsedData *SmartDiskInformation::findAttribute(quint32 id) +{ + SmartAttributeParsedData *attr = nullptr; + for (const SmartAttributeParsedData &attribute : qAsConst(m_Attributes)) { + if (id == attribute.id()) { + attr = new SmartAttributeParsedData(attribute); + break; + } + } + return attr; +} + +static quint64 u64log2(quint64 n) +{ + quint64 r; + + if (n <= 1) + return 0; + + r = 0; + for (;;) { + n = n >> 1; + if (!n) + return r; + r++; + } + return 0; +} diff --git a/src/core/smartdiskinformation.h b/src/core/smartdiskinformation.h new file mode 100644 index 0000000..dbf3d38 --- /dev/null +++ b/src/core/smartdiskinformation.h @@ -0,0 +1,175 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * Copyright (C) 2018 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_SMARTDISKINFORMATION_H +#define KPMCORE_SMARTDISKINFORMATION_H + +#include "core/smartstatus.h" + +#include +#include + +class SmartAttributeParsedData; + +/** Disk information retrieved by SMART. + + It includes a list with your SMART attributes. + + @author Caio Carvalho +*/ +class SmartDiskInformation +{ +public: + SmartDiskInformation(); + +public: + void updateBadSectors(); + + void updateOverall(); + + bool updateTemperature(); + + bool updatePowerOn(); + + bool updatePowerCycle(); + + SmartAttributeParsedData *findAttribute(quint32 id); + +public: + const QString model() const + { + return m_ModelName; /**< @return the disk model name */ + } + + const QString firmware() const + { + return m_FirmwareVersion; /**< @return the disk firmware version */ + } + + const QString serial() const + { + return m_SerialNumber; /**< @return the disk serial number */ + } + + quint64 size() const + { + return m_Size; /**< @return disk size */ + } + + bool smartStatus() const + { + return m_SmartStatus; /**< @return a boolean representing SMART status */ + } + + SmartStatus::SelfTestStatus selfTestExecutionStatus() const + { + return m_SelfTestExecutionStatus; /**< @return SMART self execution status */ + } + + SmartStatus::Overall overall() const + { + return m_Overall; /**< @return SMART overall status */ + } + + quint64 temperature() const + { + return m_Temperature; /**< @return disk temperature in kelvin */ + } + + quint64 badSectors() const + { + return m_BadSectors; /**< @return the number of bad sectors */ + } + + quint64 poweredOn() const + { + return m_PoweredOn; /**< @return quantity of time that device is powered on */ + } + + quint64 powerCycles() const + { + return m_PowerCycles; /**< @return quantity of power cycles */ + } + + QList attributes() const + { + return m_Attributes; /**< @return a list that contains the disk SMART attributes */ + } + +public: + void setModel(QString modelName) + { + m_ModelName = modelName; + } + + void setFirmware(QString firmware) + { + m_FirmwareVersion = firmware; + } + + void setSerial(QString serial) + { + m_SerialNumber = serial; + } + + void setSize(quint64 size) + { + m_Size = size; + } + + void setPowerCycles(quint64 powerCycleCt) + { + m_PowerCycles = powerCycleCt; + } + + void setSmartStatus(bool smartStatus) + { + m_SmartStatus = smartStatus; + } + + void setSelfTestExecutionStatus(SmartStatus::SelfTestStatus status) + { + m_SelfTestExecutionStatus = status; + } + + void addAttribute(SmartAttributeParsedData &attribute) + { + m_Attributes << attribute; + } + +protected: + void validateBadAttributes(); + +private: + QString m_ModelName; + QString m_FirmwareVersion; + QString m_SerialNumber; + quint64 m_Size; + quint64 m_Temperature; + quint64 m_BadSectors; + quint64 m_PoweredOn; + quint64 m_PowerCycles; + bool m_SmartStatus; + bool m_BadAttributeNow; + bool m_BadAttributeInThePast; + SmartStatus::SelfTestStatus m_SelfTestExecutionStatus; + SmartStatus::Overall m_Overall; + QList m_Attributes; +}; + +#endif // SMARTDISKINFORMATION_H diff --git a/src/core/smartparser.cpp b/src/core/smartparser.cpp new file mode 100644 index 0000000..294691e --- /dev/null +++ b/src/core/smartparser.cpp @@ -0,0 +1,161 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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/smartparser.h" + +#include "core/smartattributeparseddata.h" +#include "core/smartdiskinformation.h" + +#include "util/externalcommand.h" + +#include + +#include +#include +#include +#include +#include + +/** Creates a new SmartParser object + @param device_path device path that indicates the device that SMART must analyse +*/ +SmartParser::SmartParser(const QString &device_path) : + m_DevicePath(device_path), + m_DiskInformation(nullptr) +{ +} + +SmartParser::~SmartParser() +{ + delete m_DiskInformation; +} + +/** Initialize SmartParser data, retrieve the information from SMART JSON and initialize the disk information data */ +bool SmartParser::init() +{ + loadSmartOutput(); + + if (m_SmartOutput.isEmpty()) + return false; + + QJsonObject smartJson = m_SmartOutput.object(); + + QString model_name = QStringLiteral("model_name"); + QString firmware = QStringLiteral("firmware_version"); + QString serial_number = QStringLiteral("serial_number"); + QString device = QStringLiteral("device"); + QString smart_status = QStringLiteral("smart_status"); + QString passed = QStringLiteral("passed"); + QString self_test = QStringLiteral("self_test"); + QString status = QStringLiteral("status"); + QString value = QStringLiteral("value"); + QString user_capacity = QStringLiteral("user_capacity"); + + if (!smartJson.contains(device)) { + qDebug() << "smart disk open failed for " << devicePath() << ": " << strerror(errno); + return false; + } + + if (!smartJson.contains(smart_status)) { + qDebug() << "getting smart status failed for " << devicePath() << ": " << strerror(errno); + return false; + } + + if (!smartJson.contains(model_name) || !smartJson.contains(firmware) + || !smartJson.contains(serial_number)) { + qDebug() << "getting disk identification data failed for " << devicePath() << ": " << strerror( + errno); + return false; + } + + m_DiskInformation = new SmartDiskInformation(); + + QJsonObject smartStatus = smartJson[smart_status].toObject(); + + m_DiskInformation->setSmartStatus(smartStatus[passed].toBool()); + + m_DiskInformation->setModel(smartJson[model_name].toString()); + m_DiskInformation->setFirmware(smartJson[firmware].toString()); + m_DiskInformation->setSerial(smartJson[serial_number].toString()); + m_DiskInformation->setSize(smartJson[user_capacity].toVariant().toULongLong()); + + QJsonObject selfTest = smartJson[self_test].toObject(); + QJsonObject selfTestStatus = selfTest[status].toObject(); + + m_DiskInformation->setSelfTestExecutionStatus(static_cast(selfTestStatus[value].toInt())); + + loadAttributes(); + + m_DiskInformation->updateBadSectors(); + + m_DiskInformation->updateOverall(); + + if (!m_DiskInformation->updateTemperature()) + qDebug() << "getting temp failed for " << devicePath() << ": " << strerror(errno); + + if (!m_DiskInformation->updatePowerOn()) + qDebug() << "getting powered on time failed for " << devicePath() << ": " << strerror(errno); + + if (!m_DiskInformation->updatePowerCycle()) + qDebug() << "getting power cycles failed for " << devicePath() << ": " << strerror(errno); + + return true; +} + +/** Run smartctl command and recover its output */ +void SmartParser::loadSmartOutput() +{ + if (m_SmartOutput.isEmpty()) { + ExternalCommand smartctl(QStringLiteral("smartctl"), { QStringLiteral("--all"), QStringLiteral("--json"), devicePath() }); + + if (smartctl.run() && smartctl.exitCode() == 0) { + QByteArray output = smartctl.rawOutput(); + + m_SmartOutput = QJsonDocument::fromJson(output); + } + else + qDebug() << "smartctl initialization failed for " << devicePath() << ": " << strerror(errno); + } +} + +/** Load SMART disk attributes from JSON data */ +void SmartParser::loadAttributes() +{ + loadSmartOutput(); + + if (m_SmartOutput.isEmpty()) + return; + + QJsonObject smartJson = m_SmartOutput.object(); + + QString ata_smart_attributes = QStringLiteral("ata_smart_attributes"); + QString table = QStringLiteral("table"); + + QJsonObject ataSmartAttributes = smartJson[ata_smart_attributes].toObject(); + + QJsonArray attributeArray = ataSmartAttributes[table].toArray(); + + if (!m_DiskInformation) { + qDebug() << "error loading smart attributes for " << devicePath() << ": " << strerror(errno); + return; + } + + for (const QJsonValue &value : qAsConst(attributeArray)) { + SmartAttributeParsedData parsedObject(m_DiskInformation, value.toObject()); + m_DiskInformation->addAttribute(parsedObject); + } +} diff --git a/src/plugins/libparted/libparteddevice.h b/src/core/smartparser.h similarity index 55% rename from src/plugins/libparted/libparteddevice.h rename to src/core/smartparser.h index c8d98c0..417bd90 100644 --- a/src/plugins/libparted/libparteddevice.h +++ b/src/core/smartparser.h @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 2010 by Volker Lanz * + * Copyright (C) 2018 by Caio Carvalho * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -15,48 +15,50 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_LIBPARTEDDEVICE_H) +#ifndef KPMCORE_SMARTPARSER_H +#define KPMCORE_SMARTPARSER_H -#define KPMCORE_LIBPARTEDDEVICE_H +#include +#include -#include "backend/corebackenddevice.h" +class SmartDiskInformation; -#include +/** A parser to SMART JSON output. -#include + Responsible to execute smartctl and parse its output. -class Partition; -class PartitionTable; -class Report; -class CoreBackendPartitionTable; - -class LibPartedDevice : public CoreBackendDevice + @author Caio Carvalho +*/ +class SmartParser { - Q_DISABLE_COPY(LibPartedDevice) +public: + SmartParser(const QString &device_path); + ~SmartParser(); public: - LibPartedDevice(const QString& deviceNode); - ~LibPartedDevice(); + bool init(); public: - bool open() override; - bool openExclusive() override; - bool close() override; - - CoreBackendPartitionTable* openPartitionTable() override; - - bool createPartitionTable(Report& report, const PartitionTable& ptable) override; - - bool readData(QByteArray& buffer, qint64 offset, qint64 size) override; - bool writeData(QByteArray& buffer, qint64 offset) override; - -protected: - PedDevice* pedDevice() { - return m_PedDevice; + const QString &devicePath() const + { + return m_DevicePath; /**< @return the device path that SMART must analyse */ } + SmartDiskInformation *diskInformation() const + { + return m_DiskInformation; /**< @return a reference to parsed disk information */ + } + +protected: + void loadSmartOutput(); + + void loadAttributes(); + private: - PedDevice* m_PedDevice; + const QString m_DevicePath; + QJsonDocument m_SmartOutput; + SmartDiskInformation *m_DiskInformation; + }; -#endif +#endif // SMARTPARSER_H diff --git a/src/core/smartstatus.cpp b/src/core/smartstatus.cpp index 8b1cb91..b9e8e37 100644 --- a/src/core/smartstatus.cpp +++ b/src/core/smartstatus.cpp @@ -18,24 +18,27 @@ #include "core/smartstatus.h" +#include "core/smartparser.h" +#include "core/smartdiskinformation.h" +#include "core/smartattributeparseddata.h" + #include #include #include #include -#include #include -SmartStatus::SmartStatus(const QString& device_path) : +SmartStatus::SmartStatus(const QString &device_path) : m_DevicePath(device_path), m_InitSuccess(false), m_Status(false), m_ModelName(), m_Serial(), m_Firmware(), - m_Overall(Bad), - m_SelfTestStatus(Success), + m_Overall(Overall::Bad), + m_SelfTestStatus(SelfTestStatus::Success), m_Temp(0), m_BadSectors(0), m_PowerCycles(0), @@ -46,148 +49,31 @@ SmartStatus::SmartStatus(const QString& device_path) : void SmartStatus::update() { - SkDisk* skDisk = nullptr; - SkBool skSmartStatus = false; - uint64_t mkelvin = 0; - uint64_t skBadSectors = 0; - uint64_t skPoweredOn = 0; - uint64_t skPowerCycles = 0; + SmartParser parser(devicePath()); - if (sk_disk_open(devicePath().toLocal8Bit().constData(), &skDisk) < 0) { - qDebug() << "smart disk open failed for " << devicePath() << ": " << strerror(errno); + if (!parser.init()) { + qDebug() << "error during smart output parsing for " << devicePath() << ": " << strerror(errno); return; } - if (sk_disk_smart_status(skDisk, &skSmartStatus) < 0) { - qDebug() << "getting smart status failed for " << devicePath() << ": " << strerror(errno); - sk_disk_free(skDisk); + SmartDiskInformation *disk; + + disk = parser.diskInformation(); + + if (!disk) return; - } - setStatus(skSmartStatus); - - if (sk_disk_smart_read_data(skDisk) < 0) { - qDebug() << "reading smart data failed for " << devicePath() << ": " << strerror(errno); - sk_disk_free(skDisk); - return; - } - - const SkIdentifyParsedData* skIdentify; - - if (sk_disk_identify_parse(skDisk, &skIdentify) < 0) - qDebug() << "getting identify data failed for " << devicePath() << ": " << strerror(errno); - else { - setModelName(QString::fromLocal8Bit(skIdentify->model)); - setFirmware(QString::fromLocal8Bit(skIdentify->firmware)); - setSerial(QString::fromLocal8Bit(skIdentify->serial)); - } - - const SkSmartParsedData* skParsed; - if (sk_disk_smart_parse(skDisk, &skParsed) < 0) - qDebug() << "parsing disk smart data failed for " << devicePath() << ": " << strerror(errno); - else { - switch (skParsed->self_test_execution_status) { - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED: - setSelfTestStatus(Aborted); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED: - setSelfTestStatus(Interrupted); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL: - setSelfTestStatus(Fatal); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN: - setSelfTestStatus(ErrorUnknown); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL: - setSelfTestStatus(ErrorEletrical); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO: - setSelfTestStatus(ErrorServo); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ: - setSelfTestStatus(ErrorRead); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING: - setSelfTestStatus(ErrorHandling); - break; - - case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS: - setSelfTestStatus(InProgress); - break; - - default: - case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER: - setSelfTestStatus(Success); - break; - } - } - - SkSmartOverall overall; - - if (sk_disk_smart_get_overall(skDisk, &overall) < 0) - qDebug() << "getting status failed for " << devicePath() << ": " << strerror(errno); - else { - switch (overall) { - case SK_SMART_OVERALL_GOOD: - setOverall(Good); - break; - - case SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST: - setOverall(BadPast); - break; - - case SK_SMART_OVERALL_BAD_SECTOR: - setOverall(BadSectors); - break; - - case SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW: - setOverall(BadNow); - break; - - case SK_SMART_OVERALL_BAD_SECTOR_MANY: - setOverall(BadSectorsMany); - break; - - default: - case SK_SMART_OVERALL_BAD_STATUS: - setOverall(Bad); - break; - } - } - - if (sk_disk_smart_get_temperature(skDisk, &mkelvin) < 0) - qDebug() << "getting temp failed for " << devicePath() << ": " << strerror(errno); - else - setTemp(mkelvin); - - if (sk_disk_smart_get_bad(skDisk, &skBadSectors) < 0) - qDebug() << "getting bad sectors failed for " << devicePath() << ": " << strerror(errno); - else - setBadSectors(skBadSectors); - - if (sk_disk_smart_get_power_on(skDisk, &skPoweredOn) < 0) - qDebug() << "getting powered on time failed for " << devicePath() << ": " << strerror(errno); - else - setPoweredOn(skPoweredOn); - - if (sk_disk_smart_get_power_cycle(skDisk, &skPowerCycles) < 0) - qDebug() << "getting power cycles failed for " << devicePath() << ": " << strerror(errno); - else - setPowerCycles(skPowerCycles); - - m_Attributes.clear(); - - sk_disk_smart_parse_attributes(skDisk, callback, this); - - sk_disk_free(skDisk); + 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); } @@ -195,40 +81,41 @@ 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)); + return xi18nc("@item:intable degrees in Celsius and Fahrenheit", "%1° C / %2° F", + QLocale().toString(celsius, 1), QLocale().toString(fahrenheit, 1)); } QString SmartStatus::selfTestStatusToString(SmartStatus::SelfTestStatus s) { switch (s) { - case Aborted: + case SelfTestStatus::Aborted: return xi18nc("@item", "Aborted"); - case Interrupted: + case SelfTestStatus::Interrupted: return xi18nc("@item", "Interrupted"); - case Fatal: + case SelfTestStatus::Fatal: return xi18nc("@item", "Fatal error"); - case ErrorUnknown: + case SelfTestStatus::ErrorUnknown: return xi18nc("@item", "Unknown error"); - case ErrorEletrical: + case SelfTestStatus::ErrorEletrical: return xi18nc("@item", "Electrical error"); - case ErrorServo: + case SelfTestStatus::ErrorServo: return xi18nc("@item", "Servo error"); - case ErrorRead: + case SelfTestStatus::ErrorRead: return xi18nc("@item", "Read error"); - case ErrorHandling: + case SelfTestStatus::ErrorHandling: return xi18nc("@item", "Handling error"); - case InProgress: + case SelfTestStatus::InProgress: return xi18nc("@item", "Self test in progress"); - case Success: + case SelfTestStatus::Success: default: return xi18nc("@item", "Success"); } @@ -238,33 +125,35 @@ QString SmartStatus::selfTestStatusToString(SmartStatus::SelfTestStatus s) QString SmartStatus::overallAssessmentToString(Overall o) { switch (o) { - case Good: + case Overall::Good: return xi18nc("@item", "Healthy"); - case BadPast: + case Overall::BadPast: return xi18nc("@item", "Has been used outside of its design parameters in the past."); - case BadSectors: + case Overall::BadSectors: return xi18nc("@item", "Has some bad sectors."); - case BadNow: + case Overall::BadNow: return xi18nc("@item", "Is being used outside of its design parameters right now."); - case BadSectorsMany: + case Overall::BadSectorsMany: return xi18nc("@item", "Has many bad sectors."); - case Bad: + case Overall::Bad: default: return xi18nc("@item", "Disk failure is imminent. Backup all data!"); } } -void SmartStatus::callback(SkDisk*, const SkSmartAttributeParsedData* a, void* user_data) +void SmartStatus::addAttributes(QList attr) { - SmartStatus* self = reinterpret_cast(user_data); + m_Attributes.clear(); - SmartAttribute sm(a); - self->m_Attributes.append(sm); + for (const SmartAttributeParsedData &at : qAsConst(attr)) { + SmartAttribute sm(at); + m_Attributes.append(sm); + } } diff --git a/src/core/smartstatus.h b/src/core/smartstatus.h index c68a0ca..06defe8 100644 --- a/src/core/smartstatus.h +++ b/src/core/smartstatus.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_SMARTSTATUS_H) - +#ifndef KPMCORE_SMARTSTATUS_H #define KPMCORE_SMARTSTATUS_H #include "util/libpartitionmanagerexport.h" @@ -32,7 +31,7 @@ struct SkDisk; class LIBKPMCORE_EXPORT SmartStatus { public: - enum Overall { + enum class Overall { Good, BadPast, BadSectors, @@ -41,7 +40,7 @@ public: Bad }; - enum SelfTestStatus { + enum class SelfTestStatus { Success, Aborted, Interrupted, @@ -51,99 +50,123 @@ public: ErrorServo, ErrorRead, ErrorHandling, - InProgress + InProgress = 15, }; public: typedef QList Attributes; public: - SmartStatus(const QString& device_path); + SmartStatus(const QString &device_path); public: void update(); - const QString& devicePath() const { + const QString &devicePath() const + { return m_DevicePath; } - bool isValid() const { + bool isValid() const + { return m_InitSuccess; } - bool status() const { + bool status() const + { return m_Status; } - const QString& modelName() const { + const QString &modelName() const + { return m_ModelName; } - const QString& serial() const { + const QString &serial() const + { return m_Serial; } - const QString& firmware() const { + const QString &firmware() const + { return m_Firmware; } - quint64 temp() const { + quint64 temp() const + { return m_Temp; } - quint64 badSectors() const { + quint64 badSectors() const + { return m_BadSectors; } - quint64 powerCycles() const { + quint64 powerCycles() const + { return m_PowerCycles; } - quint64 poweredOn() const { + quint64 poweredOn() const + { return m_PoweredOn; } - const Attributes& attributes() const { + const Attributes &attributes() const + { return m_Attributes; } - Overall overall() const { + Overall overall() const + { return m_Overall; } - SelfTestStatus selfTestStatus() const { + SelfTestStatus selfTestStatus() const + { return m_SelfTestStatus; } + void addAttributes(QList attr); + static QString tempToString(quint64 mkelvin); static QString overallAssessmentToString(Overall o); static QString selfTestStatusToString(SmartStatus::SelfTestStatus s); -protected: - void setStatus(bool s) { +private: + void setStatus(bool s) + { m_Status = s; } - void setModelName(const QString& name) { + void setModelName(const QString &name) + { m_ModelName = name; } - void setSerial(const QString& s) { + void setSerial(const QString &s) + { m_Serial = s; } - void setFirmware(const QString& f) { + void setFirmware(const QString &f) + { m_Firmware = f; } - void setTemp(quint64 t) { + void setTemp(quint64 t) + { m_Temp = t; } - void setInitSuccess(bool b) { + void setInitSuccess(bool b) + { m_InitSuccess = b; } - void setBadSectors(quint64 s) { + void setBadSectors(quint64 s) + { m_BadSectors = s; } - void setPowerCycles(quint64 p) { + void setPowerCycles(quint64 p) + { m_PowerCycles = p; } - void setPoweredOn(quint64 t) { + void setPoweredOn(quint64 t) + { m_PoweredOn = t; } - void setOverall(Overall o) { + void setOverall(Overall o) + { m_Overall = o; } - void setSelfTestStatus(SelfTestStatus s) { + void setSelfTestStatus(SelfTestStatus s) + { m_SelfTestStatus = s; } - static void callback(SkDisk* skDisk, const SkSmartAttributeParsedData* a, void* user_data); - private: const QString m_DevicePath; bool m_InitSuccess; diff --git a/src/core/volumemanagerdevice.cpp b/src/core/volumemanagerdevice.cpp index 8c996cf..7c339af 100644 --- a/src/core/volumemanagerdevice.cpp +++ b/src/core/volumemanagerdevice.cpp @@ -1,5 +1,6 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * + * Copyright (C) 2018 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 * @@ -15,18 +16,24 @@ * along with this program. If not, see .* *************************************************************************/ +#include "core/device_p.h" #include "core/volumemanagerdevice.h" +#include "core/volumemanagerdevice_p.h" /** Constructs an abstract Volume Manager Device with an empty PartitionTable. * + * @param name the Device's name + * @param deviceNode the Device's node + * @param logicalExtentSize the logical extent size that device uses */ -VolumeManagerDevice::VolumeManagerDevice(const QString& name, +VolumeManagerDevice::VolumeManagerDevice(std::shared_ptr d, + const QString& name, const QString& deviceNode, - const qint64 logicalSize, + const qint64 logicalExtentSize, const qint64 totalLogical, const QString& iconName, Device::Type type) - : Device(name, deviceNode, logicalSize, totalLogical, iconName, type) + : Device(std::static_pointer_cast(d), name, deviceNode, logicalExtentSize, totalLogical, iconName, type) { } @@ -34,3 +41,9 @@ QString VolumeManagerDevice::prettyDeviceNodeList() const { return deviceNodes().join(QStringLiteral(", ")); } + +void VolumeManagerDevice::setTotalLogical(qint64 n) +{ + Q_ASSERT(n > 0); + d->m_TotalLogical = n; +} diff --git a/src/core/volumemanagerdevice.h b/src/core/volumemanagerdevice.h index db4272e..f1750dd 100644 --- a/src/core/volumemanagerdevice.h +++ b/src/core/volumemanagerdevice.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_VOLUMEMANAGERDEVICE_H) - +#ifndef KPMCORE_VOLUMEMANAGERDEVICE_H #define KPMCORE_VOLUMEMANAGERDEVICE_H #include "util/libpartitionmanagerexport.h" @@ -27,6 +26,8 @@ #include #include +class VolumeManagerDevicePrivate; + /** A Volume Manager of physical devices represented as an abstract device. * * VolumeManagerDevice is an abstract device class for volume manager. e.g: LVM, SoftRAID. @@ -41,8 +42,7 @@ class LIBKPMCORE_EXPORT VolumeManagerDevice : public Device Q_DISABLE_COPY(VolumeManagerDevice) public: - - VolumeManagerDevice(const QString& name, const QString& deviceNode, const qint64 logicalSize, const qint64 totalLogical, const QString& iconName = QString(), Device::Type type = Device::Unknown_Device); + VolumeManagerDevice(std::shared_ptr d, const QString& name, const QString& deviceNode, const qint64 logicalSectorSize, const qint64 totalLogical, const QString& iconName = QString(), Device::Type type = Device::Type::Unknown_Device); /** * @return list of physical device's path that makes up volumeManagerDevice.(e.g: /dev/sda, /dev/sdb1) @@ -52,7 +52,7 @@ public: /** * @return list of logical partition's path. */ - virtual const QStringList partitionNodes() const = 0; + virtual const QStringList& partitionNodes() const = 0; /** * @return size of logical partition at the given path in bytes. @@ -76,13 +76,13 @@ protected: * @sector sector value to be mapped (if 0, will return start sector of the partition) * @return absolute sector value as represented inside device's partitionTable */ - virtual qint64 mappedSector(const QString& partitionPath, qint64 sector) const = 0; + virtual qint64 mappedSector(const QString& partitionPath, qint64 sector) const = 0; public: - /** string deviceNodes together into comma-sperated list + /** join deviceNodes together into comma-separated list * - * @return comma-seperated list of deviceNodes + * @return comma-separated list of deviceNodes */ virtual QString prettyDeviceNodeList() const; @@ -90,10 +90,7 @@ public: * * @param n Number of sectors. */ - void setTotalLogical(qint64 n) { - Q_ASSERT(n > 0); - m_TotalLogical = n; - } + void setTotalLogical(qint64 n); }; #endif diff --git a/src/core/volumemanagerdevice_p.h b/src/core/volumemanagerdevice_p.h new file mode 100644 index 0000000..6061c26 --- /dev/null +++ b/src/core/volumemanagerdevice_p.h @@ -0,0 +1,27 @@ +/************************************************************************* + * Copyright (C) 2018 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_VOLUMEMANAGERDEVICE_P_H +#define KPMCORE_VOLUMEMANAGERDEVICE_P_H + +#include "core/device_p.h" + +class VolumeManagerDevicePrivate : public DevicePrivate +{ +}; + +#endif diff --git a/src/fs/CMakeLists.txt b/src/fs/CMakeLists.txt index b7ead6a..93661a6 100644 --- a/src/fs/CMakeLists.txt +++ b/src/fs/CMakeLists.txt @@ -16,6 +16,7 @@ set(FS_SRC fs/hpfs.cpp fs/iso9660.cpp fs/jfs.cpp + fs/linuxraidmember.cpp fs/linuxswap.cpp fs/luks.cpp fs/luks2.cpp @@ -51,6 +52,7 @@ set(FS_LIB_HDRS fs/hpfs.h fs/iso9660.h fs/jfs.h + fs/linuxraidmember.h fs/linuxswap.h fs/luks.h fs/luks2.h diff --git a/src/fs/btrfs.cpp b/src/fs/btrfs.cpp index e202213..c7c1edb 100644 --- a/src/fs/btrfs.cpp +++ b/src/fs/btrfs.cpp @@ -44,7 +44,7 @@ FileSystem::CommandSupportType btrfs::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_GetUUID = FileSystem::cmdSupportNone; btrfs::btrfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Btrfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Btrfs) { } @@ -91,12 +91,12 @@ FileSystem::SupportTool btrfs::supportToolName() const qint64 btrfs::minCapacity() const { - return 256 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 256 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 btrfs::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } int btrfs::maxLabelLength() const diff --git a/src/fs/exfat.cpp b/src/fs/exfat.cpp index 00bfe51..3173450 100644 --- a/src/fs/exfat.cpp +++ b/src/fs/exfat.cpp @@ -39,7 +39,7 @@ FileSystem::CommandSupportType exfat::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType exfat::m_GetUUID = FileSystem::cmdSupportNone; exfat::exfat(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Exfat) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Exfat) { } @@ -84,7 +84,7 @@ FileSystem::SupportTool exfat::supportToolName() const qint64 exfat::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } int exfat::maxLabelLength() const @@ -112,8 +112,8 @@ bool exfat::writeLabel(Report& report, const QString& deviceNode, const QString& bool exfat::updateUUID(Report& report, const QString& deviceNode) const { - Q_UNUSED(report); - Q_UNUSED(deviceNode); + Q_UNUSED(report) + Q_UNUSED(deviceNode) return false; } diff --git a/src/fs/ext2.cpp b/src/fs/ext2.cpp index a2e82a4..fce9274 100644 --- a/src/fs/ext2.cpp +++ b/src/fs/ext2.cpp @@ -84,7 +84,7 @@ FileSystem::SupportTool ext2::supportToolName() const qint64 ext2::maxCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB) - Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB) - Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } int ext2::maxLabelLength() const diff --git a/src/fs/ext2.h b/src/fs/ext2.h index a31265d..ec2ca59 100644 --- a/src/fs/ext2.h +++ b/src/fs/ext2.h @@ -37,7 +37,7 @@ namespace FS class LIBKPMCORE_EXPORT ext2 : public FileSystem { public: - ext2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Ext2); + ext2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Type::Ext2); public: void init() override; diff --git a/src/fs/ext3.cpp b/src/fs/ext3.cpp index 28d3710..7fa67c9 100644 --- a/src/fs/ext3.cpp +++ b/src/fs/ext3.cpp @@ -25,13 +25,13 @@ namespace FS { ext3::ext3(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - ext2(firstsector, lastsector, sectorsused, label, FileSystem::Ext3) + ext2(firstsector, lastsector, sectorsused, label, FileSystem::Type::Ext3) { } qint64 ext3::maxCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB) - Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB) - Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } bool ext3::create(Report& report, const QString& deviceNode) diff --git a/src/fs/ext4.cpp b/src/fs/ext4.cpp index 4a12831..0fa3811 100644 --- a/src/fs/ext4.cpp +++ b/src/fs/ext4.cpp @@ -25,13 +25,13 @@ namespace FS { ext4::ext4(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - ext2(firstsector, lastsector, sectorsused, label, FileSystem::Ext4) + ext2(firstsector, lastsector, sectorsused, label, FileSystem::Type::Ext4) { } qint64 ext4::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } bool ext4::create(Report& report, const QString& deviceNode) diff --git a/src/fs/extended.cpp b/src/fs/extended.cpp index 86e8420..49ab801 100644 --- a/src/fs/extended.cpp +++ b/src/fs/extended.cpp @@ -25,7 +25,7 @@ FileSystem::CommandSupportType extended::m_Shrink = FileSystem::cmdSupportCore; FileSystem::CommandSupportType extended::m_Move = FileSystem::cmdSupportCore; extended::extended(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Extended) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Extended) { } diff --git a/src/fs/f2fs.cpp b/src/fs/f2fs.cpp index 3bf3d73..f8c62cb 100644 --- a/src/fs/f2fs.cpp +++ b/src/fs/f2fs.cpp @@ -46,7 +46,7 @@ FileSystem::CommandSupportType f2fs::m_GetUUID = FileSystem::cmdSupportNone; bool f2fs::oldVersion = false; // 1.8.x or older f2fs::f2fs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::F2fs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::F2fs) { } @@ -100,12 +100,12 @@ FileSystem::SupportTool f2fs::supportToolName() const qint64 f2fs::minCapacity() const { - return 30 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 30 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 f2fs::maxCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB); } int f2fs::maxLabelLength() const diff --git a/src/fs/f2fs.h b/src/fs/f2fs.h index 280fe82..4d2d92b 100644 --- a/src/fs/f2fs.h +++ b/src/fs/f2fs.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_F2FS_H) - +#ifndef KPMCORE_F2FS_H #define KPMCORE_F2FS_H #include "util/libpartitionmanagerexport.h" diff --git a/src/fs/fat12.cpp b/src/fs/fat12.cpp index e0a8616..8124c4a 100644 --- a/src/fs/fat12.cpp +++ b/src/fs/fat12.cpp @@ -87,12 +87,12 @@ FileSystem::SupportTool fat12::supportToolName() const qint64 fat12::minCapacity() const { - return 1 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 1 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 fat12::maxCapacity() const { - return 255 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 255 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } int fat12::maxLabelLength() const @@ -167,12 +167,7 @@ bool fat12::updateUUID(Report& report, const QString& deviceNode) const ExternalCommand cmd(report, QStringLiteral("dd"), { QStringLiteral("of=") + deviceNode , QStringLiteral("bs=1"), QStringLiteral("count=4"), QStringLiteral("seek=39") }); - if (!cmd.start()) - return false; - - if (cmd.write(uuid, sizeof(uuid)) != sizeof(uuid)) - return false; - - return cmd.waitFor(-1); + cmd.write(QByteArray(uuid, sizeof(uuid))); + return cmd.start(); } } diff --git a/src/fs/fat12.h b/src/fs/fat12.h index a05dd67..5a02895 100644 --- a/src/fs/fat12.h +++ b/src/fs/fat12.h @@ -16,8 +16,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_FAT12_H) - +#ifndef KPMCORE_FAT12_H #define KPMCORE_FAT12_H #include "util/libpartitionmanagerexport.h" @@ -38,7 +37,7 @@ namespace FS class LIBKPMCORE_EXPORT fat12 : public FileSystem { public: - fat12(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Fat12); + fat12(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Type::Fat12); public: void init() override; @@ -62,10 +61,10 @@ public: return m_Create; } CommandSupportType supportGrow() const override { - return m_Grow; + return cmdSupportNone; } CommandSupportType supportShrink() const override { - return m_Shrink; + return cmdSupportNone; } CommandSupportType supportMove() const override { return m_Move; diff --git a/src/fs/fat16.cpp b/src/fs/fat16.cpp index a287385..2c768b0 100644 --- a/src/fs/fat16.cpp +++ b/src/fs/fat16.cpp @@ -33,7 +33,7 @@ namespace FS { fat16::fat16(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - fat12(firstsector, lastsector, sectorsused, label, FileSystem::Fat16) + fat12(firstsector, lastsector, sectorsused, label, FileSystem::Type::Fat16) { } @@ -75,12 +75,12 @@ bool fat16::supportToolFound() const qint64 fat16::minCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 fat16::maxCapacity() const { - return 4 * Capacity::unitFactor(Capacity::Byte, Capacity::GiB) - Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 4 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::GiB) - Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } bool fat16::create(Report& report, const QString& deviceNode) diff --git a/src/fs/fat16.h b/src/fs/fat16.h index a654431..ea988a5 100644 --- a/src/fs/fat16.h +++ b/src/fs/fat16.h @@ -16,8 +16,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_FAT16_H) - +#ifndef KPMCORE_FAT16_H #define KPMCORE_FAT16_H #include "fs/fat12.h" @@ -43,6 +42,13 @@ public: bool create(Report& report, const QString& deviceNode) override; bool resize(Report& report, const QString& deviceNode, qint64 length) const override; + CommandSupportType supportGrow() const override { + return m_Grow; + } + CommandSupportType supportShrink() const override { + return m_Shrink; + } + qint64 minCapacity() const override; qint64 maxCapacity() const override; bool supportToolFound() const override; diff --git a/src/fs/fat32.cpp b/src/fs/fat32.cpp index 2dd891c..b54e4fa 100644 --- a/src/fs/fat32.cpp +++ b/src/fs/fat32.cpp @@ -26,18 +26,18 @@ namespace FS { fat32::fat32(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - fat16(firstsector, lastsector, sectorsused, label, FileSystem::Fat32) + fat16(firstsector, lastsector, sectorsused, label, FileSystem::Type::Fat32) { } qint64 fat32::minCapacity() const { - return 32 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 32 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 fat32::maxCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB) - Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB) - Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } bool fat32::create(Report& report, const QString& deviceNode) @@ -59,12 +59,7 @@ bool fat32::updateUUID(Report& report, const QString& deviceNode) const // HACK: replace this hack with fatlabel "-i" (dosfstools 4.2) ExternalCommand cmd(report, QStringLiteral("dd"), { QStringLiteral("of=") + deviceNode, QStringLiteral("bs=1"), QStringLiteral("count=4"), QStringLiteral("seek=67") }); - if (!cmd.start()) - return false; - - if (cmd.write(uuid, sizeof(uuid)) != sizeof(uuid)) - return false; - - return cmd.waitFor(-1); + cmd.write(QByteArray(uuid, sizeof(uuid))); + return cmd.start(); } } diff --git a/src/fs/filesystem.cpp b/src/fs/filesystem.cpp index 863a29e..8de5ee4 100644 --- a/src/fs/filesystem.cpp +++ b/src/fs/filesystem.cpp @@ -1,7 +1,7 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -31,10 +31,12 @@ #include +#include #include +#include #include -const std::array< QColor, FileSystem::__lastType > FileSystem::defaultColorCode = +const std::vector FileSystem::defaultColorCode = { { QColor( 220,205,175 ), // unknown @@ -66,25 +68,40 @@ const std::array< QColor, FileSystem::__lastType > FileSystem::defaultColorCode QColor( 170,120,255 ), // udf QColor( 177,82,69 ), // iso9660 QColor( 223,39,104 ), // luks2 - QColor( 204,179,255 ) // fat12 + QColor( 204,179,255 ), // fat12 + QColor( 255,100,100 ) // linux_raid_member } }; +struct FileSystemPrivate { + FileSystem::Type m_Type; + qint64 m_FirstSector; + qint64 m_LastSector; + qint64 m_SectorSize; + qint64 m_SectorsUsed; + QString m_Label; + QString m_UUID; +}; /** Creates a new FileSystem object @param firstsector the first sector used by this FileSystem on the Device @param lastsector the last sector used by this FileSystem on the Device @param sectorsused the number of sectors in use on the FileSystem - @param l the FileSystem label - @param t the FileSystem type + @param label the FileSystem label + @param type the FileSystem type */ -FileSystem::FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& l, FileSystem::Type t) : - m_Type(t), - m_FirstSector(firstsector), - m_LastSector(lastsector), - m_SectorsUsed(sectorsused), - m_Label(l), - m_UUID() +FileSystem::FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type type) : + d(std::make_unique()) +{ + d->m_Type = type; + d->m_FirstSector = firstsector; + d->m_LastSector = lastsector; + d->m_SectorsUsed = sectorsused; + d->m_Label = label; + d->m_UUID = QString(); +} + +FileSystem::~FileSystem() { } @@ -106,7 +123,7 @@ FileSystem::Type FileSystem::detectFileSystem(const QString& partitionPath) QString FileSystem::detectMountPoint(FileSystem* fs, const QString& partitionPath) { - if (fs->type() == FileSystem::Lvm2_PV) + if (fs->type() == FileSystem::Type::Lvm2_PV) return FS::lvm2_pv::getVGName(partitionPath); if (partitionPath.isEmpty()) // Happens when during initial scan LUKS is closed @@ -131,7 +148,7 @@ bool FileSystem::detectMountStatus(FileSystem* fs, const QString& partitionPath) { bool mounted = false; - if (fs->type() == FileSystem::Lvm2_PV) { + if (fs->type() == FileSystem::Type::Lvm2_PV) { mounted = FS::lvm2_pv::getVGName(partitionPath) != QString(); } else { mounted = isMounted(partitionPath); @@ -360,13 +377,13 @@ bool FileSystem::updateBootSector(Report& report, const QString& deviceNode) con /** @return the minimum capacity valid for this FileSystem in bytes */ qint64 FileSystem::minCapacity() const { - return 8 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 8 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } /** @return the maximum capacity valid for this FileSystem in bytes */ qint64 FileSystem::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } /** @return the maximum label length valid for this FileSystem */ @@ -390,6 +407,11 @@ QString FileSystem::name(const QStringList& languages) const return nameForType(type(), languages); } +FileSystem::Type FileSystem::type() const +{ + return d->m_Type; +} + /** @return a pointer to a QString C array with all FileSystem names */ static const KLocalizedString* typeNames() { @@ -424,7 +446,8 @@ static const KLocalizedString* typeNames() kxi18nc("@item filesystem name", "udf"), kxi18nc("@item filesystem name", "iso9660"), kxi18nc("@item filesystem name", "luks2"), - kxi18nc("@item filesystem name", "fat12") + kxi18nc("@item filesystem name", "fat12"), + kxi18nc("@item filesystem name", "linux_raid_member"), }; return s; @@ -435,10 +458,9 @@ static const KLocalizedString* typeNames() */ QString FileSystem::nameForType(FileSystem::Type t, const QStringList& languages) { - Q_ASSERT(t >= 0); - Q_ASSERT(t < __lastType); + Q_ASSERT(t < Type::__lastType); - return typeNames()[t].toString(languages); + return typeNames()[static_cast(t)].toString(languages); } /** @param s the name to get the type for @@ -446,11 +468,11 @@ QString FileSystem::nameForType(FileSystem::Type t, const QStringList& languages */ FileSystem::Type FileSystem::typeForName(const QString& s, const QStringList& languages ) { - for (quint32 i = 0; i < __lastType; i++) + for (quint32 i = 0; i < static_cast(Type::__lastType); i++) if (typeNames()[i].toString(languages) == s) return static_cast(i); - return Unknown; + return Type::Unknown; } /** @return a QList of all known types */ @@ -458,8 +480,8 @@ QList FileSystem::types() { QList result; - int i = Ext2; // first "real" filesystem - while (i != __lastType) + int i = static_cast(Type::Ext2); // first "real" filesystem + while (i != static_cast(Type::__lastType)) result.append(static_cast(i++)); return result; @@ -529,9 +551,25 @@ bool FileSystem::unmount(Report& report, const QString& deviceNode) return false; } +qint64 FileSystem::firstSector() const +{ + return d->m_FirstSector; +} + +qint64 FileSystem::lastSector() const +{ + return d->m_LastSector; +} + bool FileSystem::findExternal(const QString& cmdName, const QStringList& args, int expectedCode) { - ExternalCommand cmd(cmdName, args); + QString cmdFullPath = QStandardPaths::findExecutable(cmdName); + if (cmdFullPath.isEmpty()) + cmdFullPath = QStandardPaths::findExecutable(cmdName, { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); + if (cmdFullPath.isEmpty()) + return false; + + ExternalCommand cmd(cmdFullPath, args); if (!cmd.run()) return false; @@ -547,3 +585,53 @@ FileSystem::SupportTool FileSystem::supportToolName() const { return SupportTool(); } + +void FileSystem::setFirstSector(qint64 s) +{ + d->m_FirstSector = s; +} + +void FileSystem::setLastSector(qint64 s) +{ + d->m_LastSector = s; +} + +const QString& FileSystem::label() const +{ + return d->m_Label; +} + +qint64 FileSystem::sectorSize() const +{ + return d->m_SectorSize; +} + +qint64 FileSystem::sectorsUsed() const +{ + return d->m_SectorsUsed; +} + +const QString& FileSystem::uuid() const +{ + return d->m_UUID; +} + +void FileSystem::setSectorSize(qint64 s) +{ + d->m_SectorSize = s; +} + +void FileSystem::setSectorsUsed(qint64 s) +{ + d->m_SectorsUsed = s; +} + +void FileSystem::setLabel(const QString& s) +{ + d->m_Label = s; +} + +void FileSystem::setUUID(const QString& s) +{ + d->m_UUID = s; +} diff --git a/src/fs/filesystem.h b/src/fs/filesystem.h index 27451a7..a411b24 100644 --- a/src/fs/filesystem.h +++ b/src/fs/filesystem.h @@ -1,7 +1,7 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -17,23 +17,25 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_FILESYSTEM_H) +#ifndef KPMCORE_FILESYSTEM_H #define KPMCORE_FILESYSTEM_H #include "util/libpartitionmanagerexport.h" -#include #include #include #include #include #include -#include +#include +#include +class QColor; class QValidator; class Device; class Report; +struct FileSystemPrivate; /** Base class for all FileSystems. @@ -57,40 +59,41 @@ public: }; /** Supported FileSystem types */ - enum Type { - Unknown = 0, - Extended = 1, + enum Type : int { + Unknown, + Extended, - Ext2 = 2, - Ext3 = 3, - Ext4 = 4, - LinuxSwap = 5, - Fat16 = 6, - Fat32 = 7, - Ntfs = 8, - ReiserFS = 9, - Reiser4 = 10, - Xfs = 11, - Jfs = 12, - Hfs = 13, - HfsPlus = 14, - Ufs = 15, - Unformatted = 16, - Btrfs = 17, - Hpfs = 18, - Luks = 19, - Ocfs2 = 20, - Zfs = 21, - Exfat = 22, - Nilfs2 = 23, - Lvm2_PV = 24, - F2fs = 25, - Udf = 26, - Iso9660 = 27, - Luks2 = 28, - Fat12 = 29, + Ext2, + Ext3, + Ext4, + LinuxSwap, + Fat16, + Fat32, + Ntfs, + ReiserFS, + Reiser4, + Xfs, + Jfs, + Hfs, + HfsPlus, + Ufs, + Unformatted, + Btrfs, + Hpfs, + Luks, + Ocfs2, + Zfs, + Exfat, + Nilfs2, + Lvm2_PV, + F2fs, + Udf, + Iso9660, + Luks2, + Fat12, + LinuxRaidMember, - __lastType = 30 + __lastType }; /** The type of support for a given FileSystem action */ @@ -101,15 +104,15 @@ public: cmdSupportBackend = 4 /**< supported by the backend */ }; - static const std::array< QColor, __lastType > defaultColorCode; + static const std::vector defaultColorCode; Q_DECLARE_FLAGS(CommandSupportTypes, CommandSupportType) protected: - FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t); + FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type type); public: - virtual ~FileSystem() {} + virtual ~FileSystem(); public: virtual void init() {} @@ -196,9 +199,11 @@ public: * @see nameForType() */ virtual QString name(const QStringList& languages = {}) const; - virtual FileSystem::Type type() const { - return m_Type; /**< @return the FileSystem's type */ - } + + /** + * @return the FileSystem's type + */ + virtual FileSystem::Type type() const; /** * Returns the name of the given filesystem type. If @p languages @@ -228,12 +233,12 @@ public: virtual bool mount(Report& report, const QString& deviceNode, const QString& mountPoint); virtual bool unmount(Report& report, const QString& deviceNode); - qint64 firstSector() const { - return m_FirstSector; /**< @return the FileSystem's first sector */ - } - qint64 lastSector() const { - return m_LastSector; /**< @return the FileSystem's last sector */ - } + /**< @return the FileSystem's first sector */ + qint64 firstSector() const; + + /**< @return the FileSystem's last sector */ + qint64 lastSector() const; + qint64 length() const { return lastSector() - firstSector() + 1; /**< @return the FileSystem's length */ } @@ -244,52 +249,42 @@ public: return firstByte() + length() * sectorSize() - 1; /**< @return the FileSystem's last byte */ } - void setFirstSector(qint64 s) { - m_FirstSector = s; /**< @param s the new first sector */ - } - void setLastSector(qint64 s) { - m_LastSector = s; /**< @param s the new last sector */ - } + /**< @param s the new first sector */ + void setFirstSector(qint64 s); + + /**< @param s the new last sector */ + void setLastSector(qint64 s); void move(qint64 newStartSector); - const QString& label() const { - return m_Label; /**< @return the FileSystem's label */ - } - qint64 sectorSize() const { - return m_SectorSize; /**< @return the sector size in the underlying Device */ - } - qint64 sectorsUsed() const { - return m_SectorsUsed; /**< @return the sectors in use on the FileSystem */ - } - const QString& uuid() const { - return m_UUID; /**< @return the FileSystem's UUID */ - } + /**< @return the FileSystem's label */ + const QString& label() const; - void setSectorSize(qint64 s) { - m_SectorSize = s; /**< @param s the new value for sector size */ - } - void setSectorsUsed(qint64 s) { - m_SectorsUsed = s; /**< @param s the new value for sectors in use */ - } - void setLabel(const QString& s) { - m_Label = s; /**< @param s the new label */ - } - void setUUID(const QString& s) { - m_UUID = s; /**< @param s the new UUID */ - } + /**< @return the sector size in the underlying Device */ + qint64 sectorSize() const; + + /**< @return the sectors in use on the FileSystem */ + qint64 sectorsUsed() const; + + /**< @return the FileSystem's UUID */ + const QString& uuid() const; + + /**< @param s the new value for sector size */ + void setSectorSize(qint64 s); + + /**< @param s the new value for sectors in use */ + void setSectorsUsed(qint64 s); + + /**< @param s the new label */ + void setLabel(const QString& s); + + /**< @param s the new UUID */ + void setUUID(const QString& s); protected: static bool findExternal(const QString& cmdName, const QStringList& args = QStringList(), int exptectedCode = 1); -protected: - FileSystem::Type m_Type; - qint64 m_FirstSector; - qint64 m_LastSector; - qint64 m_SectorSize; - qint64 m_SectorsUsed; - QString m_Label; - QString m_UUID; + std::unique_ptr d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileSystem::CommandSupportTypes) diff --git a/src/fs/filesystem_p.h b/src/fs/filesystem_p.h new file mode 100644 index 0000000..ed410ba --- /dev/null +++ b/src/fs/filesystem_p.h @@ -0,0 +1,36 @@ +/************************************************************************* + * Copyright (C) 2018 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_FILESYSTEM_P_H +#define KPMCORE_FILESYSTEM_P_H + +#include "fs/filesystem.h" + +#include + +class FileSystemPrivate { +public: + FileSystem::Type m_Type; + qint64 m_FirstSector; + qint64 m_LastSector; + qint64 m_SectorSize; + qint64 m_SectorsUsed; + QString m_Label; + QString m_UUID; +}; + +#endif diff --git a/src/fs/filesystemfactory.cpp b/src/fs/filesystemfactory.cpp index b17998f..6f99054 100644 --- a/src/fs/filesystemfactory.cpp +++ b/src/fs/filesystemfactory.cpp @@ -34,6 +34,7 @@ #include "fs/hpfs.h" #include "fs/iso9660.h" #include "fs/jfs.h" +#include "fs/linuxraidmember.h" #include "fs/linuxswap.h" #include "fs/luks.h" #include "fs/luks2.h" @@ -61,36 +62,37 @@ void FileSystemFactory::init() qDeleteAll(m_FileSystems); m_FileSystems.clear(); - m_FileSystems.insert(FileSystem::Btrfs, new FS::btrfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Exfat, new FS::exfat(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Ext2, new FS::ext2(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Ext3, new FS::ext3(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Ext4, new FS::ext4(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Extended, new FS::extended(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::F2fs, new FS::f2fs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Fat12, new FS::fat12(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Fat16, new FS::fat16(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Fat32, new FS::fat32(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Hfs, new FS::hfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::HfsPlus, new FS::hfsplus(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Hpfs, new FS::hpfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Iso9660, new FS::iso9660(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Jfs, new FS::jfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::LinuxSwap, new FS::linuxswap(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Luks, new FS::luks(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Luks2, new FS::luks2(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Lvm2_PV, new FS::lvm2_pv(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Nilfs2, new FS::nilfs2(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Ntfs, new FS::ntfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Ocfs2, new FS::ocfs2(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::ReiserFS, new FS::reiserfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Reiser4, new FS::reiser4(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Udf, new FS::udf(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Ufs, new FS::ufs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Unformatted, new FS::unformatted(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Unknown, new FS::unknown(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Xfs, new FS::xfs(-1, -1, -1, QString())); - m_FileSystems.insert(FileSystem::Zfs, new FS::zfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Btrfs, new FS::btrfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Exfat, new FS::exfat(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Ext2, new FS::ext2(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Ext3, new FS::ext3(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Ext4, new FS::ext4(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Extended, new FS::extended(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::F2fs, new FS::f2fs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Fat12, new FS::fat12(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Fat16, new FS::fat16(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Fat32, new FS::fat32(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Hfs, new FS::hfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::HfsPlus, new FS::hfsplus(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Hpfs, new FS::hpfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Iso9660, new FS::iso9660(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Jfs, new FS::jfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::LinuxRaidMember, new FS::linuxraidmember(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::LinuxSwap, new FS::linuxswap(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Luks, new FS::luks(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Luks2, new FS::luks2(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Lvm2_PV, new FS::lvm2_pv(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Nilfs2, new FS::nilfs2(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Ntfs, new FS::ntfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Ocfs2, new FS::ocfs2(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::ReiserFS, new FS::reiserfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Reiser4, new FS::reiser4(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Udf, new FS::udf(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Ufs, new FS::ufs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Unformatted, new FS::unformatted(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Unknown, new FS::unknown(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Xfs, new FS::xfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::Zfs, new FS::zfs(-1, -1, -1, QString())); for (const auto &fs : FileSystemFactory::map()) fs->init(); @@ -111,36 +113,37 @@ FileSystem* FileSystemFactory::create(FileSystem::Type t, qint64 firstsector, qi FileSystem* fs = nullptr; switch (t) { - case FileSystem::Btrfs: fs = new FS::btrfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Exfat: fs = new FS::exfat(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Ext2: fs = new FS::ext2(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Ext3: fs = new FS::ext3(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Ext4: fs = new FS::ext4(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Extended: fs = new FS::extended(firstsector, lastsector, sectorsused, label); break; - case FileSystem::F2fs: fs = new FS::f2fs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Fat12: fs = new FS::fat12(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Fat16: fs = new FS::fat16(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Fat32: fs = new FS::fat32(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Hfs: fs = new FS::hfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::HfsPlus: fs = new FS::hfsplus(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Hpfs: fs = new FS::hpfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Iso9660: fs = new FS::iso9660(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Jfs: fs = new FS::jfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::LinuxSwap: fs = new FS::linuxswap(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Luks: fs = new FS::luks(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Luks2: fs = new FS::luks2(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Lvm2_PV: fs = new FS::lvm2_pv(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Nilfs2: fs = new FS::nilfs2(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Ntfs: fs = new FS::ntfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Ocfs2: fs = new FS::ocfs2(firstsector, lastsector, sectorsused, label); break; - case FileSystem::ReiserFS: fs = new FS::reiserfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Reiser4: fs = new FS::reiser4(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Udf: fs = new FS::udf(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Ufs: fs = new FS::ufs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Unformatted: fs = new FS::unformatted(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Unknown: fs = new FS::unknown(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Xfs: fs = new FS::xfs(firstsector, lastsector, sectorsused, label); break; - case FileSystem::Zfs: fs = new FS::zfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Btrfs: fs = new FS::btrfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Exfat: fs = new FS::exfat(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Ext2: fs = new FS::ext2(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Ext3: fs = new FS::ext3(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Ext4: fs = new FS::ext4(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Extended: fs = new FS::extended(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::F2fs: fs = new FS::f2fs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Fat12: fs = new FS::fat12(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Fat16: fs = new FS::fat16(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Fat32: fs = new FS::fat32(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Hfs: fs = new FS::hfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::HfsPlus: fs = new FS::hfsplus(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Hpfs: fs = new FS::hpfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Iso9660: fs = new FS::iso9660(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Jfs: fs = new FS::jfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::LinuxRaidMember: fs = new FS::linuxraidmember(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::LinuxSwap: fs = new FS::linuxswap(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Luks: fs = new FS::luks(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Luks2: fs = new FS::luks2(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Lvm2_PV: fs = new FS::lvm2_pv(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Nilfs2: fs = new FS::nilfs2(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Ntfs: fs = new FS::ntfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Ocfs2: fs = new FS::ocfs2(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::ReiserFS: fs = new FS::reiserfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Reiser4: fs = new FS::reiser4(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Udf: fs = new FS::udf(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Ufs: fs = new FS::ufs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Unformatted: fs = new FS::unformatted(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Unknown: fs = new FS::unknown(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Xfs: fs = new FS::xfs(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::Zfs: fs = new FS::zfs(firstsector, lastsector, sectorsused, label); break; default: break; } diff --git a/src/fs/filesystemfactory.h b/src/fs/filesystemfactory.h index 185bc57..6679bbc 100644 --- a/src/fs/filesystemfactory.h +++ b/src/fs/filesystemfactory.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_FILESYSTEMFACTORY_H) - +#ifndef KPMCORE_FILESYSTEMFACTORY_H #define KPMCORE_FILESYSTEMFACTORY_H #include "fs/filesystem.h" diff --git a/src/fs/hfs.cpp b/src/fs/hfs.cpp index 404a4d9..15677fc 100644 --- a/src/fs/hfs.cpp +++ b/src/fs/hfs.cpp @@ -35,7 +35,7 @@ FileSystem::CommandSupportType hfs::m_Copy = FileSystem::cmdSupportNone; FileSystem::CommandSupportType hfs::m_Backup = FileSystem::cmdSupportNone; hfs::hfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Hfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Hfs) { } @@ -73,7 +73,7 @@ FileSystem::SupportTool hfs::supportToolName() const qint64 hfs::maxCapacity() const { - return 2 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB); + return 2 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB); } int hfs::maxLabelLength() const diff --git a/src/fs/hfsplus.cpp b/src/fs/hfsplus.cpp index 328d356..6d17540 100644 --- a/src/fs/hfsplus.cpp +++ b/src/fs/hfsplus.cpp @@ -35,7 +35,7 @@ FileSystem::CommandSupportType hfsplus::m_Copy = FileSystem::cmdSupportNone; FileSystem::CommandSupportType hfsplus::m_Backup = FileSystem::cmdSupportNone; hfsplus::hfsplus(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::HfsPlus) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::HfsPlus) { } @@ -73,7 +73,7 @@ FileSystem::SupportTool hfsplus::supportToolName() const qint64 hfsplus::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } int hfsplus::maxLabelLength() const diff --git a/src/fs/hpfs.cpp b/src/fs/hpfs.cpp index cfb2386..55c41a1 100644 --- a/src/fs/hpfs.cpp +++ b/src/fs/hpfs.cpp @@ -37,12 +37,12 @@ FileSystem::CommandSupportType hpfs::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType hpfs::m_GetUUID = FileSystem::cmdSupportNone; hpfs::hpfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Hpfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Hpfs) { } qint64 hpfs::maxCapacity() const { - return 2 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB); + return 2 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB); } } diff --git a/src/fs/iso9660.cpp b/src/fs/iso9660.cpp index 05d178b..d7939a9 100644 --- a/src/fs/iso9660.cpp +++ b/src/fs/iso9660.cpp @@ -21,7 +21,7 @@ namespace FS { iso9660::iso9660(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Iso9660) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Iso9660) { } diff --git a/src/fs/jfs.cpp b/src/fs/jfs.cpp index 8d3362f..dfb90a0 100644 --- a/src/fs/jfs.cpp +++ b/src/fs/jfs.cpp @@ -40,7 +40,7 @@ FileSystem::CommandSupportType jfs::m_Backup = FileSystem::cmdSupportNone; FileSystem::CommandSupportType jfs::m_SetLabel = FileSystem::cmdSupportNone; jfs::jfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Jfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Jfs) { } @@ -79,12 +79,12 @@ FileSystem::SupportTool jfs::supportToolName() const qint64 jfs::minCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 jfs::maxCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB); } int jfs::maxLabelLength() const @@ -96,7 +96,7 @@ qint64 jfs::readUsedCapacity(const QString& deviceNode) const { ExternalCommand cmd(QStringLiteral("jfs_debugfs"), QStringList() << deviceNode); - if (cmd.start() && cmd.write("dm") == 2 && cmd.waitFor()) { + if (cmd.write(QByteArrayLiteral("dm")) && cmd.start()) { qint64 blockSize = -1; QRegularExpression re(QStringLiteral("Block Size: (\\d+)")); QRegularExpressionMatch reBlockSize = re.match(cmd.output()); diff --git a/src/fs/linuxraidmember.cpp b/src/fs/linuxraidmember.cpp new file mode 100644 index 0000000..19b4825 --- /dev/null +++ b/src/fs/linuxraidmember.cpp @@ -0,0 +1,28 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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 "fs/linuxraidmember.h" + +namespace FS +{ + +linuxraidmember::linuxraidmember(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::LinuxRaidMember) +{ +} + +} diff --git a/src/fs/linuxraidmember.h b/src/fs/linuxraidmember.h new file mode 100644 index 0000000..aa71cdd --- /dev/null +++ b/src/fs/linuxraidmember.h @@ -0,0 +1,41 @@ +/************************************************************************* + * Copyright (C) 2018 by Caio Carvalho * + * * + * 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 LINUXRAIDMEMBER_H +#define LINUXRAIDMEMBER_H + +#include "util/libpartitionmanagerexport.h" + +#include "fs/filesystem.h" + +class Report; + +class QString; + +namespace FS +{ +/** A linux_raid_member file system. + @author Caio Carvalho + */ +class LIBKPMCORE_EXPORT linuxraidmember : public FileSystem +{ +public: + linuxraidmember(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); +}; +} + +#endif // LINUXRAIDMEMBER_H diff --git a/src/fs/linuxswap.cpp b/src/fs/linuxswap.cpp index edffc94..e535d67 100644 --- a/src/fs/linuxswap.cpp +++ b/src/fs/linuxswap.cpp @@ -40,7 +40,7 @@ FileSystem::CommandSupportType linuxswap::m_GetUUID = FileSystem::cmdSupportNone FileSystem::CommandSupportType linuxswap::m_UpdateUUID = FileSystem::cmdSupportNone; linuxswap::linuxswap(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::LinuxSwap) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::LinuxSwap) { } @@ -89,7 +89,7 @@ bool linuxswap::create(Report& report, const QString& deviceNode) bool linuxswap::resize(Report& report, const QString& deviceNode, qint64 length) const { - Q_UNUSED(length); + Q_UNUSED(length) const QString label = readLabel(deviceNode); const QString uuid = readUUID(deviceNode); @@ -145,14 +145,14 @@ QString linuxswap::unmountTitle() const } bool linuxswap::canMount(const QString& deviceNode, const QString& mountPoint) const { - Q_UNUSED(deviceNode); + Q_UNUSED(deviceNode) // linux swap doesn't require mount point to activate return mountPoint != QStringLiteral("/"); } bool linuxswap::mount(Report& report, const QString& deviceNode, const QString& mountPoint) { - Q_UNUSED(mountPoint); + Q_UNUSED(mountPoint) ExternalCommand cmd(report, QStringLiteral("swapon"), { deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } diff --git a/src/fs/luks.cpp b/src/fs/luks.cpp index 12ace19..6215554 100644 --- a/src/fs/luks.cpp +++ b/src/fs/luks.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -129,9 +130,8 @@ bool luks::create(Report& report, const QString& deviceNode) QStringLiteral("--type"), QStringLiteral("luks1"), QStringLiteral("luksFormat"), deviceNode }); - if (!( createCmd.start(-1) && - createCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && - createCmd.waitFor() && createCmd.exitCode() == 0)) + if (!( createCmd.write(m_passphrase.toLocal8Bit() + '\n') && + createCmd.start(-1) && createCmd.exitCode() == 0)) { return false; } @@ -141,7 +141,7 @@ bool luks::create(Report& report, const QString& deviceNode) deviceNode, suggestedMapperName(deviceNode) }); - if (!( openCmd.start(-1) && openCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && openCmd.waitFor())) + if (!( openCmd.write(m_passphrase.toLocal8Bit() + '\n') && openCmd.start(-1))) return false; setPayloadSize(); @@ -168,12 +168,12 @@ QString luks::unmountTitle() const QString luks::cryptOpenTitle() const { - return xi18nc("@title:menu", "Decrypt"); + return xi18nc("@title:menu", "Unlock"); } QString luks::cryptCloseTitle() const { - return xi18nc("@title:menu", "Deactivate"); + return xi18nc("@title:menu", "Lock"); } void luks::setPassphrase(const QString& passphrase) @@ -265,9 +265,8 @@ bool luks::cryptOpen(QWidget* parent, const QString& deviceNode) deviceNode, suggestedMapperName(deviceNode) }); - if (!( openCmd.start(-1) && - openCmd.write(passphrase.toLocal8Bit() + '\n') == passphrase.toLocal8Bit().length() + 1 && - openCmd.waitFor() && openCmd.exitCode() == 0) ) + if (!( openCmd.write(passphrase.toLocal8Bit() + '\n') && + openCmd.start(-1) && openCmd.exitCode() == 0) ) return false; if (m_innerFs) { @@ -286,8 +285,8 @@ bool luks::cryptOpen(QWidget* parent, const QString& deviceNode) if (!m_isCryptOpen) return false; - for (auto &p : LVM::pvList) - if (p.isLuks() && p.partition()->deviceNode() == deviceNode && p.partition()->fileSystem().type() == FileSystem::Lvm2_PV) + for (auto &p : LVM::pvList::list()) + if (p.isLuks() && p.partition()->deviceNode() == deviceNode && p.partition()->fileSystem().type() == FileSystem::Type::Lvm2_PV) p.setLuks(false); m_passphrase = passphrase; @@ -325,7 +324,7 @@ bool luks::cryptClose(const QString& deviceNode) m_isCryptOpen = (m_innerFs != nullptr); - for (auto &p : LVM::pvList) + for (auto &p : LVM::pvList::list()) if (!p.isLuks() && p.partition()->deviceNode() == deviceNode) p.setLuks(true); @@ -464,7 +463,7 @@ FileSystem::Type luks::type() const { if (m_isCryptOpen && m_innerFs) return m_innerFs->type(); - return FileSystem::Luks; + return FileSystem::Type::Luks; } QString luks::suggestedMapperName(const QString& deviceNode) const @@ -647,19 +646,19 @@ bool luks::canEncryptType(FileSystem::Type type) { switch (type) { - case Btrfs: - case F2fs: - case Ext2: - case Ext3: - case Ext4: - case Jfs: - case LinuxSwap: - case Lvm2_PV: - case Nilfs2: - case ReiserFS: - case Reiser4: - case Xfs: - case Zfs: + case Type::Btrfs: + case Type::F2fs: + case Type::Ext2: + case Type::Ext3: + case Type::Ext4: + case Type::Jfs: + case Type::LinuxSwap: + case Type::Lvm2_PV: + case Type::Nilfs2: + case Type::ReiserFS: + case Type::Reiser4: + case Type::Xfs: + case Type::Zfs: return true; default: return false; @@ -690,7 +689,7 @@ void luks::setPayloadSize() bool luks::testPassphrase(const QString& deviceNode, const QString& passphrase) const { ExternalCommand cmd(QStringLiteral("cryptsetup"), { QStringLiteral("open"), QStringLiteral("--tries"), QStringLiteral("1"), QStringLiteral("--test-passphrase"), deviceNode }); - if (cmd.start(-1) && cmd.write(passphrase.toLocal8Bit() + '\n') == passphrase.toLocal8Bit().length() + 1 && cmd.waitFor() && cmd.exitCode() == 0) + if (cmd.write(passphrase.toLocal8Bit() + '\n') && cmd.start(-1) && cmd.exitCode() == 0) return true; return false; diff --git a/src/fs/luks.h b/src/fs/luks.h index 9ee8ea4..7dddba1 100644 --- a/src/fs/luks.h +++ b/src/fs/luks.h @@ -17,8 +17,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_LUKS_H) - +#ifndef KPMCORE_LUKS_H #define KPMCORE_LUKS_H #include "util/libpartitionmanagerexport.h" @@ -26,11 +25,11 @@ #include "fs/filesystem.h" #include -#include class Report; class QString; +class QWidget; namespace FS { @@ -40,10 +39,10 @@ namespace FS class LIBKPMCORE_EXPORT luks : public FileSystem { public: - luks(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Luks); + luks(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t = FileSystem::Type::Luks); ~luks() override; - enum KeyLocation { + enum class KeyLocation { unknown, dmcrypt, keyring @@ -225,7 +224,7 @@ protected: qint64 m_PayloadSize; QString m_outerUuid; - luks::KeyLocation m_KeyLocation = unknown; + luks::KeyLocation m_KeyLocation = KeyLocation::unknown; }; } diff --git a/src/fs/luks2.cpp b/src/fs/luks2.cpp index 72e5554..dfc9db0 100644 --- a/src/fs/luks2.cpp +++ b/src/fs/luks2.cpp @@ -28,7 +28,7 @@ namespace FS { luks2::luks2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) - : luks(firstsector, lastsector, sectorsused, label, FileSystem::Luks2) + : luks(firstsector, lastsector, sectorsused, label, FileSystem::Type::Luks2) { } @@ -40,7 +40,7 @@ FileSystem::Type luks2::type() const { if (m_isCryptOpen && m_innerFs) return m_innerFs->type(); - return FileSystem::Luks2; + return FileSystem::Type::Luks2; } bool luks2::create(Report& report, const QString& deviceNode) @@ -56,9 +56,8 @@ bool luks2::create(Report& report, const QString& deviceNode) QStringLiteral("--type"), QStringLiteral("luks2"), QStringLiteral("luksFormat"), deviceNode }); - if (!( createCmd.start(-1) && - createCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && - createCmd.waitFor() && createCmd.exitCode() == 0)) + if (!( createCmd.write(m_passphrase.toLocal8Bit() + '\n') && + createCmd.start(-1) && createCmd.exitCode() == 0)) { return false; } @@ -68,7 +67,7 @@ bool luks2::create(Report& report, const QString& deviceNode) deviceNode, suggestedMapperName(deviceNode) }); - if (!( openCmd.start(-1) && openCmd.write(m_passphrase.toLocal8Bit() + '\n') == m_passphrase.toLocal8Bit().length() + 1 && openCmd.waitFor())) + if (!( openCmd.write(m_passphrase.toLocal8Bit() + '\n') && openCmd.start(-1))) return false; setPayloadSize(); @@ -95,13 +94,13 @@ bool luks2::resize(Report& report, const QString& deviceNode, qint64 newLength) ExternalCommand cryptResizeCmd(report, QStringLiteral("cryptsetup"), { QStringLiteral("resize"), mapperName() }); report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition %1.", deviceNode); - cryptResizeCmd.start(-1); - if (m_KeyLocation == keyring) { + if (m_KeyLocation == KeyLocation::keyring) { if (m_passphrase.isEmpty()) return false; cryptResizeCmd.write(m_passphrase.toLocal8Bit() + '\n'); } - cryptResizeCmd.waitFor(); + if (!cryptResizeCmd.start(-1)) + return false; if ( cryptResizeCmd.exitCode() == 0 ) return m_innerFs->resize(report, mapperName(), m_PayloadSize); } @@ -111,13 +110,13 @@ bool luks2::resize(Report& report, const QString& deviceNode, qint64 newLength) { QStringLiteral("--size"), QString::number(m_PayloadSize / 512), // FIXME, LUKS2 can have different sector sizes QStringLiteral("resize"), mapperName() }); report.line() << xi18nc("@info:progress", "Resizing LUKS crypt on partition %1.", deviceNode); - cryptResizeCmd.start(-1); - if (m_KeyLocation == keyring) { + if (m_KeyLocation == KeyLocation::keyring) { if (m_passphrase.isEmpty()) return false; cryptResizeCmd.write(m_passphrase.toLocal8Bit() + '\n'); } - cryptResizeCmd.waitFor(); + if (!cryptResizeCmd.start(-1)) + return false; if ( cryptResizeCmd.exitCode() == 0 ) return true; } @@ -127,16 +126,16 @@ bool luks2::resize(Report& report, const QString& deviceNode, qint64 newLength) luks::KeyLocation luks2::keyLocation() { - m_KeyLocation = unknown; + m_KeyLocation = KeyLocation::unknown; ExternalCommand statusCmd(QStringLiteral("cryptsetup"), { QStringLiteral("status"), mapperName() }); if (statusCmd.run(-1) && statusCmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("key location:\\s+(\\w+)")); QRegularExpressionMatch rem = re.match(statusCmd.output()); if (rem.hasMatch()) { if (rem.captured(1) == QStringLiteral("keyring")) - m_KeyLocation = keyring; + m_KeyLocation = KeyLocation::keyring; else if (rem.captured(1) == QStringLiteral("dm-crypt")) - m_KeyLocation = dmcrypt; + m_KeyLocation = KeyLocation::dmcrypt; } } diff --git a/src/fs/lvm2_pv.cpp b/src/fs/lvm2_pv.cpp index 91964ac..6a3a4c7 100644 --- a/src/fs/lvm2_pv.cpp +++ b/src/fs/lvm2_pv.cpp @@ -43,7 +43,7 @@ FileSystem::CommandSupportType lvm2_pv::m_GetUUID = FileSystem::cmdSupportNone; lvm2_pv::lvm2_pv(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) - : FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Lvm2_PV) + : FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Lvm2_PV) , m_PESize(0) , m_TotalPE(0) , m_AllocatedPE(0) @@ -99,7 +99,7 @@ FileSystem::SupportTool lvm2_pv::supportToolName() const qint64 lvm2_pv::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } qint64 lvm2_pv::readUsedCapacity(const QString& deviceNode) const @@ -270,10 +270,10 @@ QList lvm2_pv::getPVinNode(const PartitionNode* parent) partitions.append(getPVinNode(node)); // FIXME: reenable newly created PVs (before applying) once everything works - if(p->fileSystem().type() == FileSystem::Lvm2_PV && p->deviceNode() == p->partitionPath()) + if(p->fileSystem().type() == FileSystem::Type::Lvm2_PV && p->deviceNode() == p->partitionPath()) partitions.append(LvmPV(p->mountPoint(), p)); - if(p->fileSystem().type() == FileSystem::Luks && p->deviceNode() == p->partitionPath()) + if(p->fileSystem().type() == FileSystem::Type::Luks && p->deviceNode() == p->partitionPath()) partitions.append(LvmPV(p->mountPoint(), p, true)); } @@ -296,7 +296,11 @@ QList lvm2_pv::getPVs(const QList& devices) } -QList LVM::pvList; +namespace LVM { + +QList pvList::m_list; + +} LvmPV::LvmPV(const QString vgName, const Partition* p, bool isLuks) : m_vgName(vgName) diff --git a/src/fs/lvm2_pv.h b/src/fs/lvm2_pv.h index 895d26e..15bbd92 100644 --- a/src/fs/lvm2_pv.h +++ b/src/fs/lvm2_pv.h @@ -62,7 +62,22 @@ private: }; namespace LVM { - extern LIBKPMCORE_EXPORT QList pvList; +/** Class to access a global LVM PV list. + @author Caio Carvalho +*/ +class LIBKPMCORE_EXPORT pvList +{ +public: + static QList &list() { + return m_list; + } + +private: + pvList() { } + +private: + static QList m_list; +}; } namespace FS diff --git a/src/fs/nilfs2.cpp b/src/fs/nilfs2.cpp index d8eb131..5fd15a1 100644 --- a/src/fs/nilfs2.cpp +++ b/src/fs/nilfs2.cpp @@ -47,7 +47,7 @@ FileSystem::CommandSupportType nilfs2::m_UpdateUUID = FileSystem::cmdSupportNone FileSystem::CommandSupportType nilfs2::m_GetUUID = FileSystem::cmdSupportNone; nilfs2::nilfs2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Nilfs2) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Nilfs2) { } @@ -96,12 +96,12 @@ FileSystem::SupportTool nilfs2::supportToolName() const qint64 nilfs2::minCapacity() const { - return 128 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 128 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 nilfs2::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } int nilfs2::maxLabelLength() const diff --git a/src/fs/ntfs.cpp b/src/fs/ntfs.cpp index 9fedf0a..471638a 100644 --- a/src/fs/ntfs.cpp +++ b/src/fs/ntfs.cpp @@ -50,7 +50,7 @@ FileSystem::CommandSupportType ntfs::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType ntfs::m_GetUUID = FileSystem::cmdSupportNone; ntfs::ntfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Ntfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Ntfs) { } @@ -91,12 +91,12 @@ FileSystem::SupportTool ntfs::supportToolName() const qint64 ntfs::minCapacity() const { - return 2 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 2 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 ntfs::maxCapacity() const { - return 256 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB); + return 256 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB); } int ntfs::maxLabelLength() const @@ -125,8 +125,7 @@ qint64 ntfs::readUsedCapacity(const QString& deviceNode) const bool ntfs::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) { - ExternalCommand writeCmd(report, QStringLiteral("ntfslabel"), { QStringLiteral("--force"), deviceNode, newLabel }); - writeCmd.setProcessChannelMode(QProcess::SeparateChannels); + ExternalCommand writeCmd(report, QStringLiteral("ntfslabel"), { QStringLiteral("--force"), deviceNode, newLabel }, QProcess::SeparateChannels); if (!writeCmd.run(-1)) return false; @@ -171,7 +170,7 @@ bool ntfs::resize(Report& report, const QString& deviceNode, qint64 length) cons bool ntfs::updateUUID(Report& report, const QString& deviceNode) const { - Q_UNUSED(report); + Q_UNUSED(report) ExternalCommand cmd(QStringLiteral("ntfslabel"), { QStringLiteral("--new-serial"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; @@ -191,24 +190,22 @@ bool ntfs::updateBootSector(Report& report, const QString& deviceNode) const ExternalCommand cmd(report, QStringLiteral("dd"), { QStringLiteral("of=") + deviceNode , QStringLiteral("bs=1"), QStringLiteral("count=4"), QStringLiteral("seek=28") }); + cmd.write(QByteArray(s, sizeof(s))); if (!cmd.start()) { Log() << xi18nc("@info:progress", "Could not write new start sector to partition %1 when trying to update the NTFS boot sector.", deviceNode); return false; } - cmd.write(QByteArray(s, sizeof(s))); - cmd.waitFor(-1); // Also update backup NTFS boot sector located at the end of the partition // NOTE: this should fail if filesystem does not span the whole partition qint64 pos = (lastSector() - firstSector()) * sectorSize() + 28; ExternalCommand cmd2(report, QStringLiteral("dd"), { QStringLiteral("of=") + deviceNode , QStringLiteral("bs=1"), QStringLiteral("count=4"), QStringLiteral("seek=") + QString::number(pos) }); + cmd2.write(QByteArray(s, sizeof(s))); if (!cmd2.start()) { Log() << xi18nc("@info:progress", "Could not write new start sector to partition %1 when trying to update the NTFS boot sector.", deviceNode); return false; } - cmd2.write(QByteArray(s, sizeof(s))); - cmd2.waitFor(-1); Log() << xi18nc("@info:progress", "Updated NTFS boot sector for partition %1 successfully.", deviceNode); diff --git a/src/fs/ocfs2.cpp b/src/fs/ocfs2.cpp index dfd7366..af8eaaa 100644 --- a/src/fs/ocfs2.cpp +++ b/src/fs/ocfs2.cpp @@ -40,7 +40,7 @@ FileSystem::CommandSupportType ocfs2::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType ocfs2::m_GetUUID = FileSystem::cmdSupportNone; ocfs2::ocfs2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Ocfs2) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Ocfs2) { } @@ -89,17 +89,17 @@ FileSystem::SupportTool ocfs2::supportToolName() const qint64 ocfs2::minCapacity() const { - return 14000 * Capacity::unitFactor(Capacity::Byte, Capacity::KiB); + return 14000 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::KiB); } qint64 ocfs2::maxCapacity() const { - return 4 * Capacity::unitFactor(Capacity::Byte, Capacity::PiB); + return 4 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::PiB); } qint64 ocfs2::readUsedCapacity(const QString& deviceNode) const { - Q_UNUSED(deviceNode); + Q_UNUSED(deviceNode) return -1; } @@ -113,15 +113,12 @@ bool ocfs2::create(Report& report, const QString& deviceNode) { ExternalCommand cmd(report, QStringLiteral("mkfs.ocfs2"), { deviceNode }); - if (cmd.start()) - { - cmd.write("y\n"); - cmd.waitFor(-1); - - return cmd.exitCode() == 0; - } - else + cmd.write("y\n"); + if (!cmd.start()) return false; + + return cmd.exitCode() == 0; + } bool ocfs2::resize(Report& report, const QString& deviceNode, qint64 length) const diff --git a/src/fs/reiser4.cpp b/src/fs/reiser4.cpp index 9d54b66..38e1d1e 100644 --- a/src/fs/reiser4.cpp +++ b/src/fs/reiser4.cpp @@ -35,7 +35,7 @@ FileSystem::CommandSupportType reiser4::m_Copy = FileSystem::cmdSupportNone; FileSystem::CommandSupportType reiser4::m_Backup = FileSystem::cmdSupportNone; reiser4::reiser4(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Reiser4) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Reiser4) { } @@ -75,7 +75,7 @@ qint64 reiser4::maxCapacity() const { // looks like it's actually unknown. see // http://en.wikipedia.org/wiki/Comparison_of_file_systems - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } int reiser4::maxLabelLength() const diff --git a/src/fs/reiserfs.cpp b/src/fs/reiserfs.cpp index bb94d81..5bfeb06 100644 --- a/src/fs/reiserfs.cpp +++ b/src/fs/reiserfs.cpp @@ -42,7 +42,7 @@ FileSystem::CommandSupportType reiserfs::m_UpdateUUID = FileSystem::cmdSupportNo FileSystem::CommandSupportType reiserfs::m_GetUUID = FileSystem::cmdSupportNone; reiserfs::reiserfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::ReiserFS) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::ReiserFS) { } @@ -85,12 +85,12 @@ FileSystem::SupportTool reiserfs::supportToolName() const qint64 reiserfs::minCapacity() const { - return 32 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 32 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 reiserfs::maxCapacity() const { - return 16 * Capacity::unitFactor(Capacity::Byte, Capacity::TiB) - Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 16 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::TiB) - Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } int reiserfs::maxLabelLength() const @@ -154,15 +154,15 @@ bool reiserfs::resize(Report& report, const QString& deviceNode, qint64 length) ExternalCommand cmd(report, QStringLiteral("resize_reiserfs"), { deviceNode, QStringLiteral("-q"), QStringLiteral("-s"), QString::number(length) }); - bool rval = cmd.start(-1); + bool rval = cmd.write(QByteArrayLiteral("y\n")); if (!rval) return false; - if (cmd.write("y\n", 2) != 2) + if (!cmd.start(-1)) return false; - return cmd.waitFor(-1) && (cmd.exitCode() == 0 || cmd.exitCode() == 256); + return cmd.exitCode() == 0 || cmd.exitCode() == 256; } bool reiserfs::resizeOnline(Report& report, const QString& deviceNode, const QString&, qint64 length) const diff --git a/src/fs/udf.cpp b/src/fs/udf.cpp index f34c5f3..b0d28a9 100644 --- a/src/fs/udf.cpp +++ b/src/fs/udf.cpp @@ -40,7 +40,7 @@ FileSystem::CommandSupportType udf::m_Create = FileSystem::cmdSupportNone; bool udf::oldMkudffsVersion = false; udf::udf(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Udf) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Udf) { } diff --git a/src/fs/ufs.cpp b/src/fs/ufs.cpp index f1e7212..71f5e51 100644 --- a/src/fs/ufs.cpp +++ b/src/fs/ufs.cpp @@ -24,7 +24,7 @@ FileSystem::CommandSupportType ufs::m_Copy = FileSystem::cmdSupportCore; FileSystem::CommandSupportType ufs::m_Backup = FileSystem::cmdSupportCore; ufs::ufs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Ufs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Ufs) { } } diff --git a/src/fs/unformatted.cpp b/src/fs/unformatted.cpp index f3bcb29..e1813de 100644 --- a/src/fs/unformatted.cpp +++ b/src/fs/unformatted.cpp @@ -22,7 +22,7 @@ namespace FS FileSystem::CommandSupportType unformatted::m_Create = FileSystem::cmdSupportFileSystem; unformatted::unformatted(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Unformatted) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Unformatted) { } diff --git a/src/fs/unknown.cpp b/src/fs/unknown.cpp index 5b0a552..3f6e27f 100644 --- a/src/fs/unknown.cpp +++ b/src/fs/unknown.cpp @@ -20,7 +20,7 @@ namespace FS { unknown::unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Unknown) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Unknown) { } diff --git a/src/fs/xfs.cpp b/src/fs/xfs.cpp index f808a3e..25d2ef3 100644 --- a/src/fs/xfs.cpp +++ b/src/fs/xfs.cpp @@ -42,7 +42,7 @@ FileSystem::CommandSupportType xfs::m_Backup = FileSystem::cmdSupportNone; FileSystem::CommandSupportType xfs::m_SetLabel = FileSystem::cmdSupportNone; xfs::xfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Xfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Xfs) { } @@ -83,12 +83,12 @@ FileSystem::SupportTool xfs::supportToolName() const qint64 xfs::minCapacity() const { - return 32 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 32 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 xfs::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } int xfs::maxLabelLength() const diff --git a/src/fs/zfs.cpp b/src/fs/zfs.cpp index fccf00a..a3cb201 100644 --- a/src/fs/zfs.cpp +++ b/src/fs/zfs.cpp @@ -40,7 +40,7 @@ FileSystem::CommandSupportType zfs::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType zfs::m_GetUUID = FileSystem::cmdSupportNone; zfs::zfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Zfs) + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Zfs) { } @@ -77,12 +77,12 @@ FileSystem::SupportTool zfs::supportToolName() const qint64 zfs::minCapacity() const { - return 64 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); + return 64 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); } qint64 zfs::maxCapacity() const { - return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); + return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB); } bool zfs::remove(Report& report, const QString& deviceNode) const diff --git a/src/gui/partwidget.cpp b/src/gui/partwidget.cpp index 49b252f..57ebf23 100644 --- a/src/gui/partwidget.cpp +++ b/src/gui/partwidget.cpp @@ -21,10 +21,10 @@ #include "fs/filesystem.h" #include "util/capacity.h" -#include -#include #include #include +#include +#include /** Creates a new PartWidget @param parent pointer to the parent widget @@ -71,7 +71,7 @@ void PartWidget::updateChildren() } } -void PartWidget::setFileSystemColorCode(const std::array< QColor, FileSystem::__lastType >& colorCode) +void PartWidget::setFileSystemColorCode(const std::vector& colorCode) { m_fileSystemColorCode = colorCode; repaint(); @@ -101,12 +101,12 @@ void PartWidget::paintEvent(QPaintEvent*) if (partition()->roles().has(PartitionRole::Extended)) { drawGradient(&painter, activeColor( - m_fileSystemColorCode[ partition()->fileSystem().type() ]), + m_fileSystemColorCode[ static_cast(partition()->fileSystem().type()) ]), QRect(0, 0, width(), height())); return; } - const QColor base = activeColor(m_fileSystemColorCode[ partition()->fileSystem().type() ]); + const QColor base = activeColor(m_fileSystemColorCode[ static_cast(partition()->fileSystem().type()) ]); if (!partition()->roles().has(PartitionRole::Unallocated)) { const QColor dark = base.darker(105); diff --git a/src/gui/partwidget.h b/src/gui/partwidget.h index 452aa69..8275483 100644 --- a/src/gui/partwidget.h +++ b/src/gui/partwidget.h @@ -26,8 +26,6 @@ #include -#include - class Partition; class QPaintEvent; @@ -63,7 +61,7 @@ public: return m_Partition; /**< @return the widget's Partition */ } - void setFileSystemColorCode( const std::array< QColor, FileSystem::__lastType >& colorCode ); + void setFileSystemColorCode( const std::vector& colorCode ); protected: void paintEvent(QPaintEvent* event); @@ -76,7 +74,7 @@ protected: private: Partition* m_Partition; bool m_Active; - std::array< QColor, FileSystem::__lastType > m_fileSystemColorCode; + std::vector m_fileSystemColorCode; }; #endif diff --git a/src/jobs/backupfilesystemjob.cpp b/src/jobs/backupfilesystemjob.cpp index bf72e2f..1999983 100644 --- a/src/jobs/backupfilesystemjob.cpp +++ b/src/jobs/backupfilesystemjob.cpp @@ -24,7 +24,7 @@ #include "core/copytargetfile.h" #include "fs/filesystem.h" - +#include "util/externalcommand.h" #include "util/report.h" #include diff --git a/src/jobs/checkfilesystemjob.cpp b/src/jobs/checkfilesystemjob.cpp index 866de6a..7b7cd92 100644 --- a/src/jobs/checkfilesystemjob.cpp +++ b/src/jobs/checkfilesystemjob.cpp @@ -25,7 +25,6 @@ #include "util/report.h" #include -#include #include @@ -48,15 +47,6 @@ bool CheckFileSystemJob::run(Report& parent) if (partition().fileSystem().supportCheck() == FileSystem::cmdSupportFileSystem) rval = partition().fileSystem().check(*report, partition().deviceNode()); - // HACK - // In rare cases after moving file system to a new location file system check - // fails on the first try. As a temporary workaround, wait a bit and try again. - if (!rval) { - QThread::sleep(2); - qDebug() << "Partition might not be ready yet. Retrying..."; - rval = partition().fileSystem().check(*report, partition().deviceNode()); - } - jobFinished(*report, rval); return rval; diff --git a/src/jobs/createfilesystemjob.cpp b/src/jobs/createfilesystemjob.cpp index 8db2b47..6a285d2 100644 --- a/src/jobs/createfilesystemjob.cpp +++ b/src/jobs/createfilesystemjob.cpp @@ -49,7 +49,7 @@ bool CreateFileSystemJob::run(Report& parent) Report* report = jobStarted(parent); - if (partition().fileSystem().type() == FileSystem::Unformatted) + if (partition().fileSystem().type() == FileSystem::Type::Unformatted) return true; bool createResult; @@ -59,11 +59,11 @@ bool CreateFileSystemJob::run(Report& parent) else createResult = partition().fileSystem().create(*report, partition().deviceNode()); if (createResult) { - if (device().type() == Device::Disk_Device) { - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + if (device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { if (backendPartitionTable->setPartitionSystemType(*report, partition())) { @@ -71,15 +71,11 @@ bool CreateFileSystemJob::run(Report& parent) backendPartitionTable->commit(); } else report->line() << xi18nc("@info:progress", "Failed to set the system type for the file system on partition %1.", partition().deviceNode()); - - delete backendPartitionTable; } else report->line() << xi18nc("@info:progress", "Could not open partition table on device %1 to set the system type for partition %2.", device().deviceNode(), partition().deviceNode()); - - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Could not open device %1 to set the system type for partition %2.", device().deviceNode(), partition().deviceNode()); - } else if (device().type() == Device::LVM_Device) { + } else if (device().type() == Device::Type::LVM_Device) { rval = true; } } diff --git a/src/jobs/createpartitionjob.cpp b/src/jobs/createpartitionjob.cpp index 260aa92..e6fb374 100644 --- a/src/jobs/createpartitionjob.cpp +++ b/src/jobs/createpartitionjob.cpp @@ -50,11 +50,11 @@ bool CreatePartitionJob::run(Report& parent) Report* report = jobStarted(parent); - if (device().type() == Device::Disk_Device) { - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + if (device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { QString partitionPath = backendPartitionTable->createPartition(*report, partition()); @@ -66,15 +66,11 @@ bool CreatePartitionJob::run(Report& parent) backendPartitionTable->commit(); } else report->line() << xi18nc("@info/plain", "Failed to add partition %1 to device %2.", partition().deviceNode(), device().deviceNode()); - - delete backendPartitionTable; } else report->line() << xi18nc("@info:progress", "Could not open partition table on device %1 to create new partition %2.", device().deviceNode(), partition().deviceNode()); - - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Could not open device %1 to create new partition %2.", device().deviceNode(), partition().deviceNode()); - } else if (device().type() == Device::LVM_Device) { + } else if (device().type() == Device::Type::LVM_Device) { LvmDevice& dev = dynamic_cast(device()); partition().setState(Partition::State::None); diff --git a/src/jobs/createpartitiontablejob.cpp b/src/jobs/createpartitiontablejob.cpp index 3d2fe0b..f67cae9 100644 --- a/src/jobs/createpartitiontablejob.cpp +++ b/src/jobs/createpartitiontablejob.cpp @@ -44,18 +44,16 @@ bool CreatePartitionTableJob::run(Report& parent) Report* report = jobStarted(parent); - if (device().type() == Device::Disk_Device) { - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + if (device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice != nullptr) { Q_ASSERT(device().partitionTable()); rval = backendDevice->createPartitionTable(*report, *device().partitionTable()); - - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Creating partition table failed: Could not open device %1.", device().deviceNode()); - } else if (device().type() == Device::LVM_Device) { + } else if (device().type() == Device::Type::LVM_Device) { //TODO: figure what to do with LVM partitionTable } diff --git a/src/jobs/deactivatelogicalvolumejob.cpp b/src/jobs/deactivatelogicalvolumejob.cpp index 327ad5c..5ce85da 100644 --- a/src/jobs/deactivatelogicalvolumejob.cpp +++ b/src/jobs/deactivatelogicalvolumejob.cpp @@ -41,7 +41,7 @@ bool DeactivateLogicalVolumeJob::run(Report& parent) Report* report = jobStarted(parent); - if (device().type() == Device::LVM_Device) { + if (device().type() == Device::Type::LVM_Device) { for (const auto &p : device().partitionTable()->children()) { if (!p->roles().has(PartitionRole::Unallocated)) { if (!LvmDevice::deactivateLV(*report, *p)) { diff --git a/src/jobs/deactivatevolumegroupjob.cpp b/src/jobs/deactivatevolumegroupjob.cpp index ae56ac6..2cc2ea3 100644 --- a/src/jobs/deactivatevolumegroupjob.cpp +++ b/src/jobs/deactivatevolumegroupjob.cpp @@ -39,7 +39,7 @@ bool DeactivateVolumeGroupJob::run(Report& parent) Report* report = jobStarted(parent); - if (device().type() == Device::LVM_Device) { + if (device().type() == Device::Type::LVM_Device) { rval = LvmDevice::deactivateVG(*report, static_cast(device())); } const auto lvmPVs = static_cast(device()).physicalVolumes(); diff --git a/src/jobs/deletefilesystemjob.cpp b/src/jobs/deletefilesystemjob.cpp index da95287..176f851 100644 --- a/src/jobs/deletefilesystemjob.cpp +++ b/src/jobs/deletefilesystemjob.cpp @@ -65,7 +65,7 @@ bool DeleteFileSystemJob::run(Report& parent) if (partition().roles().has(PartitionRole::Extended)) { rval = true; - } else if (device().type() == Device::LVM_Device) { + } else if (device().type() == Device::Type::LVM_Device) { rval = true; } else { @@ -74,10 +74,10 @@ bool DeleteFileSystemJob::run(Report& parent) return false; } - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { rval = backendPartitionTable->clobberFileSystem(*report, partition()); @@ -86,13 +86,9 @@ bool DeleteFileSystemJob::run(Report& parent) report->line() << xi18nc("@info:progress", "Could not delete file system on %1.", partition().deviceNode()); else backendPartitionTable->commit(); - - delete backendPartitionTable; - } else report->line() << xi18nc("@info:progress", "Could not open partition table on device %1 to delete file system on %2.", device().deviceNode(), partition().deviceNode()); - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Could not delete file system signature for partition %1: Failed to open device %2.", partition().deviceNode(), device().deviceNode()); } diff --git a/src/jobs/deletepartitionjob.cpp b/src/jobs/deletepartitionjob.cpp index 5ba7401..9ba59bc 100644 --- a/src/jobs/deletepartitionjob.cpp +++ b/src/jobs/deletepartitionjob.cpp @@ -57,11 +57,11 @@ bool DeletePartitionJob::run(Report& parent) Report* report = jobStarted(parent); - if (device().type() == Device::Disk_Device) { - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + if (device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { rval = backendPartitionTable->deletePartition(*report, partition()); @@ -70,16 +70,11 @@ bool DeletePartitionJob::run(Report& parent) report->line() << xi18nc("@info:progress", "Could not delete partition %1.", partition().deviceNode()); else backendPartitionTable->commit(); - - delete backendPartitionTable; - } else report->line() << xi18nc("@info:progress", "Could not open partition table on device %1 to delete partition %2.", device().deviceNode(), partition().deviceNode()); - - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Deleting partition failed: Could not open device %1.", device().deviceNode()); - } else if (device().type() == Device::LVM_Device) { + } else if (device().type() == Device::Type::LVM_Device) { LvmDevice& dev = dynamic_cast(device()); rval = LvmDevice::removeLV(*report, dev, partition()); } diff --git a/src/jobs/job.cpp b/src/jobs/job.cpp index 192deb1..d850b3d 100644 --- a/src/jobs/job.cpp +++ b/src/jobs/job.cpp @@ -1,6 +1,6 @@ /************************************************************************* * Copyright (C) 2008, 2009, 2010 by Volker Lanz * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -24,84 +24,32 @@ #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" +#include "util/externalcommand.h" #include "util/report.h" #include #include +#include #include Job::Job() : - m_Status(Pending) + m_Report(nullptr), + m_Status(Status::Pending) { } bool Job::copyBlocks(Report& report, CopyTarget& target, CopySource& source) { - bool rval = true; - const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy - const qint64 blocksToCopy = source.length() / blockSize; - - qint64 readOffset = source.firstByte(); - qint64 writeOffset = target.firstByte(); - qint32 copyDirection = 1; - - if (target.firstByte() > source.firstByte()) { - readOffset = source.firstByte() + source.length() - blockSize; - writeOffset = target.firstByte() + source.length() - blockSize; - copyDirection = -1; + m_Report = &report; + ExternalCommand copyCmd; + connect(©Cmd, &ExternalCommand::progress, this, &Job::progress, Qt::QueuedConnection); + connect(©Cmd, &ExternalCommand::reportSignal, this, &Job::updateReport, Qt::QueuedConnection); + if (copyCmd.copyBlocks(source, target)) { + return true; } - report.line() << xi18nc("@info:progress", "Copying %1 blocks (%2 bytes) from %3 to %4, direction: %5.", blocksToCopy, source.length(), readOffset, writeOffset, copyDirection == 1 ? i18nc("direction: left", "left") : i18nc("direction: right", "right")); - - qint64 blocksCopied = 0; - - QByteArray buffer; - int percent = 0; - QTime t; - t.start(); - while (blocksCopied < blocksToCopy) { - if (!(rval = source.readData(buffer, readOffset + blockSize * blocksCopied * copyDirection, blockSize))) - break; - - if (!(rval = target.writeData(buffer, writeOffset + blockSize * blocksCopied * copyDirection))) - break; - - if (++blocksCopied * 100 / blocksToCopy != percent) { - percent = blocksCopied * 100 / blocksToCopy; - - if (percent % 5 == 0 && t.elapsed() > 1000) { - const qint64 mibsPerSec = (blocksCopied * blockSize / 1024 / 1024) / (t.elapsed() / 1000); - const qint64 estSecsLeft = (100 - percent) * t.elapsed() / percent / 1000; - report.line() << xi18nc("@info:progress", "Copying %1 MiB/second, estimated time left: %2", mibsPerSec, QTime(0, 0).addSecs(estSecsLeft).toString()); - } - emit progress(percent); - } - } - const qint64 lastBlock = source.length() % blockSize; - - // copy the remainder - if (rval && lastBlock > 0) { - Q_ASSERT(lastBlock < blockSize); - - const qint64 lastBlockReadOffset = copyDirection > 0 ? readOffset + blockSize * blocksCopied : source.firstByte(); - const qint64 lastBlockWriteOffset = copyDirection > 0 ? writeOffset + blockSize * blocksCopied : target.firstByte(); - - report.line() << xi18nc("@info:progress", "Copying remainder of block size %1 from %2 to %3.", lastBlock, lastBlockReadOffset, lastBlockWriteOffset); - - rval = source.readData(buffer, lastBlockReadOffset, lastBlock); - - if (rval) - rval = target.writeData(buffer, lastBlockWriteOffset); - - if (rval) - emit progress(100); - } - - - report.line() << xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 block (%2) finished.", "Copying %1 blocks (%2) finished.", blocksCopied, i18np("1 byte", "%1 bytes", target.bytesWritten())); - - return rval; + return false; } bool Job::rollbackCopyBlocks(Report& report, CopyTarget& origTarget, CopySource& origSource) @@ -159,6 +107,11 @@ void Job::emitProgress(int i) emit progress(i); } +void Job::updateReport(const QVariantMap& reportString) +{ + m_Report->line() << reportString[QStringLiteral("report")].toString(); +} + Report* Job::jobStarted(Report& parent) { emit started(); @@ -168,7 +121,7 @@ Report* Job::jobStarted(Report& parent) void Job::jobFinished(Report& report, bool b) { - setStatus(b ? Success : Error); + setStatus(b ? Status::Success : Status::Error); emit progress(numSteps()); emit finished(); @@ -184,12 +137,7 @@ QString Job::statusIcon() const QStringLiteral("dialog-error") }; - Q_ASSERT(status() >= 0 && static_cast(status()) < sizeof(icons) / sizeof(icons[0])); - - if (status() < 0 || static_cast(status()) >= sizeof(icons) / sizeof(icons[0])) - return QString(); - - return icons[status()]; + return icons[static_cast(status())]; } /** @return the Job's current status text */ @@ -201,10 +149,5 @@ QString Job::statusText() const xi18nc("@info:progress job", "Error") }; - Q_ASSERT(status() >= 0 && static_cast(status()) < sizeof(s) / sizeof(s[0])); - - if (status() < 0 || static_cast(status()) >= sizeof(s) / sizeof(s[0])) - return QString(); - - return s[status()]; + return s[static_cast(status())]; } diff --git a/src/jobs/job.h b/src/jobs/job.h index b4c46d5..e4f9416 100644 --- a/src/jobs/job.h +++ b/src/jobs/job.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_JOB_H) - +#ifndef KPMCORE_JOB_H #define KPMCORE_JOB_H #include "fs/filesystem.h" @@ -49,10 +48,10 @@ class LIBKPMCORE_EXPORT Job : public QObject public: /** Status of this Job */ - enum JobStatus { - Pending = 0, /**< Pending, not yet run */ - Success, /**< Successfully run */ - Error /**< Running generated an error */ + enum class Status : int { + Pending, /**< Pending, not yet run */ + Success, /**< Successfully run */ + Error /**< Running generated an error */ }; protected: @@ -76,11 +75,12 @@ public: virtual QString statusIcon() const; virtual QString statusText() const; - JobStatus status() const { + Status status() const { return m_Status; /**< @return the Job's current status */ } void emitProgress(int i); + void updateReport(const QVariantMap& reportString); protected: bool copyBlocks(Report& report, CopyTarget& target, CopySource& source); @@ -89,12 +89,13 @@ protected: Report* jobStarted(Report& parent); void jobFinished(Report& report, bool b); - void setStatus(JobStatus s) { + void setStatus(Status s) { m_Status = s; } private: - JobStatus m_Status; + Report *m_Report; + Status m_Status; }; #endif diff --git a/src/jobs/removevolumegroupjob.cpp b/src/jobs/removevolumegroupjob.cpp index d5026ae..97792f7 100644 --- a/src/jobs/removevolumegroupjob.cpp +++ b/src/jobs/removevolumegroupjob.cpp @@ -37,7 +37,7 @@ bool RemoveVolumeGroupJob::run(Report& parent) Report* report = jobStarted(parent); - if (device().type() == Device::LVM_Device) { + if (device().type() == Device::Type::LVM_Device) { rval = LvmDevice::removeVG(*report, dynamic_cast(device())); } diff --git a/src/jobs/resizefilesystemjob.cpp b/src/jobs/resizefilesystemjob.cpp index a3af897..e308d90 100644 --- a/src/jobs/resizefilesystemjob.cpp +++ b/src/jobs/resizefilesystemjob.cpp @@ -31,6 +31,8 @@ #include "util/report.h" #include "util/capacity.h" +#include + #include #include @@ -86,7 +88,7 @@ bool ResizeFileSystemJob::run(Report& parent) } case FileSystem::cmdSupportFileSystem: { - const qint64 newLengthInByte = Capacity(newLength() * device().logicalSize()).toInt(Capacity::Byte); + const qint64 newLengthInByte = Capacity(newLength() * device().logicalSize()).toInt(Capacity::Unit::Byte); if (partition().isMounted()) rval = partition().fileSystem().resizeOnline(*report, partition().deviceNode(), partition().mountPoint(), newLengthInByte); else @@ -112,10 +114,10 @@ bool ResizeFileSystemJob::resizeFileSystemBackend(Report& report) { bool rval = false; - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { connect(CoreBackendManager::self()->backend(), &CoreBackend::progress, this, &ResizeFileSystemJob::progress); @@ -126,12 +128,9 @@ bool ResizeFileSystemJob::resizeFileSystemBackend(Report& report) report.line() << xi18nc("@info:progress", "Successfully resized file system using internal backend functions."); backendPartitionTable->commit(); } - - delete backendPartitionTable; } else report.line() << xi18nc("@info:progress", "Could not open partition %1 while trying to resize the file system.", partition().deviceNode()); - delete backendDevice; } else report.line() << xi18nc("@info:progress", "Could not read geometry for partition %1 while trying to resize the file system.", partition().deviceNode()); diff --git a/src/jobs/resizevolumegroupjob.cpp b/src/jobs/resizevolumegroupjob.cpp index ae7f37d..3dce190 100644 --- a/src/jobs/resizevolumegroupjob.cpp +++ b/src/jobs/resizevolumegroupjob.cpp @@ -43,9 +43,9 @@ bool ResizeVolumeGroupJob::run(Report& parent) for (const auto &p : partList()) { const QString deviceNode = p->roles().has(PartitionRole::Luks) ? static_cast(&p->fileSystem())->mapperName() : p->partitionPath(); - if (type() == ResizeVolumeGroupJob::Grow) + if (type() == ResizeVolumeGroupJob::Type::Grow) rval = LvmDevice::insertPV(*report, device(), deviceNode); - else if (type() == ResizeVolumeGroupJob::Shrink) + else if (type() == ResizeVolumeGroupJob::Type::Shrink) rval = LvmDevice::removePV(*report, device(), deviceNode); if (rval == false) @@ -66,10 +66,10 @@ QString ResizeVolumeGroupJob::description() const partitionList.chop(2); const qint32 count = partList().count(); - if (type() == ResizeVolumeGroupJob::Grow) { + if (type() == ResizeVolumeGroupJob::Type::Grow) { return xi18ncp("@info/plain", "Adding LVM Physical Volume %2 to %3.", "Adding LVM Physical Volumes %2 to %3.", count, partitionList, device().name()); } - if (type() == ResizeVolumeGroupJob::Shrink) { + if (type() == ResizeVolumeGroupJob::Type::Shrink) { return xi18ncp("@info/plain", "Removing LVM Physical Volume %2 from %3.", "Removing LVM Physical Volumes %2 from %3.", count, partitionList, device().name()); } return xi18nc("@info/plain", "Resizing Volume Group %1 to %2.", device().name(), partitionList); diff --git a/src/jobs/resizevolumegroupjob.h b/src/jobs/resizevolumegroupjob.h index ca5f2de..adb2a1d 100644 --- a/src/jobs/resizevolumegroupjob.h +++ b/src/jobs/resizevolumegroupjob.h @@ -31,9 +31,9 @@ class ResizeVolumeGroupJob : public Job { public: - enum Type { - Grow = 0, - Shrink = 1 + enum class Type { + Grow, + Shrink }; public: diff --git a/src/jobs/restorefilesystemjob.cpp b/src/jobs/restorefilesystemjob.cpp index aff8d70..c7ec7e4 100644 --- a/src/jobs/restorefilesystemjob.cpp +++ b/src/jobs/restorefilesystemjob.cpp @@ -80,12 +80,12 @@ bool RestoreFileSystemJob::run(Report& parent) // create a new file system for what was restored with the length of the image file const qint64 newLastSector = targetPartition().firstSector() + copySource.length() - 1; - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(targetDevice()); + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(targetDevice()); - FileSystem::Type t = FileSystem::Unknown; + FileSystem::Type t = FileSystem::Type::Unknown; if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) t = backendPartitionTable->detectFileSystemBySector(*report, targetDevice(), targetPartition().firstSector()); diff --git a/src/jobs/setfilesystemlabeljob.cpp b/src/jobs/setfilesystemlabeljob.cpp index 7ea7a07..ed17e58 100644 --- a/src/jobs/setfilesystemlabeljob.cpp +++ b/src/jobs/setfilesystemlabeljob.cpp @@ -18,6 +18,13 @@ #include "jobs/setfilesystemlabeljob.h" +#include "backend/corebackend.h" +#include "backend/corebackendmanager.h" +#include "backend/corebackenddevice.h" +#include "backend/corebackendpartitiontable.h" + +#include "core/device_p.h" +#include "core/operationstack.h" #include "core/partition.h" #include "fs/filesystem.h" @@ -26,6 +33,8 @@ #include +#include + /** Creates a new SetFileSystemLabelJob @param p the Partition the FileSystem whose label is to be set is on @param newlabel the new label @@ -60,6 +69,18 @@ bool SetFileSystemLabelJob::run(Report& parent) if (rval) partition().fileSystem().setLabel(label()); } + else + rval = false; + + // A hack to reread partition table (commit() should be called even on non DiskDevices) + Device dev(std::make_shared(), QString(), QString(), 0, 0, QString(), Device::Type::Unknown_Device); + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(dev); + if (backendDevice) { + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); + + if (backendPartitionTable) + backendPartitionTable->commit(); + } jobFinished(*report, rval); diff --git a/src/jobs/setpartflagsjob.cpp b/src/jobs/setpartflagsjob.cpp index 2aad649..fceb583 100644 --- a/src/jobs/setpartflagsjob.cpp +++ b/src/jobs/setpartflagsjob.cpp @@ -56,10 +56,10 @@ bool SetPartFlagsJob::run(Report& parent) Report* report = jobStarted(parent); - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { int count = 0; @@ -81,12 +81,8 @@ bool SetPartFlagsJob::run(Report& parent) if (rval) backendPartitionTable->commit(); - - delete backendPartitionTable; } else report->line() << xi18nc("@info:progress", "Could not open partition table on device %1 to set partition flags for partition %2.", device().deviceNode(), partition().deviceNode()); - - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Could not open device %1 to set partition flags for partition %2.", device().deviceNode(), partition().deviceNode()); diff --git a/src/jobs/setpartgeometryjob.cpp b/src/jobs/setpartgeometryjob.cpp index 7a4fbd7..b1c9d6f 100644 --- a/src/jobs/setpartgeometryjob.cpp +++ b/src/jobs/setpartgeometryjob.cpp @@ -55,11 +55,11 @@ bool SetPartGeometryJob::run(Report& parent) Report* report = jobStarted(parent); - if(device().type() == Device::Disk_Device) { - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); + if(device().type() == Device::Type::Disk_Device || device().type() == Device::Type::SoftwareRAID_Device) { + std::unique_ptr backendDevice = CoreBackendManager::self()->backend()->openDevice(device()); if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + std::unique_ptr backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { rval = backendPartitionTable->updateGeometry(*report, partition(), newStart(), newStart() + newLength() - 1); @@ -69,14 +69,10 @@ bool SetPartGeometryJob::run(Report& parent) partition().setLastSector(newStart() + newLength() - 1); backendPartitionTable->commit(); } - - delete backendPartitionTable; } - - delete backendDevice; } else report->line() << xi18nc("@info:progress", "Could not open device %1 while trying to resize/move partition %2.", device().deviceNode(), partition().deviceNode()); - } else if (device().type() == Device::LVM_Device) { + } else if (device().type() == Device::Type::LVM_Device) { partition().setFirstSector(newStart()); partition().setLastSector(newStart() + newLength() - 1); diff --git a/src/ops/backupoperation.h b/src/ops/backupoperation.h index 9f3002d..21fd3f8 100644 --- a/src/ops/backupoperation.h +++ b/src/ops/backupoperation.h @@ -61,6 +61,7 @@ protected: Device& targetDevice() { return m_TargetDevice; } + const Device& targetDevice() const { return m_TargetDevice; } diff --git a/src/ops/createpartitiontableoperation.cpp b/src/ops/createpartitiontableoperation.cpp index 9260be6..f68b122 100644 --- a/src/ops/createpartitiontableoperation.cpp +++ b/src/ops/createpartitiontableoperation.cpp @@ -21,6 +21,7 @@ #include "core/device.h" #include "core/partitiontable.h" #include "core/partition.h" +#include "core/raid/softwareraid.h" #include "jobs/createpartitiontablejob.h" @@ -93,7 +94,18 @@ bool CreatePartitionTableOperation::execute(Report& parent) */ bool CreatePartitionTableOperation::canCreate(const Device* device) { - return (device != nullptr) && (device->partitionTable() == nullptr || !device->partitionTable()->isChildMounted()) && (device->type() != Device::LVM_Device); + if (device == nullptr) + return false; + + if (device->type() == Device::Type::SoftwareRAID_Device) { + const SoftwareRAID* raid = static_cast(device); + + if (raid->status() == SoftwareRAID::Status::Inactive) + return false; + } + + return (device->partitionTable() == nullptr || !device->partitionTable()->isChildMounted()) + && (device->type() != Device::Type::LVM_Device); } QString CreatePartitionTableOperation::description() const diff --git a/src/ops/createvolumegroupoperation.cpp b/src/ops/createvolumegroupoperation.cpp index 8771bc0..5930905 100644 --- a/src/ops/createvolumegroupoperation.cpp +++ b/src/ops/createvolumegroupoperation.cpp @@ -35,18 +35,23 @@ CreateVolumeGroupOperation::CreateVolumeGroupOperation(const QString& vgName, const QVector& pvList, const qint32 peSize) : Operation(), m_CreateVolumeGroupJob(new CreateVolumeGroupJob(vgName, pvList, peSize)), - m_PVList(pvList) + m_PVList(pvList), + m_vgName(vgName) { addJob(createVolumeGroupJob()); } QString CreateVolumeGroupOperation::description() const { - return xi18nc("@info/plain", "Create a new LVM volume group."); + return xi18nc("@info/plain", "Create a new LVM volume group named \'%1\'.", m_vgName); } -bool CreateVolumeGroupOperation::targets(const Partition&) const +bool CreateVolumeGroupOperation::targets(const Partition& partition) const { + for (const auto &p : m_PVList) { + if (partition == *p) + return true; + } return false; } diff --git a/src/ops/createvolumegroupoperation.h b/src/ops/createvolumegroupoperation.h index c75c45c..4fa49f2 100644 --- a/src/ops/createvolumegroupoperation.h +++ b/src/ops/createvolumegroupoperation.h @@ -69,6 +69,7 @@ protected: private: CreateVolumeGroupJob* m_CreateVolumeGroupJob; const QVector m_PVList; + QString m_vgName; }; #endif diff --git a/src/ops/deactivatevolumegroupoperation.cpp b/src/ops/deactivatevolumegroupoperation.cpp index a002e84..2a2db83 100644 --- a/src/ops/deactivatevolumegroupoperation.cpp +++ b/src/ops/deactivatevolumegroupoperation.cpp @@ -63,7 +63,7 @@ void DeactivateVolumeGroupOperation::undo() */ bool DeactivateVolumeGroupOperation::isDeactivatable(const VolumeManagerDevice* dev) { - if (dev->type() == Device::LVM_Device) { + if (dev->type() == Device::Type::LVM_Device) { for (const auto &p : dev->partitionTable()->children()) { if (p->isMounted()) { return false; diff --git a/src/ops/deleteoperation.cpp b/src/ops/deleteoperation.cpp index 8a9bf59..d1ec00c 100644 --- a/src/ops/deleteoperation.cpp +++ b/src/ops/deleteoperation.cpp @@ -20,7 +20,9 @@ #include "core/partition.h" #include "core/device.h" +#include "core/lvmdevice.h" #include "core/partitiontable.h" +#include "core/raid/softwareraid.h" #include "fs/luks.h" #include "jobs/deletepartitionjob.h" @@ -45,13 +47,13 @@ DeleteOperation::DeleteOperation(Device& d, Partition* p, ShredAction shred) : m_DeletePartitionJob(new DeletePartitionJob(targetDevice(), deletedPartition())) { switch (shredAction()) { - case NoShred: + case ShredAction::NoShred: m_DeleteFileSystemJob = static_cast(new DeleteFileSystemJob(targetDevice(), deletedPartition())); break; - case ZeroShred: + case ShredAction::ZeroShred: m_DeleteFileSystemJob = static_cast(new ShredFileSystemJob(targetDevice(), deletedPartition(), false)); break; - case RandomShred: + case ShredAction::RandomShred: m_DeleteFileSystemJob = static_cast(new ShredFileSystemJob(targetDevice(), deletedPartition(), true)); } @@ -89,7 +91,7 @@ void DeleteOperation::undo() QString DeleteOperation::description() const { - if (shredAction() != NoShred) + if (shredAction() != ShredAction::NoShred) return xi18nc("@info:status", "Shred partition %1 (%2, %3)", deletedPartition().deviceNode(), Capacity::formatByteSize(deletedPartition().capacity()), deletedPartition().fileSystem().name()); else return xi18nc("@info:status", "Delete partition %1 (%2, %3)", deletedPartition().deviceNode(), Capacity::formatByteSize(deletedPartition().capacity()), deletedPartition().fileSystem().name()); @@ -118,6 +120,30 @@ bool DeleteOperation::canDelete(const Partition* p) if (p->isMounted()) return false; + if (p->fileSystem().type() == FileSystem::Type::Lvm2_PV) { + if (LvmDevice::s_DirtyPVs.contains(p)) + return false; + } + else if (p->fileSystem().type() == FileSystem::Type::LinuxRaidMember) { + if (SoftwareRAID::isRaidMember(p->partitionPath())) + return false; + } + else if (p->fileSystem().type() == FileSystem::Type::Luks || p->fileSystem().type() == FileSystem::Type::Luks2) { + // See if innerFS is LVM + FileSystem *fs = static_cast(&p->fileSystem())->innerFS(); + + if (fs) { + if (fs->type() == FileSystem::Type::Lvm2_PV) { + if (LvmDevice::s_DirtyPVs.contains(p)) + return false; + } + else if (fs->type() == FileSystem::Type::LinuxRaidMember) { + if (SoftwareRAID::isRaidMember(p->partitionPath())) + return false; + } + } + } + if (p->roles().has(PartitionRole::Unallocated)) return false; @@ -126,7 +152,7 @@ bool DeleteOperation::canDelete(const Partition* p) if (p->roles().has(PartitionRole::Luks)) { - const FS::luks* luksFs = dynamic_cast(&p->fileSystem()); + const FS::luks* luksFs = static_cast(&p->fileSystem()); if (!luksFs) return false; diff --git a/src/ops/deleteoperation.h b/src/ops/deleteoperation.h index 0796840..682abd2 100644 --- a/src/ops/deleteoperation.h +++ b/src/ops/deleteoperation.h @@ -43,18 +43,18 @@ class LIBKPMCORE_EXPORT DeleteOperation : public Operation Q_DISABLE_COPY(DeleteOperation) public: - enum ShredAction { - NoShred = 0, + enum class ShredAction { + NoShred, ZeroShred, RandomShred }; - DeleteOperation(Device& d, Partition* p, ShredAction shred = NoShred); + DeleteOperation(Device& d, Partition* p, ShredAction shred = ShredAction::NoShred); ~DeleteOperation(); public: QString iconName() const override { - return shredAction() == NoShred ? + return shredAction() == ShredAction::NoShred ? QStringLiteral("edit-delete") : QStringLiteral("edit-delete-shred"); } diff --git a/src/ops/newoperation.cpp b/src/ops/newoperation.cpp index 29b19ad..fb4fcce 100644 --- a/src/ops/newoperation.cpp +++ b/src/ops/newoperation.cpp @@ -55,7 +55,7 @@ NewOperation::NewOperation(Device& d, Partition* p) : const FileSystem& fs = newPartition().fileSystem(); - if (fs.type() != FileSystem::Extended) { + if (fs.type() != FileSystem::Type::Extended) { // It would seem tempting to skip the CreateFileSystemJob or the // SetFileSystemLabelJob if either has nothing to do (unformatted FS or // empty label). However, the user might later on decide to change FS or @@ -65,7 +65,7 @@ NewOperation::NewOperation(Device& d, Partition* p) : m_CreateFileSystemJob = new CreateFileSystemJob(targetDevice(), newPartition(), fs.label()); addJob(createFileSystemJob()); - if (fs.type() == FileSystem::Lvm2_PV) { + if (fs.type() == FileSystem::Type::Lvm2_PV) { m_SetPartFlagsJob = new SetPartFlagsJob(targetDevice(), newPartition(), PartitionTable::FlagLvm); addJob(setPartFlagsJob()); } diff --git a/src/ops/operation.cpp b/src/ops/operation.cpp index 8115913..c837f29 100644 --- a/src/ops/operation.cpp +++ b/src/ops/operation.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see .* *************************************************************************/ -#include "ops/operation.h" +#include "ops/operation_p.h" #include "core/partition.h" #include "core/device.h" @@ -32,10 +32,10 @@ #include Operation::Operation() : - m_Status(StatusNone), - m_Jobs(), - m_ProgressBase(0) + d(std::make_unique()) { + d->m_Status = StatusNone; + d->m_ProgressBase = 0; } Operation::~Operation() @@ -172,3 +172,33 @@ bool Operation::execute(Report& parent) return rval; } + +Operation::OperationStatus Operation::status() const +{ + return d->m_Status; +} + +void Operation::setStatus(OperationStatus s) +{ + d->m_Status = s; +} + +QList& Operation::jobs() +{ + return d->m_Jobs; +} + +const QList& Operation::jobs() const +{ + return d->m_Jobs; +} + +void Operation::setProgressBase(qint32 i) +{ + d->m_ProgressBase = i; +} + +qint32 Operation::progressBase() const +{ + return d->m_ProgressBase; +} diff --git a/src/ops/operation.h b/src/ops/operation.h index d4dd29f..e551043 100644 --- a/src/ops/operation.h +++ b/src/ops/operation.h @@ -1,5 +1,6 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2018 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 * @@ -15,8 +16,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_OPERATION_H) - +#ifndef KPMCORE_OPERATION_H #define KPMCORE_OPERATION_H #include "util/libpartitionmanagerexport.h" @@ -25,10 +25,13 @@ #include #include +#include + class Partition; class Device; -class OperationStack; class Job; +class OperationPrivate; +class OperationStack; class OperationRunner; class Report; @@ -108,17 +111,16 @@ public: virtual bool targets(const Device&) const = 0; virtual bool targets(const Partition&) const = 0; - virtual OperationStatus status() const { - return m_Status; /**< @return the current status */ - } + /**< @return the current status */ + virtual OperationStatus status() const; + virtual QString statusText() const; virtual QString statusIcon() const; - virtual void setStatus(OperationStatus s) { - m_Status = s; /**< @param s the new status */ - } + /**< @param s the new status */ + virtual void setStatus(OperationStatus s); - LIBKPMCORE_EXPORT qint32 totalProgress() const; + qint32 totalProgress() const; protected: void onJobStarted(); @@ -129,24 +131,14 @@ protected: void addJob(Job* job); - QList& jobs() { - return m_Jobs; - } - const QList& jobs() const { - return m_Jobs; - } + QList& jobs(); + const QList& jobs() const; - void setProgressBase(qint32 i) { - m_ProgressBase = i; - } - qint32 progressBase() const { - return m_ProgressBase; - } + void setProgressBase(qint32 i); + qint32 progressBase() const; private: - OperationStatus m_Status; - QList m_Jobs; - qint32 m_ProgressBase; + std::unique_ptr d; }; #endif diff --git a/src/ops/operation_p.h b/src/ops/operation_p.h new file mode 100644 index 0000000..3a37595 --- /dev/null +++ b/src/ops/operation_p.h @@ -0,0 +1,31 @@ +/************************************************************************* + * Copyright (C) 2018 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_OPERATION_P_H +#define KPMCORE_OPERATION_P_H + +#include "ops/operation.h" + +class OperationPrivate +{ +public: + Operation::OperationStatus m_Status; + QList m_Jobs; + qint32 m_ProgressBase; +}; + +#endif diff --git a/src/ops/removevolumegroupoperation.cpp b/src/ops/removevolumegroupoperation.cpp index c269bbb..1c16433 100644 --- a/src/ops/removevolumegroupoperation.cpp +++ b/src/ops/removevolumegroupoperation.cpp @@ -18,6 +18,7 @@ #include "ops/removevolumegroupoperation.h" #include "jobs/removevolumegroupjob.h" +#include "core/lvmdevice.h" #include "core/partition.h" #include "core/partitiontable.h" #include "core/volumemanagerdevice.h" @@ -45,11 +46,28 @@ QString RemoveVolumeGroupOperation::description() const void RemoveVolumeGroupOperation::preview() { m_PartitionTable = device().partitionTable(); + + if (device().type() == Device::Type::LVM_Device) { + LvmDevice& lvm = static_cast(device()); + + LvmDevice::s_OrphanPVs << lvm.physicalVolumes(); + } + device().setPartitionTable(new PartitionTable(PartitionTable::vmd, 0, device().totalLogical() - 1)); } void RemoveVolumeGroupOperation::undo() { + if (device().type() == Device::Type::LVM_Device) { + LvmDevice& lvm = static_cast(device()); + + const QVector constOrphanList = LvmDevice::s_OrphanPVs; + + for (const Partition* p : constOrphanList) + if (lvm.physicalVolumes().contains(p)) + LvmDevice::s_OrphanPVs.removeAll(p); + } + device().setPartitionTable(m_PartitionTable); } @@ -61,13 +79,13 @@ void RemoveVolumeGroupOperation::undo() bool RemoveVolumeGroupOperation::isRemovable(const VolumeManagerDevice* dev) { // TODO: allow removal when LVs are inactive. - if (dev->type() == Device::LVM_Device) { + if (dev->type() == Device::Type::LVM_Device) { if (dev->partitionTable()->children().count() == 0) // This is necessary to prevent a crash during applying of operations return true; else if (dev->partitionTable()->children().count() > 1) return false; else - if (dev->partitionTable()->children().first()->fileSystem().type() == FileSystem::Unknown) + if (dev->partitionTable()->children().first()->fileSystem().type() == FileSystem::Type::Unknown) return true; } diff --git a/src/ops/resizeoperation.cpp b/src/ops/resizeoperation.cpp index 3bac007..249f2b1 100644 --- a/src/ops/resizeoperation.cpp +++ b/src/ops/resizeoperation.cpp @@ -20,6 +20,7 @@ #include "core/partition.h" #include "core/device.h" +#include "core/lvmdevice.h" #include "core/partitiontable.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" @@ -32,6 +33,7 @@ #include "ops/checkoperation.h" #include "fs/filesystem.h" +#include "fs/luks.h" #include "util/capacity.h" #include "util/report.h" @@ -332,6 +334,9 @@ bool ResizeOperation::canGrow(const Partition* p) if (p == nullptr) return false; + if (isLVMPVinNewlyVG(p)) + return false; + // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::State::New && !p->roles().has(PartitionRole::Luks)) return true; @@ -351,6 +356,9 @@ bool ResizeOperation::canShrink(const Partition* p) if (p == nullptr) return false; + if (isLVMPVinNewlyVG(p)) + return false; + // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::State::New && !p->roles().has(PartitionRole::Luks)) return true; @@ -373,6 +381,9 @@ bool ResizeOperation::canMove(const Partition* p) if (p == nullptr) return false; + if (isLVMPVinNewlyVG(p)) + return false; + // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::State::New) // too many bad things can happen for LUKS partitions @@ -387,3 +398,24 @@ bool ResizeOperation::canMove(const Partition* p) return p->fileSystem().supportMove() != FileSystem::cmdSupportNone; } + +bool ResizeOperation::isLVMPVinNewlyVG(const Partition *p) +{ + if (p->fileSystem().type() == FileSystem::Type::Lvm2_PV) { + if (LvmDevice::s_DirtyPVs.contains(p)) + return true; + } + else if (p->fileSystem().type() == FileSystem::Type::Luks || p->fileSystem().type() == FileSystem::Type::Luks2) { + // See if innerFS is LVM + FileSystem *fs = static_cast(&p->fileSystem())->innerFS(); + + if (fs) { + if (fs->type() == FileSystem::Type::Lvm2_PV) { + if (LvmDevice::s_DirtyPVs.contains(p)) + return true; + } + } + } + + return false; +} diff --git a/src/ops/resizeoperation.h b/src/ops/resizeoperation.h index e620b5a..c8bf148 100644 --- a/src/ops/resizeoperation.h +++ b/src/ops/resizeoperation.h @@ -158,6 +158,9 @@ protected: return m_CheckResizedJob; } +private: + static bool isLVMPVinNewlyVG(const Partition* p); + private: Device& m_TargetDevice; Partition& m_Partition; diff --git a/src/ops/resizevolumegroupoperation.cpp b/src/ops/resizevolumegroupoperation.cpp index d253746..6996899 100644 --- a/src/ops/resizevolumegroupoperation.cpp +++ b/src/ops/resizevolumegroupoperation.cpp @@ -89,12 +89,12 @@ ResizeVolumeGroupOperation::ResizeVolumeGroupOperation(LvmDevice& d, const QVect // *DO NOTHING* } else { if (!toInsertList.isEmpty()) { - m_GrowVolumeGroupJob = new ResizeVolumeGroupJob(d, toInsertList, ResizeVolumeGroupJob::Grow); + m_GrowVolumeGroupJob = new ResizeVolumeGroupJob(d, toInsertList, ResizeVolumeGroupJob::Type::Grow); addJob(growVolumeGroupJob()); } if (!toRemoveList.isEmpty()) { m_MovePhysicalVolumeJob = new MovePhysicalVolumeJob(d, toRemoveList); - m_ShrinkVolumeGroupJob = new ResizeVolumeGroupJob(d, toRemoveList, ResizeVolumeGroupJob::Shrink); + m_ShrinkVolumeGroupJob = new ResizeVolumeGroupJob(d, toRemoveList, ResizeVolumeGroupJob::Type::Shrink); addJob(movePhysicalVolumeJob()); addJob(shrinkvolumegroupjob()); } diff --git a/src/ops/restoreoperation.cpp b/src/ops/restoreoperation.cpp index ae89d01..2699660 100644 --- a/src/ops/restoreoperation.cpp +++ b/src/ops/restoreoperation.cpp @@ -225,7 +225,7 @@ Partition* RestoreOperation::createRestorePartition(const Device& device, Partit return nullptr; const qint64 end = start + fileInfo.size() / device.logicalSize() - 1; - Partition* p = new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Unknown, start, end, device.logicalSize()), start, end, QString()); + Partition* p = new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Type::Unknown, start, end, device.logicalSize()), start, end, QString()); p->setState(Partition::State::Restore); return p; diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 3a717ef..098738c 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -15,17 +15,13 @@ ############################################ -option(PARTMAN_LIBPARTEDBACKEND "Build the libparted backend plugin." ON) +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + option(PARTMAN_SFDISKBACKEND "Build the sfdisk backend plugin." ON) -if (PARTMAN_LIBPARTEDBACKEND) - add_subdirectory(libparted) -endif (PARTMAN_LIBPARTEDBACKEND) - -option(PARTMAN_SFDISKBACKEND "Build the sfdisk backend plugin." ON) - -if (PARTMAN_SFDISKBACKEND) - add_subdirectory(sfdisk) -endif (PARTMAN_SFDISKBACKEND) + if (PARTMAN_SFDISKBACKEND) + add_subdirectory(sfdisk) + endif (PARTMAN_SFDISKBACKEND) +endif() option(PARTMAN_DUMMYBACKEND "Build the dummy backend plugin." ON) diff --git a/src/plugins/dummy/CMakeLists.txt b/src/plugins/dummy/CMakeLists.txt index f5b2a26..1c04df4 100644 --- a/src/plugins/dummy/CMakeLists.txt +++ b/src/plugins/dummy/CMakeLists.txt @@ -22,6 +22,6 @@ set (pmdummybackendplugin_SRCS add_library(pmdummybackendplugin SHARED ${pmdummybackendplugin_SRCS}) -target_link_libraries(pmdummybackendplugin kpmcore) +target_link_libraries(pmdummybackendplugin kpmcore KF5::I18n KF5::CoreAddons) install(TARGETS pmdummybackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) diff --git a/src/plugins/dummy/dummybackend.cpp b/src/plugins/dummy/dummybackend.cpp index 5abc6f2..ae6f938 100644 --- a/src/plugins/dummy/dummybackend.cpp +++ b/src/plugins/dummy/dummybackend.cpp @@ -74,7 +74,7 @@ FileSystem::Type DummyBackend::detectFileSystem(const QString& deviceNode) { Q_UNUSED(deviceNode) - return FileSystem::Unknown; + return FileSystem::Type::Unknown; } QString DummyBackend::readLabel(const QString& deviceNode) const @@ -91,31 +91,27 @@ QString DummyBackend::readUUID(const QString& deviceNode) const return QString(); } -CoreBackendDevice* DummyBackend::openDevice(const Device& d) +std::unique_ptr DummyBackend::openDevice(const Device& d) { - DummyDevice* device = new DummyDevice(d.deviceNode()); + std::unique_ptr device = std::make_unique(d.deviceNode()); - if (device == nullptr || !device->open()) { - delete device; + if (!device->open()) device = nullptr; - } return device; } -CoreBackendDevice* DummyBackend::openDeviceExclusive(const Device& d) +std::unique_ptr DummyBackend::openDeviceExclusive(const Device& d) { - DummyDevice* device = new DummyDevice(d.deviceNode()); + std::unique_ptr device = std::make_unique(d.deviceNode()); - if (device == nullptr || !device->openExclusive()) { - delete device; + if (!device->openExclusive()) device = nullptr; - } return device; } -bool DummyBackend::closeDevice(CoreBackendDevice* coreDevice) +bool DummyBackend::closeDevice(std::unique_ptr coreDevice) { return coreDevice->close(); } diff --git a/src/plugins/dummy/dummybackend.h b/src/plugins/dummy/dummybackend.h index 12aad6b..a9f8fb3 100644 --- a/src/plugins/dummy/dummybackend.h +++ b/src/plugins/dummy/dummybackend.h @@ -45,9 +45,9 @@ public: void initFSSupport() override; QList scanDevices(bool excludeReadOnly = false) override; - CoreBackendDevice* openDevice(const Device& d) override; - CoreBackendDevice* openDeviceExclusive(const Device& d) override; - bool closeDevice(CoreBackendDevice* coreDevice) override; + std::unique_ptr openDevice(const Device& d) override; + std::unique_ptr openDeviceExclusive(const Device& d) override; + bool closeDevice(std::unique_ptr coreDevice) override; Device* scanDevice(const QString& deviceNode) override; FileSystem::Type detectFileSystem(const QString& deviceNode) override; QString readLabel(const QString& deviceNode) const override; diff --git a/src/plugins/dummy/dummydevice.cpp b/src/plugins/dummy/dummydevice.cpp index 92c2de7..26634a7 100644 --- a/src/plugins/dummy/dummydevice.cpp +++ b/src/plugins/dummy/dummydevice.cpp @@ -47,45 +47,15 @@ bool DummyDevice::close() return true; } -CoreBackendPartitionTable* DummyDevice::openPartitionTable() +std::unique_ptr DummyDevice::openPartitionTable() { - CoreBackendPartitionTable* ptable = new DummyPartitionTable(); - - if (ptable == nullptr || !ptable->open()) { - delete ptable; - ptable = nullptr; - } - - return ptable; + return std::make_unique(DummyPartitionTable()); } bool DummyDevice::createPartitionTable(Report& report, const PartitionTable& ptable) { - Q_UNUSED(report); - Q_UNUSED(ptable); - - return true; -} - -bool DummyDevice::readData(QByteArray& buffer, qint64 offset, qint64 size) -{ - Q_UNUSED(buffer); - Q_UNUSED(offset); - Q_UNUSED(size); - - if (!isExclusive()) - return false; - - return true; -} - -bool DummyDevice::writeData(QByteArray& buffer, qint64 offset) -{ - Q_UNUSED(buffer); - Q_UNUSED(offset); - - if (!isExclusive()) - return false; + Q_UNUSED(report) + Q_UNUSED(ptable) return true; } diff --git a/src/plugins/dummy/dummydevice.h b/src/plugins/dummy/dummydevice.h index 9493365..746c346 100644 --- a/src/plugins/dummy/dummydevice.h +++ b/src/plugins/dummy/dummydevice.h @@ -41,12 +41,9 @@ public: bool openExclusive() override; bool close() override; - CoreBackendPartitionTable* openPartitionTable() override; + std::unique_ptr openPartitionTable() override; bool createPartitionTable(Report& report, const PartitionTable& ptable) override; - - bool readData(QByteArray& buffer, qint64 offset, qint64 size) override; - bool writeData(QByteArray& buffer, qint64 offset) override; }; #endif diff --git a/src/plugins/dummy/dummypartitiontable.cpp b/src/plugins/dummy/dummypartitiontable.cpp index 8da9328..90dd93f 100644 --- a/src/plugins/dummy/dummypartitiontable.cpp +++ b/src/plugins/dummy/dummypartitiontable.cpp @@ -96,7 +96,7 @@ FileSystem::Type DummyPartitionTable::detectFileSystemBySector(Report& report, c Q_UNUSED(device) Q_UNUSED(sector) - FileSystem::Type rval = FileSystem::Unknown; + FileSystem::Type rval = FileSystem::Type::Unknown; return rval; } diff --git a/src/plugins/libparted/CMakeLists.txt b/src/plugins/libparted/CMakeLists.txt deleted file mode 100644 index abe9d81..0000000 --- a/src/plugins/libparted/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# 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 . - -find_package(LIBPARTED REQUIRED) - -if (LIBPARTED_FILESYSTEM_SUPPORT) - add_definitions(-DLIBPARTED_FILESYSTEM_SUPPORT) -endif (LIBPARTED_FILESYSTEM_SUPPORT) - -if (LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) - add_definitions(-DLIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) -endif (LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) - -include_directories(${LIBPARTED_INCLUDE_DIR}) - -set (pmlibpartedbackendplugin_SRCS - libpartedbackend.cpp - libparteddevice.cpp - libpartedpartitiontable.cpp - ${CMAKE_SOURCE_DIR}/src/backend/corebackenddevice.cpp -) - -add_library(pmlibpartedbackendplugin SHARED ${pmlibpartedbackendplugin_SRCS}) - -target_link_libraries(pmlibpartedbackendplugin kpmcore ${LIBPARTED_LIBS} KF5::I18n) - -install(TARGETS pmlibpartedbackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) diff --git a/src/plugins/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp deleted file mode 100644 index db73577..0000000 --- a/src/plugins/libparted/libpartedbackend.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/************************************************************************* - * Copyright (C) 2008-2012 by Volker Lanz * - * Copyright (C) 2015-2016 by Teo Mrnjavac * - * Copyright (C) 2016-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 .* - *************************************************************************/ - -/** @file -*/ - -#include "plugins/libparted/libpartedbackend.h" -#include "plugins/libparted/libparteddevice.h" -#include "plugins/libparted/pedflags.h" - -#include "core/lvmdevice.h" -#include "core/partition.h" -#include "core/partitiontable.h" -#include "core/partitionalignment.h" - -#include "fs/filesystem.h" -#include "fs/filesystemfactory.h" - -#include "fs/fat16.h" -#include "fs/hfs.h" -#include "fs/hfsplus.h" -#include "fs/luks.h" -#include "fs/lvm2_pv.h" - -#include "util/globallog.h" -#include "util/externalcommand.h" -#include "util/helpers.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin();) - -static QString s_lastPartedExceptionMessage; - -/** Callback to handle exceptions from libparted - @param e the libparted exception to handle -*/ -static PedExceptionOption pedExceptionHandler(PedException* e) -{ - Log(Log::error) << xi18nc("@info:status", "LibParted Exception: %1", QString::fromLocal8Bit(e->message)); - s_lastPartedExceptionMessage = QString::fromLocal8Bit(e->message); - return PED_EXCEPTION_UNHANDLED; -} - -// -------------------------------------------------------------------------- - -// The following structs and the typedef come from libparted's internal gpt sources. -// It's very unfortunate there is no public API to get at the first and last usable -// sector for GPT a partition table, so this is the only (libparted) way to get that -// information (another way would be to read the GPT header and parse the -// information ourselves; if the libparted devs begin changing these internal -// structs for each point release and break our code, we'll have to do just that). - -typedef struct { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint8_t clock_seq_hi_and_reserved; - uint8_t clock_seq_low; - uint8_t node[6]; -} /* __attribute__ ((packed)) */ efi_guid_t; - - -struct __attribute__((packed)) _GPTDiskData { - PedGeometry data_area; - int entry_count; - efi_guid_t uuid; -}; - -typedef struct _GPTDiskData GPTDiskData; - -// -------------------------------------------------------------------------- - -/** Get the first sector a Partition may cover on a given Device - @param d the Device in question - @return the first sector usable by a Partition -*/ -static qint64 firstUsableSector(const Device& d) -{ - PedDevice* pedDevice = ped_device_get(d.deviceNode().toLocal8Bit().constData()); - PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; - - qint64 rval = 0; - if (pedDisk) - rval = static_cast(pedDisk->dev->bios_geom.sectors); - - if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { - GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); - PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); - - if (geom) - rval = static_cast(geom->start); - else - rval += 32; - } - - ped_disk_destroy(pedDisk); - - return rval; -} - -/** Get the last sector a Partition may cover on a given Device - @param d the Device in question - @return the last sector usable by a Partition -*/ -static qint64 lastUsableSector(const Device& d) -{ - PedDevice* pedDevice = ped_device_get(d.deviceNode().toLocal8Bit().constData()); - PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; - - qint64 rval = 0; - if (pedDisk) - rval = static_cast< qint64 >( pedDisk->dev->bios_geom.sectors ) * - static_cast< qint64 >( pedDisk->dev->bios_geom.heads ) * - static_cast< qint64 >( pedDisk->dev->bios_geom.cylinders - 1 ); - - if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { - GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); - PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); - - if (geom) - rval = geom->end; - else - rval -= 32; - } - - ped_disk_destroy(pedDisk); - - return rval; -} - -/** Reads sectors used on a FileSystem using libparted functions. - @param pedDisk pointer to pedDisk where the Partition and its FileSystem are - @param p the Partition the FileSystem is on - @return the number of sectors used -*/ -#if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT -static qint64 readSectorsUsedLibParted(PedDisk* pedDisk, const Partition& p) -{ - Q_ASSERT(pedDisk); - - qint64 rval = -1; - - PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, p.firstSector()); - - if (pedPartition) { - PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom); - - if (pedFileSystem) { - if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem)) { - rval = pedConstraint->min_size; - ped_constraint_destroy(pedConstraint); - } - - ped_file_system_close(pedFileSystem); - } - } - - return rval; -} -#endif - -/** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. - @param pedDisk pointer to pedDisk where the Partition and its FileSystem are - @param p the Partition the FileSystem is on - @param mountPoint mount point of the partition in question -*/ -static void readSectorsUsed(PedDisk* pedDisk, const Device& d, Partition& p, const QString& mountPoint) -{ - if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::LinuxSwap && p.fileSystem().type() != FileSystem::Lvm2_PV) { - const QStorageInfo storage = QStorageInfo(mountPoint); - if (p.isMounted() && storage.isValid()) - p.fileSystem().setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / d.logicalSize()); - } - else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) - p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSize()); -#if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT - else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportCore) - p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(pedDisk, p)); -#else - Q_UNUSED(pedDisk); -#endif -} - -static PartitionTable::Flags activeFlags(PedPartition* p) -{ - PartitionTable::Flags flags = PartitionTable::FlagNone; - - // We might get here with a pedPartition just picked up from libparted that is - // unallocated. Libparted doesn't like it if we ask for flags for unallocated - // space. - if (p->num <= 0) - return flags; - - for (const auto &flag : flagmap) - if (ped_partition_is_flag_available(p, flag.pedFlag) && ped_partition_get_flag(p, flag.pedFlag)) - flags |= flag.flag; - - return flags; -} - -static PartitionTable::Flags availableFlags(PedPartition* p) -{ - PartitionTable::Flags flags; - - // see above. - if (p->num <= 0) - return flags; - - for (const auto &flag : flagmap) { - if (ped_partition_is_flag_available(p, flag.pedFlag)) { - // Workaround: libparted claims the hidden flag is available for extended partitions, but - // throws an error when we try to set or clear it. So skip this combination. Also see setFlag. - if (p->type != PED_PARTITION_EXTENDED || flag.flag != PartitionTable::FlagHidden) - flags |= flag.flag; - } - } - - return flags; -} - -/** Constructs a LibParted object. */ -LibPartedBackend::LibPartedBackend(QObject*, const QList&) : - CoreBackend() -{ - ped_exception_set_handler(pedExceptionHandler); -} - -void LibPartedBackend::initFSSupport() -{ -#if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT - if (FS::fat16::m_Shrink == FileSystem::cmdSupportNone) - FS::fat16::m_Shrink = FileSystem::cmdSupportBackend; - - if (FS::fat16::m_Grow == FileSystem::cmdSupportNone) - FS::fat16::m_Grow = FileSystem::cmdSupportBackend; - - if (FS::hfs::m_Shrink == FileSystem::cmdSupportNone) - FS::hfs::m_Shrink = FileSystem::cmdSupportBackend; - - if (FS::hfsplus::m_Shrink == FileSystem::cmdSupportNone) - FS::hfsplus::m_Shrink = FileSystem::cmdSupportBackend; - - if (FS::hfs::m_GetUsed == FileSystem::cmdSupportNone) - FS::hfs::m_GetUsed = FileSystem::cmdSupportBackend; - - if (FS::hfsplus::m_GetUsed == FileSystem::cmdSupportNone) - FS::hfsplus::m_GetUsed = FileSystem::cmdSupportBackend; -#endif -} - -/** Scans a Device for Partitions. - - This method will scan a Device for all Partitions on it, detect the FileSystem for each Partition, - try to determine the FileSystem usage, read the FileSystem label and store it all in newly created - objects that are in the end added to the Device's PartitionTable. - - @param d Device - @param pedDisk libparted pointer to the partition table -*/ -void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) -{ - Q_ASSERT(pedDisk); - Q_ASSERT(d.partitionTable()); - - PedPartition* pedPartition = nullptr; - - QList partitions; - - while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { - if (pedPartition->num < 1) - continue; - - PartitionRole::Roles r = PartitionRole::None; - - FileSystem::Type type = FileSystem::Unknown; - char* pedPath = ped_partition_get_path(pedPartition); - const QString partitionNode = pedPath ? QString::fromLocal8Bit(pedPath) : QString(); - free(pedPath); - type = detectFileSystem(partitionNode); - - switch (pedPartition->type) { - case PED_PARTITION_NORMAL: - r = PartitionRole::Primary; - break; - - case PED_PARTITION_EXTENDED: - r = PartitionRole::Extended; - type = FileSystem::Extended; - break; - - case PED_PARTITION_LOGICAL: - r = PartitionRole::Logical; - break; - - default: - continue; - } - - // Find an extended partition this partition is in. - PartitionNode* parent = d.partitionTable()->findPartitionBySector(pedPartition->geom.start, PartitionRole(PartitionRole::Extended)); - - // None found, so it's a primary in the device's partition table. - if (parent == nullptr) - parent = d.partitionTable(); - - FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end, d.logicalSize()); - fs->scan(partitionNode); - QString mountPoint; - bool mounted; - - // libparted does not handle LUKS partitions - if (fs->type() == FileSystem::Luks) { - r |= PartitionRole::Luks; - FS::luks* luksFs = static_cast(fs); - luksFs->initLUKS(); - QString mapperNode = luksFs->mapperName(); - mountPoint = FileSystem::detectMountPoint(fs, mapperNode); - mounted = FileSystem::detectMountStatus(fs, mapperNode); - } else { - mountPoint = FileSystem::detectMountPoint(fs, partitionNode); - mounted = FileSystem::detectMountStatus(fs, partitionNode); - } - - Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end, partitionNode, availableFlags(pedPartition), mountPoint, mounted, activeFlags(pedPartition)); - - if (!part->roles().has(PartitionRole::Luks)) - readSectorsUsed(pedDisk, d, *part, mountPoint); - - if (fs->supportGetLabel() != FileSystem::cmdSupportNone) - fs->setLabel(fs->readLabel(part->deviceNode())); - - // GPT partitions support partition labels and partition UUIDs - if(d.partitionTable()->type() == PartitionTable::TableType::gpt) - part->setLabel(QLatin1String(ped_partition_get_name(pedPartition))); - - if (fs->supportGetUUID() != FileSystem::cmdSupportNone) - fs->setUUID(fs->readUUID(part->deviceNode())); - - parent->append(part); - partitions.append(part); - } - - d.partitionTable()->updateUnallocated(d); - - if (d.partitionTable()->isSectorBased(d)) - d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased); - - for (const Partition * part : qAsConst(partitions)) - PartitionAlignment::isAligned(d, *part); -} - -/** Create a Device for the given device_node and scan it for partitions. - @param deviceNode the device node (e.g. "/dev/sda") - @return the created Device object. callers need to free this. -*/ -Device* LibPartedBackend::scanDevice(const QString& deviceNode) -{ - PedDevice* pedDevice = ped_device_get(deviceNode.toLocal8Bit().constData()); - - if (pedDevice == nullptr) { - Log(Log::warning) << xi18nc("@info:status", "Could not access device %1", deviceNode); - Log(Log::warning) << xi18nc("@info:status", "Checking if this device is LVM VG"); - - // Look if this device is a LVM VG - ExternalCommand checkVG(QStringLiteral("lvm"), { QStringLiteral("vgdisplay"), deviceNode }); - - if (checkVG.run(-1) && checkVG.exitCode() == 0) - { - QList availableDevices = scanDevices(); - - LvmDevice::scanSystemLVM(availableDevices); - - for (Device *device : qAsConst(availableDevices)) - if (device->deviceNode() == deviceNode) - return device; - } - - return nullptr; - } - - Log(Log::information) << xi18nc("@info:status", "Device found: %1", QString::fromLocal8Bit(pedDevice->model)); - - DiskDevice* d = new DiskDevice(QString::fromLocal8Bit(pedDevice->model), QString::fromLocal8Bit(pedDevice->path), pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size); - - PedDisk* pedDisk = ped_disk_new(pedDevice); - - if (pedDisk) { - const PartitionTable::TableType type = PartitionTable::nameToTableType(QString::fromLocal8Bit(pedDisk->type->name)); - CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector(*d), lastUsableSector(*d))); - CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), ped_disk_get_max_primary_partition_count(pedDisk)); - - scanDevicePartitions(*d, pedDisk); - } - - ped_device_destroy(pedDevice); - return d; -} - -QList LibPartedBackend::scanDevices(bool excludeReadOnly) -{ -// TODO: add another bool option for loopDevices - QList result; - QStringList deviceNodes; - - ExternalCommand cmd(QStringLiteral("lsblk"), - { QStringLiteral("--nodeps"), - QStringLiteral("--paths"), - QStringLiteral("--sort"), QStringLiteral("name"), - QStringLiteral("--json"), - QStringLiteral("--output"), - QStringLiteral("type,name") }); - - if (cmd.run(-1) && cmd.exitCode() == 0) { - const QJsonDocument jsonDocument = QJsonDocument::fromJson(cmd.rawOutput()); - QJsonObject jsonObject = jsonDocument.object(); - const QJsonArray jsonArray = jsonObject[QLatin1String("blockdevices")].toArray(); - for (const auto &deviceLine : jsonArray) { - QJsonObject deviceObject = deviceLine.toObject(); - if (deviceObject[QLatin1String("type")].toString() != QLatin1String("disk")) - continue; - - const QString deviceNode = deviceObject[QLatin1String("name")].toString(); - if (excludeReadOnly) { - QString deviceName = deviceNode; - deviceName.remove(QStringLiteral("/dev/")); - QFile f(QStringLiteral("/sys/block/%1/ro").arg(deviceName)); - if (f.open(QIODevice::ReadOnly)) - if (f.readLine().trimmed().toInt() == 1) - continue; - } - deviceNodes << deviceNode; - } - - int totalDevices = deviceNodes.length(); - for (int i = 0; i < totalDevices; ++i) { - const QString deviceNode = deviceNodes[i]; - - emitScanProgress(deviceNode, i * 100 / totalDevices); - Device* device = scanDevice(deviceNode); - if(device != nullptr) { - result.append(device); - } - } - - LvmDevice::scanSystemLVM(result); - } - - return result; -} - -/** Detects the type of a FileSystem given a PedDevice and a PedPartition - @param partitionPath path to the partition - @return the detected FileSystem type (FileSystem::Unknown if not detected) -*/ -FileSystem::Type LibPartedBackend::detectFileSystem(const QString& partitionPath) -{ - FileSystem::Type rval = FileSystem::Unknown; - - blkid_cache cache; - if (blkid_get_cache(&cache, nullptr) == 0) { - blkid_dev dev; - - if ((dev = blkid_get_dev(cache, - partitionPath.toLocal8Bit().constData(), - BLKID_DEV_NORMAL)) != nullptr) { - char *string = blkid_get_tag_value(cache, "TYPE", partitionPath.toLocal8Bit().constData()); - QString s = QString::fromLocal8Bit(string); - free(string); - - if (s == QStringLiteral("ext2")) rval = FileSystem::Ext2; - else if (s == QStringLiteral("ext3")) rval = FileSystem::Ext3; - else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Ext4; - else if (s == QStringLiteral("swap")) rval = FileSystem::LinuxSwap; - else if (s == QStringLiteral("ntfs")) rval = FileSystem::Ntfs; - else if (s == QStringLiteral("reiserfs")) rval = FileSystem::ReiserFS; - else if (s == QStringLiteral("reiser4")) rval = FileSystem::Reiser4; - else if (s == QStringLiteral("xfs")) rval = FileSystem::Xfs; - else if (s == QStringLiteral("jfs")) rval = FileSystem::Jfs; - else if (s == QStringLiteral("hfs")) rval = FileSystem::Hfs; - else if (s == QStringLiteral("hfsplus")) rval = FileSystem::HfsPlus; - else if (s == QStringLiteral("ufs")) rval = FileSystem::Ufs; - else if (s == QStringLiteral("vfat")) { - // libblkid uses SEC_TYPE to distinguish between FAT16 and FAT32 - string = blkid_get_tag_value(cache, "SEC_TYPE", partitionPath.toLocal8Bit().constData()); - QString st = QString::fromLocal8Bit(string); - free(string); - if (st == QStringLiteral("msdos")) - rval = FileSystem::Fat16; - else - rval = FileSystem::Fat32; - } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Btrfs; - else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Ocfs2; - else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Zfs; - else if (s == QStringLiteral("hpfs")) rval = FileSystem::Hpfs; - else if (s == QStringLiteral("crypto_LUKS")) rval = FileSystem::Luks; - else if (s == QStringLiteral("exfat")) rval = FileSystem::Exfat; - else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; - else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; - else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; - else if (s == QStringLiteral("udf")) rval = FileSystem::Udf; - else if (s == QStringLiteral("iso9660")) rval = FileSystem::Iso9660; - else - qWarning() << "blkid: unknown file system type " << s << " on " << partitionPath; - } - - blkid_put_cache(cache); - } - - return rval; -} - -static QString readBlkIdValue(const QString& deviceNode, const QString& tag) -{ - blkid_cache cache; - QString rval; - - if (blkid_get_cache(&cache, nullptr) == 0) { - blkid_dev dev; - - char* label = nullptr; - if ((dev = blkid_get_dev(cache, deviceNode.toLocal8Bit().constData(), BLKID_DEV_NORMAL)) != nullptr && - (label = blkid_get_tag_value(cache, tag.toLocal8Bit().constData(), deviceNode.toLocal8Bit().constData()))) { - rval = QString::fromLocal8Bit(label); - free(label); - } - - blkid_put_cache(cache); - } - - return rval; -} - -QString LibPartedBackend::readLabel(const QString& deviceNode) const -{ - return readBlkIdValue(deviceNode, QStringLiteral("LABEL")); -} - -QString LibPartedBackend::readUUID(const QString& deviceNode) const -{ - return readBlkIdValue(deviceNode, QStringLiteral("UUID")); -} - -CoreBackendDevice* LibPartedBackend::openDevice(const Device& d) -{ - LibPartedDevice* device = new LibPartedDevice(d.deviceNode()); - - if (device == nullptr || !device->open()) { - delete device; - device = nullptr; - } - - return device; -} - -CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const Device& d) -{ - LibPartedDevice* device = new LibPartedDevice(d.deviceNode()); - - if (device == nullptr || !device->openExclusive()) { - delete device; - device = nullptr; - } - - return device; -} - -bool LibPartedBackend::closeDevice(CoreBackendDevice* core_device) -{ - return core_device->close(); -} - -PedPartitionFlag LibPartedBackend::getPedFlag(PartitionTable::Flag flag) -{ - for (const auto &f : flagmap) - if (f.flag == flag) - return f.pedFlag; - - return static_cast(-1); -} - -QString LibPartedBackend::lastPartedExceptionMessage() -{ - return s_lastPartedExceptionMessage; -} - -#include "libpartedbackend.moc" diff --git a/src/plugins/libparted/libpartedbackend.h b/src/plugins/libparted/libpartedbackend.h deleted file mode 100644 index 238ec03..0000000 --- a/src/plugins/libparted/libpartedbackend.h +++ /dev/null @@ -1,80 +0,0 @@ -/************************************************************************* - * Copyright (C) 2008, 2010 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 .* - *************************************************************************/ - -#if !defined(KPMCORE_LIBPARTED_H) - -#define KPMCORE_LIBPARTED_H - -#include "backend/corebackend.h" - -#include "core/partitiontable.h" -#include "core/diskdevice.h" -#include "util/libpartitionmanagerexport.h" - -#include "fs/filesystem.h" - -#include - -#include -#include -#include - -class LibPartedDevice; -class LibPartedPartitionTable; -class LibPartedPartition; -class OperationStack; - -class KPluginFactory; -class QString; - -/** Backend plugin for libparted. - - @author Volker Lanz -*/ -class LibPartedBackend : public CoreBackend -{ - friend class KPluginFactory; - friend class LibPartedPartition; - friend class LibPartedDevice; - friend class LibPartedPartitionTable; - - Q_DISABLE_COPY(LibPartedBackend) - -private: - LibPartedBackend(QObject* parent, const QList& args); - -public: - void initFSSupport() override; - - CoreBackendDevice* openDevice(const Device& d) override; - CoreBackendDevice* openDeviceExclusive(const Device& d) override; - bool closeDevice(CoreBackendDevice* core_device) override; - Device* scanDevice(const QString& deviceNode) override; - QList scanDevices(bool excludeReadOnly = false) override; - FileSystem::Type detectFileSystem(const QString& partitionPath) override; - QString readLabel(const QString& deviceNode) const override; - QString readUUID(const QString& deviceNode) const override; - - static QString lastPartedExceptionMessage(); - -private: - static PedPartitionFlag getPedFlag(PartitionTable::Flag flag); - void scanDevicePartitions(Device& d, PedDisk* pedDisk); -}; - -#endif diff --git a/src/plugins/libparted/libparteddevice.cpp b/src/plugins/libparted/libparteddevice.cpp deleted file mode 100644 index 6354b58..0000000 --- a/src/plugins/libparted/libparteddevice.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/************************************************************************* - * 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 "plugins/libparted/libparteddevice.h" -#include "plugins/libparted/libpartedpartitiontable.h" - -#include "core/partitiontable.h" - -#include "util/globallog.h" -#include "util/report.h" - -#include - -LibPartedDevice::LibPartedDevice(const QString& deviceNode) : - CoreBackendDevice(deviceNode), - m_PedDevice(nullptr) -{ -} - -LibPartedDevice::~LibPartedDevice() -{ - if (pedDevice()) - close(); -} - -bool LibPartedDevice::open() -{ - Q_ASSERT(pedDevice() == nullptr); - - if (pedDevice()) - return false; - - m_PedDevice = ped_device_get(deviceNode().toLocal8Bit().constData()); - - return m_PedDevice != nullptr; -} - -bool LibPartedDevice::openExclusive() -{ - bool rval = open() && ped_device_open(pedDevice()); - - if (rval) - setExclusive(true); - - return rval; -} - -bool LibPartedDevice::close() -{ - Q_ASSERT(pedDevice()); - - if (pedDevice() && isExclusive()) { - ped_device_close(pedDevice()); - setExclusive(false); - } - - m_PedDevice = nullptr; - return true; -} - -CoreBackendPartitionTable* LibPartedDevice::openPartitionTable() -{ - CoreBackendPartitionTable* ptable = new LibPartedPartitionTable(pedDevice()); - - if (ptable == nullptr || !ptable->open()) { - delete ptable; - ptable = nullptr; - } - - return ptable; -} - -bool LibPartedDevice::createPartitionTable(Report& report, const PartitionTable& ptable) -{ - PedDiskType* pedDiskType = ped_disk_type_get(ptable.typeName().toLocal8Bit().constData()); - - if (pedDiskType == nullptr) { - report.line() << xi18nc("@info:progress", "Creating partition table failed: Could not retrieve partition table type \"%1\" for %2.", ptable.typeName(), deviceNode()); - return false; - } - - PedDevice* dev = ped_device_get(deviceNode().toLocal8Bit().constData()); - - if (dev == nullptr) { - report.line() << xi18nc("@info:progress", "Creating partition table failed: Could not open backend device %1.", deviceNode()); - return false; - } - - PedDisk* disk = ped_disk_new_fresh(dev, pedDiskType); - - if (disk == nullptr) { - report.line() << xi18nc("@info:progress", "Creating partition table failed: Could not create a new partition table in the backend for device %1.", deviceNode()); - return false; - } - - return LibPartedPartitionTable::commit(disk); -} - -bool LibPartedDevice::readData(QByteArray& buffer, qint64 offset, qint64 size) -{ - if (!isExclusive()) - return false; - - void *data = malloc(size); - bool rval = ped_device_read(pedDevice(), data, offset / pedDevice()->sector_size, size / pedDevice()->sector_size); - buffer = QByteArray(static_cast(data), size); - free(data); - return rval; -} - -bool LibPartedDevice::writeData(QByteArray& buffer, qint64 offset) -{ - if (!isExclusive()) - return false; - - return ped_device_write(pedDevice(), static_cast(buffer.constData()), offset / pedDevice()->sector_size, buffer.size() / pedDevice()->sector_size); -} diff --git a/src/plugins/libparted/libpartedpartitiontable.cpp b/src/plugins/libparted/libpartedpartitiontable.cpp deleted file mode 100644 index 79074ea..0000000 --- a/src/plugins/libparted/libpartedpartitiontable.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010, 2011, 2012 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 "plugins/libparted/libpartedpartitiontable.h" -#include "plugins/libparted/libpartedbackend.h" - -#include "backend/corebackend.h" -#include "backend/corebackendmanager.h" - -#include "core/partition.h" -#include "core/device.h" - -#include "fs/filesystem.h" - -#include "util/report.h" -#include "util/externalcommand.h" - -#include - -#include - -LibPartedPartitionTable::LibPartedPartitionTable(PedDevice* device) : - CoreBackendPartitionTable(), - m_PedDevice(device), - m_PedDisk(nullptr) -{ -} - -LibPartedPartitionTable::~LibPartedPartitionTable() -{ - ped_disk_destroy(m_PedDisk); -} - -bool LibPartedPartitionTable::open() -{ - m_PedDisk = ped_disk_new(pedDevice()); - - return m_PedDisk != nullptr; -} - -bool LibPartedPartitionTable::commit(quint32 timeout) -{ - return commit(pedDisk(), timeout); -} - -bool LibPartedPartitionTable::commit(PedDisk* pd, quint32 timeout) -{ - if (pd == nullptr) - return false; - - bool rval = ped_disk_commit_to_dev(pd); - - if (rval) - rval = ped_disk_commit_to_os(pd); - - if (!ExternalCommand(QStringLiteral("udevadm"), QStringList() << QStringLiteral("settle") << QStringLiteral("--timeout=") + QString::number(timeout)).run() && - !ExternalCommand(QStringLiteral("udevsettle"), QStringList() << QStringLiteral("--timeout=") + QString::number(timeout)).run()) - sleep(timeout); - - return rval; -} - -static const struct { - FileSystem::Type type; - QString name; -} mapFileSystemTypeToLibPartedName[] = { - { FileSystem::Btrfs, QStringLiteral("btrfs") }, - { FileSystem::Ext2, QStringLiteral("ext2") }, - { FileSystem::Ext3, QStringLiteral("ext3") }, - { FileSystem::Ext4, QStringLiteral("ext4") }, - { FileSystem::LinuxSwap, QStringLiteral("linux-swap") }, - { FileSystem::Fat16, QStringLiteral("fat16") }, - { FileSystem::Fat32, QStringLiteral("fat32") }, - { FileSystem::Nilfs2, QStringLiteral("nilfs2") }, - { FileSystem::Ntfs, QStringLiteral("ntfs") }, - { FileSystem::Exfat, QStringLiteral("ntfs") }, - { FileSystem::ReiserFS, QStringLiteral("reiserfs") }, - { FileSystem::Reiser4, QStringLiteral("reiserfs") }, - { FileSystem::Xfs, QStringLiteral("xfs") }, - { FileSystem::Jfs, QStringLiteral("jfs") }, - { FileSystem::Hfs, QStringLiteral("hfs") }, - { FileSystem::HfsPlus, QStringLiteral("hfs+") }, - { FileSystem::Ufs, QStringLiteral("ufs") }, - { FileSystem::Udf, QStringLiteral("ntfs") }, - { FileSystem::Iso9660, QStringLiteral("iso9660") } -}; - -static PedFileSystemType* getPedFileSystemType(FileSystem::Type t) -{ - for (quint32 i = 0; i < sizeof(mapFileSystemTypeToLibPartedName) / sizeof(mapFileSystemTypeToLibPartedName[0]); i++) - if (mapFileSystemTypeToLibPartedName[i].type == t) - return ped_file_system_type_get(mapFileSystemTypeToLibPartedName[i].name.toLocal8Bit().constData()); - - // if we didn't find anything, go with ext2 as a safe fallback - return ped_file_system_type_get("ext2"); -} - -QString LibPartedPartitionTable::createPartition(Report& report, const Partition& partition) -{ - Q_ASSERT(partition.devicePath() == QString::fromLocal8Bit(pedDevice()->path)); - - QString rval = QString(); - - // According to libParted docs, PedPartitionType can be "nullptr if unknown". That's obviously wrong, - // it's a typedef for an enum. So let's use something the libparted devs will hopefully never - // use... - PedPartitionType pedType = static_cast(0xffffffff); - - if (partition.roles().has(PartitionRole::Extended)) - pedType = PED_PARTITION_EXTENDED; - else if (partition.roles().has(PartitionRole::Logical)) - pedType = PED_PARTITION_LOGICAL; - else if (partition.roles().has(PartitionRole::Primary)) - pedType = PED_PARTITION_NORMAL; - - if (pedType == static_cast(0xffffffff)) { - report.line() << xi18nc("@info:progress", "Unknown partition role for new partition %1 (roles: %2)", partition.deviceNode(), partition.roles().toString()); - return QString(); - } - - PedFileSystemType* pedFsType = (partition.roles().has(PartitionRole::Extended) || partition.fileSystem().type() == FileSystem::Unformatted) ? nullptr : getPedFileSystemType(partition.fileSystem().type()); - - PedPartition* pedPartition = ped_partition_new(pedDisk(), pedType, pedFsType, partition.firstSector(), partition.lastSector()); - - if (pedPartition == nullptr) { - report.line() << xi18nc("@info:progress", "Failed to create new partition %1.", partition.deviceNode()); - return QString(); - } - - PedConstraint* pedConstraint = nullptr; - PedGeometry* pedGeometry = ped_geometry_new(pedDevice(), partition.firstSector(), partition.length()); - - if (pedGeometry) - pedConstraint = ped_constraint_exact(pedGeometry); - ped_geometry_destroy(pedGeometry); - - if (pedConstraint == nullptr) { - report.line() << i18nc("@info:progress", "Failed to create a new partition: could not get geometry for constraint."); - return QString(); - } - - if (ped_disk_add_partition(pedDisk(), pedPartition, pedConstraint)) { - char *pedPath = ped_partition_get_path(pedPartition); - rval = QString::fromLocal8Bit(pedPath); - free(pedPath); - } - else { - report.line() << xi18nc("@info:progress", "Failed to add partition %1 to device %2.", partition.deviceNode(), QString::fromLocal8Bit(pedDisk()->dev->path)); - report.line() << LibPartedBackend::lastPartedExceptionMessage(); - } - - ped_constraint_destroy(pedConstraint); - - return rval; -} - -bool LibPartedPartitionTable::deletePartition(Report& report, const Partition& partition) -{ - Q_ASSERT(partition.devicePath() == QString::fromLocal8Bit(pedDevice()->path)); - - bool rval = false; - - PedPartition* pedPartition = partition.roles().has(PartitionRole::Extended) - ? ped_disk_extended_partition(pedDisk()) - : ped_disk_get_partition_by_sector(pedDisk(), partition.firstSector()); - - if (pedPartition) { - rval = ped_disk_delete_partition(pedDisk(), pedPartition); - - if (!rval) - report.line() << xi18nc("@info:progress", "Could not delete partition %1.", partition.deviceNode()); - } else - report.line() << xi18nc("@info:progress", "Deleting partition failed: Partition to delete (%1) not found on disk.", partition.deviceNode()); - - return rval; -} - -bool LibPartedPartitionTable::updateGeometry(Report& report, const Partition& partition, qint64 sector_start, qint64 sector_end) -{ - Q_ASSERT(partition.devicePath() == QString::fromLocal8Bit(pedDevice()->path)); - - bool rval = false; - - PedPartition* pedPartition = (partition.roles().has(PartitionRole::Extended)) - ? ped_disk_extended_partition(pedDisk()) - : ped_disk_get_partition_by_sector(pedDisk(), partition.firstSector()); - - if (pedPartition) { - if (PedGeometry* pedGeometry = ped_geometry_new(pedDevice(), sector_start, sector_end - sector_start + 1)) { - if (PedConstraint* pedConstraint = ped_constraint_exact(pedGeometry)) { - if (ped_disk_set_partition_geom(pedDisk(), pedPartition, pedConstraint, sector_start, sector_end)) - rval = true; - else - report.line() << xi18nc("@info:progress", "Could not set geometry for partition %1 while trying to resize/move it.", partition.deviceNode()); - ped_constraint_destroy(pedConstraint); - } else - report.line() << xi18nc("@info:progress", "Could not get constraint for partition %1 while trying to resize/move it.", partition.deviceNode()); - ped_geometry_destroy(pedGeometry); - } else - report.line() << xi18nc("@info:progress", "Could not get geometry for partition %1 while trying to resize/move it.", partition.deviceNode()); - } else - report.line() << xi18nc("@info:progress", "Could not open partition %1 while trying to resize/move it.", partition.deviceNode()); - - return rval; -} - -bool LibPartedPartitionTable::clobberFileSystem(Report& report, const Partition& partition) -{ - bool rval = false; - - if (PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk(), partition.firstSector())) { - if (pedPartition->type == PED_PARTITION_NORMAL || pedPartition->type == PED_PARTITION_LOGICAL) { - if (ped_device_open(pedDevice())) { - //reiser4 stores "ReIsEr4" at sector 128 with a sector size of 512 bytes - - // We need to use memset instead of = {0} because clang sucks. - const long long zeroes_length = pedDevice()->sector_size*129; - char* zeroes = new char[zeroes_length]; - memset(zeroes, 0, zeroes_length*sizeof(char)); - - rval = ped_geometry_write(&pedPartition->geom, zeroes, 0, 129); - delete[] zeroes; - - if (!rval) - report.line() << xi18nc("@info:progress", "Failed to erase filesystem signature on partition %1.", partition.deviceNode()); - - ped_device_close(pedDevice()); - } - } else - rval = true; - } else - report.line() << xi18nc("@info:progress", "Could not delete file system on partition %1: Failed to get partition.", partition.deviceNode()); - - return rval; -} - -#if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT -static void pedTimerHandler(PedTimer* pedTimer, void*) -{ - CoreBackendManager::self()->backend()->emitProgress(pedTimer->frac * 100); -} -#endif - -bool LibPartedPartitionTable::resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) -{ - bool rval = false; - -#if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT - if (PedGeometry* originalGeometry = ped_geometry_new(pedDevice(), partition.fileSystem().firstSector(), partition.fileSystem().length())) { - if (PedFileSystem* pedFileSystem = ped_file_system_open(originalGeometry)) { - if (PedGeometry* resizedGeometry = ped_geometry_new(pedDevice(), partition.fileSystem().firstSector(), newLength)) { - PedTimer* pedTimer = ped_timer_new(pedTimerHandler, nullptr); - rval = ped_file_system_resize(pedFileSystem, resizedGeometry, pedTimer); - ped_timer_destroy(pedTimer); - - if (!rval) - report.line() << xi18nc("@info:progress", "Could not resize file system on partition %1.", partition.deviceNode()); - ped_geometry_destroy(resizedGeometry); - } else - report.line() << xi18nc("@info:progress", "Could not get geometry for resized partition %1 while trying to resize the file system.", partition.deviceNode()); - - ped_file_system_close(pedFileSystem); - } else - report.line() << xi18nc("@info:progress", "Could not open partition %1 while trying to resize the file system.", partition.deviceNode()); - ped_geometry_destroy(originalGeometry); - } else - report.line() << xi18nc("@info:progress", "Could not read geometry for partition %1 while trying to resize the file system.", partition.deviceNode()); -#else - Q_UNUSED(report) - Q_UNUSED(partition) - Q_UNUSED(newLength) -#endif - - return rval; -} - -FileSystem::Type LibPartedPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector) -{ - PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk(), sector); - - char* pedPath = ped_partition_get_path(pedPartition); - FileSystem::Type type = FileSystem::Unknown; - if (pedPartition && pedPath) - type = CoreBackendManager::self()->backend()->detectFileSystem(QString::fromLocal8Bit(pedPath)); - else - report.line() << xi18nc("@info:progress", "Could not determine file system of partition at sector %1 on device %2.", sector, device.deviceNode()); - free(pedPath); - - return type; -} - -bool LibPartedPartitionTable::setPartitionSystemType(Report& report, const Partition& partition) -{ - PedFileSystemType* pedFsType = (partition.roles().has(PartitionRole::Extended) || partition.fileSystem().type() == FileSystem::Unformatted) ? nullptr : getPedFileSystemType(partition.fileSystem().type()); - if (pedFsType == nullptr) { - report.line() << xi18nc("@info:progress", "Could not update the system type for partition %1.", partition.deviceNode()); - report.line() << xi18nc("@info:progress", "No file system defined."); - return false; - } - - PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk(), partition.firstSector()); - if (pedPartition == nullptr) { - report.line() << xi18nc("@info:progress", "Could not update the system type for partition %1.", partition.deviceNode()); - report.line() << xi18nc("@info:progress", "No partition found at sector %1.", partition.firstSector()); - return false; - } - - return ped_partition_set_system(pedPartition, pedFsType) != 0; -} - -bool LibPartedPartitionTable::setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) -{ - PedPartition* pedPartition; - if (partition.roles().has(PartitionRole::Extended)) - pedPartition = ped_disk_extended_partition(pedDisk()); - else - pedPartition = ped_disk_get_partition_by_sector(pedDisk(), partition.firstSector()); - if (pedPartition == nullptr) { - QString deviceNode = QString::fromUtf8(pedDevice()->path); - report.line() << xi18nc("@info:progress", "Could not find partition %1 on device %2 to set partition flags.", partition.deviceNode(), deviceNode); - return false; - } - - const PedPartitionFlag f = LibPartedBackend::getPedFlag(flag); - - // ignore flags that don't exist for this partition - if (!ped_partition_is_flag_available(pedPartition, f)) { - report.line() << xi18nc("@info:progress", "The flag \"%1\" is not available on the partition's partition table.", PartitionTable::flagName(flag)); - return true; - } - - // Workaround: libparted claims the hidden flag is available for extended partitions, but - // throws an error when we try to set or clear it. So skip this combination. - if (pedPartition->type == PED_PARTITION_EXTENDED && flag == PartitionTable::FlagHidden) - return true; - - if (!ped_partition_set_flag(pedPartition, f, state ? 1 : 0)) - return false; - - return true; -} diff --git a/src/plugins/libparted/libpartedpartitiontable.h b/src/plugins/libparted/libpartedpartitiontable.h deleted file mode 100644 index 268d0bb..0000000 --- a/src/plugins/libparted/libpartedpartitiontable.h +++ /dev/null @@ -1,68 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010, 2011 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_LIBPARTEDPARTITIONTABLE_H) - -#define KPMCORE_LIBPARTEDPARTITIONTABLE_H - -#include "backend/corebackendpartitiontable.h" - -#include "fs/filesystem.h" - -#include - -#include - -class CoreBackendPartition; -class Report; -class Partition; - -class LibPartedPartitionTable : public CoreBackendPartitionTable -{ -public: - LibPartedPartitionTable(PedDevice* device); - ~LibPartedPartitionTable(); - -public: - bool open() override; - - bool commit(quint32 timeout = 10) override; - static bool commit(PedDisk* pd, quint32 timeout = 10); - - QString createPartition(Report& report, const Partition& partition) override; - bool deletePartition(Report& report, const Partition& partition) override; - bool updateGeometry(Report& report, const Partition& partition, qint64 sector_start, qint64 sector_end) override; - bool clobberFileSystem(Report& report, const Partition& partition) override; - bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) override; - FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) override; - bool setPartitionSystemType(Report& report, const Partition& partition) override; - bool setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) override; - -private: - PedDevice* pedDevice() { - return m_PedDevice; - } - PedDisk* pedDisk() { - return m_PedDisk; - } - -private: - PedDevice* m_PedDevice; - PedDisk* m_PedDisk; -}; - -#endif diff --git a/src/plugins/libparted/pedflags.h b/src/plugins/libparted/pedflags.h deleted file mode 100644 index 3687a18..0000000 --- a/src/plugins/libparted/pedflags.h +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * Copyright (C) 2008, 2010 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 .* - *************************************************************************/ - -#ifndef KPMCORE_PEDFLAGS_H -#define KPMCORE_PEDFLAGS_H - -static struct { - PedPartitionFlag pedFlag; - PartitionTable::Flag flag; -} flagmap[] = { - { PED_PARTITION_BOOT, PartitionTable::FlagBoot }, - { PED_PARTITION_ROOT, PartitionTable::FlagRoot }, - { PED_PARTITION_SWAP, PartitionTable::FlagSwap }, - { PED_PARTITION_HIDDEN, PartitionTable::FlagHidden }, - { PED_PARTITION_RAID, PartitionTable::FlagRaid }, - { PED_PARTITION_LVM, PartitionTable::FlagLvm }, - { PED_PARTITION_LBA, PartitionTable::FlagLba }, - { PED_PARTITION_HPSERVICE, PartitionTable::FlagHpService }, - { PED_PARTITION_PALO, PartitionTable::FlagPalo }, - { PED_PARTITION_PREP, PartitionTable::FlagPrep }, - { PED_PARTITION_MSFT_RESERVED, PartitionTable::FlagMsftReserved }, - { PED_PARTITION_BIOS_GRUB, PartitionTable::FlagBiosGrub }, - { PED_PARTITION_APPLE_TV_RECOVERY, PartitionTable::FlagAppleTvRecovery }, - { PED_PARTITION_DIAG, PartitionTable::FlagDiag }, // generic diagnostics flag - { PED_PARTITION_LEGACY_BOOT, PartitionTable::FlagLegacyBoot }, - { PED_PARTITION_MSFT_DATA, PartitionTable::FlagMsftData }, - { PED_PARTITION_IRST, PartitionTable::FlagIrst }, // Intel Rapid Start partition - { PED_PARTITION_ESP, PartitionTable::FlagEsp } // EFI system -}; - -#endif diff --git a/src/plugins/libparted/pmlibpartedbackendplugin.json b/src/plugins/libparted/pmlibpartedbackendplugin.json deleted file mode 100644 index ffce7f7..0000000 --- a/src/plugins/libparted/pmlibpartedbackendplugin.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "KPlugin": { - "Authors": [ - { - "Email": "vl@fidra.de", - "Name": "Volker Lanz", - "Name[x-test]": "xxVolker Lanzxx" - } - ], - "Category": "BackendPlugin", - "Description": "The LibParted backend for KDE Partition Manager", - "Description[ca@valencia]": "Un dorsal pel LibParted del gestor de particions del KDE", - "Description[ca]": "Un dorsal pel LibParted del gestor de particions del KDE", - "Description[cs]": "Podpůrná vrstva LibParted pro správce diskových oddílů pro KDE", - "Description[de]": "Das LibParted-Backend für die KDE-Partitionsverwaltung", - "Description[es]": "El motor LibParted para el gestor de particiones de KDE", - "Description[gl]": "A infraestrutura de LibPartes para o xestor de particións de KDE.", - "Description[it]": "Il motore LibParted del gestore delle partizioni di KDE", - "Description[nl]": "De LibParted-backend voor de KDE-partitiebeheerder", - "Description[pl]": "Silnik LibParted dla zarządzania partycjami", - "Description[pt]": "A infra-estrutura da LibParted para o Gestor de Partições do KDE", - "Description[sk]": "LibParted - program v pozadí pre KDE Správcu partícií", - "Description[sv]": "LibParted bakgrundsprogrammet till KDE:s partitionshanterare", - "Description[uk]": "Сервер LibParted для Керування розділами KDE", - "Description[x-test]": "xxThe LibParted backend for KDE Partition Managerxx", - "Description[zh_CN]": "KDE 分区管理器的 LibParted 后端", - "EnabledByDefault": true, - "Icon": "preferences-plugin", - "Id": "pmlibpartedbackendplugin", - "License": "GPL", - "Name": "KDE Partition Manager LibParted Backend", - "Name[ca@valencia]": "Dorsal pel LibParted del gestor de particions del KDE", - "Name[ca]": "Dorsal pel LibParted del gestor de particions del KDE", - "Name[cs]": "Podpůrná vrstva LibParted pro správce diskových oddílů pro KDE", - "Name[de]": "KDE-Partitionsverwaltung LibParted-Backend", - "Name[es]": "Motor LibParted para el gestor de particiones de KDE", - "Name[fi]": "KDE:n osionhallinnan LibParted-taustaosa", - "Name[fr]": "Moteur LibParted pour le gestionnaire de partitions de KDE", - "Name[gl]": "Infraestrutura de LibPartes para o xestor de particións de KDE", - "Name[id]": "Backend LibParted Pengelola Parisi KDE", - "Name[it]": "Motore LibParted del gestore delle partizioni di KDE", - "Name[nb]": "KDE partisjonsbehandler, bakgrunnsmotor for libparted", - "Name[nl]": "KDE-partitiebeheerder LibParted-backend", - "Name[pl]": "Silnik LibParted zarządzania partycjami", - "Name[pt]": "Infra-Estrutura da LibParted para o Gestor de Partições do KDE", - "Name[sk]": "LibParted - program v pozadí pre KDE Správcu partícií", - "Name[sv]": "KDE:s partitionshanterare LibParted bakgrundsprogram", - "Name[uk]": "Сервер LibParted Керування розділами KDE", - "Name[x-test]": "xxKDE Partition Manager LibParted Backendxx", - "Name[zh_CN]": "KDE 分区管理器 LibParted 后端", - "ServiceTypes": [ - "PartitionManager/Plugin" - ], - "Version": "1", - "Website": "http://www.partitionmanager.org" - } -} diff --git a/src/plugins/sfdisk/CMakeLists.txt b/src/plugins/sfdisk/CMakeLists.txt index 6b7df1a..d910c3f 100644 --- a/src/plugins/sfdisk/CMakeLists.txt +++ b/src/plugins/sfdisk/CMakeLists.txt @@ -22,6 +22,6 @@ set (pmsfdiskbackendplugin_SRCS add_library(pmsfdiskbackendplugin SHARED ${pmsfdiskbackendplugin_SRCS}) -target_link_libraries(pmsfdiskbackendplugin kpmcore) +target_link_libraries(pmsfdiskbackendplugin kpmcore KF5::I18n KF5::CoreAddons) install(TARGETS pmsfdiskbackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) diff --git a/src/plugins/sfdisk/sfdiskbackend.cpp b/src/plugins/sfdisk/sfdiskbackend.cpp index bd641e6..31e7dd8 100644 --- a/src/plugins/sfdisk/sfdiskbackend.cpp +++ b/src/plugins/sfdisk/sfdiskbackend.cpp @@ -25,6 +25,7 @@ #include "core/lvmdevice.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" +#include "core/raid/softwareraid.h" #include "fs/filesystemfactory.h" #include "fs/luks.h" @@ -105,7 +106,8 @@ QList SfdiskBackend::scanDevices(bool excludeReadOnly) } } - LvmDevice::scanSystemLVM(result); + SoftwareRAID::scanSoftwareRAID(result); + LvmDevice::scanSystemLVM(result); // LVM scanner needs all other devices, so should be last } return result; @@ -126,61 +128,66 @@ Device* SfdiskBackend::scanDevice(const QString& deviceNode) ExternalCommand sizeCommand2(QStringLiteral("blockdev"), { QStringLiteral("--getss"), deviceNode }); ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), deviceNode } ); - if ( modelCommand.run(-1) && modelCommand.exitCode() == 0 - && sizeCommand.run(-1) && sizeCommand.exitCode() == 0 - && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 - && jsonCommand.run(-1) ) + if ( sizeCommand.run(-1) && sizeCommand.exitCode() == 0 + && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 + && jsonCommand.run(-1) ) { - QString modelName = modelCommand.output(); - modelName = modelName.left(modelName.length() - 1); + Device* d = nullptr; qint64 deviceSize = sizeCommand.output().trimmed().toLongLong(); - - Log(Log::information) << xi18nc("@info:status", "Device found: %1", modelName); int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong(); - DiskDevice* d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); - if (jsonCommand.exitCode() != 0) - return d; + QFile mdstat(QStringLiteral("/proc/mdstat")); - const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object(); - const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject(); + if (mdstat.open(QIODevice::ReadOnly)) { + QTextStream stream(&mdstat); - QString tableType = partitionTable[QLatin1String("label")].toString(); - const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); + QString content = stream.readAll(); - qint64 firstUsableSector = 0, lastUsableSector = d->totalSectors(); - if (type == PartitionTable::gpt) { - firstUsableSector = partitionTable[QLatin1String("firstlba")].toVariant().toLongLong(); - lastUsableSector = partitionTable[QLatin1String("lastlba")].toVariant().toLongLong(); - } - if (lastUsableSector < firstUsableSector) { - return nullptr; - } + mdstat.close(); + + QRegularExpression re(QStringLiteral("md([\\/\\w]+)\\s+:")); + QRegularExpressionMatchIterator i = re.globalMatch(content); + + while (i.hasNext()) { + + QRegularExpressionMatch reMatch = i.next(); + + QString name = reMatch.captured(1); + + if ((QStringLiteral("/dev/md") + name) == deviceNode) { + Log(Log::Level::information) << xi18nc("@info:status", "Software RAID Device found: %1", deviceNode); + + d = new SoftwareRAID( QStringLiteral("md") + name, SoftwareRAID::Status::Active ); + + break; + } - setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector)); - switch (type) { - case PartitionTable::gpt: - { - // Read the maximum number of GPT partitions - qint32 maxEntries; - ExternalCommand ddCommand(QStringLiteral("dd"), { QStringLiteral("skip=1"), QStringLiteral("count=1"), QStringLiteral("if=") + deviceNode}, QProcess::SeparateChannels); - if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) { - QByteArray gptHeader = ddCommand.rawOutput(); - QByteArray gptMaxEntries = gptHeader.mid(80, 4); - QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly); - stream.setByteOrder(QDataStream::LittleEndian); - stream >> maxEntries; } - else - maxEntries = 128; - CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxEntries); - } - default: - break; } - scanDevicePartitions(*d, partitionTable[QLatin1String("partitions")].toArray()); - return d; + if ( d == nullptr && modelCommand.run(-1) && modelCommand.exitCode() == 0 ) + { + QString modelName = modelCommand.output(); + modelName = modelName.left(modelName.length() - 1); + + Log(Log::Level::information) << xi18nc("@info:status", "Disk Device found: %1", modelName); + + d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); + } + + if ( d ) + { + if (jsonCommand.exitCode() != 0) + return d; + + const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object(); + const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject(); + + if (!updateDevicePartitionTable(*d, partitionTable)) + return nullptr; + + return d; + } } else { @@ -189,7 +196,6 @@ Device* SfdiskBackend::scanDevice(const QString& deviceNode) if (checkVG.run(-1) && checkVG.exitCode() == 0) { - qDebug() << "Trying to find LVM VG"; QList availableDevices = scanDevices(); LvmDevice::scanSystemLVM(availableDevices); @@ -226,13 +232,13 @@ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartit else if (partitionType == QStringLiteral("21686148-6449-6E6F-744E-656564454649")) activeFlags = PartitionTable::FlagBiosGrub; - FileSystem::Type type = FileSystem::Unknown; + FileSystem::Type type = FileSystem::Type::Unknown; type = detectFileSystem(partitionNode); PartitionRole::Roles r = PartitionRole::Primary; if ( (d.partitionTable()->type() == PartitionTable::msdos || d.partitionTable()->type() == PartitionTable::msdos_sectorbased) && partitionType.toInt() == 5 ) { r = PartitionRole::Extended; - type = FileSystem::Extended; + type = FileSystem::Type::Extended; } // Find an extended partition this partition is in. @@ -250,7 +256,7 @@ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartit QString mountPoint; bool mounted; // sfdisk does not handle LUKS partitions - if (fs->type() == FileSystem::Luks || fs->type() == FileSystem::Luks2) { + if (fs->type() == FileSystem::Type::Luks || fs->type() == FileSystem::Type::Luks2) { r |= PartitionRole::Luks; FS::luks* luksFs = static_cast(fs); luksFs->initLUKS(); @@ -291,13 +297,71 @@ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartit PartitionAlignment::isAligned(d, *part); } +bool SfdiskBackend::updateDevicePartitionTable(Device &d, const QJsonObject &jsonPartitionTable) +{ + QString tableType = jsonPartitionTable[QLatin1String("label")].toString(); + const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); + + qint64 firstUsableSector = 0, lastUsableSector; + + if ( d.type() == Device::Type::Disk_Device ) + { + const DiskDevice* diskDevice = static_cast(&d); + + lastUsableSector = diskDevice->totalSectors(); + } + else if ( d.type() == Device::Type::SoftwareRAID_Device ) + { + const SoftwareRAID* raidDevice = static_cast(&d); + + lastUsableSector = raidDevice->totalLogical() - 1; + } + + if (type == PartitionTable::gpt) { + firstUsableSector = jsonPartitionTable[QLatin1String("firstlba")].toVariant().toLongLong(); + lastUsableSector = jsonPartitionTable[QLatin1String("lastlba")].toVariant().toLongLong(); + } + + if (lastUsableSector < firstUsableSector) { + return false; + } + + setPartitionTableForDevice(d, new PartitionTable(type, firstUsableSector, lastUsableSector)); + switch (type) { + case PartitionTable::gpt: + { + // Read the maximum number of GPT partitions + qint32 maxEntries; + ExternalCommand ddCommand(QStringLiteral("dd"), + { QStringLiteral("skip=1"), QStringLiteral("count=1"), (QStringLiteral("if=") + d.deviceNode()) }, + QProcess::SeparateChannels); + if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) { + QByteArray gptHeader = ddCommand.rawOutput(); + QByteArray gptMaxEntries = gptHeader.mid(80, 4); + QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream >> maxEntries; + } + else + maxEntries = 128; + CoreBackend::setPartitionTableMaxPrimaries(*d.partitionTable(), maxEntries); + } + default: + break; + } + + scanDevicePartitions(d, jsonPartitionTable[QLatin1String("partitions")].toArray()); + + return true; +} + /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ void SfdiskBackend::readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint) { - if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::LinuxSwap && p.fileSystem().type() != FileSystem::Lvm2_PV) { + if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::Type::LinuxSwap && p.fileSystem().type() != FileSystem::Type::Lvm2_PV) { const QStorageInfo storage = QStorageInfo(mountPoint); if (p.isMounted() && storage.isValid()) p.fileSystem().setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / d.logicalSize()); @@ -308,7 +372,7 @@ void SfdiskBackend::readSectorsUsed(const Device& d, Partition& p, const QString FileSystem::Type SfdiskBackend::detectFileSystem(const QString& partitionPath) { - FileSystem::Type rval = FileSystem::Unknown; + FileSystem::Type rval = FileSystem::Type::Unknown; ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), @@ -331,43 +395,44 @@ FileSystem::Type SfdiskBackend::detectFileSystem(const QString& partitionPath) version = reFileSystemVersion.captured(1); } - if (s == QStringLiteral("ext2")) rval = FileSystem::Ext2; - else if (s == QStringLiteral("ext3")) rval = FileSystem::Ext3; - else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Ext4; - else if (s == QStringLiteral("swap")) rval = FileSystem::LinuxSwap; - else if (s == QStringLiteral("ntfs-3g")) rval = FileSystem::Ntfs; - else if (s == QStringLiteral("reiserfs")) rval = FileSystem::ReiserFS; - else if (s == QStringLiteral("reiser4")) rval = FileSystem::Reiser4; - else if (s == QStringLiteral("xfs")) rval = FileSystem::Xfs; - else if (s == QStringLiteral("jfs")) rval = FileSystem::Jfs; - else if (s == QStringLiteral("hfs")) rval = FileSystem::Hfs; - else if (s == QStringLiteral("hfsplus")) rval = FileSystem::HfsPlus; - else if (s == QStringLiteral("ufs")) rval = FileSystem::Ufs; + if (s == QStringLiteral("ext2")) rval = FileSystem::Type::Ext2; + else if (s == QStringLiteral("ext3")) rval = FileSystem::Type::Ext3; + else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Type::Ext4; + else if (s == QStringLiteral("swap")) rval = FileSystem::Type::LinuxSwap; + else if (s == QStringLiteral("ntfs")) rval = FileSystem::Type::Ntfs; + else if (s == QStringLiteral("reiserfs")) rval = FileSystem::Type::ReiserFS; + else if (s == QStringLiteral("reiser4")) rval = FileSystem::Type::Reiser4; + else if (s == QStringLiteral("xfs")) rval = FileSystem::Type::Xfs; + else if (s == QStringLiteral("jfs")) rval = FileSystem::Type::Jfs; + else if (s == QStringLiteral("hfs")) rval = FileSystem::Type::Hfs; + else if (s == QStringLiteral("hfsplus")) rval = FileSystem::Type::HfsPlus; + else if (s == QStringLiteral("ufs")) rval = FileSystem::Type::Ufs; else if (s == QStringLiteral("vfat")) { if (version == QStringLiteral("FAT32")) - rval = FileSystem::Fat32; + rval = FileSystem::Type::Fat32; else if (version == QStringLiteral("FAT16")) - rval = FileSystem::Fat16; + rval = FileSystem::Type::Fat16; else if (version == QStringLiteral("FAT12")) - rval = FileSystem::Fat12; + rval = FileSystem::Type::Fat12; } - else if (s == QStringLiteral("btrfs")) rval = FileSystem::Btrfs; - else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Ocfs2; - else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Zfs; - else if (s == QStringLiteral("hpfs")) rval = FileSystem::Hpfs; + else if (s == QStringLiteral("btrfs")) rval = FileSystem::Type::Btrfs; + else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Type::Ocfs2; + else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Type::Zfs; + else if (s == QStringLiteral("hpfs")) rval = FileSystem::Type::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) { if (version == QStringLiteral("1")) - rval = FileSystem::Luks; + rval = FileSystem::Type::Luks; else if (version == QStringLiteral("2")) { - rval = FileSystem::Luks2; + rval = FileSystem::Type::Luks2; } } - else if (s == QStringLiteral("exfat")) rval = FileSystem::Exfat; - else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; - else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; - else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; - else if (s == QStringLiteral("udf")) rval = FileSystem::Udf; - else if (s == QStringLiteral("iso9660")) rval = FileSystem::Iso9660; + else if (s == QStringLiteral("exfat")) rval = FileSystem::Type::Exfat; + else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Type::Nilfs2; + else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Type::Lvm2_PV; + else if (s == QStringLiteral("f2fs")) rval = FileSystem::Type::F2fs; + else if (s == QStringLiteral("udf")) rval = FileSystem::Type::Udf; + else if (s == QStringLiteral("iso9660")) rval = FileSystem::Type::Iso9660; + else if (s == QStringLiteral("linux_raid_member")) rval = FileSystem::Type::LinuxRaidMember; else qWarning() << "unknown file system type " << s << " on " << partitionPath; } @@ -420,31 +485,27 @@ PartitionTable::Flags SfdiskBackend::availableFlags(PartitionTable::TableType ty return flags; } -CoreBackendDevice* SfdiskBackend::openDevice(const Device& d) +std::unique_ptr SfdiskBackend::openDevice(const Device& d) { - SfdiskDevice* device = new SfdiskDevice(d); + std::unique_ptr device = std::make_unique(d); - if (device == nullptr || !device->open()) { - delete device; + if (!device->open()) device = nullptr; - } return device; } -CoreBackendDevice* SfdiskBackend::openDeviceExclusive(const Device& d) +std::unique_ptr SfdiskBackend::openDeviceExclusive(const Device& d) { - SfdiskDevice* device = new SfdiskDevice(d); + std::unique_ptr device = std::make_unique(d); - if (device == nullptr || !device->openExclusive()) { - delete device; + if (!device->openExclusive()) device = nullptr; - } return device; } -bool SfdiskBackend::closeDevice(CoreBackendDevice* coreDevice) +bool SfdiskBackend::closeDevice(std::unique_ptr coreDevice) { return coreDevice->close(); } diff --git a/src/plugins/sfdisk/sfdiskbackend.h b/src/plugins/sfdisk/sfdiskbackend.h index 5cb3291..dcc7952 100644 --- a/src/plugins/sfdisk/sfdiskbackend.h +++ b/src/plugins/sfdisk/sfdiskbackend.h @@ -47,9 +47,9 @@ public: void initFSSupport() override; QList scanDevices(bool excludeReadOnly = false) override; - CoreBackendDevice* openDevice(const Device& d) override; - CoreBackendDevice* openDeviceExclusive(const Device& d) override; - bool closeDevice(CoreBackendDevice* coreDevice) override; + std::unique_ptr openDevice(const Device& d) override; + std::unique_ptr openDeviceExclusive(const Device& d) override; + bool closeDevice(std::unique_ptr coreDevice) override; Device* scanDevice(const QString& deviceNode) override; FileSystem::Type detectFileSystem(const QString& partitionPath) override; QString readLabel(const QString& deviceNode) const override; @@ -58,6 +58,7 @@ public: private: static void readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint); void scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions); + bool updateDevicePartitionTable(Device& d, const QJsonObject& jsonPartitionTable); static PartitionTable::Flags availableFlags(PartitionTable::TableType type); }; diff --git a/src/plugins/sfdisk/sfdiskdevice.cpp b/src/plugins/sfdisk/sfdiskdevice.cpp index 2869a79..b866e7e 100644 --- a/src/plugins/sfdisk/sfdiskdevice.cpp +++ b/src/plugins/sfdisk/sfdiskdevice.cpp @@ -58,9 +58,9 @@ bool SfdiskDevice::close() return true; } -CoreBackendPartitionTable* SfdiskDevice::openPartitionTable() +std::unique_ptr SfdiskDevice::openPartitionTable() { - return new SfdiskPartitionTable(m_device); + return std::make_unique(m_device); } bool SfdiskDevice::createPartitionTable(Report& report, const PartitionTable& ptable) @@ -72,47 +72,10 @@ bool SfdiskDevice::createPartitionTable(Report& report, const PartitionTable& pt tableType = ptable.typeName().toLocal8Bit(); ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { m_device->deviceNode() } ); - if ( createCommand.start(-1) && createCommand.write(QByteArrayLiteral("label: ") + tableType + - QByteArrayLiteral("\nwrite\n")) && createCommand.waitFor() ) { + if ( createCommand.write(QByteArrayLiteral("label: ") + tableType + + QByteArrayLiteral("\nwrite\n")) && createCommand.start(-1) ) { return createCommand.output().contains(QStringLiteral("Script header accepted.")); } return false; } - -bool SfdiskDevice::readData(QByteArray& buffer, qint64 offset, qint64 size) -{ - if (!isExclusive()) - return false; - - ExternalCommand ddCommand(QStringLiteral("dd"), { - QStringLiteral("skip=") + QString::number(offset), - QStringLiteral("bs=") + QString::number(size), - QStringLiteral("count=1"), - QStringLiteral("iflag=skip_bytes"), - QStringLiteral("if=") + m_device->deviceNode() }, QProcess::SeparateChannels); - if (ddCommand.run(-1) && ddCommand.exitCode() == 0) { - buffer = ddCommand.rawOutput(); - return true; - } - - return false; -} - -bool SfdiskDevice::writeData(QByteArray& buffer, qint64 offset) -{ - if (!isExclusive()) - return false; - - ExternalCommand ddCommand(QStringLiteral("dd"), { - QStringLiteral("of=") + m_device->deviceNode(), - QStringLiteral("seek=") + QString::number(offset), - QStringLiteral("bs=1M"), - QStringLiteral("oflag=seek_bytes"), - QStringLiteral("conv=fsync") }, QProcess::SeparateChannels); - if ( ddCommand.start(-1) && ddCommand.write(buffer) == buffer.size() && ddCommand.waitFor() && ddCommand.exitCode() == 0 ) { - return true; - } - - return false; -} diff --git a/src/plugins/sfdisk/sfdiskdevice.h b/src/plugins/sfdisk/sfdiskdevice.h index aeab327..eab4a12 100644 --- a/src/plugins/sfdisk/sfdiskdevice.h +++ b/src/plugins/sfdisk/sfdiskdevice.h @@ -42,13 +42,10 @@ public: bool openExclusive() override; bool close() override; - CoreBackendPartitionTable* openPartitionTable() override; + std::unique_ptr openPartitionTable() override; bool createPartitionTable(Report& report, const PartitionTable& ptable) override; - bool readData(QByteArray& buffer, qint64 offset, qint64 size) override; - bool writeData(QByteArray& buffer, qint64 offset) override; - private: const Device *m_device; }; diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 62c4417..470e783 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -22,14 +22,13 @@ #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 @@ -54,9 +53,15 @@ bool SfdiskPartitionTable::open() bool SfdiskPartitionTable::commit(quint32 timeout) { - if ( !(ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run() && - ExternalCommand(QStringLiteral("partx"), { QStringLiteral("--update"), m_device->deviceNode() }).run()) ) - sleep(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(); return true; } @@ -74,13 +79,18 @@ QString SfdiskPartitionTable::createPartition(Report& report, const Partition& p // NOTE: at least on GPT partition types "are" partition flags ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--append"), partition.devicePath() } ); - if ( createCommand.start(-1) && createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) + + if ( createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) + type + - QByteArrayLiteral(" size=") + QByteArray::number(partition.length()) + QByteArrayLiteral("\nwrite\n")) && createCommand.waitFor() ) { - QRegularExpression re(QStringLiteral("Created a new partition (\\d)")); + 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()) - return partition.devicePath() + rem.captured(1); + + 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()); @@ -101,10 +111,10 @@ bool SfdiskPartitionTable::deletePartition(Report& report, const Partition& part 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.start(-1) && sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) + + if ( sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) + QByteArrayLiteral(" size=") + QByteArray::number(sectorEnd - sectorStart + 1) + QByteArrayLiteral("\nY\n")) - && sfdiskCommand.waitFor() && sfdiskCommand.exitCode() == 0) { + && sfdiskCommand.start(-1) && sfdiskCommand.exitCode() == 0) { return true; } @@ -135,7 +145,7 @@ bool SfdiskPartitionTable::resizeFileSystem(Report& report, const Partition& par FileSystem::Type SfdiskPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector) { - FileSystem::Type type = FileSystem::Unknown; + FileSystem::Type type = FileSystem::Type::Unknown; ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), device.deviceNode() } ); if (jsonCommand.run(-1) && jsonCommand.exitCode() == 0) { @@ -160,24 +170,24 @@ static struct { FileSystem::Type type; QLatin1String partitionType[2]; // GPT, MBR } typemap[] = { - { FileSystem::Btrfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Ext2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Ext3, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Ext4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::LinuxSwap, { QLatin1String("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"), QLatin1String("82") } }, - { FileSystem::Fat12, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, - { FileSystem::Fat16, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, - { FileSystem::Fat32, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, - { FileSystem::Nilfs2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Ntfs, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, - { FileSystem::Exfat, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, - { FileSystem::ReiserFS, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Reiser4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Xfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Jfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, - { FileSystem::Hfs, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af")} }, - { FileSystem::HfsPlus, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af") } }, - { FileSystem::Udf, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } } + { 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 }; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index c8b2ea3..4d1fd5a 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -16,3 +16,25 @@ set(UTIL_LIB_HDRS util/htmlreport.h util/report.h ) + +qt5_generate_dbus_interface( + externalcommand_interface.h + org.kde.kpmcore.externalcommand.xml +) + +add_executable(kpmcore_externalcommand util/externalcommandhelper.cpp) + +target_link_libraries(kpmcore_externalcommand + qca-qt5 + Qt5::Core + Qt5::DBus + KF5::Auth + KF5::I18n +) + +install(TARGETS kpmcore_externalcommand DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) +install( FILES util/org.kde.kpmcore.helperinterface.conf DESTINATION ${SYSCONF_INSTALL_DIR}/dbus-1/system.d ) +install( FILES util/org.kde.kpmcore.applicationinterface.conf DESTINATION ${SYSCONF_INSTALL_DIR}/dbus-1/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) diff --git a/src/util/capacity.cpp b/src/util/capacity.cpp index 9e8d6fa..8edde69 100644 --- a/src/util/capacity.cpp +++ b/src/util/capacity.cpp @@ -44,9 +44,9 @@ Capacity::Capacity(const Partition& p, Type t) : m_Size(-1) { switch (t) { - case Used: m_Size = p.used(); break; - case Available: m_Size = p.available(); break; - case Total: m_Size = p.capacity(); + case Type::Used: m_Size = p.used(); break; + case Type::Available: m_Size = p.available(); break; + case Type::Total: m_Size = p.capacity(); } } @@ -64,7 +64,7 @@ Capacity::Capacity(const Device& d) : */ qint64 Capacity::toInt(Unit u) const { - return static_cast(m_Size / unitFactor(Byte, u)); + return static_cast(m_Size / unitFactor(Unit::Byte, u)); } /** Returns the Capacity as double converted to the given Unit. @@ -73,7 +73,7 @@ qint64 Capacity::toInt(Unit u) const */ double Capacity::toDouble(Unit u) const { - return static_cast(m_Size) / unitFactor(Byte, u); + return static_cast(m_Size) / unitFactor(Unit::Byte, u); } /** Returns a factor to convert between two Units. @@ -86,14 +86,14 @@ qint64 Capacity::unitFactor(Unit from, Unit to) Q_ASSERT(from <= to); if (from > to) { - qWarning() << "from: " << from << ", to: " << to; + qWarning() << "from: " << static_cast(from) << ", to: " << static_cast(to); return 1; } qint64 result = 1; - qint32 a = from; - qint32 b = to; + qint32 a = static_cast(from); + qint32 b = static_cast(to); while (b-- > a) result *= 1024; @@ -122,7 +122,7 @@ QString Capacity::unitName(Unit u, qint64 val) if (static_cast(u) >= sizeof(unitNames) / sizeof(unitNames[0])) return xi18nc("@item:intext unit", "(unknown unit)"); - return unitNames[u]; + return unitNames[static_cast(u)]; } /** Determine if the capacity is valid. diff --git a/src/util/capacity.h b/src/util/capacity.h index 023bcfd..1346080 100644 --- a/src/util/capacity.h +++ b/src/util/capacity.h @@ -15,9 +15,9 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_CAPACITY_H) - +#ifndef KPMCORE_CAPACITY_H #define KPMCORE_CAPACITY_H + #include "util/libpartitionmanagerexport.h" class Partition; @@ -36,16 +36,16 @@ class LIBKPMCORE_EXPORT Capacity { public: /** Units we can deal with */ - enum Unit { Byte, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB }; + enum Unit : uint { Byte, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB }; /** Type of capacity to print */ - enum Type { Used, Available, Total }; + enum class Type { Used, Available, Total }; /** Flags for printing */ - enum Flag { NoFlags = 0, AppendUnit = 1, AppendBytes = 2 }; + enum class Flag { NoFlags = 0, AppendUnit = 1, AppendBytes = 2 }; Q_DECLARE_FLAGS(Flags, Flag) public: explicit Capacity(qint64 size); - explicit Capacity(const Partition& p, Type t = Total); + explicit Capacity(const Partition& p, Type t = Type::Total); Capacity(const Device& d); public: diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index 2bbc8fc..86e10f3 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,6 +1,6 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * - * Copyright (C) 2016 by Andrius Štikonas * + * Copyright (C) 2016-2018 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 * @@ -16,29 +16,72 @@ * 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/globallog.h" #include "util/externalcommand.h" - #include "util/report.h" +#include +#include +#include +#include +#include #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; + DBusThread *m_thread; + QProcess::ProcessChannelMode processChannelMode; +}; + +KAuth::ExecuteJob* ExternalCommand::m_job; +QCA::PrivateKey* ExternalCommand::privateKey; +QCA::Initializer* ExternalCommand::init; +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) : - QProcess(), - m_Report(nullptr), - m_Command(cmd), - m_Args(args), - m_ExitCode(-1), - m_Output() + d(std::make_unique()) { - setup(processChannelMode); + d->m_Report = nullptr; + d->m_Command = cmd; + 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; } /** Creates a new ExternalCommand instance with Report. @@ -47,62 +90,164 @@ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, co @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(Report& report, const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : - QProcess(), - m_Report(report.newChild()), - m_Command(cmd), - m_Args(args), - m_ExitCode(-1), - m_Output() + d(std::make_unique()) { - setup(processChannelMode); + d->m_Report = report.newChild(); + d->m_Command = cmd; + d->m_Args = args; + d->m_ExitCode = -1; + d->m_Output = QByteArray(); + + d->processChannelMode = processChannelMode; } -void ExternalCommand::setup(const QProcess::ProcessChannelMode processChannelMode) +ExternalCommand::~ExternalCommand() { - setEnvironment(QStringList() << QStringLiteral("LC_ALL=C") << QStringLiteral("PATH=") + QString::fromLocal8Bit(getenv("PATH")) << QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1")); - setProcessChannelMode(processChannelMode); - - connect(this, qOverload(&QProcess::finished), this, &ExternalCommand::onFinished); - connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); } -/** Starts the external command. +// 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) { - QProcess::start(command(), args()); + Q_UNUSED(timeout) if (report()) { report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); } - if (!waitForStarted(timeout)) - { - if (report()) - report()->line() << xi18nc("@info:status", "(Command timeout while starting)"); + 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 system bus"; return false; } - return true; + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), + QStringLiteral("/Helper"), + QStringLiteral("org.kde.kpmcore.externalcommand"), + QDBusConnection::systemBus()); + + iface.setTimeout(10 * 24 * 3600 * 1000); // 10 days + + bool rval = false; + if (iface.isValid()) { + QByteArray request; + const quint64 nonce = getNonce(iface); + request.setNum(nonce); + request.append(cmd.toUtf8()); + for (const auto &argument : qAsConst(d->m_Args)) + request.append(argument.toUtf8()); + request.append(d->m_Input); + request.append(d->processChannelMode); + + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + + QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("start"), + privateKey->signMessage(hash, QCA::EMSA3_Raw), + nonce, + 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(); + } + + return rval; } -/** 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) +bool ExternalCommand::copyBlocks(CopySource& source, CopyTarget& target) { - closeWriteChannel(); + bool rval = true; + const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy - if (!waitForFinished(timeout)) { - if (report()) - report()->line() << xi18nc("@info:status", "(Command timeout while running)"); + if (!QDBusConnection::systemBus().isConnected()) { + qWarning() << "Could not connect to DBus system bus"; return false; } - onReadOutput(); + // 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); + + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); + iface.setTimeout(10 * 24 * 3600 * 1000); // 10 days + if (iface.isValid()) { + QByteArray request; + + const quint64 nonce = getNonce(iface); + request.setNum(nonce); + request.append(source.path().toUtf8()); + request.append(QByteArray::number(source.firstByte())); + request.append(QByteArray::number(source.length())); + request.append(target.path().toUtf8()); + request.append(QByteArray::number(target.firstByte())); + request.append(QByteArray::number(blockSize)); + + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + + // Use asynchronous DBus calls, so that we can process reports and progress + QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("copyblocks"), + privateKey->signMessage(hash, QCA::EMSA3_Raw), + nonce, + 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.argumentAt<0>(); + } + setExitCode(!rval); + }; + + connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); + loop.exec(); + } + + return rval; +} + + +bool ExternalCommand::write(const QByteArray& input) +{ + d->m_Input = input; return true; } @@ -112,27 +257,181 @@ bool ExternalCommand::waitFor(int timeout) */ bool ExternalCommand::run(int timeout) { - return start(timeout) && waitFor(timeout) && exitStatus() == 0; + return start(timeout) /* && exitStatus() == 0*/; } void ExternalCommand::onReadOutput() { - const QByteArray s = readAllStandardOutput(); +// 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); +} - 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; +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; +} + +bool ExternalCommand::startHelper() +{ + if (!QDBusConnection::systemBus().isConnected()) { + qWarning() << "Could not connect to DBus session bus"; + return false; + } + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); + if (iface.isValid()) { + exit(0); } - m_Output += s; + d->m_thread = new DBusThread; + d->m_thread->start(); - if (report()) - *report() << QString::fromLocal8Bit(s); + init = new QCA::Initializer; + // Generate RSA key pair for signing external command requests + if (!QCA::isSupported("pkey") || !QCA::PKey::supportedIOTypes().contains(QCA::PKey::RSA)) { + qCritical() << xi18n("QCA does not support RSA."); + return false; + } + + privateKey = new QCA::PrivateKey; + *privateKey = QCA::KeyGenerator().createRSA(4096); + if(privateKey->isNull()) { + qCritical() << xi18n("Failed to make private RSA key."); + return false; + } + + if (!privateKey->canSign()) { + qCritical() << xi18n("Generated key cannot be used for signatures."); + return false; + } + + QCA::PublicKey pubkey = privateKey->toPublicKey(); + + 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; + arguments.insert(QStringLiteral("pubkey"), pubkey.toDER()); + 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::onFinished(int exitCode, QProcess::ExitStatus exitStatus) +void ExternalCommand::stopHelper() { - Q_UNUSED(exitStatus) - setExitCode(exitCode); + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); + if (iface.isValid()) { + QByteArray request; + const quint64 nonce = getNonce(iface); + request.setNum(nonce); + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + iface.call(QStringLiteral("exit"), privateKey->signMessage(hash, QCA::EMSA3_Raw), nonce); + } + + delete privateKey; + delete init; +} + +quint64 ExternalCommand::getNonce(QDBusInterface& iface) +{ + QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("getNonce")); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall); + QEventLoop loop; + unsigned long long rval = 0; + + auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { + loop.exit(); + + if (watcher->isError()) + qWarning() << watcher->error(); + else { + QDBusPendingReply reply = *watcher; + rval = reply; + } + }; + + connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); + loop.exec(); + return rval; +} + +void DBusThread::run() +{ + if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface"))) { + qWarning() << QDBusConnection::systemBus().lastError().message(); + return; + } + if (!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 9b0b9cc..14b7c2f 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -1,5 +1,6 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2016-2018 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 * @@ -15,18 +16,39 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_EXTERNALCOMMAND_H) - +#ifndef KPMCORE_EXTERNALCOMMAND_H #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" +#include #include -#include #include +#include #include +#include +#include +#include + +class KJob; +namespace KAuth { class ExecuteJob; } +namespace QCA { class PrivateKey; class Initializer; } class Report; +class CopySource; +class CopyTarget; +class QDBusInterface; +struct ExternalCommandPrivate; + +class DBusThread : public QThread +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.ping") + void run() override; + +public Q_SLOTS: + Q_SCRIPTABLE void ping() {return;}; +}; /** An external command. @@ -35,57 +57,89 @@ class Report; @author Volker Lanz @author Andrius Štikonas */ -class LIBKPMCORE_EXPORT ExternalCommand : public QProcess +class LIBKPMCORE_EXPORT ExternalCommand : public QObject { + Q_OBJECT Q_DISABLE_COPY(ExternalCommand) public: - explicit ExternalCommand(const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = MergedChannels); - explicit ExternalCommand(Report& report, const QString& cmd = QString(), const QStringList& args = QStringList(), const QProcess::ProcessChannelMode processChannelMode = MergedChannels); + 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: - void setCommand(const QString& cmd) { m_Command = cmd; } /**< @param cmd the command to run */ - const QString& command() const { return m_Command; } /**< @return the command to run */ + bool copyBlocks(CopySource& source, CopyTarget& target); - void addArg(const QString& s) { m_Args << s; } /**< @param s the argument to add */ - const QStringList& args() const { return m_Args; } /**< @return the arguments */ - void setArgs(const QStringList& args) { m_Args = args; } /**< @param args the new arguments */ + /**< @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 waitFor(int timeout = 30000); bool run(int timeout = 30000); - int exitCode() const { - return m_ExitCode; /**< @return the exit code */ + /**< @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); } + + // 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; } - const QString output() const { - return QString::fromLocal8Bit(m_Output); /**< @return the command output */ - } +Q_SIGNALS: + void progress(int); + void reportSignal(const QVariantMap&); - const QByteArray& rawOutput() const { - return m_Output; /**< @return the command output */ - } - - Report* report() { - return m_Report; /**< @return pointer to the Report or nullptr */ - } - -protected: - void setExitCode(int i) { - m_ExitCode = i; - } - void setup(const QProcess::ProcessChannelMode processChannelMode); - - void onFinished(int exitCode, QProcess::ExitStatus exitStatus); - void onReadOutput(); +public Q_SLOTS: + void emitProgress(KJob*, unsigned long percent) { emit progress(percent); }; private: - Report *m_Report; - QString m_Command; - QStringList m_Args; - int m_ExitCode; - QByteArray m_Output; + void setExitCode(int i); + + void onReadOutput(); + static quint64 getNonce(QDBusInterface& iface); + +private: + std::unique_ptr d; + + // KAuth + static quint64 m_Nonce; + static KAuth::ExecuteJob *m_job; + static QCA::Initializer *init; + static QCA::PrivateKey *privateKey; + static bool helperStarted; + static QWidget *parent; }; #endif diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp new file mode 100644 index 0000000..d542324 --- /dev/null +++ b/src/util/externalcommandhelper.cpp @@ -0,0 +1,338 @@ +/************************************************************************* + * Copyright (C) 2017-2018 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 + +/** 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 DBus interface that we ping. + * If helper is not busy than it exits when ping fails. 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. + * + * This helper also starts another DBus interface where it listens to + * command execution requests from the application that started the helper. + * These requests are validated using public key cryptography, to prevent + * other unprivileged applications from gaining root privileges. +*/ +ActionReply ExternalCommandHelper::init(const QVariantMap& args) +{ + ActionReply reply; + if (!QDBusConnection::systemBus().isConnected()) { + qWarning() << "Could not connect to DBus system bus"; + reply.addData(QStringLiteral("success"), false); + return reply; + } + + if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface"))) { + qWarning() << QDBusConnection::systemBus().lastError().message(); + reply.addData(QStringLiteral("success"), false); + return reply; + } + if (!QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) { + qWarning() << QDBusConnection::systemBus().lastError().message(); + reply.addData(QStringLiteral("success"), false); + return reply; + } + + m_publicKey = QCA::PublicKey::fromDER(args[QStringLiteral("pubkey")].toByteArray()); + + HelperSupport::progressStep(QVariantMap()); + auto timeout = [this] () { + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.applicationinterface"), + QStringLiteral("/Application"), + QStringLiteral("org.kde.kpmcore.ping"), + QDBusConnection::systemBus()); + iface.setTimeout(2000); // 2 seconds; + auto pcall = iface.asyncCall(QStringLiteral("ping")); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); + auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { + if (watcher->isError()) { + qWarning() << watcher->error(); + m_loop.exit(); + } + }; + connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); + }; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, timeout); + timer->start(5000); // 5 seconds + m_loop.exec(); + reply.addData(QStringLiteral("success"), true); + + return reply; +} + +/** Generates cryptographic nonce + * @return nonce +*/ +quint64 ExternalCommandHelper::getNonce() +{ + quint64 nonce = m_Generator.generate(); + m_Nonces.insert(nonce); + return nonce; +} + +/** 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(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(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; +} + +bool ExternalCommandHelper::copyblocks(const QByteArray& signature, const quint64 nonce, const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize) +{ + if (m_Nonces.find(nonce) != m_Nonces.end()) + m_Nonces.erase( nonce ); + else + return false; + + QByteArray request; + + request.setNum(nonce); + request.append(sourceDevice.toUtf8()); + request.append(QByteArray::number(sourceFirstByte)); + request.append(QByteArray::number(sourceLength)); + request.append(targetDevice.toUtf8()); + request.append(QByteArray::number(targetFirstByte)); + request.append(QByteArray::number(blockSize)); + + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + if (!m_publicKey.verifyMessage(hash, signature, QCA::EMSA3_Raw)) { + qCritical() << xi18n("Invalid cryptographic signature"); + return false; + } + + const qint64 blocksToCopy = sourceLength / blockSize; + qint64 readOffset = sourceFirstByte; + qint64 writeOffset = targetFirstByte; + qint32 copyDirection = 1; + + 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); + + return rval; +} + +QVariantMap ExternalCommandHelper::start(const QByteArray& signature, const quint64 nonce, const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode) +{ + QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); + QVariantMap reply; + reply[QStringLiteral("success")] = true; + + if (m_Nonces.find(nonce) != m_Nonces.end()) + m_Nonces.erase( nonce ); + else { + reply[QStringLiteral("success")] = false; + return reply; + } + + QByteArray request; + request.setNum(nonce); + request.append(command.toUtf8()); + for (const auto &argument : arguments) + request.append(argument.toUtf8()); + request.append(input); + request.append(processChannelMode); + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + if (!m_publicKey.verifyMessage(hash, signature, QCA::EMSA3_Raw)) { + qCritical() << xi18n("Invalid cryptographic signature"); + reply[QStringLiteral("success")] = false; + return reply; + } + +// connect(&cmd, &QProcess::readyReadStandardOutput, this, &ExternalCommandHelper::onReadOutput); + + m_cmd.setEnvironment( { QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1") } ); + m_cmd.setProcessChannelMode(static_cast(processChannelMode)); + 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; +} + +void ExternalCommandHelper::exit(const QByteArray& signature, const quint64 nonce) +{ + QByteArray request; + if (m_Nonces.find(nonce) == m_Nonces.end()) + return; + + request.setNum(nonce); + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + if (!m_publicKey.verifyMessage(hash, signature, QCA::EMSA3_Raw)) { + qCritical() << xi18n("Invalid cryptographic signature"); + return; + } + + m_loop.exit(); + + QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper")); + QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface")); +} + +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 new file mode 100644 index 0000000..4f1cab7 --- /dev/null +++ b/src/util/externalcommandhelper.h @@ -0,0 +1,69 @@ +/************************************************************************* + * Copyright (C) 2017-2018 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 +#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(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 quint64 getNonce(); + Q_SCRIPTABLE QVariantMap start(const QByteArray& signature, const quint64 nonce, const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode); + Q_SCRIPTABLE bool copyblocks(const QByteArray& signature, const quint64 nonce, const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize); + Q_SCRIPTABLE void exit(const QByteArray& signature, const quint64 nonce); + +private: + void onReadOutput(); + + QEventLoop m_loop; + QCA::Initializer initializer; + QCA::PublicKey m_publicKey; + QRandomGenerator64 m_Generator; + std::unordered_set m_Nonces; + QString m_command; + QString m_sourceDevice; + QProcess m_cmd; + +// QByteArray output; +}; + +#endif diff --git a/src/util/globallog.h b/src/util/globallog.h index 26eef60..3cb57fd 100644 --- a/src/util/globallog.h +++ b/src/util/globallog.h @@ -15,8 +15,7 @@ * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_GLOBALLOG_H) - +#ifndef KPMCORE_GLOBALLOG_H #define KPMCORE_GLOBALLOG_H #include "util/libpartitionmanagerexport.h" @@ -28,15 +27,15 @@ class LIBKPMCORE_EXPORT Log { public: - enum Level { - debug = 0, - information = 1, - warning = 2, - error = 3 + enum class Level { + debug, + information, + warning, + error, }; public: - Log(Level lev = information) : ref(1), level(lev) {} + Log(Level lev = Level::information) : ref(1), level(lev) {} ~Log(); Log(const Log& other) : ref(other.ref + 1), level(other.level) {} diff --git a/src/util/helpers.cpp b/src/util/helpers.cpp index 87661e0..6f2cfc9 100644 --- a/src/util/helpers.cpp +++ b/src/util/helpers.cpp @@ -56,7 +56,7 @@ KAboutData aboutKPMcore() KAboutData aboutData( QStringLiteral("kpmcore"), xi18nc("@title", "KPMcore"), QStringLiteral(VERSION), xi18nc("@title", "Library for managing partitions"), - KAboutLicense::GPL_V3, xi18nc("@info:credit", "© 2008-2017 KPMcore developers" ) ); + KAboutLicense::GPL_V3, xi18nc("@info:credit", "© 2008-2018 KPMcore developers" ) ); aboutData.setOrganizationDomain(QByteArray("kde.org")); aboutData.setProductName(QByteArray("kpmcore")); aboutData.setHomepage(QStringLiteral("https://commits.kde.org/kpmcore")); @@ -67,6 +67,7 @@ KAboutData aboutKPMcore() 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")); return aboutData; } diff --git a/src/util/libpartitionmanagerexport.h b/src/util/libpartitionmanagerexport.h index 457f0e8..9459e65 100644 --- a/src/util/libpartitionmanagerexport.h +++ b/src/util/libpartitionmanagerexport.h @@ -15,15 +15,9 @@ * along with this program. If not, see .* *************************************************************************/ -// #include "libpartitionmanager_export.h" - -#if !defined(KPMCORE_LIBPARTITIONMANAGEREXPORT_H) -#define KPMCORE_LIBPARTITIONMANAGEREXPORT_H +#ifndef LIBKPMCORE_EXPORT #include - -#if !defined(LIBKPMCORE_EXPORT) #define LIBKPMCORE_EXPORT Q_DECL_EXPORT -#endif #endif diff --git a/src/util/org.kde.kpmcore.applicationinterface.conf b/src/util/org.kde.kpmcore.applicationinterface.conf new file mode 100644 index 0000000..59113ff --- /dev/null +++ b/src/util/org.kde.kpmcore.applicationinterface.conf @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/util/org.kde.kpmcore.externalcommand.actions b/src/util/org.kde.kpmcore.externalcommand.actions new file mode 100644 index 0000000..b60f6ee --- /dev/null +++ b/src/util/org.kde.kpmcore.externalcommand.actions @@ -0,0 +1,8 @@ +[Domain] +Icon=partitionmanager + +[org.kde.kpmcore.externalcommand.init] +Name=Start external command daemon +Description=Root privileges are needed to manage disks +Policy=auth_admin +Persistence=session diff --git a/src/util/org.kde.kpmcore.helperinterface.conf b/src/util/org.kde.kpmcore.helperinterface.conf new file mode 100644 index 0000000..990dadf --- /dev/null +++ b/src/util/org.kde.kpmcore.helperinterface.conf @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 81c8b4c..67f63a2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,11 +15,12 @@ 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) macro (kpm_test name) - add_executable(${name} ${ARGN} $) - target_link_libraries(${name} kpmcore) + add_executable(${name} ${ARGN}) + target_link_libraries(${name} testhelpers kpmcore Qt5::Core) endmacro() ### @@ -29,13 +30,13 @@ 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 $) ### # @@ -45,3 +46,19 @@ 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/helpers.cpp b/test/helpers.cpp index de1b73f..9ec2703 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -20,6 +20,7 @@ #include "helpers.h" #include "backend/corebackendmanager.h" +#include "util/externalcommand.h" #include #include @@ -56,3 +57,8 @@ KPMCoreInitializer::KPMCoreInitializer( const QString& backendName ) : KPMCoreInitializer::KPMCoreInitializer( const char* backend ) : KPMCoreInitializer( QString::fromLatin1( backend ) ) { } + +KPMCoreInitializer::~KPMCoreInitializer() +{ + ExternalCommand::stopHelper(); +} diff --git a/test/helpers.h b/test/helpers.h index 0900418..44f4cd7 100644 --- a/test/helpers.h +++ b/test/helpers.h @@ -23,7 +23,7 @@ class QString; /** - * Use RAII to initialize the KPMcore library. Just instantiatie one + * Use RAII to initialize the KPMcore library. Just instantiate one * object of this class to do "normal" initialization. */ class KPMCoreInitializer @@ -32,6 +32,7 @@ public: KPMCoreInitializer(); /// Default backend KPMCoreInitializer( const QString& backend ); /// Use named backend KPMCoreInitializer( const char* backend ); /// Use named backend + ~KPMCoreInitializer(); bool isValid() const { diff --git a/test/testdevicescanner.cpp b/test/testdevicescanner.cpp index 88b2451..0629e6d 100644 --- a/test/testdevicescanner.cpp +++ b/test/testdevicescanner.cpp @@ -30,9 +30,12 @@ #include "core/partition.h" #include "util/capacity.h" +#include #include #include +#include + using PartitionList = QList; // Recursive helper for flatten(), adds partitions that @@ -63,16 +66,19 @@ PartitionList flatten(PartitionTable *table) int main( int argc, char **argv ) { + QCoreApplication app(argc, argv); + std::unique_ptr i; + if (argc != 2) { - KPMCoreInitializer i; - if (!i.isValid()) + i = std::make_unique(); + if (!i->isValid()) return 1; } else { - KPMCoreInitializer i( argv[1] ); - if (!i.isValid()) + i = std::make_unique( argv[1] ); + if (!i->isValid()) return 1; } @@ -110,6 +116,5 @@ int main( int argc, char **argv ) } return 0; - } diff --git a/test/testexternalcommand.cpp b/test/testexternalcommand.cpp new file mode 100644 index 0000000..aec3d31 --- /dev/null +++ b/test/testexternalcommand.cpp @@ -0,0 +1,66 @@ +/************************************************************************* + * 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+ + + +#include "helpers.h" +#include "backend/corebackendmanager.h" +#include "util/externalcommand.h" + +#include +#include +#include + +class runcmd : public QThread { +public: + +void run() +{ + ExternalCommand blkidCmd(QStringLiteral("blkid"), {}); + blkidCmd.run(); + qDebug().noquote() << blkidCmd.output(); +} + +}; + +class runcmd2 : public QThread { +public: + +void run() +{ + ExternalCommand lsblkCmd(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--json") }); + lsblkCmd.run(); + qDebug().noquote() << lsblkCmd.output(); +} +}; + + +int main( int argc, char **argv ) +{ + QCoreApplication app(argc, argv); + KPMCoreInitializer i(QStringLiteral("pmsfdiskbackendplugin")); + + runcmd a; + runcmd2 b; + a.start(); + a.wait(); + b.start(); + b.wait(); + + return 0; +} diff --git a/test/testinit.cpp b/test/testinit.cpp index 276c0f8..fd97f56 100644 --- a/test/testinit.cpp +++ b/test/testinit.cpp @@ -21,10 +21,13 @@ // 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; diff --git a/test/testlist.cpp b/test/testlist.cpp index 91ff06a..1960e00 100644 --- a/test/testlist.cpp +++ b/test/testlist.cpp @@ -27,9 +27,12 @@ #include "core/partition.h" #include "util/capacity.h" +#include #include #include +#include + using PartitionList = QList; // Recursive helper for flatten(), adds partitions that @@ -60,16 +63,19 @@ PartitionList flatten(PartitionTable *table) int main( int argc, char **argv ) { + QCoreApplication app(argc, argv); + std::unique_ptr i; + if (argc != 2) { - KPMCoreInitializer i; - if (!i.isValid()) + i = std::make_unique(); + if (!i->isValid()) return 1; } else { - KPMCoreInitializer i( argv[1] ); - if (!i.isValid()) + i = std::make_unique( argv[1] ); + if (!i->isValid()) return 1; } @@ -102,6 +108,5 @@ int main( int argc, char **argv ) } return 0; - } diff --git a/test/testsmart.cpp b/test/testsmart.cpp new file mode 100644 index 0000000..2236af7 --- /dev/null +++ b/test/testsmart.cpp @@ -0,0 +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 + +static QString getDefaultDevicePath(); +static bool testSmartStatus(); +static bool testSmartParser(); + +int main(int argc, char **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() != SmartStatus::SelfTestStatus::Success) + 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; +}