diff --git a/CMakeLists.txt b/CMakeLists.txt index 86a89da..70e8b44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,127 +1,127 @@ # Copyright (C) 2008 by Volker Lanz -# Copyright (C) 2014-2017 by Andrius Štikonas +# Copyright (C) 2014-2019 by Andrius Štikonas # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . project(kpmcore) cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(CMAKE_USE_RELATIVE_PATHS OFF) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) # Dependencies set(QT_MIN_VERSION "5.10.0") -set(KF5_MIN_VERSION "5.25") +set(KF5_MIN_VERSION "5.56") set(BLKID_MIN_VERSION "2.32") # Qca-qt5 (tested with botan and ossl backends) # Runtime # smartmontools 7.0 # Qca plugin (botan or ossl) set(VERSION_MAJOR "3") -set(VERSION_MINOR "50") +set(VERSION_MINOR "80") set(VERSION_RELEASE "0") set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}) set(SOVERSION "8") add_definitions(-D'VERSION="${VERSION}"') #" set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(GenerateExportHeader) include(ECMSetupVersion) ecm_setup_version(${VERSION} VARIABLE_PREFIX KPMCORE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpmcore_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfigVersion.cmake" SOVERSION ${SOVERSION}) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Gui Widgets ) # Load the frameworks we need find_package(KF5 ${KF5_MIN_VERSION} REQUIRED Auth CoreAddons I18n WidgetsAddons ) find_package(Qca-qt5 REQUIRED) # use sane compile flags add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_CAST_TO_BYTEARRAY -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS ) kde_enable_exceptions() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") find_package(PkgConfig REQUIRED) pkg_check_modules(BLKID REQUIRED blkid>=${BLKID_MIN_VERSION}) endif() include_directories(${Qt5Core_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ${BLKID_INCLUDE_DIRS} lib/ src/) add_subdirectory(src) # create a Config.cmake and a ConfigVersion.cmake file and install them set(INCLUDE_INSTALL_DIR "include/kpmcore/") set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KPMcore") configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KPMcoreConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS INCLUDE_INSTALL_DIR ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KPMcoreConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KPMcoreTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPMcoreTargets.cmake ) ki18n_install(po) set_target_properties( kpmcore PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSION} ) message(STATUS "kpmcore ${VERSION} will be built for install into ${CMAKE_INSTALL_PREFIX}") feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) enable_testing() add_subdirectory(test) diff --git a/INSTALL.md b/INSTALL.md index 6b3a278..78235de 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,42 +1,42 @@ Building and installing KDE Partition Manager Core Library from source ========================================================= ## Dependencies -* [util-linux](https://github.com/karelzak/util-linux) 2.32 +* [util-linux](https://github.com/karelzak/util-linux) 2.34 * [Qt](https://www.qt.io/) 5.10 * QCA -* Tier 2 [KDE Frameworks](https://www.kde.org/products/frameworks/) 5.25 +* Tier 2 [KDE Frameworks](https://www.kde.org/products/frameworks/) 5.56 ## Configure KPMcore is built with [cmake](https://cmake.org/). It is recommended to build out of tree: After unpacking the source, create a separate build directory and run cmake there: -``` +```bash $ tar xf kpmcore-x.y.z.tar.xz $ cd kpmcore-x.y.z $ mkdir build $ cd build $ cmake .. ``` If all dependencies are met, cmake configures the build directory. ## Build and install Just run make and make install in the build directory. The default install path is `/usr/local`, so installing will need write privileges there. You can configure a different install path by passing `-DCMAKE_INSTALL_PREFIX=` to cmake when configuring. To change the install path after configuring and building, run -``` +```bash $ ccmake . ``` in the build directory and modify `CMAKE_INSTALL_PREFIX` there. diff --git a/README.md b/README.md index 9b16d66..bf21350 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,104 @@ # KPMcore > KPMcore, the KDE Partition Manager core, is a library for examining > and modifying partitions, disk devices, and filesystems on a > Linux system. It provides a unified programming interface over > top of (external) system-manipulation tools. KPMcore is a library for examining and manipulating all facets of storage devices on a system: * raw disk devices * partition tables on a device * filesystems within a partition There are multiple backends so that KPMcore can support different operating systems, although the only functional backend is the one for Linux systems: * sfdisk backend (Linux) * null backend ## Using KPMcore Most of the usage information on KPMcore is included in the API documentation; this section contains only high-level usage information. ### Finding KPMcore with CMake KPMcore supports CMake as (meta-)build system and installs suitable CMake support files. Typical use of of KPMcore in a `CMakeLists.txt` looks like this: -``` +```cmake find_package( KPMcore 3.2 REQUIRED ) include_directories( ${KPMCORE_INCLUDE_DIR} ) target_link_libraries( target kpmcore ) ``` There are no imported targets defined for KPMcore. ### Initialization An application must initialize the library and load a suitable backend before using KPMcore functions. By convention, the environment variable `KPMCORE_BACKEND` names a backend, and typical initialization code will look like this (or use the class `KPMCoreInitializer` from `test/helpers.h`): -``` +```cpp #include #include bool initKPMcore() { static bool inited = false; if ( inited ) return true; QByteArray env = qgetenv( "KPMCORE_BACKEND" ); auto backendName = env.isEmpty() ? CoreBackendManager::defaultBackendName() : env; if ( !CoreBackendManager::self()->load( backendName ) { qWarning() << "Failed to load backend plugin" << backendName; return false; } inited = true; return true; } ``` This code uses the environment variable if set, and otherwise falls back to a default backend suitable for the current platform. Calling KPMcore functions before the library is initialized will result in undefined behavior. ### Devices After the backend is initialized you can scan for available devices. If you only want devices from the loaded backend you can call -``` +```cpp QList devices = backend->scanDevices( excludeReadOnly ); ``` where `bool` option `excludeReadOnly` specifies whether to exclude read only devices. #### KPMcore device scanner Alternatively, you can use KPMcore device scanner -``` +```cpp #include #include #include // First create operationStack with another QObject as parent, we will use nullptr here. OperationStack *operationStack = new OperationStack(nullptr); DeviceScanner *deviceScanner = new DeviceScanner(nullptr, *operationStack); deviceScanner->scan(); // use start() for scanning in the background thread QList devices = operationStack->previewDevices(); ``` Then `deviceScanner` scans for the devices in a background thread. After scanning is complete `DeviceScanner::finished()` signal will be emitted. Then the devices can accessed using `operationStack->previewDevices()`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 500b9a8..e839af2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,66 +1,66 @@ # Copyright (C) 2008, 2012 by Volker Lanz # Copyright (C) 2015 by Teo Mrnjavac # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################ add_definitions(-DTRANSLATION_DOMAIN=\"kpmcore\") include(backend/CMakeLists.txt) include(core/CMakeLists.txt) include(util/CMakeLists.txt) include(ops/CMakeLists.txt) include(jobs/CMakeLists.txt) include(fs/CMakeLists.txt) include(gui/CMakeLists.txt) set(kpmcore_SRCS ${BACKEND_SRC} ${FS_SRC} ${CORE_SRC} ${OPS_SRC} ${JOBS_SRC} ${UTIL_SRC} ${GUI_SRC} ) ki18n_wrap_ui(kpmcore_SRCS ${gui_UIFILES}) add_library(kpmcore SHARED ${kpmcore_SRCS}) target_link_libraries( kpmcore PUBLIC Qt5::Core PRIVATE ${BLKID_LIBRARIES} Qt5::DBus Qt5::Gui qca-qt5 KF5::I18n KF5::CoreAddons KF5::WidgetsAddons - KF5::Auth + KF5::AuthCore ) install(TARGETS kpmcore EXPORT KPMcoreTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CORE_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/core/ COMPONENT Devel) install(FILES ${BACKEND_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/backend/ COMPONENT Devel) install(FILES ${FS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/fs/ COMPONENT Devel) install(FILES ${JOBS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/jobs/ COMPONENT Devel) install(FILES ${OPS_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/ops/ COMPONENT Devel) install(FILES ${UTIL_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/util/ COMPONENT Devel) install(FILES ${GUI_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/kpmcore/gui/ COMPONENT Devel) ############################################ add_subdirectory(plugins) diff --git a/src/backend/corebackend.h b/src/backend/corebackend.h index 386d67f..f9bc32f 100644 --- a/src/backend/corebackend.h +++ b/src/backend/corebackend.h @@ -1,179 +1,195 @@ /************************************************************************* * 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 .* *************************************************************************/ #if !defined(KPMCORE_COREBACKEND_H) #define KPMCORE_COREBACKEND_H #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include #include #include class CoreBackendManager; class CoreBackendDevice; struct CoreBackendPrivate; class Device; class PartitionTable; class QString; +enum class ScanFlag : uint8_t { + includeReadOnly = 0x1, /**< devices that are read-only according to the kernel */ + includeLoopback = 0x2, +}; +Q_DECLARE_FLAGS(ScanFlags, ScanFlag) +Q_DECLARE_OPERATORS_FOR_FLAGS(ScanFlags) + /** * Interface class for backend plugins. * @author Volker Lanz */ class LIBKPMCORE_EXPORT CoreBackend : public QObject { Q_OBJECT Q_DISABLE_COPY(CoreBackend) friend class CoreBackendManager; protected: CoreBackend(); virtual ~CoreBackend(); Q_SIGNALS: /** * Emitted to inform about progress of any kind. * @param i the progress in percent (from 0 to 100) */ void progress(int i); /** * Emitted to inform about scan progress. * @param deviceNode the device being scanned just now (e.g. "/dev/sda") * @param i the progress in percent (from 0 to 100) */ void scanProgress(const QString& deviceNode, int i); public: /** * Return the plugin's unique Id from JSON metadata * @return the plugin's unique Id from JSON metadata */ QString id(); /** * Return the plugin's version from JSON metadata * @return the plugin's version from JSON metadata */ QString version(); /** * Initialize the plugin's FileSystem support */ virtual void initFSSupport() = 0; /** * Scan for devices in the system. - * @param excludeReadOnly when true, devices that are read-only - * according to the kernel are left out of the list. + * @param excludeReadOnly when true, are left out of the list. * When false (the default) all devices, writable or * not, including CD ROM devices, are returned. * @return a QList of pointers to Device instances. The caller is responsible * for deleting these objects. * @note A Device object is a description of the device, not * an object to operate on. See openDevice(). */ - virtual QList scanDevices(bool excludeReadOnly = false) = 0; + [[deprecated("port to scanDevices(ScanFlags)")]] virtual QList scanDevices(bool excludeReadOnly = false) = 0; + + /** + * Scan for devices in the system. + * @param scanFlags can be used to expand the list of scanned devices. + * @return a QList of pointers to Device instances. The caller is responsible + * for deleting these objects. + * @note A Device object is a description of the device, not + * an object to operate on. See openDevice(). + */ + virtual QList scanDevices(const ScanFlags scanFlags) = 0; /** * Scan a single device in the system. * @param deviceNode The path to the device that is to be scanned (e.g. /dev/sda1) * @return FileSystem type of the device on deviceNode */ virtual FileSystem::Type detectFileSystem(const QString& deviceNode) = 0; /** * Read a file system label * @param deviceNode The path to the device that is to be scanned (e.g. /dev/sda1) * @return FileSystem label on deviceNode */ virtual QString readLabel(const QString& deviceNode) const = 0; /** * Read a file system UUID * @param deviceNode The path to the device that is to be scanned (e.g. /dev/sda1) * @return FileSystem UUID on deviceNode */ virtual QString readUUID(const QString& deviceNode) const = 0; /** * Scan a single device in the system. * @param deviceNode The path to the device that is to be scanned (e.g. /dev/sda) * @return a pointer to a Device instance. The caller is responsible for deleting * this object. */ virtual Device* scanDevice(const QString& deviceNode) = 0; /** * 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. */ 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. */ 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. */ virtual bool closeDevice(std::unique_ptr coreDevice) = 0; /** * Emit progress. * @param i the progress in percent (from 0 to 100) * This is used to emit a progress() signal from somewhere deep inside the plugin * backend code if that is ever necessary. */ virtual void emitProgress(int i); /** * Emit scan progress. * @param deviceNode the path to the device just being scanned (e.g. /dev/sda) * @param i the progress in percent (from 0 to 100) * This is used to emit a scanProgress() signal from the backend device scanning * code. */ virtual void emitScanProgress(const QString& deviceNode, int i); protected: static void setPartitionTableForDevice(Device& d, PartitionTable* p); static void setPartitionTableMaxPrimaries(PartitionTable& p, qint32 max_primaries); private: void setId(const QString& id); void setVersion(const QString& version); private: std::unique_ptr d; }; #endif diff --git a/src/core/devicescanner.cpp b/src/core/devicescanner.cpp index 7024290..bff979b 100644 --- a/src/core/devicescanner.cpp +++ b/src/core/devicescanner.cpp @@ -1,73 +1,73 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/devicescanner.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/operationstack.h" #include "core/device.h" #include "core/diskdevice.h" #include "fs/lvm2_pv.h" #include "util/externalcommand.h" #include /** Constructs a DeviceScanner @param ostack the OperationStack where the devices will be created */ DeviceScanner::DeviceScanner(QObject* parent, OperationStack& ostack) : QThread(parent), m_OperationStack(ostack) { setupConnections(); } void DeviceScanner::setupConnections() { connect(CoreBackendManager::self()->backend(), &CoreBackend::scanProgress, this, &DeviceScanner::progress); } void DeviceScanner::clear() { operationStack().clearOperations(); operationStack().clearDevices(); } void DeviceScanner::run() { scan(); } void DeviceScanner::scan() { emit progress(QString(), 0); clear(); - const QList deviceList = CoreBackendManager::self()->backend()->scanDevices(); + const QList deviceList = CoreBackendManager::self()->backend()->scanDevices(ScanFlag::includeLoopback); for (const auto &d : deviceList) operationStack().addDevice(d); operationStack().sortDevices(); } diff --git a/src/core/fstab.cpp b/src/core/fstab.cpp index 6bdc7f2..650c96f 100644 --- a/src/core/fstab.cpp +++ b/src/core/fstab.cpp @@ -1,287 +1,268 @@ /************************************************************************* * Copyright (C) 2009, 2010 by Volker Lanz * * Copyright (C) 2016 by Teo Mrnjavac * * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/fstab.h" #include "util/externalcommand.h" +#include "util/report.h" #if defined(Q_OS_LINUX) #include #endif #include #include #include #include #include #include static void parseFsSpec(const QString& m_fsSpec, FstabEntry::Type& m_entryType, QString& m_deviceNode); static QString findBlkIdDevice(const char *token, const QString& value); struct FstabEntryPrivate { 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; }; 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()) { 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 ) { FstabEntryList fstabEntries; QFile fstabFile( fstabPath ); if ( fstabFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) { const QStringList fstabLines = QString::fromLocal8Bit(fstabFile.readAll()).split( QLatin1Char('\n') ); for ( const QString& rawLine : fstabLines ) { QString line = rawLine.trimmed(); if ( line.startsWith( QLatin1Char('#') ) || line.isEmpty()) { fstabEntries.push_back( { {}, {}, {}, {}, {}, {}, line } ); continue; } QString comment = line.section( QLatin1Char('#'), 1 ); QStringList splitLine = line.section( QLatin1Char('#'), 0, 0 ).split( QRegularExpression(QStringLiteral("[\\s]+")), QString::SkipEmptyParts ); // We now split the standard components of /etc/fstab entry: // (0) path, or UUID, or LABEL, etc, // (1) mount point, // (2) file system type, // (3) options, // (4) dump frequency (optional, defaults to 0), no comment is allowed if omitted, // (5) pass number (optional, defaults to 0), no comment is allowed if omitted, // (#) comment (optional). switch (splitLine.length()) { case 4: fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3) } ); break; case 5: fstabEntries.push_back( {splitLine.at(0), splitLine.at(1), splitLine.at(2), splitLine.at(3), splitLine.at(4).toInt() } ); break; case 6: 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.push_back( { {}, {}, {}, {}, {}, {}, QLatin1Char('#') + line } ); } } fstabFile.close(); 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; QString canonicalPath = QFileInfo(deviceNode).canonicalFilePath(); const FstabEntryList fstabEntryList = readFstabEntries( fstabPath ); for (const FstabEntry &entry : fstabEntryList) if (QFileInfo(entry.deviceNode()).canonicalFilePath() == canonicalPath) mountPoints.append(entry.mountPoint()); return mountPoints; } static QString findBlkIdDevice(const char *token, const QString& value) { QString rval; #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, FstabEntry::Type& m_entryType, QString& m_deviceNode) { m_entryType = FstabEntry::Type::comment; if (m_fsSpec.startsWith(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 = FstabEntry::Type::label; m_deviceNode = findBlkIdDevice("LABEL", QString(m_fsSpec).remove(QStringLiteral("LABEL="))); } else if (m_fsSpec.startsWith(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 = FstabEntry::Type::label; m_deviceNode = findBlkIdDevice("PARTLABEL", QString(m_fsSpec).remove(QStringLiteral("PARTLABEL="))); } else if (m_fsSpec.startsWith(QStringLiteral("/"))) { m_entryType = FstabEntry::Type::deviceNode; m_deviceNode = m_fsSpec; } } -static void writeEntry(QFile& output, const FstabEntry& entry) +static void writeEntry(QTextStream& s, const FstabEntry& entry) { - QTextStream s(&output); if (entry.entryType() == FstabEntry::Type::comment) { s << entry.comment() << "\n"; return; } QString options; if (entry.options().size() > 0) { options = entry.options().join(QLatin1Char(',')); if (options.isEmpty()) options = QStringLiteral("defaults"); } else options = QStringLiteral("defaults"); s << entry.fsSpec() << "\t" << (entry.mountPoint().isEmpty() ? QStringLiteral("none") : entry.mountPoint()) << "\t" << entry.type() << "\t" << options << "\t" << entry.dumpFreq() << "\t" << entry.passNumber() << "\t" << entry.comment() << "\n"; } bool writeMountpoints(const FstabEntryList& fstabEntries, const QString& filename) { - QTemporaryFile out; - out.setAutoRemove(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); - ExternalCommand mvCmd(QStringLiteral("mv"), { filename, bakFilename } ); - - if ( !(mvCmd.run(-1) && mvCmd.exitCode() == 0) ) { - qWarning() << "could not backup " << filename << " to " << bakFilename; - return false; - } - - ExternalCommand mvCmd2(QStringLiteral("mv"), { out.fileName(), filename } ); + Report report(nullptr); + QByteArray fstabContents; + QTextStream out(&fstabContents); - if ( !(mvCmd2.run(-1) && mvCmd2.exitCode() == 0) ) { - qWarning() << "could not move " << out.fileName() << " to " << filename; - return false; - } - } + for (const auto &e : fstabEntries) + writeEntry(out, e); - return true; + ExternalCommand cmd; + return cmd.writeData(report, fstabContents, filename, 0); } diff --git a/src/core/lvmdevice.cpp b/src/core/lvmdevice.cpp index c6c05d4..64ff0f3 100644 --- a/src/core/lvmdevice.cpp +++ b/src/core/lvmdevice.cpp @@ -1,561 +1,561 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * 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 * * 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/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 "util/externalcommand.h" #include "util/helpers.h" #include "util/globallog.h" #include "util/report.h" #include #include #include #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; 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(std::make_shared(), vgName, (QStringLiteral("/dev/") + vgName), getPeSize(vgName), getTotalPE(vgName), iconName, Device::Type::LVM_Device) { 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(); } /** * shared list of PV's paths that will be added to any VGs. * (have been added to an operation, but not yet applied) */ 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() { } void LvmDevice::initPartitions() { qint64 firstUsable = 0; 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); } 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()) { Partition *p = scanPartition(lvPath, pTable); pList.append(p); } return pList; } /** scan and construct a partition(LV) at a given path * * NOTE: * LVM partition has 2 different start and end sector values * 1. representing the actual LV start from 0 -> size of LV - 1 * 2. representing abstract LV's sector inside a VG partitionTable * start from last sector + 1 of last Partitions -> size of LV - 1 * Reason for this is for the LV Partition to work nicely with other parts of the codebase * without too many special cases. * * @param lvPath LVM Logical Volume path * @param pTable Abstract partition table representing partitions of LVM Volume Group * @return initialized Partition(LV) */ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTable) const { activateLV(lvPath); qint64 lvSize = getTotalLE(lvPath); qint64 startSector = mappedSector(lvPath, 0); qint64 endSector = startSector + lvSize - 1; FileSystem::Type type = FileSystem::detectFileSystem(lvPath); FileSystem* fs = FileSystemFactory::create(type, 0, lvSize - 1, logicalSize()); fs->scan(lvPath); PartitionRole::Roles r = PartitionRole::Lvm_Lv; QString mountPoint; bool mounted; // Handle LUKS partition if (fs->type() == FileSystem::Type::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, lvPath); mounted = FileSystem::detectMountStatus(fs, lvPath); if (mountPoint != QString() && fs->type() != FileSystem::Type::LinuxSwap) { const QStorageInfo storage = QStorageInfo(mountPoint); if (logicalSize() > 0 && fs->type() != FileSystem::Type::Luks && mounted && storage.isValid()) fs->setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / logicalSize() ); } else if (fs->supportGetUsed() == FileSystem::cmdSupportFileSystem) fs->setSectorsUsed(qCeil(fs->readUsedCapacity(lvPath) / static_cast(logicalSize()))); } if (fs->supportGetLabel() != FileSystem::cmdSupportNone) { fs->setLabel(fs->readLabel(lvPath)); } if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(lvPath)); Partition* part = new Partition(pTable, *this, PartitionRole(r), fs, startSector, endSector, lvPath, - PartitionTable::Flag::FlagNone, + PartitionTable::Flag::None, mountPoint, mounted); return part; } /** scan and construct list of initialized LvmDevice objects. * * @param devices list of initialized Devices */ 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::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::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::list())) if (p.vgName() == d->name()) d->physicalVolumes().append(p.partition()); } qint64 LvmDevice::mappedSector(const QString& lvPath, qint64 sector) const { qint64 mSector = 0; QStringList lvpathList = partitionNodes(); qint32 devIndex = lvpathList.indexOf(lvPath); if (devIndex) { for (int i = 0; i < devIndex; i++) { mSector += LVSizeMap()->value(lvpathList[i]); } mSector += sector; } return mSector; } const QStringList LvmDevice::deviceNodes() const { QStringList pvList; for (const auto &p : physicalVolumes()) { if (p->roles().has(PartitionRole::Luks)) pvList << static_cast(&p->fileSystem())->mapperName(); else pvList << p->partitionPath(); } return pvList; } const QStringList& LvmDevice::partitionNodes() const { return d_ptr->m_LVPathList; } qint64 LvmDevice::partitionSize(QString& partitionPath) const { return LVSizeMap()->value(partitionPath); } const QStringList LvmDevice::getVGs() { QStringList vgList; QString output = getField(QStringLiteral("vg_name")); if (!output.isEmpty()) { const QStringList vgNameList = output.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const auto &vgName : vgNameList) { vgList.append(vgName.trimmed()); } } return vgList; } const QStringList LvmDevice::getLVs(const QString& vgName) { QStringList lvPathList; QString cmdOutput = getField(QStringLiteral("lv_path"), vgName); if (cmdOutput.size()) { const QStringList tempPathList = cmdOutput.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const auto &lvPath : tempPathList) { lvPathList.append(lvPath.trimmed()); } } return lvPathList; } qint64 LvmDevice::getPeSize(const QString& vgName) { QString val = getField(QStringLiteral("vg_extent_size"), vgName); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 LvmDevice::getTotalPE(const QString& vgName) { QString val = getField(QStringLiteral("vg_extent_count"), vgName); return val.isEmpty() ? -1 : val.toInt(); } qint64 LvmDevice::getAllocatedPE(const QString& vgName) { return getTotalPE(vgName) - getFreePE(vgName); } qint64 LvmDevice::getFreePE(const QString& vgName) { QString val = getField(QStringLiteral("vg_free_count"), vgName); return val.isEmpty() ? -1 : val.toInt(); } QString LvmDevice::getUUID(const QString& vgName) { QString val = getField(QStringLiteral("vg_uuid"), vgName); return val.isEmpty() ? QStringLiteral("---") : val; } /** Get LVM vgs command output with field name * * @param fieldName LVM field name * @param vgName the name of LVM Volume Group * @return raw output of command output, usually with many spaces within the returned string * */ QString LvmDevice::getField(const QString& fieldName, const QString& vgName) { QStringList args = { QStringLiteral("vgs"), QStringLiteral("--foreign"), QStringLiteral("--readonly"), QStringLiteral("--noheadings"), QStringLiteral("--units"), QStringLiteral("B"), QStringLiteral("--nosuffix"), QStringLiteral("--options"), fieldName }; if (!vgName.isEmpty()) { args << vgName; } ExternalCommand cmd(QStringLiteral("lvm"), args, QProcess::ProcessChannelMode::SeparateChannels); if (cmd.run(-1) && cmd.exitCode() == 0) { return cmd.output().trimmed(); } return QString(); } qint64 LvmDevice::getTotalLE(const QString& lvPath) { ExternalCommand cmd(QStringLiteral("lvm"), { QStringLiteral("lvdisplay"), lvPath}); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Current LE\\h+(\\d+)")); QRegularExpressionMatch match = re.match(cmd.output()); if (match.hasMatch()) { return match.captured(1).toInt(); } } Log(Log::Level::error) << xi18nc("@info:status", "An error occurred while running lvdisplay."); return -1; } bool LvmDevice::removeLV(Report& report, LvmDevice& d, Partition& p) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvremove"), QStringLiteral("--yes"), p.partitionPath()}); if (cmd.run(-1) && cmd.exitCode() == 0) { d.partitionTable()->remove(&p); return true; } return false; } bool LvmDevice::createLV(Report& report, LvmDevice& d, Partition& p, const QString& lvName) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(p.length()), QStringLiteral("--name"), lvName, d.name()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createLVSnapshot(Report& report, Partition& p, const QString& name, const qint64 extents) { QString numExtents = (extents > 0) ? QString::number(extents) : QString::number(p.length()); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), numExtents, QStringLiteral("--snapshot"), QStringLiteral("--name"), name, p.partitionPath() }); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::resizeLV(Report& report, Partition& p) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvresize"), QStringLiteral("--force"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(p.length()), p.partitionPath()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removePV(Report& report, LvmDevice& d, const QString& pvPath) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgreduce"), d.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::insertPV(Report& report, LvmDevice& d, const QString& pvPath) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgextend"), QStringLiteral("--yes"), d.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::movePV(Report& report, const QString& pvPath, const QStringList& destinations) { if (FS::lvm2_pv::getAllocatedPE(pvPath) <= 0) return true; QStringList args = { QStringLiteral("pvmove") }; args << pvPath; if (!destinations.isEmpty()) for (const auto &destPath : destinations) args << destPath.trimmed(); ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createVG(Report& report, const QString vgName, const QVector& pvList, const qint32 peSize) { QStringList args = { QStringLiteral("vgcreate"), QStringLiteral("--physicalextentsize"), QString::number(peSize) }; args << vgName; for (const auto &p : pvList) { if (p->roles().has(PartitionRole::Luks)) args << static_cast(&p->fileSystem())->mapperName(); else args << p->partitionPath(); } ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removeVG(Report& report, LvmDevice& d) { bool deactivated = deactivateVG(report, d); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgremove"), QStringLiteral("--force"), d.name() }); return (deactivated && cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::deactivateVG(Report& report, const LvmDevice& d) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("vgchange"), QStringLiteral("--activate"), QStringLiteral("n"), d.name() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::deactivateLV(Report& report, const Partition& p) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("lvchange"), QStringLiteral("--activate"), QStringLiteral("n"), p.partitionPath() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::activateVG(Report& report, const LvmDevice& d) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("vgchange"), QStringLiteral("--activate"), QStringLiteral("y"), d.name() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::activateLV(const QString& lvPath) { ExternalCommand deactivate(QStringLiteral("lvm"), { QStringLiteral("lvchange"), QStringLiteral("--activate"), QStringLiteral("y"), 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; } void LvmDevice::setFreePE(qint64 freePE) const { d_ptr->m_freePE = freePE; d_ptr->m_allocPE = d_ptr->m_totalPE - freePE; } QString LvmDevice::UUID() const { return d_ptr->m_UUID; } std::unique_ptr>& LvmDevice::LVSizeMap() const { return d_ptr->m_LVSizeMap; } diff --git a/src/core/lvmdevice.h b/src/core/lvmdevice.h index 5b6b2ac..86cea4e 100644 --- a/src/core/lvmdevice.h +++ b/src/core/lvmdevice.h @@ -1,108 +1,111 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * 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 .* *************************************************************************/ #ifndef KPMCORE_LVMDEVICE_H #define KPMCORE_LVMDEVICE_H #include "core/device.h" #include "core/volumemanagerdevice.h" #include "util/libpartitionmanagerexport.h" #include #include #include #include #include #include class PartitionTable; class Report; class Partition; class SmartStatus; /** Representation of LVM Volume Group(VG). Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. @see Device, VolumeManagerDevice, PartitionTable, Partition */ class LIBKPMCORE_EXPORT LvmDevice : public VolumeManagerDevice { Q_DISABLE_COPY(LvmDevice) + friend class VolumeManagerDevice; + public: LvmDevice(const QString& name, const QString& iconName = QString()); ~LvmDevice(); public: const QStringList deviceNodes() const override; const QStringList& partitionNodes() const override; qint64 partitionSize(QString& partitionPath) const override; static QVector s_DirtyPVs; static QVector s_OrphanPVs; - static void scanSystemLVM(QList& devices); - static const QStringList getVGs(); static const QStringList getLVs(const QString& vgName); static qint64 getPeSize(const QString& vgName); static qint64 getTotalPE(const QString& vgName); static qint64 getAllocatedPE(const QString& vgName); static qint64 getFreePE(const QString& vgName); static QString getUUID(const QString& vgName); static QString getField(const QString& fieldName, const QString& vgName = QString()); static qint64 getTotalLE(const QString& lvPath); static bool removeLV(Report& report, LvmDevice& d, Partition& p); static bool createLV(Report& report, LvmDevice& d, Partition& p, const QString& lvName); static bool createLVSnapshot(Report& report, Partition& p, const QString& name, const qint64 extents = 0); static bool resizeLV(Report& report, Partition& p); static bool deactivateLV(Report& report, const Partition& p); static bool activateLV(const QString& deviceNode); static bool removePV(Report& report, LvmDevice& d, const QString& pvPath); static bool insertPV(Report& report, LvmDevice& d, const QString& pvPath); static bool movePV(Report& report, const QString& pvPath, const QStringList& destinations = QStringList()); static bool removeVG(Report& report, LvmDevice& d); static bool createVG(Report& report, const QString vgName, const QVector& pvList, const qint32 peSize = 4); // peSize in megabytes static bool deactivateVG(Report& report, const LvmDevice& d); 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; qint64 totalPE() const; qint64 allocatedPE() const; qint64 freePE() const; void setFreePE(qint64 freePE) const; QString UUID() const; protected: std::unique_ptr>& LVSizeMap() const; + +private: + static void scanSystemLVM(QList& devices); }; #endif diff --git a/src/core/operationrunner.h b/src/core/operationrunner.h index f91468d..d22dfe2 100644 --- a/src/core/operationrunner.h +++ b/src/core/operationrunner.h @@ -1,95 +1,95 @@ /************************************************************************* * Copyright (C) 2008 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_OPERATIONRUNNER_H) #define KPMCORE_OPERATIONRUNNER_H #include "util/libpartitionmanagerexport.h" #include #include #include class Operation; class OperationStack; class Report; /** Thread to run the Operations in the OperationStack. Runs the OperationStack when the user applies operations. @author Volker Lanz */ class LIBKPMCORE_EXPORT OperationRunner : public QThread { Q_OBJECT Q_DISABLE_COPY(OperationRunner) public: OperationRunner(QObject* parent, OperationStack& ostack); public: - void run(); + void run() override; qint32 numJobs() const; qint32 numOperations() const; qint32 numProgressSub() const; bool isCancelling() const { return m_Cancelling; /**< @return if the user has requested cancelling */ } void cancel() const { m_Cancelling = true; /**< Sets cancelling to true. */ } QMutex& suspendMutex() const { return m_SuspendMutex; /**< @return the QMutex used for syncing */ } QString description(qint32 op) const; void setReport(Report* report) { m_Report = report; /**< @param report the Report to use while running */ } Q_SIGNALS: void progressSub(int); void opStarted(int, Operation*); void opFinished(int, Operation*); void finished(); void cancelled(); void error(); protected: OperationStack& operationStack() { return m_OperationStack; } const OperationStack& operationStack() const { return m_OperationStack; } void setCancelling(bool b) { m_Cancelling = b; } Report& report() { Q_ASSERT(m_Report); return *m_Report; } private: OperationStack& m_OperationStack; Report* m_Report; mutable QMutex m_SuspendMutex; mutable volatile bool m_Cancelling; }; #endif diff --git a/src/core/partition.h b/src/core/partition.h index 5b46afc..7b6c0f8 100644 --- a/src/core/partition.h +++ b/src/core/partition.h @@ -1,281 +1,281 @@ /************************************************************************* * Copyright (C) 2008 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 .* *************************************************************************/ #ifndef KPMCORE_PARTITION_H #define KPMCORE_PARTITION_H #include "core/partitionnode.h" #include "core/partitionrole.h" #include "core/partitiontable.h" #include "util/libpartitionmanagerexport.h" #include #include #include class Device; class OperationStack; class CoreBackendPartitionTable; class PartitionAlignment; class PartResizerWidget; class ResizeDialog; class InsertDialog; class NewDialog; class EditMountPointDialog; class PartPropsDialog; class SizeDialogBase; class CreateFileSystemOperation; class RestoreOperation; class SetPartFlagsOperation; class CopyOperation; class NewOperation; class ResizeOperation; class SetPartGeometryJob; class CreatePartitionJob; class SetPartFlagsJob; class RestoreFileSystemJob; class FileSystem; class Report; class QString; class QTextStream; /** A partition or some unallocated space on a Device. Represent partitions in a PartitionTable on a Device. Partitions can be unallocated, thus not all instances really are partitions in the way the user would see them. Extended partitions have child objects that represent the logicals inside them. @see PartitionTable, Device, FileSystem @author Volker Lanz */ class LIBKPMCORE_EXPORT Partition : public PartitionNode { public: /** A Partition state -- where did it come from? */ enum State { None, /**< exists on disk */ New, /**< from a NewOperation */ Copy, /**< from a CopyOperation */ Restore, /**< from a RestoreOperation */ StateNone [[deprecated("Use Partition::State::None")]] = None, StateNew [[deprecated("Use Partition::State::New")]] = New, StateCopy [[deprecated("Use Partition::State::Copy")]] = Copy, StateRestore [[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); + Partition(PartitionNode* parent, const Device& device, const PartitionRole& role, FileSystem* fs, qint64 sectorStart, qint64 sectorEnd, QString partitionPath, PartitionTable::Flags availableFlags = PartitionTable::Flag::None, const QString& mountPoint = QString(), bool mounted = false, PartitionTable::Flags activeFlags = PartitionTable::Flag::None, State state = State::None); ~Partition() override; Partition(const Partition& other, PartitionNode* parent = nullptr); Partition& operator=(const Partition&); bool operator==(const Partition& other) const; bool operator!=(const Partition& other) const; qint32 number() const { return m_Number; /**< @return the Partition's device number, e.g. 7 for /dev/sdd7 */ } bool isRoot() const override { return false; /**< @return always false for Partition */ } PartitionNode* parent() override { return m_Parent; /**< @return the Partition's parent PartitionNode */ } const PartitionNode* parent() const override { return m_Parent; /**< @return the Partition's parent PartitionNode */ } Partitions& children() override { return m_Children; /**< @return the Partition's children. empty for non-extended. */ } const Partitions& children() const override { return m_Children; /**< @return the Partition's children. empty for non-extended. */ } const QString& devicePath() const { return m_DevicePath; /**< @return the Partition's device path, e.g. /dev/sdd */ } const QString& partitionPath() const { return m_PartitionPath; /**< @return the Partition's path, e.g. /dev/sdd1 */ } const QString& label() const { return m_Label; /**< @return the GPT Partition label */ } const QString& uuid() const { return m_UUID; /**< @return the GPT Partition UUID */ } qint64 firstSector() const { return m_FirstSector; /**< @return the Partition's first sector on the Device */ } qint64 lastSector() const { return m_LastSector; /**< @return the Partition's last sector on the Device */ } qint64 firstByte() const { return firstSector() * sectorSize(); /**< @return the Partition's first byte on the Device */ } qint64 lastByte() const { return firstByte() + length() * sectorSize() - 1; /**< @return the Partition's last byte on the Device */ } qint64 sectorsUsed() const; qint64 sectorSize() const { return m_SectorSize; /**< @return the sector size on the Partition's Device */ } qint64 length() const { return lastSector() - firstSector() + 1; /**< @return the length of the Partition */ } qint64 capacity() const { return length() * sectorSize(); /**< @return the capacity of the Partition in bytes */ } qint64 used() const { return sectorsUsed() < 0 ? -1 : sectorsUsed() * sectorSize(); /**< @return the number of used sectors in the Partition's FileSystem */ } qint64 available() const { return sectorsUsed() < 0 ? -1 : capacity() - used(); /**< @return the number of free sectors in the Partition's FileSystem */ } qint64 minimumSectors() const; qint64 maximumSectors() const; qint64 maxFirstSector() const; qint64 minLastSector() const; QString deviceNode() const; const PartitionRole& roles() const { return m_Roles; /**< @return the Partition's role(s) */ } const QString& mountPoint() const { return m_MountPoint; /**< @return the Partition's mount point */ } PartitionTable::Flags activeFlags() const { return m_ActiveFlags; /**< @return the flags currently set for this Partition */ } PartitionTable::Flags availableFlags() const { return m_AvailableFlags; /**< @return the flags available for this Partition */ } bool isMounted() const { return m_IsMounted; /**< @return true if Partition is mounted */ } FileSystem& fileSystem() { return *m_FileSystem; /**< @return the Partition's FileSystem */ } const FileSystem& fileSystem() const { return *m_FileSystem; /**< @return the Partition's FileSystem */ } State state() const { return m_State; /**< @return the Partition's state */ } bool hasChildren() const; bool mount(Report& report); bool unmount(Report& report); bool canMount() const; bool canUnmount() const; void adjustLogicalNumbers(qint32 deletedNumber, qint32 insertedNumber) const; void checkChildrenMounted(); void setFirstSector(qint64 s) { m_FirstSector = s; } void setLastSector(qint64 s) { m_LastSector = s; } 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 */ } void append(Partition* p) override { m_Children.append(p); std::sort(m_Children.begin(), m_Children.end(), [] (const Partition *a, const Partition *b) -> bool {return a->firstSector() < b->firstSector();}); } void setDevicePath(const QString& s) { m_DevicePath = s; } void setPartitionPath(const QString& s); void setRoles(const PartitionRole& r) { m_Roles = r; } void setMountPoint(const QString& s) { m_MountPoint = s; } void setFlags(PartitionTable::Flags f) { m_ActiveFlags = f; } void setSectorSize(qint32 s) { m_SectorSize = s; } void move(qint64 newStartSector); void setMounted(bool b); void setFlag(PartitionTable::Flag f) { - m_ActiveFlags |= f; + m_ActiveFlags = m_ActiveFlags.setFlag(f); } void unsetFlag(PartitionTable::Flag f) { - m_ActiveFlags &= ~f; + m_ActiveFlags = m_ActiveFlags.setFlag(f, false); } void setParent(PartitionNode* p) { m_Parent = p; } void setFileSystem(FileSystem* fs); void setState(State s) { m_State = s; } void deleteFileSystem(); private: void setNumber(qint32 n) { m_Number = n; } qint32 m_Number; Partitions m_Children; QPointer< PartitionNode > m_Parent; FileSystem* m_FileSystem; PartitionRole m_Roles; qint64 m_FirstSector; qint64 m_LastSector; QString m_DevicePath; QString m_Label; QString m_UUID; QString m_PartitionPath; QString m_MountPoint; PartitionTable::Flags m_AvailableFlags; PartitionTable::Flags m_ActiveFlags; bool m_IsMounted; qint64 m_SectorSize; State m_State; }; QTextStream& operator<<(QTextStream& stream, const Partition& p); #endif diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index dd0b4be..2dee90e 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -1,592 +1,606 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * Copyright (C) 2016 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 .* *************************************************************************/ /** @file */ #include "core/partitiontable.h" #include "core/partition.h" #include "core/device.h" #include "core/diskdevice.h" #include "core/lvmdevice.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/globallog.h" #include #include #include /** Creates a new PartitionTable object with type MSDOS @param type name of the PartitionTable type (e.g. "msdos" or "gpt") */ PartitionTable::PartitionTable(TableType type, qint64 firstUsable, qint64 lastUsable) : PartitionNode() , m_Children() , m_MaxPrimaries(maxPrimariesForTableType(type)) , m_Type(type) , m_FirstUsable(firstUsable) , m_LastUsable(lastUsable) { } /** Copy constructor for PartitionTable. * @param other the other PartitionTable. */ PartitionTable::PartitionTable(const PartitionTable& other) : PartitionNode() , m_Children() , m_MaxPrimaries(other.m_MaxPrimaries) , m_Type(other.m_Type) , m_FirstUsable(other.m_FirstUsable) , m_LastUsable(other.m_LastUsable) { for (Partitions::const_iterator it = other.m_Children.constBegin(); it != other.m_Children.constEnd(); ++it) { m_Children.append(new Partition(**it, this)); } } /** Destroys a PartitionTable object, destroying all children */ PartitionTable::~PartitionTable() { clearChildren(); } /** Gets the number of free sectors before a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors before @returns the number of free sectors before the Partition */ qint64 PartitionTable::freeSectorsBefore(const Partition& p) const { const Partition* pred = predecessor(p); // due to the space required for extended boot records the // below is NOT the same as pred->length() if (pred && pred->roles().has(PartitionRole::Unallocated)) return p.firstSector() - pred->firstSector(); return 0; } /** Gets the number of free sectors after a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors after @returns the number of free sectors after the Partition */ qint64 PartitionTable::freeSectorsAfter(const Partition& p) const { const Partition* succ = successor(p); // due to the space required for extended boot records the // below is NOT the same as succ->length() if (succ && succ->roles().has(PartitionRole::Unallocated)) return succ->lastSector() - p.lastSector(); return 0; } qint64 PartitionTable::freeSectors() const { qint64 sectors = 0; for (const auto &p : children()) { if (p->roles().has(PartitionRole::Unallocated)) { sectors += p->length(); } } return sectors; } /** @return true if the PartitionTable has an extended Partition */ bool PartitionTable::hasExtended() const { for (const auto &p : children()) if (p->roles().has(PartitionRole::Extended)) return true; return false; } /** @return pointer to the PartitionTable's extended Partition or nullptr if none exists */ Partition* PartitionTable::extended() const { for (const auto &p : children()) if (p->roles().has(PartitionRole::Extended)) return p; return nullptr; } /** Gets valid PartitionRoles for a Partition @param p the Partition @return valid roles for the given Partition */ PartitionRole::Roles PartitionTable::childRoles(const Partition& p) const { Q_ASSERT(p.parent()); PartitionRole::Roles r = p.parent()->isRoot() ? PartitionRole::Primary : PartitionRole::Logical; if (r == PartitionRole::Primary && hasExtended() == false && tableTypeSupportsExtended(type())) r |= PartitionRole::Extended; return r; } /** @return the number of primaries in this PartitionTable */ int PartitionTable::numPrimaries() const { int result = 0; for (const auto &p : children()) if (p->roles().has(PartitionRole::Primary) || p->roles().has(PartitionRole::Extended)) result++; return result; } /** Appends a Partition to this PartitionTable @param partition pointer of the partition to append. Must not be nullptr. */ void PartitionTable::append(Partition* partition) { children().append(partition); std::sort(children().begin(), children().end(), [] (const Partition *a, const Partition *b) -> bool {return a->firstSector() < b->firstSector();}); } /** @param f the flag to get the name for @returns the flags name or an empty QString if the flag is not known */ QString PartitionTable::flagName(Flag f) { switch (f) { - case PartitionTable::FlagBoot: + case PartitionTable::Flag::Boot: return xi18nc("@item partition flag", "boot"); - case PartitionTable::FlagRoot: + case PartitionTable::Flag::Root: return xi18nc("@item partition flag", "root"); - case PartitionTable::FlagSwap: + case PartitionTable::Flag::Swap: return xi18nc("@item partition flag", "swap"); - case PartitionTable::FlagHidden: + case PartitionTable::Flag::Hidden: return xi18nc("@item partition flag", "hidden"); - case PartitionTable::FlagRaid: + case PartitionTable::Flag::Raid: return xi18nc("@item partition flag", "raid"); - case PartitionTable::FlagLvm: + case PartitionTable::Flag::Lvm: return xi18nc("@item partition flag", "lvm"); - case PartitionTable::FlagLba: + case PartitionTable::Flag::Lba: return xi18nc("@item partition flag", "lba"); - case PartitionTable::FlagHpService: + case PartitionTable::Flag::HpService: return xi18nc("@item partition flag", "hpservice"); - case PartitionTable::FlagPalo: + case PartitionTable::Flag::Palo: return xi18nc("@item partition flag", "palo"); - case PartitionTable::FlagPrep: + case PartitionTable::Flag::Prep: return xi18nc("@item partition flag", "prep"); - case PartitionTable::FlagMsftReserved: + case PartitionTable::Flag::MsftReserved: return xi18nc("@item partition flag", "msft-reserved"); - case PartitionTable::FlagBiosGrub: + case PartitionTable::Flag::BiosGrub: return xi18nc("@item partition flag", "bios-grub"); - case PartitionTable::FlagAppleTvRecovery: + case PartitionTable::Flag::AppleTvRecovery: return xi18nc("@item partition flag", "apple-tv-recovery"); - case PartitionTable::FlagDiag: + case PartitionTable::Flag::Diag: return xi18nc("@item partition flag", "diag"); - case PartitionTable::FlagLegacyBoot: + case PartitionTable::Flag::LegacyBoot: return xi18nc("@item partition flag", "legacy-boot"); - case PartitionTable::FlagMsftData: + case PartitionTable::Flag::MsftData: return xi18nc("@item partition flag", "msft-data"); - case PartitionTable::FlagIrst: + case PartitionTable::Flag::Irst: return xi18nc("@item partition flag", "irst"); default: break; } return QString(); } /** @return list of all flags */ const QList PartitionTable::flagList() { QList rval; - rval.append(PartitionTable::FlagBoot); - rval.append(PartitionTable::FlagRoot); - rval.append(PartitionTable::FlagSwap); - rval.append(PartitionTable::FlagHidden); - rval.append(PartitionTable::FlagRaid); - rval.append(PartitionTable::FlagLvm); - rval.append(PartitionTable::FlagLba); - rval.append(PartitionTable::FlagHpService); - rval.append(PartitionTable::FlagPalo); - rval.append(PartitionTable::FlagPrep); - rval.append(PartitionTable::FlagMsftReserved); - rval.append(PartitionTable::FlagBiosGrub); - rval.append(PartitionTable::FlagAppleTvRecovery); - rval.append(PartitionTable::FlagDiag); - rval.append(PartitionTable::FlagLegacyBoot); - rval.append(PartitionTable::FlagMsftData); - rval.append(PartitionTable::FlagIrst); + rval.append(PartitionTable::Flag::Boot); + rval.append(PartitionTable::Flag::Root); + rval.append(PartitionTable::Flag::Swap); + rval.append(PartitionTable::Flag::Hidden); + rval.append(PartitionTable::Flag::Raid); + rval.append(PartitionTable::Flag::Lvm); + rval.append(PartitionTable::Flag::Lba); + rval.append(PartitionTable::Flag::HpService); + rval.append(PartitionTable::Flag::Palo); + rval.append(PartitionTable::Flag::Prep); + rval.append(PartitionTable::Flag::MsftReserved); + rval.append(PartitionTable::Flag::BiosGrub); + rval.append(PartitionTable::Flag::AppleTvRecovery); + rval.append(PartitionTable::Flag::Diag); + rval.append(PartitionTable::Flag::LegacyBoot); + rval.append(PartitionTable::Flag::MsftData); + rval.append(PartitionTable::Flag::Irst); return rval; } /** @param flags the flags to get the names for @returns QStringList of the flags' names */ QStringList PartitionTable::flagNames(Flags flags) { QStringList rval; int f = 1; QString s; while (!(s = flagName(static_cast(f))).isEmpty()) { if (flags & f) rval.append(s); f <<= 1; } return rval; } +/** @param list QStringList of the flags' names + @returns flags corresponding to names +*/ +PartitionTable::Flags PartitionTable::flagsFromList(const QStringList list) +{ + Flags flags; + + for (const auto &flag : flagList()) + if (list.contains(flagName(flag))) + flags.setFlag(flag); + + return flags; +} + bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end) { if (d.type() == Device::Type::Disk_Device) { const DiskDevice& device = dynamic_cast(d); if (!parent.isRoot()) { Partition* extended = dynamic_cast(&parent); if (extended == nullptr) { qWarning() << "extended is null. start: " << start << ", end: " << end << ", device: " << device.deviceNode(); return false; } // Leave a track (cylinder aligned) or sector alignment sectors (sector based) free at the // start for a new partition's metadata start += device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); // .. and also at the end for the metadata for a partition to follow us, if we're not // at the end of the extended partition if (end < extended->lastSector()) end -= device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); } return end - start + 1 >= PartitionAlignment::sectorAlignment(device); } else if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { if (end - start + 1 > 0) { return true; } } return false; } /** Creates a new unallocated Partition on the given Device. @param device the Device to create the new Partition on @param parent the parent PartitionNode for the new Partition @param start the new Partition's start sector @param end the new Partition's end sector @return pointer to the newly created Partition object or nullptr if the Partition could not be created */ Partition* createUnallocated(const Device& device, PartitionNode& parent, qint64 start, qint64 end) { PartitionRole::Roles r = PartitionRole::Unallocated; if (!parent.isRoot()) 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::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::Type::Unknown, start, end, device.logicalSize()), start, end, QString()); } /** Removes all unallocated children from a PartitionNode @param p pointer to the parent to remove unallocated children from */ void PartitionTable::removeUnallocated(PartitionNode* p) { Q_ASSERT(p); qint32 i = 0; while (i < p->children().size()) { Partition* child = p->children()[i]; if (child->roles().has(PartitionRole::Unallocated)) { p->remove(child); delete child; continue; } if (child->roles().has(PartitionRole::Extended)) removeUnallocated(child); i++; } } /** @overload */ void PartitionTable::removeUnallocated() { removeUnallocated(this); } /** Inserts unallocated children for a Device's PartitionTable with the given parent. This method inserts unallocated Partitions for a parent, usually the Device this PartitionTable is on. It will also insert unallocated Partitions in any extended Partitions it finds. @warning This method assumes that no unallocated Partitions exist when it is called. @param d the Device this PartitionTable and @p p are on @param p the parent PartitionNode (may be this or an extended Partition) @param start the first sector to begin looking for free space */ void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 start) { Q_ASSERT(p); qint64 lastEnd = start; 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(); }); for (const auto &child : children()) { qint64 totalSectors = child->length(); child->setFirstSector(lastEnd); child->setLastSector(lastEnd + totalSectors - 1); lastEnd += totalSectors; } } else { const auto pChildren = p->children(); for (const auto &child : pChildren) { p->insert(createUnallocated(d, *p, lastEnd, child->firstSector() - 1)); if (child->roles().has(PartitionRole::Extended)) insertUnallocated(d, child, child->firstSector()); lastEnd = child->lastSector() + 1; } } 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 (!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. @param d the Device this PartitionTable is on */ void PartitionTable::updateUnallocated(const Device& d) { removeUnallocated(); insertUnallocated(d, this, firstUsable()); } qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t) { Q_UNUSED(t) if (d.type() == Device::Type::LVM_Device || d.type() == Device::Type::SoftwareRAID_Device) { return 0; } const DiskDevice& diskDevice = dynamic_cast(d); return PartitionAlignment::sectorAlignment(diskDevice); } qint64 PartitionTable::defaultLastUsable(const Device& d, TableType t) { if (t == gpt) return d.totalLogical() - 1 - 32 - 1; return d.totalLogical() - 1; } static struct { const QLatin1String name; /**< name of partition table type */ quint32 maxPrimaries; /**< max numbers of primary partitions supported */ bool canHaveExtended; /**< does partition table type support extended partitions */ bool isReadOnly; /**< does KDE Partition Manager support this only in read only mode */ PartitionTable::TableType type; /**< enum type */ } tableTypes[] = { { QLatin1String("aix"), 4, false, true, PartitionTable::aix }, { QLatin1String("bsd"), 8, false, true, PartitionTable::bsd }, { QLatin1String("dasd"), 1, false, true, PartitionTable::dasd }, { QLatin1String("msdos"), 4, true, false, PartitionTable::msdos }, { QLatin1String("msdos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QLatin1String("dos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QLatin1String("dvh"), 16, true, true, PartitionTable::dvh }, { QLatin1String("gpt"), 128, false, false, PartitionTable::gpt }, { QLatin1String("loop"), 1, false, true, PartitionTable::loop }, { QLatin1String("mac"), 0xffff, false, true, PartitionTable::mac }, { QLatin1String("pc98"), 16, false, true, PartitionTable::pc98 }, { QLatin1String("amiga"), 128, false, true, PartitionTable::amiga }, { QLatin1String("sun"), 8, false, true, PartitionTable::sun }, { QLatin1String("vmd"), 0xffff, false, false, PartitionTable::vmd } }; PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) { for (const auto &type : tableTypes) if (n == type.name) return type.type; return PartitionTable::unknownTableType; } QString PartitionTable::tableTypeToName(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.name; return xi18nc("@item partition table name", "unknown"); } qint32 PartitionTable::maxPrimariesForTableType(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.maxPrimaries; return 1; } bool PartitionTable::tableTypeSupportsExtended(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.canHaveExtended; return false; } bool PartitionTable::tableTypeIsReadOnly(TableType l) { for (const auto &type : tableTypes) if (l == type.type) return type.isReadOnly; return false; } /** Simple heuristic to determine if the PartitionTable is sector aligned (i.e. if its Partitions begin at sectors evenly divisable by PartitionAlignment::sectorAlignment(). @return true if is sector aligned, otherwise false */ bool PartitionTable::isSectorBased(const Device& d) const { if (d.type() == Device::Type::Disk_Device) { const DiskDevice& diskDevice = dynamic_cast(d); if (type() == PartitionTable::msdos) { // the default for empty partition tables is sector based if (numPrimaries() == 0) return true; quint32 numCylinderAligned = 0; quint32 numSectorAligned = 0; // see if we have more cylinder aligned partitions than sector // aligned ones. for (const auto &p : children()) { if (p->firstSector() % PartitionAlignment::sectorAlignment(diskDevice) == 0) numSectorAligned++; else if (p->firstSector() % diskDevice.cylinderSize() == 0) numCylinderAligned++; } return numSectorAligned >= numCylinderAligned; } return type() == PartitionTable::msdos_sectorbased; } return false; } void PartitionTable::setType(const Device& d, TableType t) { setFirstUsableSector(defaultFirstUsable(d, t)); setLastUsableSector(defaultLastUsable(d, t)); m_Type = t; updateUnallocated(d); } QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable) { stream << "type: \"" << ptable.typeName() << "\"\n" << "align: \"" << (ptable.type() == PartitionTable::msdos ? "cylinder" : "sector") << "\"\n" << "\n# number start end type roles label flags\n"; QList partitions; for (const auto &p : ptable.children()) { if (!p->roles().has(PartitionRole::Unallocated)) { partitions.append(p); if (p->roles().has(PartitionRole::Extended)) { const auto partChildren = p->children(); for (const auto &child : partChildren) { if (!child->roles().has(PartitionRole::Unallocated)) partitions.append(child); } } } } std::sort(partitions.begin(), partitions.end(), [](const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); }); for (const auto &p : qAsConst(partitions)) stream << *p; return stream; } diff --git a/src/core/partitiontable.h b/src/core/partitiontable.h index de5f3ef..92bed82 100644 --- a/src/core/partitiontable.h +++ b/src/core/partitiontable.h @@ -1,204 +1,221 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * Copyright (C) 2016 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_PARTITIONTABLE_H) - +#ifndef KPMCORE_PARTITIONTABLE_H #define KPMCORE_PARTITIONTABLE_H #include "util/libpartitionmanagerexport.h" #include "core/partitionnode.h" #include "core/partitionrole.h" #include #include class Device; class Partition; class CoreBackend; class QTextStream; /** The partition table (a.k.a Disk Label) PartitionTable represents a partition table (or disk label). PartitionTable has child nodes that represent Partitions. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartitionTable : public PartitionNode { PartitionTable &operator=(const PartitionTable &) = delete; friend class CoreBackend; friend LIBKPMCORE_EXPORT QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); public: - enum TableType : qint8 { + enum TableType : int8_t { unknownTableType = -1, aix, bsd, dasd, msdos, msdos_sectorbased, dvh, gpt, loop, mac, pc98, amiga, sun, vmd /* Volume Manager Device */ }; /** Partition flags */ - enum Flag : qint32 { - FlagNone = 0, - FlagBoot = 1, - FlagRoot = 2, - FlagSwap = 4, - FlagHidden = 8, - FlagRaid = 16, - FlagLvm = 32, - FlagLba = 64, - FlagHpService = 128, - FlagPalo = 256, - FlagPrep = 512, - FlagMsftReserved = 1024, - FlagBiosGrub = 2048, - FlagAppleTvRecovery = 4096, - FlagDiag = 8192, - FlagLegacyBoot = 16384, - FlagMsftData = 32768, - FlagIrst = 65536, - FlagEsp [[deprecated]] = FlagBoot + enum Flag : uint32_t { + None = 0x0, + Boot = 0x1, + Root = 0x2, + Swap = 0x4, + Hidden = 0x8, + Raid = 0x10, + Lvm = 0x20, + Lba = 0x40, + HpService = 0x80, + Palo = 0x100, + Prep = 0x200, + MsftReserved = 0x400, + BiosGrub = 0x800, + AppleTvRecovery = 0x1000, + Diag = 0x2000, + LegacyBoot = 0x4000, + MsftData = 0x8000, + Irst = 0x100000, + FlagNone [[deprecated("Use PartitionTable::Flag::None")]] = None, + FlagBoot [[deprecated("Use PartitionTable::Flag::Boot")]] = Boot, + FlagRoot [[deprecated("Use PartitionTable::Flag::Root")]] = Root, + FlagSwap [[deprecated("Use PartitionTable::Flag::Swap")]] = Swap, + FlagHidden [[deprecated("Use PartitionTable::Flag::Hidden")]] = Hidden, + FlagRaid [[deprecated("Use PartitionTable::Flag::Raid")]] = Raid, + FlagLvm [[deprecated("Use PartitionTable::Flag::Lvm")]] = Lvm, + FlagLba [[deprecated("Use PartitionTable::Flag::Lba")]] = Lba, + FlagHpService [[deprecated("Use PartitionTable::Flag::HpService")]] = HpService, + FlagPalo [[deprecated("Use PartitionTable::Flag::Palo")]] = Palo, + FlagPrep [[deprecated("Use PartitionTable::Flag::Prep")]] = Prep, + FlagMsftReserved [[deprecated("Use PartitionTable::Flag::MsftReserved")]] = MsftReserved, + FlagBiosGrub [[deprecated("Use PartitionTable::Flag::BiosGrub")]] = BiosGrub, + FlagAppleTvRecovery [[deprecated("Use PartitionTable::Flag::AppleTvRecovery")]] = AppleTvRecovery, + FlagDiag [[deprecated("Use PartitionTable::Flag::Diag")]] = Diag, + FlagLegacyBoot [[deprecated("Use PartitionTable::Flag::LegacyBoot")]] = LegacyBoot, + FlagMsftData [[deprecated("Use PartitionTable::Flag::MsftData")]] = MsftData, + FlagIrst [[deprecated("Use PartitionTable::Flag::Irst")]] = Irst, + FlagEsp [[deprecated("Use PartitionTable::Flag::Boot")]] = Boot }; Q_DECLARE_FLAGS(Flags, Flag) - Q_FLAG(Flag) public: PartitionTable(TableType type, qint64 firstUsable, qint64 lastUsable); PartitionTable(const PartitionTable& other); ~PartitionTable() override; public: PartitionNode* parent() override { return nullptr; /**< @return always nullptr for PartitionTable */ } const PartitionNode* parent() const override { return nullptr; /**< @return always nullptr for PartitionTable */ } bool isRoot() const override { return true; /**< @return always true for PartitionTable */ } bool isReadOnly() const { return tableTypeIsReadOnly(type()); /**< @return true if the PartitionTable is read only */ } Partitions& children() override { return m_Children; /**< @return the children in this PartitionTable */ } const Partitions& children() const override { return m_Children; /**< @return the children in this PartitionTable */ } void setType(const Device& d, TableType t); void append(Partition* partition) override; qint64 freeSectorsBefore(const Partition& p) const; qint64 freeSectorsAfter(const Partition& p) const; qint64 freeSectors() const; bool hasExtended() const; Partition* extended() const; PartitionRole::Roles childRoles(const Partition& p) const; qint32 numPrimaries() const; qint32 maxPrimaries() const { return m_MaxPrimaries; /**< @return max number of primary partitions this PartitionTable can handle */ } PartitionTable::TableType type() const { return m_Type; /**< @return the PartitionTable's type */ } const QString typeName() const { return tableTypeToName(type()); /**< @return the name of this PartitionTable type */ } qint64 firstUsable() const { return m_FirstUsable; } qint64 lastUsable() const { return m_LastUsable; } void setFirstUsableSector(qint64 s) { m_FirstUsable = s; } void setLastUsableSector(qint64 s) { m_LastUsable = s; } void updateUnallocated(const Device& d); void insertUnallocated(const Device& d, PartitionNode* p, qint64 start); bool isSectorBased(const Device& d) const; static const QList flagList(); static QString flagName(Flag f); static QStringList flagNames(Flags f); + static PartitionTable::Flags flagsFromList(const QStringList list); static bool getUnallocatedRange(const Device& device, PartitionNode& parent, qint64& start, qint64& end); static void removeUnallocated(PartitionNode* p); void removeUnallocated(); static qint64 defaultFirstUsable(const Device& d, TableType t); static qint64 defaultLastUsable(const Device& d, TableType t); static PartitionTable::TableType nameToTableType(const QString& n); static QString tableTypeToName(TableType l); static qint32 maxPrimariesForTableType(TableType l); static bool tableTypeSupportsExtended(TableType l); static bool tableTypeIsReadOnly(TableType l); protected: void setMaxPrimaries(qint32 n) { m_MaxPrimaries = n; } private: Partitions m_Children; qint32 m_MaxPrimaries; TableType m_Type; qint64 m_FirstUsable; qint64 m_LastUsable; }; Q_DECLARE_OPERATORS_FOR_FLAGS(PartitionTable::Flags) QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); #endif diff --git a/src/core/raid/softwareraid.h b/src/core/raid/softwareraid.h index 37f351a..dceb395 100644 --- a/src/core/raid/softwareraid.h +++ b/src/core/raid/softwareraid.h @@ -1,125 +1,127 @@ /************************************************************************* * 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) + friend class VolumeManagerDevice; + 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; 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& devicePath); 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(Report& report, const QString& deviceNode); static bool reassembleSoftwareRAID(Report& report, const QString& deviceNode); static QString getRaidArrayName(const QString& partitionPath); static void setRaidConfigurationFilePath(const QString& filePath); static QString raidConfigurationFilePath(); static QString getDefaultRaidConfigFile(); static bool failPV(Report& report, const QString& devicePath, const QString& physicalVolume); //static bool insertPV(Report& report, SoftwareRAID& raid, const QString& deviceNode); static bool removePV(Report& report, const QString& devicePath, const QString& physicalVolume); protected: void initPartitions() override; qint64 mappedSector(const QString &partitionPath, qint64 sector) const override; private: static bool eraseDeviceMDSuperblock(const QString& path); static bool updateConfigurationFile(const QString& path); + + static void scanSoftwareRAID(QList& devices); static QString getDetail(const QString& path); static QString getRAIDConfiguration(); static QString getDeviceInformation(const QString& deviceName); void setPartitionNodes(const QStringList& partitionNodes); private: static QString s_raidConfigurationFile; }; #endif // SOFTWARERAID_H diff --git a/src/core/volumemanagerdevice.cpp b/src/core/volumemanagerdevice.cpp index aadc660..185c18f 100644 --- a/src/core/volumemanagerdevice.cpp +++ b/src/core/volumemanagerdevice.cpp @@ -1,62 +1,70 @@ /************************************************************************* * 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 * * 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/device_p.h" #include "core/partition.h" #include "core/volumemanagerdevice.h" #include "core/volumemanagerdevice_p.h" +#include "core/lvmdevice.h" +#include "core/raid/softwareraid.h" #define d_ptr std::static_pointer_cast(d) /** 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(std::shared_ptr d, const QString& name, const QString& deviceNode, const qint64 logicalExtentSize, const qint64 totalLogical, const QString& iconName, Device::Type type) : Device(std::static_pointer_cast(d), name, deviceNode, logicalExtentSize, totalLogical, iconName, type) { } +void VolumeManagerDevice::scanDevices(QList& devices) +{ + SoftwareRAID::scanSoftwareRAID(devices); + LvmDevice::scanSystemLVM(devices); // LVM scanner needs all other devices, so should be last +} + QString VolumeManagerDevice::prettyDeviceNodeList() const { return deviceNodes().join(QStringLiteral(", ")); } void VolumeManagerDevice::setTotalLogical(qint64 n) { Q_ASSERT(n > 0); d_ptr->m_TotalLogical = n; } QVector& VolumeManagerDevice::physicalVolumes() { return d_ptr->m_PVs; } const QVector& VolumeManagerDevice::physicalVolumes() const { return d_ptr->m_PVs; } diff --git a/src/core/volumemanagerdevice.h b/src/core/volumemanagerdevice.h index 6f3aa69..c107d42 100644 --- a/src/core/volumemanagerdevice.h +++ b/src/core/volumemanagerdevice.h @@ -1,101 +1,103 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * 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_H #define KPMCORE_VOLUMEMANAGERDEVICE_H #include "util/libpartitionmanagerexport.h" #include "core/device.h" #include #include #include #include class Partition; 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. * example of physical device: /dev/sda, /dev/sdb1. * * Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. * * @see Device, PartitionTable, Partition */ class LIBKPMCORE_EXPORT VolumeManagerDevice : public Device { Q_DISABLE_COPY(VolumeManagerDevice) public: 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) */ virtual const QStringList deviceNodes() const = 0; /** * @return list of logical partition's path. */ virtual const QStringList& partitionNodes() const = 0; /** * @return size of logical partition at the given path in bytes. */ virtual qint64 partitionSize(QString& partitionPath) const = 0; protected: /** Initialize device's partition table and partitions. * */ virtual void initPartitions() = 0; /** absolute sector as represented inside the device's partitionTable * * For VolumeMangerDevice to works with the rest of the codebase, partitions are stringed * one after another to create a representation of PartitionTable and partition just like * real disk device. * * @param partitionPath logical partition path * @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; public: + static void scanDevices(QList& devices); + /** join deviceNodes together into comma-separated list * * @return comma-separated list of deviceNodes */ virtual QString prettyDeviceNodeList() const; /** Resize device total number of logical sectors. * * @param n Number of sectors. */ void setTotalLogical(qint64 n); QVector& physicalVolumes(); const QVector& physicalVolumes() const; }; #endif diff --git a/src/fs/CMakeLists.txt b/src/fs/CMakeLists.txt index 93661a6..fd050b5 100644 --- a/src/fs/CMakeLists.txt +++ b/src/fs/CMakeLists.txt @@ -1,71 +1,75 @@ set(FS_SRC + fs/apfs.cpp + fs/bitlocker.cpp fs/btrfs.cpp fs/exfat.cpp fs/ext2.cpp fs/ext3.cpp fs/ext4.cpp fs/extended.cpp fs/f2fs.cpp fs/fat12.cpp fs/fat16.cpp fs/fat32.cpp fs/filesystem.cpp fs/filesystemfactory.cpp fs/hfs.cpp fs/hfsplus.cpp fs/hpfs.cpp fs/iso9660.cpp fs/jfs.cpp fs/linuxraidmember.cpp fs/linuxswap.cpp fs/luks.cpp fs/luks2.cpp fs/lvm2_pv.cpp fs/nilfs2.cpp fs/ntfs.cpp fs/ocfs2.cpp fs/reiser4.cpp fs/reiserfs.cpp fs/udf.cpp fs/ufs.cpp fs/unformatted.cpp fs/unknown.cpp fs/xfs.cpp fs/zfs.cpp ) set(FS_LIB_HDRS + fs/apfs.h + fs/bitlocker.h fs/btrfs.h fs/exfat.h fs/ext2.h fs/ext3.h fs/ext4.h fs/extended.h fs/f2fs.h fs/fat12.h fs/fat16.h fs/fat32.h fs/filesystem.h fs/filesystemfactory.h fs/hfs.h fs/hfsplus.h fs/hpfs.h fs/iso9660.h fs/jfs.h fs/linuxraidmember.h fs/linuxswap.h fs/luks.h fs/luks2.h fs/lvm2_pv.h fs/nilfs2.h fs/ntfs.h fs/ocfs2.h fs/reiser4.h fs/reiserfs.h fs/udf.h fs/ufs.h fs/unformatted.h fs/unknown.h fs/xfs.h fs/zfs.h ) diff --git a/src/fs/unknown.cpp b/src/fs/apfs.cpp similarity index 73% copy from src/fs/unknown.cpp copy to src/fs/apfs.cpp index 3f6e27f..984cef2 100644 --- a/src/fs/unknown.cpp +++ b/src/fs/apfs.cpp @@ -1,33 +1,30 @@ /************************************************************************* - * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2019 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ -#include "fs/unknown.h" +#include "fs/apfs.h" namespace FS { -unknown::unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Unknown) -{ -} +FileSystem::CommandSupportType apfs::m_Move = FileSystem::cmdSupportCore; +FileSystem::CommandSupportType apfs::m_Copy = FileSystem::cmdSupportCore; +FileSystem::CommandSupportType apfs::m_Backup = FileSystem::cmdSupportCore; -bool unknown::canMount(const QString & deviceNode, const QString & mountPoint) const +apfs::apfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Apfs) { - Q_UNUSED(deviceNode) - Q_UNUSED(mountPoint) - return false; } } diff --git a/src/fs/unknown.h b/src/fs/apfs.h similarity index 64% copy from src/fs/unknown.h copy to src/fs/apfs.h index 3d20d67..2af3a40 100644 --- a/src/fs/unknown.h +++ b/src/fs/apfs.h @@ -1,46 +1,61 @@ /************************************************************************* - * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2019 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_UNKNOWN_H) - -#define KPMCORE_UNKNOWN_H +#ifndef KPMCORE_APFS_H +#define KPMCORE_APFS_H #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include +class QString; + namespace FS { -/** A pseudo file system for partitions whose file system we cannot determine. - @author Volker Lanz -*/ -class LIBKPMCORE_EXPORT unknown : public FileSystem +/** An APFS file system. + @author Andrius Štikonas + */ +class LIBKPMCORE_EXPORT apfs : public FileSystem { public: - unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); + apfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); public: + CommandSupportType supportMove() const override { + return m_Move; + } + CommandSupportType supportCopy() const override { + return m_Copy; + } + CommandSupportType supportBackup() const override { + return m_Backup; + } + bool supportToolFound() const override { return true; } - bool canMount(const QString & deviceNode, const QString & mountPoint) const override; + +public: + static CommandSupportType m_Move; + static CommandSupportType m_Copy; + static CommandSupportType m_Backup; }; } #endif diff --git a/src/fs/unknown.cpp b/src/fs/bitlocker.cpp similarity index 72% copy from src/fs/unknown.cpp copy to src/fs/bitlocker.cpp index 3f6e27f..15280cf 100644 --- a/src/fs/unknown.cpp +++ b/src/fs/bitlocker.cpp @@ -1,33 +1,30 @@ /************************************************************************* - * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2019 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ -#include "fs/unknown.h" +#include "fs/bitlocker.h" namespace FS { -unknown::unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : - FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Unknown) -{ -} +FileSystem::CommandSupportType bitlocker::m_Move = FileSystem::cmdSupportCore; +FileSystem::CommandSupportType bitlocker::m_Copy = FileSystem::cmdSupportCore; +FileSystem::CommandSupportType bitlocker::m_Backup = FileSystem::cmdSupportCore; -bool unknown::canMount(const QString & deviceNode, const QString & mountPoint) const +bitlocker::bitlocker(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::BitLocker) { - Q_UNUSED(deviceNode) - Q_UNUSED(mountPoint) - return false; } } diff --git a/src/fs/unknown.h b/src/fs/bitlocker.h similarity index 63% copy from src/fs/unknown.h copy to src/fs/bitlocker.h index 3d20d67..493eb4d 100644 --- a/src/fs/unknown.h +++ b/src/fs/bitlocker.h @@ -1,46 +1,61 @@ /************************************************************************* - * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2019 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ -#if !defined(KPMCORE_UNKNOWN_H) - -#define KPMCORE_UNKNOWN_H +#ifndef KPMCORE_BITLOCKER_H +#define KPMCORE_BITLOCKER_H #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include +class QString; + namespace FS { -/** A pseudo file system for partitions whose file system we cannot determine. - @author Volker Lanz -*/ -class LIBKPMCORE_EXPORT unknown : public FileSystem +/** A Bitlocker encrypted file system. + @author Andrius Štikonas + */ +class LIBKPMCORE_EXPORT bitlocker : public FileSystem { public: - unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); + bitlocker(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); public: + CommandSupportType supportMove() const override { + return m_Move; + } + CommandSupportType supportCopy() const override { + return m_Copy; + } + CommandSupportType supportBackup() const override { + return m_Backup; + } + bool supportToolFound() const override { return true; } - bool canMount(const QString & deviceNode, const QString & mountPoint) const override; + +public: + static CommandSupportType m_Move; + static CommandSupportType m_Copy; + static CommandSupportType m_Backup; }; } #endif diff --git a/src/fs/filesystem.cpp b/src/fs/filesystem.cpp index 62164c4..2a3be13 100644 --- a/src/fs/filesystem.cpp +++ b/src/fs/filesystem.cpp @@ -1,640 +1,644 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * 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 * * 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/fstab.h" #include "core/raid/softwareraid.h" #include "fs/filesystem.h" #include "fs/lvm2_pv.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "util/externalcommand.h" #include "util/capacity.h" #include "util/helpers.h" #include #include #include #include #include const std::vector FileSystem::defaultColorCode = { { QColor( 220,205,175 ), // unknown QColor( 187,249,207 ), // extended QColor( 102,121,150 ), // ext2 QColor( 122,145,180 ), // ext3 QColor( 143,170,210 ), // ext4 QColor( 155,155,130 ), // swap QColor( 204,179,215 ), // fat16 QColor( 229,201,240 ), // fat32 QColor( 244,214,255 ), // ntfs QColor( 216,220,135 ), // reiser QColor( 251,255,157 ), // reiser4 QColor( 200,255,254 ), // xfs QColor( 137,200,198 ), // jfs QColor( 210,136,142 ), // hfs QColor( 240,165,171 ), // hfs+ QColor( 151,220,134 ), // ufs QColor( 220,205,175 ), // unformatted QColor( 173,205,255 ), // btrfs QColor( 176,155,185 ), // hpfs QColor( 170,30,77 ), // luks QColor( 96,140,85 ), // ocfs2 QColor( 33,137,108 ), // zfs QColor( 250,230,255 ), // exfat QColor( 242,155,104 ), // nilfs2 QColor( 160,210,180 ), // lvm2 pv QColor( 255,170,0 ), // f2fs QColor( 170,120,255 ), // udf QColor( 177,82,69 ), // iso9660 QColor( 223,39,104 ), // luks2 - QColor( 204,179,255 ), // fat12 - QColor( 255,100,100 ) // linux_raid_member + QColor( 204,179,255 ), // fat12 + QColor( 255,100,100 ), // linux_raid_member + QColor( 110,20,50 ), // bitlocker + QColor( 255,155,174 ), // apfs } }; 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 label the FileSystem label @param type the FileSystem type */ 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() { } /** Reads the capacity in use on this FileSystem @param deviceNode the device node for the Partition the FileSystem is on @return the used capacity in bytes or -1 in case of an error */ qint64 FileSystem::readUsedCapacity(const QString& deviceNode) const { Q_UNUSED(deviceNode) return -1; } FileSystem::Type FileSystem::detectFileSystem(const QString& partitionPath) { return CoreBackendManager::self()->backend()->detectFileSystem(partitionPath); } QString FileSystem::detectMountPoint(FileSystem* fs, const QString& partitionPath) { if (fs->type() == FileSystem::Type::Lvm2_PV) return FS::lvm2_pv::getVGName(partitionPath); else if (fs->type() == FileSystem::Type::LinuxRaidMember) return SoftwareRAID::getRaidArrayName(partitionPath); if (partitionPath.isEmpty()) // Happens when during initial scan LUKS is closed return QString(); QStringList mountPoints; QFileInfo partitionPathFileInfo(partitionPath); QString partitionCanonicalPath = partitionPathFileInfo.canonicalFilePath(); const QList mountedVolumes = QStorageInfo::mountedVolumes(); for (const QStorageInfo &storage : mountedVolumes) { if (partitionCanonicalPath == QFileInfo(QString::fromLocal8Bit(storage.device())).canonicalFilePath() ) { mountPoints.append(storage.rootPath()); } } mountPoints.append(possibleMountPoints(partitionPath)); return mountPoints.isEmpty() ? QString() : mountPoints.first(); } bool FileSystem::detectMountStatus(FileSystem* fs, const QString& partitionPath) { bool mounted = false; if (fs->type() == FileSystem::Type::Lvm2_PV) { mounted = FS::lvm2_pv::getVGName(partitionPath) != QString(); } else { mounted = isMounted(partitionPath); } return mounted; } /** Reads the label for this FileSystem @param deviceNode the device node for the Partition the FileSystem is on @return the FileSystem label or an empty string in case of error */ QString FileSystem::readLabel(const QString& deviceNode) const { return CoreBackendManager::self()->backend()->readLabel(deviceNode); } /** Creates a new FileSystem @param report Report to write status information to @param deviceNode the device node for the Partition to create the FileSystem on @return true if successful */ bool FileSystem::create(Report& report, const QString& deviceNode) { Q_UNUSED(report) Q_UNUSED(deviceNode) return true; } /** Creates a new FileSystem with a specified Label @param report Report to write status information to @param deviceNode the device node for the Partition to create the FileSystem on @param label the new label for the FileSystem @return true if successful */ bool FileSystem::createWithLabel(Report& report, const QString& deviceNode, const QString& label) { Q_UNUSED(report) Q_UNUSED(deviceNode) Q_UNUSED(label) return true; } /** Scans a new FileSystem and load file system specific class variables. * @param deviceNode the device node for the Partition to create the FileSystem on */ void FileSystem::scan(const QString& deviceNode) { Q_UNUSED(deviceNode) } /** Resize a FileSystem to a given new length @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @param newLength the new length for the FileSystem in bytes @return true on success */ bool FileSystem::resize(Report& report, const QString& deviceNode, qint64 newLength) const { Q_UNUSED(report) Q_UNUSED(deviceNode) Q_UNUSED(newLength) return true; } /** Resize a mounted FileSystem to a given new length @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @param mountPoint the mount point where FileSystem is mounted on @param newLength the new length for the FileSystem in bytes @return true on success */ bool FileSystem::resizeOnline(Report& report, const QString& deviceNode, const QString& mountPoint, qint64 newLength) const { Q_UNUSED(report) Q_UNUSED(deviceNode) Q_UNUSED(mountPoint) Q_UNUSED(newLength) return true; } /** Move a FileSystem to a new start sector @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @param newStartSector the new start sector for the FileSystem @return true on success */ bool FileSystem::move(Report& report, const QString& deviceNode, qint64 newStartSector) const { Q_UNUSED(report) Q_UNUSED(deviceNode) Q_UNUSED(newStartSector) return true; } /** Writes a label for the FileSystem to disk @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @param newLabel the new label for the FileSystem @return true on success */ bool FileSystem::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) { Q_UNUSED(report) Q_UNUSED(deviceNode) Q_UNUSED(newLabel) return true; } /** Writes a label for the FileSystem to disk @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @param mountPoint the mount point where FileSystem is mounted on @param newLabel the new label for the FileSystem @return true on success */ bool FileSystem::writeLabelOnline(Report& report, const QString& deviceNode, const QString& mountPoint, const QString& newLabel) { Q_UNUSED(report) Q_UNUSED(deviceNode) Q_UNUSED(mountPoint) Q_UNUSED(newLabel) return true; } /** Copies a FileSystem from one Partition to another @param report Report to write status information to @param targetDeviceNode device node of the target Partition @param sourceDeviceNode device node of the source Partition @return true on success */ bool FileSystem::copy(Report& report, const QString& targetDeviceNode, const QString& sourceDeviceNode) const { Q_UNUSED(report) Q_UNUSED(targetDeviceNode) Q_UNUSED(sourceDeviceNode) return true; } /** Backs up a FileSystem to a file @param report Report to write status information to @param sourceDevice Device the source FileSystem is on @param deviceNode device node of the source Partition @param filename name of the file to backup to @return true on success */ bool FileSystem::backup(Report& report, const Device& sourceDevice, const QString& deviceNode, const QString& filename) const { Q_UNUSED(report) Q_UNUSED(sourceDevice) Q_UNUSED(deviceNode) Q_UNUSED(filename) return false; } /** Removes a FileSystem @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @return true if FileSystem is removed */ bool FileSystem::remove(Report& report, const QString& deviceNode) const { Q_UNUSED(report) Q_UNUSED(deviceNode) return true; } /** Checks a FileSystem for errors @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @return true if FileSystem is error-free */ bool FileSystem::check(Report& report, const QString& deviceNode) const { Q_UNUSED(report) Q_UNUSED(deviceNode) return true; } /** Updates a FileSystem UUID on disk @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @return true on success */ bool FileSystem::updateUUID(Report& report, const QString& deviceNode) const { Q_UNUSED(report) Q_UNUSED(deviceNode) return true; } /** Returns the FileSystem UUID by calling a FileSystem-specific helper program @param deviceNode the device node for the Partition the FileSystem is on @return the UUID or an empty string if the FileSystem does not support UUIDs */ QString FileSystem::readUUID(const QString& deviceNode) const { return CoreBackendManager::self()->backend()->readUUID(deviceNode); } /** Give implementations of FileSystem a chance to update the boot sector after the file system has been moved or copied. @param report Report to write status information to @param deviceNode the device node for the Partition the FileSystem is on @return true on success */ bool FileSystem::updateBootSector(Report& report, const QString& deviceNode) const { Q_UNUSED(report) Q_UNUSED(deviceNode) return true; } /** @return the minimum capacity valid for this FileSystem in bytes */ qint64 FileSystem::minCapacity() const { 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::Unit::Byte, Capacity::Unit::EiB); } /** @return the maximum label length valid for this FileSystem */ int FileSystem::maxLabelLength() const { return 16; } /** Validates the label for this FileSystem * @param parent the parent widget passed to the QObject constructor * @return QValidator to validate the file system label line edit input */ QValidator* FileSystem::labelValidator(QObject *parent) const { Q_UNUSED(parent) return nullptr; } /** @return this FileSystem's type as printable name */ 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() { static const KLocalizedString s[] = { kxi18nc("@item filesystem name", "unknown"), kxi18nc("@item filesystem name", "extended"), kxi18nc("@item filesystem name", "ext2"), kxi18nc("@item filesystem name", "ext3"), kxi18nc("@item filesystem name", "ext4"), kxi18nc("@item filesystem name", "linuxswap"), kxi18nc("@item filesystem name", "fat16"), kxi18nc("@item filesystem name", "fat32"), kxi18nc("@item filesystem name", "ntfs"), kxi18nc("@item filesystem name", "reiser"), kxi18nc("@item filesystem name", "reiser4"), kxi18nc("@item filesystem name", "xfs"), kxi18nc("@item filesystem name", "jfs"), kxi18nc("@item filesystem name", "hfs"), kxi18nc("@item filesystem name", "hfsplus"), kxi18nc("@item filesystem name", "ufs"), kxi18nc("@item filesystem name", "unformatted"), kxi18nc("@item filesystem name", "btrfs"), kxi18nc("@item filesystem name", "hpfs"), kxi18nc("@item filesystem name", "luks"), kxi18nc("@item filesystem name", "ocfs2"), kxi18nc("@item filesystem name", "zfs"), kxi18nc("@item filesystem name", "exfat"), kxi18nc("@item filesystem name", "nilfs2"), kxi18nc("@item filesystem name", "lvm2 pv"), kxi18nc("@item filesystem name", "f2fs"), kxi18nc("@item filesystem name", "udf"), kxi18nc("@item filesystem name", "iso9660"), kxi18nc("@item filesystem name", "luks2"), kxi18nc("@item filesystem name", "fat12"), kxi18nc("@item filesystem name", "linux_raid_member"), + kxi18nc("@item filesystem name", "BitLocker"), + kxi18nc("@item filesystem name", "apfs"), }; return s; } /** @param t the type to get the name for @return the printable name for the given type */ QString FileSystem::nameForType(FileSystem::Type t, const QStringList& languages) { Q_ASSERT(t < Type::__lastType); return typeNames()[static_cast(t)].toString(languages); } /** @param s the name to get the type for @return the type for the name or FileSystem::Unknown if not found */ FileSystem::Type FileSystem::typeForName(const QString& s, const QStringList& languages ) { for (quint32 i = 0; i < static_cast(Type::__lastType); i++) if (typeNames()[i].toString(languages) == s) return static_cast(i); return Type::Unknown; } /** @return a QList of all known types */ QList FileSystem::types() { QList result; int i = static_cast(Type::Ext2); // first "real" filesystem while (i != static_cast(Type::__lastType)) result.append(static_cast(i++)); return result; } /** @return printable menu title for mounting this FileSystem */ QString FileSystem::mountTitle() const { return xi18nc("@title:menu", "Mount"); } /** @return printable menu title for unmounting this FileSystem */ QString FileSystem::unmountTitle() const { return xi18nc("@title:menu", "Unmount"); } /** Moves a FileSystem to a new start sector. @param newStartSector where the FileSystem should be moved to */ void FileSystem::move(qint64 newStartSector) { const qint64 savedLength = length(); setFirstSector(newStartSector); setLastSector(newStartSector + savedLength - 1); } bool FileSystem::canMount(const QString& deviceNode, const QString& mountPoint) const { Q_UNUSED(deviceNode) // cannot mount if we have no mount points return !mountPoint.isEmpty(); } /** Attempt to mount this FileSystem on a given mount point @param report the report to write information to @param deviceNode the path to the device that is to be unmounted @param mountPoint the mount point to mount the FileSystem on @return true on success */ bool FileSystem::mount(Report& report, const QString &deviceNode, const QString &mountPoint) { ExternalCommand mountCmd( report, QStringLiteral("mount"), { QStringLiteral("--verbose"), deviceNode, mountPoint }); if (mountCmd.run() && mountCmd.exitCode() == 0) { return true; } return false; } /** Attempt to unmount this FileSystem @param report the report to write information to @param deviceNode the path to the device that is to be unmounted @return true on success */ bool FileSystem::unmount(Report& report, const QString& deviceNode) { ExternalCommand umountCmd( report, QStringLiteral("umount"), { QStringLiteral("--verbose"), QStringLiteral("--all-targets"), deviceNode }); if ( umountCmd.run() && umountCmd.exitCode() == 0 ) return true; 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) { 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; return cmd.exitCode() == 0 || cmd.exitCode() == expectedCode; } bool FileSystem::supportToolFound() const { return false; } 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 a411b24..7007d60 100644 --- a/src/fs/filesystem.h +++ b/src/fs/filesystem.h @@ -1,292 +1,294 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * 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 * * 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_H #define KPMCORE_FILESYSTEM_H #include "util/libpartitionmanagerexport.h" #include #include #include #include #include #include #include class QColor; class QValidator; class Device; class Report; struct FileSystemPrivate; /** Base class for all FileSystems. Represents a file system and handles support for various types of operations that can be performed on those. @author Volker Lanz */ class LIBKPMCORE_EXPORT FileSystem { Q_DISABLE_COPY(FileSystem) public: class SupportTool { public: explicit SupportTool(const QString& n = QString(), const QUrl& u = QUrl()) : name(n), url(u) {} const QString name; const QUrl url; }; /** Supported FileSystem types */ enum Type : int { Unknown, Extended, 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, + BitLocker, + Apfs, __lastType }; /** The type of support for a given FileSystem action */ enum CommandSupportType { cmdSupportNone = 0, /**< no support */ cmdSupportCore = 1, /**< internal support */ cmdSupportFileSystem = 2, /**< supported by some external command */ cmdSupportBackend = 4 /**< supported by the backend */ }; static const std::vector defaultColorCode; Q_DECLARE_FLAGS(CommandSupportTypes, CommandSupportType) protected: FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type type); public: virtual ~FileSystem(); public: virtual void init() {} virtual void scan(const QString& deviceNode); virtual qint64 readUsedCapacity(const QString& deviceNode) const; virtual QString readLabel(const QString& deviceNode) const; virtual bool create(Report& report, const QString& deviceNode); virtual bool createWithLabel(Report& report, const QString& deviceNode, const QString& label); virtual bool resize(Report& report, const QString& deviceNode, qint64 newLength) const; virtual bool resizeOnline(Report& report, const QString& deviceNode, const QString& mountPoint, qint64 newLength) const; virtual bool move(Report& report, const QString& deviceNode, qint64 newStartSector) const; virtual bool writeLabel(Report& report, const QString& deviceNode, const QString& newLabel); virtual bool writeLabelOnline(Report& report, const QString& deviceNode, const QString& mountPoint, const QString& newLabel); virtual bool copy(Report& report, const QString& targetDeviceNode, const QString& sourceDeviceNode) const; virtual bool backup(Report& report, const Device& sourceDevice, const QString& deviceNode, const QString& filename) const; virtual bool remove(Report& report, const QString& deviceNode) const; virtual bool check(Report& report, const QString& deviceNode) const; virtual bool updateUUID(Report& report, const QString& deviceNode) const; virtual QString readUUID(const QString& deviceNode) const; virtual bool updateBootSector(Report& report, const QString& deviceNode) const; virtual CommandSupportType supportGetUsed() const { return cmdSupportNone; /**< @return CommandSupportType for getting used capacity */ } virtual CommandSupportType supportGetLabel() const { return cmdSupportNone; /**< @return CommandSupportType for reading label*/ } virtual CommandSupportType supportCreate() const { return cmdSupportNone; /**< @return CommandSupportType for creating */ } virtual CommandSupportType supportCreateWithLabel() const { return cmdSupportNone; /**< @return CommandSupportType for creating */ } virtual CommandSupportType supportGrow() const { return cmdSupportNone; /**< @return CommandSupportType for growing */ } virtual CommandSupportType supportGrowOnline() const { return cmdSupportNone; /**< @return CommandSupportType for online growing */ } virtual CommandSupportType supportShrink() const { return cmdSupportNone; /**< @return CommandSupportType for shrinking */ } virtual CommandSupportType supportShrinkOnline() const { return cmdSupportNone; /**< @return CommandSupportType for shrinking */ } virtual CommandSupportType supportMove() const { return cmdSupportNone; /**< @return CommandSupportType for moving */ } virtual CommandSupportType supportCheck() const { return cmdSupportNone; /**< @return CommandSupportType for checking */ } virtual CommandSupportType supportCheckOnline() const { return cmdSupportNone; /**< @return CommandSupportType for checking */ } virtual CommandSupportType supportCopy() const { return cmdSupportNone; /**< @return CommandSupportType for copying */ } virtual CommandSupportType supportBackup() const { return cmdSupportNone; /**< @return CommandSupportType for backing up */ } virtual CommandSupportType supportSetLabel() const { return cmdSupportNone; /**< @return CommandSupportType for setting label */ } virtual CommandSupportType supportSetLabelOnline() const { return cmdSupportNone; /**< @return CommandSupportType for setting label of mounted file systems */ } virtual CommandSupportType supportUpdateUUID() const { return cmdSupportNone; /**< @return CommandSupportType for updating the UUID */ } virtual CommandSupportType supportGetUUID() const { return cmdSupportNone; /**< @return CommandSupportType for reading the UUID */ } virtual qint64 minCapacity() const; virtual qint64 maxCapacity() const; virtual int maxLabelLength() const; virtual QValidator* labelValidator(QObject *parent = nullptr) const; virtual SupportTool supportToolName() const; virtual bool supportToolFound() const; /** * Returns the (possibly translated) name of the type of this filesystem. * @see nameForType() */ virtual QString name(const QStringList& languages = {}) const; /** * @return the FileSystem's type */ virtual FileSystem::Type type() const; /** * Returns the name of the given filesystem type. If @p languages * is an empty list, uses the translated name of the filesystem, * in the default locale. If languages is {"C"}, an untranslated * string is returned. Passing other lists of language identifiers * may yield unpredicatable results -- see the documentation of * KLocalizedString() for details on the way toString() is used. * Returns a single QString with the name. */ static QString nameForType(FileSystem::Type t, const QStringList& languages = {}); static QList types(); static FileSystem::Type typeForName(const QString& s, const QStringList& languages = {}); static FileSystem::Type detectFileSystem(const QString& partitionPath); static QString detectMountPoint(FileSystem* fs, const QString& partitionPath); static bool detectMountStatus(FileSystem* fs, const QString& partitionPath); /**< @return true if this FileSystem can be mounted */ virtual bool canMount(const QString& deviceNode, const QString& mountPoint) const; virtual bool canUnmount(const QString&) const { return true; /**< @return true if this FileSystem can be unmounted */ } virtual QString mountTitle() const; virtual QString unmountTitle() const; virtual bool mount(Report& report, const QString& deviceNode, const QString& mountPoint); virtual bool unmount(Report& report, const QString& deviceNode); /**< @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 */ } qint64 firstByte() const { return firstSector() * sectorSize(); /**< @return the FileSystem's first byte */ } qint64 lastByte() const { return firstByte() + length() * sectorSize() - 1; /**< @return the FileSystem's last byte */ } /**< @param s the new first sector */ void setFirstSector(qint64 s); /**< @param s the new last sector */ void setLastSector(qint64 s); void move(qint64 newStartSector); /**< @return the FileSystem's label */ const QString& label() const; /**< @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); std::unique_ptr d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileSystem::CommandSupportTypes) #endif diff --git a/src/fs/filesystemfactory.cpp b/src/fs/filesystemfactory.cpp index 6f99054..77ba066 100644 --- a/src/fs/filesystemfactory.cpp +++ b/src/fs/filesystemfactory.cpp @@ -1,180 +1,186 @@ /************************************************************************* * Copyright (C) 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 "fs/filesystemfactory.h" #include "fs/filesystem.h" +#include "fs/apfs.h" +#include "fs/bitlocker.h" #include "fs/btrfs.h" #include "fs/exfat.h" #include "fs/ext2.h" #include "fs/ext3.h" #include "fs/ext4.h" #include "fs/extended.h" #include "fs/f2fs.h" #include "fs/fat12.h" #include "fs/fat16.h" #include "fs/fat32.h" #include "fs/hfs.h" #include "fs/hfsplus.h" #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" #include "fs/lvm2_pv.h" #include "fs/nilfs2.h" #include "fs/ntfs.h" #include "fs/ocfs2.h" #include "fs/reiser4.h" #include "fs/reiserfs.h" #include "fs/udf.h" #include "fs/ufs.h" #include "fs/unformatted.h" #include "fs/unknown.h" #include "fs/xfs.h" #include "fs/zfs.h" #include "backend/corebackendmanager.h" #include "backend/corebackend.h" FileSystemFactory::FileSystems FileSystemFactory::m_FileSystems; /** Initializes the instance. */ void FileSystemFactory::init() { qDeleteAll(m_FileSystems); m_FileSystems.clear(); + m_FileSystems.insert(FileSystem::Type::Apfs, new FS::apfs(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Type::BitLocker, new FS::bitlocker(-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(); CoreBackendManager::self()->backend()->initFSSupport(); } /** Creates a new FileSystem @param t the FileSystem's type @param firstsector the FileSystem's first sector relative to the Device @param lastsector the FileSystem's last sector relative to the Device @param sectorsused the number of used sectors in the FileSystem @param label the FileSystem's label @return pointer to the newly created FileSystem object or nullptr if FileSystem could not be created */ FileSystem* FileSystemFactory::create(FileSystem::Type t, qint64 firstsector, qint64 lastsector, qint64 sectorSize, qint64 sectorsused, const QString& label, const QString& uuid) { FileSystem* fs = nullptr; switch (t) { - 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::Apfs: fs = new FS::apfs (firstsector, lastsector, sectorsused, label); break; + case FileSystem::Type::BitLocker: fs = new FS::bitlocker (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; + 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; } if (fs != nullptr) { fs->setUUID(uuid); fs->setSectorSize(sectorSize); } return fs; } /** @overload */ FileSystem* FileSystemFactory::create(const FileSystem& other) { return create(other.type(), other.firstSector(), other.lastSector(), other.sectorSize(), other.sectorsUsed(), other.label(), other.uuid()); } /** @return the map of FileSystems */ const FileSystemFactory::FileSystems& FileSystemFactory::map() { return m_FileSystems; } /** Clones a FileSystem from another one, but with a new type. @param newType the new FileSystem's type @param other the old FileSystem to clone @return pointer to the newly created FileSystem or nullptr in case of errors */ FileSystem* FileSystemFactory::cloneWithNewType(FileSystem::Type newType, const FileSystem& other) { return create(newType, other.firstSector(), other.lastSector(), other.sectorSize(), other.sectorsUsed(), other.label()); } diff --git a/src/fs/unknown.cpp b/src/fs/unknown.cpp index 3f6e27f..173d596 100644 --- a/src/fs/unknown.cpp +++ b/src/fs/unknown.cpp @@ -1,33 +1,36 @@ /************************************************************************* * Copyright (C) 2008 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 .* *************************************************************************/ #include "fs/unknown.h" namespace FS { + +FileSystem::CommandSupportType unknown::m_Move = FileSystem::cmdSupportNone; + unknown::unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Unknown) { } bool unknown::canMount(const QString & deviceNode, const QString & mountPoint) const { Q_UNUSED(deviceNode) Q_UNUSED(mountPoint) return false; } } diff --git a/src/fs/unknown.h b/src/fs/unknown.h index 3d20d67..cf87ca4 100644 --- a/src/fs/unknown.h +++ b/src/fs/unknown.h @@ -1,46 +1,51 @@ /************************************************************************* * Copyright (C) 2008 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_UNKNOWN_H) - +#ifndef KPMCORE_UNKNOWN_H #define KPMCORE_UNKNOWN_H #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include namespace FS { /** A pseudo file system for partitions whose file system we cannot determine. @author Volker Lanz */ class LIBKPMCORE_EXPORT unknown : public FileSystem { public: unknown(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); public: bool supportToolFound() const override { return true; } bool canMount(const QString & deviceNode, const QString & mountPoint) const override; + + CommandSupportType supportMove() const override { + return m_Move; + } + + static CommandSupportType m_Move; }; } #endif diff --git a/src/gui/partresizerwidget.h b/src/gui/partresizerwidget.h index 7265ae6..929223b 100644 --- a/src/gui/partresizerwidget.h +++ b/src/gui/partresizerwidget.h @@ -1,215 +1,215 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_PARTRESIZERWIDGET_H) #define KPMCORE_PARTRESIZERWIDGET_H #include "util/libpartitionmanagerexport.h" #include #include class Partition; class PartWidget; class Device; class NewDialog; class QPaintEvent; class QResizeEvent; class QMouseEvent; /** Widget that allows the user to resize a Partition. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartResizerWidget : public QWidget { friend class NewDialog; Q_OBJECT Q_DISABLE_COPY(PartResizerWidget) public: PartResizerWidget(QWidget* parent); public: void init(Device& d, Partition& p, qint64 minFirst, qint64 maxLast, bool read_only = false, bool move_allowed = true); qint64 totalSectors() const { return maximumLastSector() - minimumFirstSector() + 1; /**< @return total sectors (free + Partition's length) */ } qint64 minimumFirstSector(bool aligned = false) const; /**< @return the lowest allowed first sector */ void setMinimumFirstSector(qint64 s) { m_MinimumFirstSector = s; /**< @param s the new lowest allowed first sector */ } qint64 maximumFirstSector(bool aligned = false) const; /**< @return the highest allowed first sector */ void setMaximumFirstSector(qint64 s) { m_MaximumFirstSector = s; /**< @param s the new highest allowed first sector */ } qint64 minimumLastSector(bool aligned = false) const; /**< @return the lowest allowed last sector */ void setMinimumLastSector(qint64 s) { m_MinimumLastSector = s; /**< @param s the new lowest allowed last sector */ } qint64 maximumLastSector(bool aligned = false) const; /**< @return the highest allowed last sector */ void setMaximumLastSector(qint64 s) { m_MaximumLastSector = s; /**< @param s the new highest allowed last sector */ } void setMinimumLength(qint64 s); qint64 minimumLength() const { return m_MinimumLength; /**< @return minimum length for Partition */ } void setMaximumLength(qint64 s); qint64 maximumLength() const { return m_MaximumLength; /**< @return maximum length for the Partition */ } void setMoveAllowed(bool b); bool moveAllowed() const { return m_MoveAllowed; /**< @return true if moving the Partition is allowed */ } bool readOnly() const { return m_ReadOnly; /**< @return true if the widget is read only */ } void setReadOnly(bool b) { m_ReadOnly = b; /**< @param b the new value for read only */ } bool align() const { return m_Align; /**< @return true if the Partition is to be aligned */ } void setAlign(bool b) { m_Align = b; /**< @param b the new value for aligning the Partition */ } qint32 handleWidth() const; /**< @return the handle width in pixels */ static qint32 handleHeight() { return m_HandleHeight; /**< @return the handle height in pixels */ } Q_SIGNALS: void firstSectorChanged(qint64); void lastSectorChanged(qint64); public: bool updateFirstSector(qint64 newFirstSector); bool updateLastSector(qint64 newLastSector); bool movePartition(qint64 newFirstSector); protected: Partition& partition() { Q_ASSERT(m_Partition); return *m_Partition; } const Partition& partition() const { Q_ASSERT(m_Partition); return *m_Partition; } void setPartition(Partition& p) { m_Partition = &p; } Device& device() { Q_ASSERT(m_Device); return *m_Device; } const Device& device() const { Q_ASSERT(m_Device); return *m_Device; } void setDevice(Device& d) { m_Device = &d; } - void paintEvent(QPaintEvent* event); - void resizeEvent(QResizeEvent* event); - void mousePressEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); - void mouseReleaseEvent(QMouseEvent* event); + void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; PartWidget& partWidget() { Q_ASSERT(m_PartWidget); return *m_PartWidget; } const PartWidget& partWidget() const { Q_ASSERT(m_PartWidget); return *m_PartWidget; } void updatePositions(); int partWidgetStart() const; int partWidgetWidth() const; QLabel& leftHandle() { return m_LeftHandle; } QLabel& rightHandle() { return m_RightHandle; } long double sectorsPerPixel() const; void set(qint64 newCap, qint64 newFreeBefore, qint64 newFreeAfter); void resizeLogicals(qint64 deltaFirst, qint64 deltaLast, bool force = false); bool checkAlignment(const Partition& child, qint64 delta) const; QWidget* draggedWidget() { return m_DraggedWidget; } const QWidget* draggedWidget() const { return m_DraggedWidget; } bool checkConstraints(qint64 first, qint64 last) const; private: Device* m_Device; Partition* m_Partition; PartWidget* m_PartWidget; qint64 m_MinimumFirstSector; qint64 m_MaximumFirstSector; qint64 m_MinimumLastSector; qint64 m_MaximumLastSector; qint64 m_MinimumLength; qint64 m_MaximumLength; QLabel m_LeftHandle; QLabel m_RightHandle; QWidget* m_DraggedWidget; int m_Hotspot; bool m_MoveAllowed; bool m_ReadOnly; bool m_Align; static const qint32 m_HandleHeight; }; #endif diff --git a/src/gui/partwidget.h b/src/gui/partwidget.h index 8275483..dde4500 100644 --- a/src/gui/partwidget.h +++ b/src/gui/partwidget.h @@ -1,81 +1,81 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_PARTWIDGET_H) #define KPMCORE_PARTWIDGET_H #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include "partwidgetbase.h" #include class Partition; class QPaintEvent; class QResizeEvent; /** Widget that represents a Partition. Represents a single Partition (possibly with its children, in case of an extended Partition) in the GUI. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartWidget : public PartWidgetBase { Q_OBJECT public: explicit PartWidget(QWidget* parent, Partition* p = nullptr); void init(Partition* p); void setActive(bool b) { m_Active = b; } bool isActive() const { return m_Active; /**< @return true if this is the currently active widget */ } void updateChildren(); Partition* partition() { return m_Partition; /**< @return the widget's Partition */ } const Partition* partition() const { return m_Partition; /**< @return the widget's Partition */ } void setFileSystemColorCode( const std::vector& colorCode ); protected: - void paintEvent(QPaintEvent* event); - void resizeEvent(QResizeEvent* event); + void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; QColor activeColor(const QColor& col) const; void drawGradient(QPainter* painter, const QColor& color, const QRect& rect, bool active = false) const; private: Partition* m_Partition; bool m_Active; std::vector m_fileSystemColorCode; }; #endif diff --git a/src/ops/copyoperation.cpp b/src/ops/copyoperation.cpp index 08e670c..e3e9d37 100644 --- a/src/ops/copyoperation.cpp +++ b/src/ops/copyoperation.cpp @@ -1,330 +1,330 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "ops/copyoperation.h" #include "core/partition.h" #include "core/device.h" #include "jobs/createpartitionjob.h" #include "jobs/deletepartitionjob.h" #include "jobs/checkfilesystemjob.h" #include "jobs/copyfilesystemjob.h" #include "jobs/resizefilesystemjob.h" #include "fs/filesystemfactory.h" #include "util/capacity.h" #include "util/report.h" #include #include #include /** Creates a new CopyOperation. @param targetdevice the Device to copy the Partition to @param copiedpartition pointer to the new Partition object on the target Device. May not be nullptr. @param sourcedevice the Device where to copy from @param sourcepartition pointer to the Partition to copy from. May not be nullptr. */ CopyOperation::CopyOperation(Device& targetdevice, Partition* copiedpartition, Device& sourcedevice, Partition* sourcepartition) : Operation(), m_TargetDevice(targetdevice), m_CopiedPartition(copiedpartition), m_SourceDevice(sourcedevice), m_SourcePartition(sourcepartition), m_OverwrittenPartition(nullptr), m_MustDeleteOverwritten(false), m_CheckSourceJob(nullptr), m_CreatePartitionJob(nullptr), m_CopyFSJob(nullptr), m_CheckTargetJob(nullptr), m_MaximizeJob(nullptr), m_Description(updateDescription()) { Q_ASSERT(targetDevice().partitionTable()); Partition* dest = targetDevice().partitionTable()->findPartitionBySector(copiedPartition().firstSector(), PartitionRole(PartitionRole::Primary | PartitionRole::Logical | PartitionRole::Unallocated)); if (dest == nullptr) qWarning() << "destination partition not found at sector " << copiedPartition().firstSector(); Q_ASSERT(dest); if (dest && !dest->roles().has(PartitionRole::Unallocated)) { copiedPartition().setLastSector(dest->lastSector()); setOverwrittenPartition(dest); } addJob(m_CheckSourceJob = new CheckFileSystemJob(sourcePartition())); if (overwrittenPartition() == nullptr) addJob(m_CreatePartitionJob = new CreatePartitionJob(targetDevice(), copiedPartition())); addJob(m_CopyFSJob = new CopyFileSystemJob(targetDevice(), copiedPartition(), sourceDevice(), sourcePartition())); addJob(m_CheckTargetJob = new CheckFileSystemJob(copiedPartition())); addJob(m_MaximizeJob = new ResizeFileSystemJob(targetDevice(), copiedPartition())); } CopyOperation::~CopyOperation() { if (status() == StatusPending) delete m_CopiedPartition; if (status() == StatusFinishedSuccess || status() == StatusFinishedWarning || status() == StatusError) cleanupOverwrittenPartition(); } bool CopyOperation::targets(const Device& d) const { return d == targetDevice(); } bool CopyOperation::targets(const Partition& p) const { return p == copiedPartition(); } void CopyOperation::preview() { if (overwrittenPartition()) removePreviewPartition(targetDevice(), *overwrittenPartition()); insertPreviewPartition(targetDevice(), copiedPartition()); } void CopyOperation::undo() { removePreviewPartition(targetDevice(), copiedPartition()); if (overwrittenPartition()) insertPreviewPartition(targetDevice(), *overwrittenPartition()); } bool CopyOperation::execute(Report& parent) { bool rval = false; bool warning = false; Report* report = parent.newChild(description()); // check the source first if ((rval = checkSourceJob()->run(*report))) { // At this point, if the target partition is to be created and not overwritten, it // will still have the wrong device path (the one of the source device). We need // to adjust that before we're creating it. copiedPartition().setDevicePath(targetDevice().deviceNode()); // either we have no partition to create (because we're overwriting) or creating // must be successful if (!createPartitionJob() || (rval = createPartitionJob()->run(*report))) { // set the state of the target partition from StateCopy to StateNone or checking // it will fail (because its deviceNode() will still be "Copy of sdXn"). This is // only required for overwritten partitions, but doesn't hurt in any case. copiedPartition().setState(Partition::State::None); // if we have overwritten a partition, reset device path and number if (overwrittenPartition()) { copiedPartition().setDevicePath(overwrittenPartition()->devicePath()); copiedPartition().setPartitionPath(overwrittenPartition()->partitionPath()); } // now run the copy job itself if ((rval = copyFSJob()->run(*report))) { // and if the copy job succeeded, check the target if ((rval = checkTargetJob()->run(*report))) { // ok, everything went well rval = true; // if maximizing doesn't work, just warn the user, don't fail if (!maximizeJob()->run(*report)) { report->line() << xi18nc("@info:status", "Maximizing file system on target partition %1 to the size of the partition failed.", copiedPartition().deviceNode()); warning = true; } } else report->line() << xi18nc("@info:status", "Checking target partition %1 after copy failed.", copiedPartition().deviceNode()); } else { if (createPartitionJob()) { DeletePartitionJob deleteJob(targetDevice(), copiedPartition()); deleteJob.run(*report); } report->line() << xi18nc("@info:status", "Copying source to target partition failed."); } } else report->line() << xi18nc("@info:status", "Creating target partition for copying failed."); } else report->line() << xi18nc("@info:status", "Checking source partition %1 failed.", sourcePartition().deviceNode()); if (rval) setStatus(warning ? StatusFinishedWarning : StatusFinishedSuccess); else setStatus(StatusError); report->setStatus(xi18nc("@info:status (success, error, warning...) of operation", "%1: %2", description(), statusText())); return rval; } QString CopyOperation::updateDescription() const { if (overwrittenPartition()) { if (copiedPartition().length() == overwrittenPartition()->length()) return xi18nc("@info:status", "Copy partition %1 (%2, %3) to %4 (%5, %6)", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), overwrittenPartition()->deviceNode(), Capacity::formatByteSize(overwrittenPartition()->capacity()), overwrittenPartition()->fileSystem().name() ); return xi18nc("@info:status", "Copy partition %1 (%2, %3) to %4 (%5, %6) and grow it to %7", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), overwrittenPartition()->deviceNode(), Capacity::formatByteSize(overwrittenPartition()->capacity()), overwrittenPartition()->fileSystem().name(), Capacity::formatByteSize(copiedPartition().capacity()) ); } if (copiedPartition().length() == sourcePartition().length()) return xi18nc("@info:status", "Copy partition %1 (%2, %3) to unallocated space (starting at %4) on %5", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSize()), targetDevice().deviceNode() ); return xi18nc("@info:status", "Copy partition %1 (%2, %3) to unallocated space (starting at %4) on %5 and grow it to %6", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSize()), targetDevice().deviceNode(), Capacity::formatByteSize(copiedPartition().capacity()) ); } void CopyOperation::setOverwrittenPartition(Partition* p) { // this code is also in RestoreOperation. cleanupOverwrittenPartition(); m_OverwrittenPartition = p; // If the overwritten partition has no other operation that owns it (e.g., an OperationNew or // an OperationRestore), we're the new owner. So remember that, because after the operations all // have executed and we're asked to clean up after ourselves, the state of the overwritten partition // might have changed: If it was a new one and the NewOperation has successfully run, the state will // then be StateNone. m_MustDeleteOverwritten = (p && p->state() == Partition::State::None); } void CopyOperation::cleanupOverwrittenPartition() { if (mustDeleteOverwritten()) { delete overwrittenPartition(); m_OverwrittenPartition = nullptr; } } /** Creates a new copied Partition. @param target the target Partition to copy to (may be unallocated) @param source the source Partition to copy @return pointer to the newly created Partition object */ Partition* CopyOperation::createCopy(const Partition& target, const Partition& source) { Partition* p = target.roles().has(PartitionRole::Unallocated) ? new Partition(source) : new Partition(target); p->setDevicePath(source.devicePath()); p->setPartitionPath(source.partitionPath()); p->setState(Partition::State::Copy); p->deleteFileSystem(); p->setFileSystem(FileSystemFactory::create(source.fileSystem())); p->fileSystem().setFirstSector(p->firstSector()); p->fileSystem().setLastSector(p->lastSector()); - p->setFlags(PartitionTable::FlagNone); + p->setFlags(PartitionTable::Flag::None); return p; } /** Can a Partition be copied? @param p the Partition in question, may be nullptr. @return true if @p p can be copied. */ bool CopyOperation::canCopy(const Partition* p) { if (p == nullptr) return false; if (p->state() == Partition::State::New && p->roles().has(PartitionRole::Luks)) return false; if (p->isMounted()) return false; // FIXME: Does not work well enough yet if (p->roles().has(PartitionRole::Lvm_Lv)) return false; // Normally, copying partitions that have not been written to disk yet should // be forbidden here. The operation stack, however, will take care of these // problematic cases when pushing the CopyOperation onto the stack. return p->fileSystem().supportCopy() != FileSystem::cmdSupportNone; } /** Can a Partition be pasted on another one? @param p the Partition to be pasted to, may be nullptr @param source the Partition to be pasted, may be nullptr @return true if @p source can be pasted on @p p */ bool CopyOperation::canPaste(const Partition* p, const Partition* source) { if (p == nullptr || source == nullptr) return false; if (p->isMounted()) return false; if (p->roles().has(PartitionRole::Extended)) return false; if (p->roles().has(PartitionRole::Lvm_Lv)) return false; if (p == source) return false; if (source->length() > p->length()) return false; if (!p->roles().has(PartitionRole::Unallocated) && p->capacity() > source->fileSystem().maxCapacity()) return false; return true; } diff --git a/src/ops/newoperation.cpp b/src/ops/newoperation.cpp index fb4fcce..40bdc62 100644 --- a/src/ops/newoperation.cpp +++ b/src/ops/newoperation.cpp @@ -1,135 +1,135 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "ops/newoperation.h" #include "core/partition.h" #include "core/device.h" #include "core/partitionnode.h" #include "jobs/createpartitionjob.h" #include "jobs/createfilesystemjob.h" #include "jobs/setfilesystemlabeljob.h" #include "jobs/setpartflagsjob.h" #include "jobs/checkfilesystemjob.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/capacity.h" #include #include /** Creates a new NewOperation. @param d the Device to create a new Partition on @param p pointer to the new Partition to create. May not be nullptr. */ NewOperation::NewOperation(Device& d, Partition* p) : Operation(), m_TargetDevice(d), m_NewPartition(p), m_CreatePartitionJob(new CreatePartitionJob(targetDevice(), newPartition())), m_CreateFileSystemJob(nullptr), m_SetPartFlagsJob(nullptr), m_SetFileSystemLabelJob(nullptr), m_CheckFileSystemJob(nullptr) { addJob(createPartitionJob()); const FileSystem& fs = newPartition().fileSystem(); 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 // label. The operation stack will merge these operations with this one here // and if the jobs don't exist things will break. m_CreateFileSystemJob = new CreateFileSystemJob(targetDevice(), newPartition(), fs.label()); addJob(createFileSystemJob()); if (fs.type() == FileSystem::Type::Lvm2_PV) { - m_SetPartFlagsJob = new SetPartFlagsJob(targetDevice(), newPartition(), PartitionTable::FlagLvm); + m_SetPartFlagsJob = new SetPartFlagsJob(targetDevice(), newPartition(), PartitionTable::Flag::Lvm); addJob(setPartFlagsJob()); } m_SetFileSystemLabelJob = new SetFileSystemLabelJob(newPartition(), fs.label()); addJob(setLabelJob()); m_CheckFileSystemJob = new CheckFileSystemJob(newPartition()); addJob(checkJob()); } } NewOperation::~NewOperation() { if (status() == StatusPending) delete m_NewPartition; } bool NewOperation::targets(const Device& d) const { return d == targetDevice(); } bool NewOperation::targets(const Partition& p) const { return p == newPartition(); } void NewOperation::preview() { insertPreviewPartition(targetDevice(), newPartition()); } void NewOperation::undo() { removePreviewPartition(targetDevice(), newPartition()); } QString NewOperation::description() const { return xi18nc("@info:status", "Create a new partition (%1, %2) on %3", Capacity::formatByteSize(newPartition().capacity()), newPartition().fileSystem().name(), targetDevice().deviceNode()); } /** Can a Partition be created somewhere? @param p the Partition where a new Partition is to be created, may be nullptr @return true if a new Partition can be created in @p p */ bool NewOperation::canCreateNew(const Partition* p) { return p != nullptr && p->roles().has(PartitionRole::Unallocated); } Partition* NewOperation::createNew(const Partition& cloneFrom, FileSystem::Type type) { Partition* p = new Partition(cloneFrom); p->deleteFileSystem(); p->setFileSystem(FileSystemFactory::create(type, p->firstSector(), p->lastSector(), p->sectorSize())); p->setState(Partition::State::New); p->setPartitionPath(QString()); return p; } diff --git a/src/plugins/dummy/dummybackend.cpp b/src/plugins/dummy/dummybackend.cpp index ae6f938..cd527cb 100644 --- a/src/plugins/dummy/dummybackend.cpp +++ b/src/plugins/dummy/dummybackend.cpp @@ -1,119 +1,125 @@ /************************************************************************* * 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 .* *************************************************************************/ /** @file */ #include "plugins/dummy/dummybackend.h" #include "plugins/dummy/dummydevice.h" #include "core/diskdevice.h" #include "core/partition.h" #include "core/partitiontable.h" #include "util/globallog.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(DummyBackendFactory, "pmdummybackendplugin.json", registerPlugin();) DummyBackend::DummyBackend(QObject*, const QList&) : CoreBackend() { } void DummyBackend::initFSSupport() { } -QList DummyBackend::scanDevices(bool excludeLoop) +QList DummyBackend::scanDevices(bool excludeReadOnly) { - Q_UNUSED(excludeLoop) + Q_UNUSED(excludeReadOnly) + return scanDevices(ScanFlags()); +} + +QList DummyBackend::scanDevices(const ScanFlags scanFlags) +{ + Q_UNUSED(scanFlags) QList result; result.append(scanDevice(QStringLiteral("/dev/sda"))); emitScanProgress(QStringLiteral("/dev/sda"), 100); - return result; + return scanDevices(false); } Device* DummyBackend::scanDevice(const QString& deviceNode) { DiskDevice* d = new DiskDevice(QStringLiteral("Dummy Device"), QStringLiteral("/tmp") + deviceNode, 255, 30, 63, 512); CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(PartitionTable::msdos_sectorbased, 2048, d->totalSectors() - 2048)); CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), 128); d->partitionTable()->updateUnallocated(*d); d->setIconName(QStringLiteral("drive-harddisk")); CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), 4); return d; } FileSystem::Type DummyBackend::detectFileSystem(const QString& deviceNode) { Q_UNUSED(deviceNode) return FileSystem::Type::Unknown; } QString DummyBackend::readLabel(const QString& deviceNode) const { Q_UNUSED(deviceNode) return QString(); } QString DummyBackend::readUUID(const QString& deviceNode) const { Q_UNUSED(deviceNode) return QString(); } std::unique_ptr DummyBackend::openDevice(const Device& d) { std::unique_ptr device = std::make_unique(d.deviceNode()); if (!device->open()) device = nullptr; return device; } std::unique_ptr DummyBackend::openDeviceExclusive(const Device& d) { std::unique_ptr device = std::make_unique(d.deviceNode()); if (!device->openExclusive()) device = nullptr; return device; } bool DummyBackend::closeDevice(std::unique_ptr coreDevice) { return coreDevice->close(); } #include "dummybackend.moc" diff --git a/src/plugins/dummy/dummybackend.h b/src/plugins/dummy/dummybackend.h index a9f8fb3..aa73d7f 100644 --- a/src/plugins/dummy/dummybackend.h +++ b/src/plugins/dummy/dummybackend.h @@ -1,57 +1,58 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_DUMMYBACKEND_H) #define KPMCORE_DUMMYBACKEND_H #include "backend/corebackend.h" #include #include class Device; class KPluginFactory; class QString; /** Dummy backend plugin that doesn't really do anything. @author Volker Lanz */ class DummyBackend : public CoreBackend { friend class KPluginFactory; Q_DISABLE_COPY(DummyBackend) private: DummyBackend(QObject* parent, const QList& args); public: void initFSSupport() override; QList scanDevices(bool excludeReadOnly = false) override; + QList scanDevices(const ScanFlags scanFlags) 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; QString readUUID(const QString& deviceNode) const override; }; #endif diff --git a/src/plugins/dummy/pmdummybackendplugin.json b/src/plugins/dummy/pmdummybackendplugin.json index 93919b3..df950d4 100644 --- a/src/plugins/dummy/pmdummybackendplugin.json +++ b/src/plugins/dummy/pmdummybackendplugin.json @@ -1,89 +1,98 @@ { "KPlugin": { "Authors": [ { "Email": "vl@fidra.de", "Name": "Volker Lanz", "Name[ast]": "Volker Lanz", "Name[ca@valencia]": "Volker Lanz", "Name[ca]": "Volker Lanz", "Name[cs]": "Volker Lanz", + "Name[da]": "Volker Lanz", "Name[de]": "Volker Lanz", "Name[el]": "Volker Lanz", "Name[en_GB]": "Volker Lanz", "Name[es]": "Volker Lanz", "Name[fi]": "Volker Lanz", "Name[fr]": "Volker Lanz", "Name[gl]": "Volker Lanz", + "Name[id]": "Volker Lanz", "Name[it]": "Volker Lanz", "Name[ko]": "Volker Lanz", "Name[lt]": "Volker Lanz", "Name[nl]": "Volker Lanz", "Name[pl]": "Volker Lanz", "Name[pt]": "Volker Lanz", "Name[pt_BR]": "Volker Lanz", + "Name[ru]": "Volker Lanz", "Name[sk]": "Volker Lanz", "Name[sv]": "Volker Lanz", "Name[uk]": "Volker Lanz", "Name[x-test]": "xxVolker Lanzxx", - "Name[zh_CN]": "Volker Lanz" + "Name[zh_CN]": "Volker Lanz", + "Name[zh_TW]": "Volker Lanz" } ], "Category": "BackendPlugin", "Description": "A KDE Partition Manager dummy backend for testing purposes.", "Description[ca@valencia]": "Un dorsal fals del gestor de particions del KDE amb la finalitat de fer proves.", "Description[ca]": "Un dorsal fals del gestor de particions del KDE amb la finalitat de fer proves.", "Description[cs]": "Falešná podpůrná vrstva pro správce diskových oddílů KDE pro testovací účely.", + "Description[da]": "En KDE-partitionshåndtering med attrap-backend til testformål.", "Description[de]": "Ein Dummy-Backend für die KDE-Partitionsverwaltung zu Testzwecken.", "Description[el]": "Ένα εικονικό σύστημα υποστήριξης διαχειριστή κατατμήσεων του KDE για δοκιμές.", "Description[en_GB]": "A KDE Partition Manager dummy backend for testing purposes.", "Description[es]": "Un motor de simulación para el gestor de particiones de KDE para hacer pruebas.", "Description[fi]": "KDE:n osionhallinnan valetaustaosa testaustarkoituksiin.", "Description[fr]": "Un moteur de test pour le gestionnaire de partitions de KDE pour faire des essais.", "Description[gl]": "Unha infraestrutura de probas para o xestor de particións de KDE.", + "Description[id]": "Sebuah backend dumi Pengelola Partisi KDE untuk tujuan pengujian.", "Description[it]": "Un motore fittizio del gestore delle partizioni di KDE per scopi di prova.", "Description[ko]": "테스트를 위한 KDE 파티션 관리자 더미 백엔드입니다.", "Description[lt]": "KDE skaidinių tvarkyklės netikra galinė sąsaja skirta testavimui.", "Description[nl]": "Dummy backend van KDE-partitiebeheerder voor testdoeleinden", "Description[pl]": "Fikcyjny silnik zarządzania partycjami do prób.", "Description[pt]": "Uma infra-estrutura de testes para o Gestor de Partições do KDE.", "Description[sk]": "Testovací program v pozadí KDE Správcu partícií", "Description[sv]": "Ett bakgrundsprogram till KDE:s partitionshanterare i testsyfte.", "Description[uk]": "Тестовий додаток сервера Керування розділами KDE.", "Description[x-test]": "xxA KDE Partition Manager dummy backend for testing purposes.xx", "Description[zh_CN]": "测试用的 KDE 分区管理器的虚拟后端", + "Description[zh_TW]": "使用虛設後端的 KDE 磁碟分割區管理員,可用來測試。", "EnabledByDefault": true, "Icon": "preferences-plugin", "Id": "pmdummybackendplugin", "License": "GPL", "Name": "KDE Partition Manager Dummy Backend", "Name[ca@valencia]": "Dorsal fals del gestor de particions del KDE", "Name[ca]": "Dorsal fals del gestor de particions del KDE", "Name[cs]": "Podpůrná vrstva pro správce diskových oddílů pro KDE", + "Name[da]": "KDE-partitionshåndtering med attrap-backend", "Name[de]": "KDE-Partitionsverwaltung Dummy-Backend", "Name[el]": "KDE Εικονικό σύστημα υποστήριξης διαχειριστή κατατμήσεων", "Name[en_GB]": "KDE Partition Manager Dummy Backend", "Name[es]": "Motor de simulación para el gestor de particiones de KDE", "Name[fi]": "KDE:n osionhallinnan valetaustaosa", "Name[fr]": "Moteur de test pour le gestionnaire de partitions de KDE", "Name[gl]": "Infraestrutura de probas para o xestor de particións de KDE", - "Name[id]": "Backend Tiruan Pengelola Partisi KDE", + "Name[id]": "Backend Dumi Pengelola Partisi KDE", "Name[it]": "Motore fittizio del gestore delle partizioni di KDE", "Name[ko]": "KDE 파티션 관리자 더미 백엔드", "Name[lt]": "KDE skaidinių tvarkyklės netikra galinė sąsaja", "Name[nb]": "Attrapp-motor for KDE partisjonsbehandler", "Name[nl]": "Dummy backend van KDE-partitiebeheerder", "Name[pl]": "Fikcyjny silnik zarządzania partycjami", "Name[pt]": "Infra-Estrutura de Testes para o Gestor de Partições do KDE", "Name[sk]": "Testovací program v pozadí KDE Správcu partícií", "Name[sv]": "KDE:s partitionshanterare bakgrundsprogram för test", "Name[uk]": "Тестовий додаток сервера Керування розділами KDE", "Name[x-test]": "xxKDE Partition Manager Dummy Backendxx", "Name[zh_CN]": "KDE 分区管理器虚拟后端", + "Name[zh_TW]": "KDE 磁碟分割區管理員 (虛設後端)", "ServiceTypes": [ "PartitionManager/Plugin" ], "Version": "1", "Website": "http://www.partitionmanager.org" } } diff --git a/src/plugins/sfdisk/pmsfdiskbackendplugin.json b/src/plugins/sfdisk/pmsfdiskbackendplugin.json index 4ccb803..b70aa73 100644 --- a/src/plugins/sfdisk/pmsfdiskbackendplugin.json +++ b/src/plugins/sfdisk/pmsfdiskbackendplugin.json @@ -1,85 +1,95 @@ { "KPlugin": { "Authors": [ { "Email": "andrius@stikonas.eu", "Name": "Andrius Štikonas", "Name[ast]": "Andrius Štikonas", "Name[ca@valencia]": "Andrius Štikonas", "Name[ca]": "Andrius Štikonas", "Name[cs]": "Andrius Štikonas", + "Name[da]": "Andrius Štikonas", "Name[de]": "Andrius Štikonas", "Name[el]": "Andrius Štikonas", "Name[en_GB]": "Andrius Štikonas", "Name[es]": "Andrius Štikonas", "Name[fi]": "Andrius Štikonas", "Name[fr]": "Andrius Štikonas", "Name[gl]": "Andrius Štikonas", + "Name[id]": "Andrius Štikonas", "Name[it]": "Andrius Štikonas", "Name[ko]": "Andrius Štikonas", "Name[lt]": "Andrius Štikonas", "Name[nl]": "Andrius Štikonas", "Name[pl]": "Andrius Štikonas", "Name[pt]": "Andrius Štikonas", "Name[pt_BR]": "Andrius Štikonas", + "Name[ru]": "Andrius Štikonas", "Name[sk]": "Andrius Štikonas", "Name[sv]": "Andrius Štikonas", "Name[uk]": "Andrius Štikonas", "Name[x-test]": "xxAndrius Štikonasxx", - "Name[zh_CN]": "Andrius Štikonas" + "Name[zh_CN]": "Andrius Štikonas", + "Name[zh_TW]": "Andrius Štikonas" } ], "Category": "BackendPlugin", "Description": "A KDE Partition Manager sfdisk backend.", "Description[ca@valencia]": "Un dorsal «sfdisk» del gestor de particions del KDE.", "Description[ca]": "Un dorsal «sfdisk» del gestor de particions del KDE.", "Description[cs]": "Podpůrná vrstva sfdisk pro správce diskových oddílů pro KDE.", + "Description[da]": "En KDE-partitionshåndtering med sfdisk-backend.", "Description[de]": "Ein sfdisk-Backend für die KDE-Partitionsverwaltung.", "Description[el]": "Σύστημα υποστήριξης sfdisk διαχειριστή κατατμήσεων του KDE.", "Description[en_GB]": "A KDE Partition Manager sfdisk backend.", "Description[es]": "Motor sfdisk para el gestor de particiones de KDE.", "Description[fi]": "KDE:n osionhallinnan sfdisk-taustaosa", "Description[fr]": "Moteur sfdisk pour le gestionnaire de partitions de KDE.", "Description[gl]": "Unha infraestrutura de sfdisk para o xestor de particións de KDE.", + "Description[id]": "Sebuah backend sfdisk Pengelola Partisi KDE", "Description[it]": "Un motore sfdisk del gestore delle partizioni di KDE.", "Description[ko]": "KDE 파티션 관리자 sfdisk 백엔드입니다.", "Description[lt]": "KDE skaidinių tvarkyklės sfdisk galinė sąsaja.", "Description[nl]": "Een sfdisk backend van KDE-partitiebeheerder.", "Description[pl]": "Silnik sfdisk zarządzania partycjami.", "Description[pt]": "A infra-estrutura do 'sfdisk' para o Gestor de Partições do KDE.", "Description[sk]": "Backend KDE správcu partícií sfdisk.", "Description[sv]": "Ett sfdisk bakgrundsprogram till KDE:s partitionshanterare", "Description[uk]": "Додаток sfdisk сервера Керування розділами KDE.", "Description[x-test]": "xxA KDE Partition Manager sfdisk backend.xx", + "Description[zh_TW]": "使用 sfdisk 作為後端的 KDE 磁碟分割區管理員。", "EnabledByDefault": true, "Icon": "preferences-plugin", "Id": "pmsfdiskbackendplugin", "License": "GPL", "Name": "KDE Partition Manager sfdisk Backend", "Name[ca@valencia]": "Dorsal «sfdisk» del gestor de particions del KDE", "Name[ca]": "Dorsal «sfdisk» del gestor de particions del KDE", "Name[cs]": "Podpůrná vrstva sfdisk pro správce diskových oddílů pro KDE", + "Name[da]": "KDE-partitionshåndtering med sfdisk-backend", "Name[de]": "KDE-Partitionsverwaltung sfdisk-Backend", "Name[el]": "KDE Σύστημα υποστήριξης sfdisk διαχειριστή κατατμήσεων", "Name[en_GB]": "KDE Partition Manager sfdisk Backend", "Name[es]": "Motor sfdisk para el gestor de particiones de KDE", "Name[fi]": "KDE:n osionhallinnan sfdisk-taustaosa", "Name[fr]": "Moteur sfdisk pour le gestionnaire de partitions de KDE", "Name[gl]": "Infraestrutura de sfdisk para o xestor de particións de KDE", + "Name[id]": "Backend sfdisk Pengelola Partisi KDE", "Name[it]": "Motore sfdisk del gestore delle partizioni di KDE", "Name[ko]": "KDE 파티션 관리자 sfdisk 백엔드", "Name[lt]": "KDE skaidinių tvarkyklės sfdisk galinė sąsaja", "Name[nl]": "Sfdisk backend van KDE-partitiebeheerder", "Name[pl]": "Silnik sfdisk zarządzania partycjami", "Name[pt]": "Infra-Estrutura do Sfdisk para o Gestor de Partições do KDE", "Name[sk]": "Backend KDE správcu partícií sfdisk", "Name[sv]": "KDE:s partitionshanterare sfdisk bakgrundsprogram", "Name[uk]": "Додаток sfdisk сервера Керування розділами KDE", "Name[x-test]": "xxKDE Partition Manager sfdisk Backendxx", + "Name[zh_TW]": "KDE 磁碟分割區管理員 (sfdisk 後端)", "ServiceTypes": [ "PartitionManager/Plugin" ], "Version": "1", "Website": "http://www.partitionmanager.org" } } diff --git a/src/plugins/sfdisk/sfdiskbackend.cpp b/src/plugins/sfdisk/sfdiskbackend.cpp index b544bf5..a4458cf 100644 --- a/src/plugins/sfdisk/sfdiskbackend.cpp +++ b/src/plugins/sfdisk/sfdiskbackend.cpp @@ -1,534 +1,541 @@ /************************************************************************* * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ /** @file */ #include "plugins/sfdisk/sfdiskbackend.h" #include "plugins/sfdisk/sfdiskdevice.h" #include "core/copysourcedevice.h" #include "core/copytargetbytearray.h" #include "core/diskdevice.h" #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" #include "fs/luks2.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(SfdiskBackendFactory, "pmsfdiskbackendplugin.json", registerPlugin();) SfdiskBackend::SfdiskBackend(QObject*, const QList&) : CoreBackend() { } void SfdiskBackend::initFSSupport() { } QList SfdiskBackend::scanDevices(bool excludeReadOnly) { -// TODO: add another bool option for loopDevices + return scanDevices(excludeReadOnly ? ScanFlags() : ScanFlag::includeReadOnly); +} + +QList SfdiskBackend::scanDevices(const ScanFlags scanFlags) +{ + const bool includeReadOnly = scanFlags.testFlag(ScanFlag::includeReadOnly); + const bool includeLoopback = scanFlags.testFlag(ScanFlag::includeLoopback); + 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()); const 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")) + if (! (deviceObject[QLatin1String("type")].toString() == QLatin1String("disk") + || (includeLoopback && deviceObject[QLatin1String("type")].toString() == QLatin1String("loop")) )) + { continue; + } const QString deviceNode = deviceObject[QLatin1String("name")].toString(); - if (excludeReadOnly) { + if (!includeReadOnly) { 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); } } - - SoftwareRAID::scanSoftwareRAID(result); - LvmDevice::scanSystemLVM(result); // LVM scanner needs all other devices, so should be last + } + VolumeManagerDevice::scanDevices(result); // scan all types of VolumeManagerDevices + return result; } + /** 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* SfdiskBackend::scanDevice(const QString& deviceNode) { ExternalCommand modelCommand(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--noheadings"), QStringLiteral("--output"), QStringLiteral("model"), deviceNode }); ExternalCommand sizeCommand(QStringLiteral("blockdev"), { QStringLiteral("--getsize64"), deviceNode }); ExternalCommand sizeCommand2(QStringLiteral("blockdev"), { QStringLiteral("--getss"), deviceNode }); ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), deviceNode }, QProcess::ProcessChannelMode::SeparateChannels ); if ( sizeCommand.run(-1) && sizeCommand.exitCode() == 0 && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 && jsonCommand.run(-1) ) { Device* d = nullptr; qint64 deviceSize = sizeCommand.output().trimmed().toLongLong(); int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong(); 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+:")); 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; } - } } if ( d == nullptr && modelCommand.run(-1) && modelCommand.exitCode() == 0 ) { QString name = modelCommand.output(); - name = name.left(name.length() - 1); + name = name.left(name.length() - 1).replace(QLatin1Char('_'), QLatin1Char(' ')); if (name.trimmed().isEmpty()) { // Get 'lsblk --output kname' in the cases where the model name is not available. // As lsblk doesn't have an option to include a separator in its output, it is // necessary to run it again getting only the kname as output. ExternalCommand kname(QStringLiteral("lsblk"), {QStringLiteral("--nodeps"), QStringLiteral("--noheadings"), QStringLiteral("--output"), QStringLiteral("kname"), deviceNode}); if (kname.run(-1) && kname.exitCode() == 0) name = kname.output().trimmed(); } ExternalCommand transport(QStringLiteral("lsblk"), {QStringLiteral("--nodeps"), QStringLiteral("--noheadings"), QStringLiteral("--output"), QStringLiteral("tran"), deviceNode}); QString icon; if (transport.run(-1) && transport.exitCode() == 0) if (transport.output().trimmed() == QStringLiteral("usb")) icon = QStringLiteral("drive-removable-media-usb"); Log(Log::Level::information) << xi18nc("@info:status", "Device found: %1", name); d = new DiskDevice(name, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize, icon); } 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 { // 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 && device->deviceNode() == deviceNode) return device; } } return nullptr; } /** 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. */ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions) { Q_ASSERT(d.partitionTable()); QList partitions; for (const auto &partition : jsonPartitions) { const QJsonObject partitionObject = partition.toObject(); const QString partitionNode = partitionObject[QLatin1String("node")].toString(); const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); const qint64 size = partitionObject[QLatin1String("size")].toVariant().toLongLong(); const QString partitionType = partitionObject[QLatin1String("type")].toString(); - PartitionTable::Flags activeFlags = partitionObject[QLatin1String("bootable")].toBool() ? PartitionTable::FlagBoot : PartitionTable::FlagNone; + PartitionTable::Flags activeFlags = partitionObject[QLatin1String("bootable")].toBool() ? PartitionTable::Flag::Boot : PartitionTable::Flag::None; if (partitionType == QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B")) - activeFlags |= PartitionTable::FlagBoot; + activeFlags |= PartitionTable::Flag::Boot; else if (partitionType == QStringLiteral("21686148-6449-6E6F-744E-656564454649")) - activeFlags |= PartitionTable::FlagBiosGrub; + activeFlags |= PartitionTable::Flag::BiosGrub; 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 ) { + if ( (d.partitionTable()->type() == PartitionTable::msdos || d.partitionTable()->type() == PartitionTable::msdos_sectorbased) && + ( partitionType == QStringLiteral("5") || partitionType == QStringLiteral("f") ) ) { r = PartitionRole::Extended; type = FileSystem::Type::Extended; } // Find an extended partition this partition is in. PartitionNode* parent = d.partitionTable()->findPartitionBySector(start, PartitionRole(PartitionRole::Extended)); // None found, so it's a primary in the device's partition table. if (parent == nullptr) parent = d.partitionTable(); else r = PartitionRole::Logical; FileSystem* fs = FileSystemFactory::create(type, start, start + size - 1, d.logicalSize()); fs->scan(partitionNode); QString mountPoint; bool mounted; // sfdisk does not handle LUKS partitions if (fs->type() == FileSystem::Type::Luks || fs->type() == FileSystem::Type::Luks2) { 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, start, start + size - 1, partitionNode, availableFlags(d.partitionTable()->type()), mountPoint, mounted, activeFlags); if (!part->roles().has(PartitionRole::Luks)) readSectorsUsed(d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); if (d.partitionTable()->type() == PartitionTable::TableType::gpt) { part->setLabel(partitionObject[QLatin1String("name")].toString()); part->setUUID(partitionObject[QLatin1String("uuid")].toString()); } 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); } 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; + qint64 firstUsableSector = 0; + qint64 lastUsableSector; - if ( d.type() == Device::Type::Disk_Device ) - { + if (d.type() == Device::Type::Disk_Device) { const DiskDevice* diskDevice = static_cast(&d); lastUsableSector = diskDevice->totalSectors(); } - else if ( d.type() == Device::Type::SoftwareRAID_Device ) - { + 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; QByteArray gptHeader; CopySourceDevice source(d, 512, 1023); CopyTargetByteArray target(gptHeader); ExternalCommand copyCmd; if (copyCmd.copyBlocks(source, target)) { 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); + break; } 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::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()); } else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSize()); } FileSystem::Type SfdiskBackend::detectFileSystem(const QString& partitionPath) { FileSystem::Type rval = FileSystem::Type::Unknown; ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), partitionPath }); if (udevCommand.run(-1) && udevCommand.exitCode() == 0) { QRegularExpression re(QStringLiteral("ID_FS_TYPE=(\\w+)")); QRegularExpression re2(QStringLiteral("ID_FS_VERSION=(\\w+)")); QRegularExpressionMatch reFileSystemType = re.match(udevCommand.output()); QRegularExpressionMatch reFileSystemVersion = re2.match(udevCommand.output()); QString s; if (reFileSystemType.hasMatch()) { s = reFileSystemType.captured(1); } QString version; if (reFileSystemVersion.hasMatch()) { version = reFileSystemVersion.captured(1); } 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::Type::Fat32; else if (version == QStringLiteral("FAT16")) rval = FileSystem::Type::Fat16; else if (version == QStringLiteral("FAT12")) rval = FileSystem::Type::Fat12; } 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::Type::Luks; else if (version == QStringLiteral("2")) { rval = FileSystem::Type::Luks2; } } 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 if (s == QStringLiteral("BitLocker")) rval = FileSystem::Type::BitLocker; + else if (s == QStringLiteral("apfs")) rval = FileSystem::Type::Apfs; else qWarning() << "unknown file system type " << s << " on " << partitionPath; } return rval; } QString SfdiskBackend::readLabel(const QString& deviceNode) const { ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), deviceNode }); udevCommand.run(); QRegularExpression re(QStringLiteral("ID_FS_LABEL=(.*)")); QRegularExpressionMatch reFileSystemLabel = re.match(udevCommand.output()); if (reFileSystemLabel.hasMatch()) return reFileSystemLabel.captured(1); return QString(); } QString SfdiskBackend::readUUID(const QString& deviceNode) const { ExternalCommand udevCommand(QStringLiteral("udevadm"), { QStringLiteral("info"), QStringLiteral("--query=property"), deviceNode }); udevCommand.run(); QRegularExpression re(QStringLiteral("ID_FS_UUID=(.*)")); QRegularExpressionMatch reFileSystemUUID = re.match(udevCommand.output()); if (reFileSystemUUID.hasMatch()) return reFileSystemUUID.captured(1); return QString(); } PartitionTable::Flags SfdiskBackend::availableFlags(PartitionTable::TableType type) { PartitionTable::Flags flags; if (type == PartitionTable::gpt) { // These are not really flags but for now keep them for compatibility // We should implement changing partition type - flags = PartitionTable::Flag::FlagBiosGrub | - PartitionTable::Flag::FlagBoot; + flags = PartitionTable::Flag::BiosGrub | + PartitionTable::Flag::Boot; } else if (type == PartitionTable::msdos || type == PartitionTable::msdos_sectorbased) - flags = PartitionTable::FlagBoot; + flags = PartitionTable::Flag::Boot; return flags; } std::unique_ptr SfdiskBackend::openDevice(const Device& d) { std::unique_ptr device = std::make_unique(d); if (!device->open()) device = nullptr; return device; } std::unique_ptr SfdiskBackend::openDeviceExclusive(const Device& d) { std::unique_ptr device = std::make_unique(d); if (!device->openExclusive()) device = nullptr; return device; } bool SfdiskBackend::closeDevice(std::unique_ptr coreDevice) { return coreDevice->close(); } #include "sfdiskbackend.moc" diff --git a/src/plugins/sfdisk/sfdiskbackend.h b/src/plugins/sfdisk/sfdiskbackend.h index dcc7952..9a659c8 100644 --- a/src/plugins/sfdisk/sfdiskbackend.h +++ b/src/plugins/sfdisk/sfdiskbackend.h @@ -1,65 +1,66 @@ /************************************************************************* * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(SFDISKBACKEND__H) #define SFDISKBACKEND__H #include "backend/corebackend.h" #include "core/partition.h" #include "fs/filesystem.h" #include #include class Device; class KPluginFactory; class QString; /** Backend plugin for sfdisk @author Andrius Štikonas */ class SfdiskBackend : public CoreBackend { friend class KPluginFactory; Q_DISABLE_COPY(SfdiskBackend) private: SfdiskBackend(QObject* parent, const QList& args); public: void initFSSupport() override; QList scanDevices(bool excludeReadOnly = false) override; + QList scanDevices(const ScanFlags scanFlags) 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; QString readUUID(const QString& deviceNode) const override; 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); }; #endif diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 3d13efd..fa420bf 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -1,271 +1,271 @@ /************************************************************************* * Copyright (C) 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "plugins/sfdisk/sfdiskpartitiontable.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/partition.h" #include "core/device.h" #include "core/raid/softwareraid.h" #include "fs/filesystem.h" #include "util/report.h" #include "util/externalcommand.h" #include #include #include #include #include SfdiskPartitionTable::SfdiskPartitionTable(const Device* d) : CoreBackendPartitionTable(), m_device(d) { } SfdiskPartitionTable::~SfdiskPartitionTable() { } bool SfdiskPartitionTable::open() { return true; } bool SfdiskPartitionTable::commit(quint32 timeout) { if (m_device->type() == Device::Type::SoftwareRAID_Device) ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("control"), QStringLiteral("--stop-exec-queue") }).run(); ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run(); ExternalCommand(QStringLiteral("blockdev"), { QStringLiteral("--rereadpt"), m_device->deviceNode() }).run(); QThread::msleep(1000); 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; } QString SfdiskPartitionTable::createPartition(Report& report, const Partition& partition) { if ( !(partition.roles().has(PartitionRole::Extended) || partition.roles().has(PartitionRole::Logical) || partition.roles().has(PartitionRole::Primary) ) ) { report.line() << xi18nc("@info:progress", "Unknown partition role for new partition %1 (roles: %2)", partition.deviceNode(), partition.roles().toString()); return QString(); } QByteArray type = QByteArray(); if (partition.roles().has(PartitionRole::Extended)) type = QByteArrayLiteral(" type=5"); // NOTE: at least on GPT partition types "are" partition flags ExternalCommand createCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--append"), partition.devicePath() } ); if ( createCommand.write(QByteArrayLiteral("start=") + QByteArray::number(partition.firstSector()) + type + QByteArrayLiteral(" size=") + QByteArray::number(partition.length()) + QByteArrayLiteral("\nwrite\n")) && createCommand.start(-1) ) { QRegularExpression re(QStringLiteral("Created a new partition (\\d+)")); QRegularExpressionMatch rem = re.match(createCommand.output()); if (rem.hasMatch()) { if ( partition.devicePath().back().isDigit() ) return partition.devicePath() + QLatin1Char('p') + rem.captured(1); else return partition.devicePath() + rem.captured(1); } } report.line() << xi18nc("@info:progress", "Failed to add partition %1 to device %2.", partition.deviceNode(), m_device->deviceNode()); return QString(); } bool SfdiskPartitionTable::deletePartition(Report& report, const Partition& partition) { ExternalCommand deleteCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), QStringLiteral("--delete"), partition.devicePath(), QString::number(partition.number()) } ); if (deleteCommand.run(-1) && deleteCommand.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Could not delete partition %1.", partition.devicePath()); return false; } bool SfdiskPartitionTable::updateGeometry(Report& report, const Partition& partition, qint64 sectorStart, qint64 sectorEnd) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--force"), partition.devicePath(), QStringLiteral("-N"), QString::number(partition.number()) } ); if ( sfdiskCommand.write(QByteArrayLiteral("start=") + QByteArray::number(sectorStart) + QByteArrayLiteral(" size=") + QByteArray::number(sectorEnd - sectorStart + 1) + QByteArrayLiteral("\nY\n")) && sfdiskCommand.start(-1) && sfdiskCommand.exitCode() == 0) { return true; } report.line() << xi18nc("@info:progress", "Could not set geometry for partition %1 while trying to resize/move it.", partition.devicePath()); return false; } bool SfdiskPartitionTable::clobberFileSystem(Report& report, const Partition& partition) { ExternalCommand wipeCommand(report, QStringLiteral("wipefs"), { QStringLiteral("--all"), partition.partitionPath() } ); if (wipeCommand.run(-1) && wipeCommand.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Failed to erase filesystem signature on partition %1.", partition.partitionPath()); return false; } bool SfdiskPartitionTable::resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) { // sfdisk does not have any partition resize capabilities Q_UNUSED(report) Q_UNUSED(partition) Q_UNUSED(newLength) return false; } FileSystem::Type SfdiskPartitionTable::detectFileSystemBySector(Report& report, const Device& device, qint64 sector) { FileSystem::Type type = FileSystem::Type::Unknown; ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), device.deviceNode() } ); if (jsonCommand.run(-1) && jsonCommand.exitCode() == 0) { const QJsonArray partitionTable = QJsonDocument::fromJson(jsonCommand.rawOutput()).object()[QLatin1String("partitiontable")].toObject()[QLatin1String("partitions")].toArray(); for (const auto &partition : partitionTable) { const QJsonObject partitionObject = partition.toObject(); const qint64 start = partitionObject[QLatin1String("start")].toVariant().toLongLong(); if (start == sector) { const QString deviceNode = partitionObject[QLatin1String("node")].toString(); type = CoreBackendManager::self()->backend()->detectFileSystem(deviceNode); return type; } } } report.line() << xi18nc("@info:progress", "Could not determine file system of partition at sector %1 on device %2.", sector, device.deviceNode()); return type; } static struct { FileSystem::Type type; QLatin1String partitionType[2]; // GPT, MBR } typemap[] = { { FileSystem::Type::Btrfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext3, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ext4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::LinuxSwap, { QLatin1String("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"), QLatin1String("82") } }, { FileSystem::Type::Fat12, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Type::Fat16, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Type::Fat32, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::Nilfs2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Ntfs, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::Exfat, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Type::ReiserFS, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Reiser4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Xfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Jfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Type::Hfs, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af")} }, { FileSystem::Type::HfsPlus, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af") } }, { FileSystem::Type::Udf, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } } // Add ZFS too }; static QLatin1String getPartitionType(FileSystem::Type t, PartitionTable::TableType tableType) { quint8 type; switch (tableType) { case PartitionTable::TableType::gpt: type = 0; break; case PartitionTable::TableType::msdos: case PartitionTable::TableType::msdos_sectorbased: type = 1; break; default:; return QLatin1String(); } for (quint32 i = 0; i < sizeof(typemap) / sizeof(typemap[0]); i++) if (typemap[i].type == t) return typemap[i].partitionType[type]; return QLatin1String(); } bool SfdiskPartitionTable::setPartitionSystemType(Report& report, const Partition& partition) { QString partitionType = getPartitionType(partition.fileSystem().type(), m_device->partitionTable()->type()); if (partitionType.isEmpty()) return true; ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), partitionType } ); return sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0; } bool SfdiskPartitionTable::setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) { if (m_device->partitionTable()->type() == PartitionTable::TableType::msdos || m_device->partitionTable()->type() == PartitionTable::TableType::msdos_sectorbased) { // We only allow setting one active partition per device - if (flag == PartitionTable::Flag::FlagBoot && state == true) { + if (flag == PartitionTable::Flag::Boot && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QString::number(partition.number()) } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; - } else if (flag == PartitionTable::Flag::FlagBoot && state == false) { + } else if (flag == PartitionTable::Flag::Boot && state == false) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QStringLiteral("-") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } } - if (flag == PartitionTable::Flag::FlagBoot && state == true) { + if (flag == PartitionTable::Flag::Boot && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } - if (flag == PartitionTable::Flag::FlagBoot && state == false) + if (flag == PartitionTable::Flag::Boot && state == false) setPartitionSystemType(report, partition); - if (flag == PartitionTable::Flag::FlagBiosGrub && state == true) { + if (flag == PartitionTable::Flag::BiosGrub && state == true) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--part-type"), m_device->deviceNode(), QString::number(partition.number()), QStringLiteral("21686148-6449-6E6F-744E-656564454649") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; else return false; } - if (flag == PartitionTable::Flag::FlagBiosGrub && state == false) + if (flag == PartitionTable::Flag::BiosGrub && state == false) setPartitionSystemType(report, partition); return true; } diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 34dcbb9..c761f92 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,66 +1,66 @@ set(application_interface_xml org.kde.kpmcore.applicationinterface.xml) set(helper_interface_xml org.kde.kpmcore.helperinterface.xml) qt5_generate_dbus_interface( util/externalcommand.h ${application_interface_xml} OPTIONS -a ) qt5_generate_dbus_interface( util/externalcommandhelper.h ${helper_interface_xml} OPTIONS -a ) qt5_add_dbus_interface(ApplicationInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${application_interface_xml} externalcommand_interface) qt5_add_dbus_interface(HelperInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${helper_interface_xml} externalcommandhelper_interface) set(UTIL_SRC ${HelperInterface_SRCS} util/capacity.cpp util/externalcommand.cpp util/globallog.cpp util/helpers.cpp util/htmlreport.cpp util/report.cpp ) set(UTIL_LIB_HDRS util/libpartitionmanagerexport.h util/capacity.h util/externalcommand.h util/globallog.h util/helpers.h util/htmlreport.h util/report.h ) add_executable(kpmcore_externalcommand ${ApplicationInterface_SRCS} util/externalcommandhelper.cpp ) add_executable(kpmcore_mdadmupdateconf util/mdadmupdateconf.cpp ) add_definitions( -DLIBEXECDIRPATH="${LIBEXEC_INSTALL_DIR}" ) target_link_libraries(kpmcore_externalcommand qca-qt5 Qt5::Core Qt5::DBus - KF5::Auth + KF5::AuthCore KF5::I18n ) install(TARGETS kpmcore_mdadmupdateconf DESTINATION ${LIBEXEC_INSTALL_DIR}) 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.h b/src/util/capacity.h index 1346080..215d822 100644 --- a/src/util/capacity.h +++ b/src/util/capacity.h @@ -1,90 +1,90 @@ /************************************************************************* * Copyright (C) 2008 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 .* *************************************************************************/ #ifndef KPMCORE_CAPACITY_H #define KPMCORE_CAPACITY_H #include "util/libpartitionmanagerexport.h" class Partition; class Device; #include /** Represent any kind of capacity. Any kind of capacity that can be expressed in units of Byte, KiB, MiB and so on. Also prints capacities in nicely formatted ways. @author Volker Lanz */ class LIBKPMCORE_EXPORT Capacity { public: /** Units we can deal with */ - enum Unit : uint { Byte, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB }; + enum class Unit : uint8_t {Byte, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB }; /** Type of capacity to print */ - enum class Type { Used, Available, Total }; + enum class Type : uint8_t { Used, Available, Total }; /** Flags for printing */ - enum class Flag { NoFlags = 0, AppendUnit = 1, AppendBytes = 2 }; + enum class Flag : uint8_t { NoFlags = 0, AppendUnit = 1, AppendBytes = 2 }; Q_DECLARE_FLAGS(Flags, Flag) public: explicit Capacity(qint64 size); explicit Capacity(const Partition& p, Type t = Type::Total); Capacity(const Device& d); public: bool operator==(const Capacity& other) const { return other.m_Size == m_Size; } bool operator!=(const Capacity& other) const { return other.m_Size != m_Size; } bool operator>(const Capacity& other) const { return other.m_Size > m_Size; } bool operator<(const Capacity& other) const { return other.m_Size < m_Size; } bool operator>=(const Capacity& other) const { return other.m_Size >= m_Size; } bool operator<=(const Capacity& other) const { return other.m_Size <= m_Size; } qint64 toInt(Unit u) const; double toDouble(Unit u) const; bool isValid() const; static QString formatByteSize(double size, int precision = 2); static const QString& invalidString() { return m_InvalidString; /**< @return string representing an invalid capacity */ } static QString unitName(Unit u, qint64 val = 1); static qint64 unitFactor(Unit from, Unit to); private: qint64 m_Size; static const QString m_InvalidString; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Capacity::Flags) #endif diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index 72b1b14..75a2459 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -1,482 +1,484 @@ /************************************************************************* * 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 * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "backend/corebackendmanager.h" #include "core/device.h" #include "core/copysource.h" #include "core/copytarget.h" #include "core/copytargetbytearray.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" #include "util/globallog.h" #include "util/externalcommand.h" #include "util/report.h" #include "externalcommandhelper_interface.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) : d(std::make_unique()) { 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. @param report the Report to write output to. @param cmd the command to run @param args the arguments to pass to the command */ ExternalCommand::ExternalCommand(Report& report, const QString& cmd, const QStringList& args, const QProcess::ProcessChannelMode processChannelMode) : d(std::make_unique()) { d->m_Report = report.newChild(); d->m_Command = cmd; d->m_Args = args; d->m_ExitCode = -1; d->m_Output = QByteArray(); d->processChannelMode = processChannelMode; } ExternalCommand::~ExternalCommand() { } // void ExternalCommand::setup() // { // connect(this, qOverload(&QProcess::finished), this, &ExternalCommand::onFinished); // connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput); // } /** Executes the external command. @param timeout timeout to wait for the process to start @return true on success */ bool ExternalCommand::start(int timeout) { Q_UNUSED(timeout) if (command().isEmpty()) return false; if (report()) report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" )) qDebug() << xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" "))); QString cmd = QStandardPaths::findExecutable(command()); if (cmd.isEmpty()) cmd = QStandardPaths::findExecutable(command(), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); if (!QDBusConnection::systemBus().isConnected()) { qWarning() << "Could not connect to DBus system bus"; return false; } auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days bool rval = false; QByteArray request; const quint64 nonce = interface->getNonce(); 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 = interface->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; } bool ExternalCommand::copyBlocks(CopySource& source, CopyTarget& target) { bool rval = true; const qint64 blockSize = 10 * 1024 * 1024; // number of bytes per block to copy if (!QDBusConnection::systemBus().isConnected()) { qWarning() << "Could not connect to DBus system bus"; return false; } // TODO KF6:Use new signal-slot syntax connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days QByteArray request; const quint64 nonce = interface->getNonce(); 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); QDBusPendingCall pcall = interface->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.value()[QStringLiteral("success")].toBool(); CopyTargetByteArray *byteArrayTarget = dynamic_cast(&target); if (byteArrayTarget) byteArrayTarget->m_Array = reply.value()[QStringLiteral("targetByteArray")].toByteArray(); } setExitCode(!rval); }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); return rval; } bool ExternalCommand::writeData(Report& commandReport, const QByteArray& buffer, const QString& deviceNode, const quint64 firstByte) { d->m_Report = commandReport.newChild(); if (report()) report()->setCommand(xi18nc("@info:status", "Command: %1 %2", command(), args().join(QStringLiteral(" ")))); bool rval = true; if (!QDBusConnection::systemBus().isConnected()) { qWarning() << "Could not connect to DBus system bus"; return false; } auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus(), this); interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days QByteArray request; const quint64 nonce = interface->getNonce(); request.setNum(nonce); request.append(buffer); request.append(deviceNode.toUtf8()); request.append(QByteArray::number(firstByte)); QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); QDBusPendingCall pcall = interface->writeData(privateKey->signMessage(hash, QCA::EMSA3_Raw), nonce, buffer, deviceNode, firstByte); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); QEventLoop loop; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { QDBusPendingReply reply = *watcher; rval = reply.argumentAt<0>(); } setExitCode(!rval); }; connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); loop.exec(); return rval; } bool ExternalCommand::write(const QByteArray& input) { + if ( qEnvironmentVariableIsSet( "KPMCORE_DEBUG" )) + qDebug() << "Command input:" << QString::fromLocal8Bit(input); d->m_Input = input; return true; } /** Runs the command. @param timeout timeout to use for waiting when starting and when waiting for the process to finish @return true on success */ bool ExternalCommand::run(int timeout) { return start(timeout) /* && exitStatus() == 0*/; } void ExternalCommand::onReadOutput() { // const QByteArray s = readAllStandardOutput(); // // if(m_Output.length() > 10*1024*1024) { // prevent memory overflow for badly corrupted file systems // if (report()) // report()->line() << xi18nc("@info:status", "(Command is printing too much output)"); // return; // } // // m_Output += s; // // if (report()) // *report() << QString::fromLocal8Bit(s); } void ExternalCommand::setCommand(const QString& cmd) { d->m_Command = cmd; } const QString& ExternalCommand::command() const { return d->m_Command; } const QStringList& ExternalCommand::args() const { return d->m_Args; } void ExternalCommand::addArg(const QString& s) { d->m_Args << s; } void ExternalCommand::setArgs(const QStringList& args) { d->m_Args = args; } int ExternalCommand::exitCode() const { return d->m_ExitCode; } const QString ExternalCommand::output() const { return QString::fromLocal8Bit(d->m_Output); } const QByteArray& ExternalCommand::rawOutput() const { return d->m_Output; } Report* ExternalCommand::report() { return d->m_Report; } void ExternalCommand::setExitCode(int i) { d->m_ExitCode = i; } bool ExternalCommand::startHelper() { if (!QDBusConnection::systemBus().isConnected()) { - qWarning() << "Could not connect to DBus session bus"; + qWarning() << "Could not connect to DBus system bus"; return false; } QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); if (iface.isValid()) { exit(0); } d->m_thread = new DBusThread; d->m_thread->start(); 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::stopHelper() { auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"), QStringLiteral("/Helper"), QDBusConnection::systemBus()); QByteArray request; const quint64 nonce = interface->getNonce(); request.setNum(nonce); QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); interface->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; + quint64 rval = 0; auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { loop.exit(); if (watcher->isError()) qWarning() << watcher->error(); else { - QDBusPendingReply reply = *watcher; + 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_whitelist.h b/src/util/externalcommand_whitelist.h index 12207d0..7d81719 100644 --- a/src/util/externalcommand_whitelist.h +++ b/src/util/externalcommand_whitelist.h @@ -1,107 +1,104 @@ /************************************************************************* * 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_EXTERNALCOMMAND_WHITELIST_H #define KPMCORE_EXTERNALCOMMAND_WHITELIST_H -const QString allowedCommands[] = { -// TODO try to remove these later -QStringLiteral("mv"), - +QString allowedCommands[] = { // TODO no root needed QStringLiteral("lsblk"), QStringLiteral("udevadm"), //Core programs QStringLiteral("blockdev"), QStringLiteral("sfdisk"), QStringLiteral("wipefs"), QStringLiteral("lvm"), QStringLiteral("mdadm"), QStringLiteral("mount"), QStringLiteral("umount"), QStringLiteral("smartctl"), // FileSystem utilties QStringLiteral("btrfs"), QStringLiteral("mkfs.btrfs"), QStringLiteral("btrfstune"), QStringLiteral("exfatfsck"), QStringLiteral("mkfs.exfat"), QStringLiteral("exfatlabel"), QStringLiteral("dumpe2fs"), QStringLiteral("e2fsck"), QStringLiteral("mkfs.ext2"), QStringLiteral("resize2fs"), QStringLiteral("e2label"), QStringLiteral("tune2fs"), QStringLiteral("mkfs.ext3"), QStringLiteral("mkfs.ext4"), QStringLiteral("mkfs.f2fs"), QStringLiteral("fsck.f2fs"), QStringLiteral("resize.f2fs"), QStringLiteral("fsck.fat"), QStringLiteral("fatlabel"), QStringLiteral("mkfs.fat"), QStringLiteral("fatresize"), QStringLiteral("hfsck"), QStringLiteral("hformat"), QStringLiteral("fsck.hfsplus"), QStringLiteral("mkfs.hfsplus"), QStringLiteral("jfs_debugfs"), QStringLiteral("jfs_tune"), QStringLiteral("fsck.jfs"), QStringLiteral("mkfs.jfs"), QStringLiteral("mkswap"), QStringLiteral("swaplabel"), QStringLiteral("swapon"), QStringLiteral("swapoff"), QStringLiteral("cryptsetup"), QStringLiteral("dmsetup"), QStringLiteral("fsck.nilfs2"), QStringLiteral("mkfs.nilfs2"), QStringLiteral("nilfs-tune"), QStringLiteral("nilfs-resize"), QStringLiteral("ntfsresize"), QStringLiteral("mkfs.ntfs"), QStringLiteral("ntfsclone"), QStringLiteral("ntfslabel"), QStringLiteral("fsck.ocfs2"), QStringLiteral("mkfs.ocfs2"), QStringLiteral("debugfs.ocfs2"), QStringLiteral("tunefs.ocfs2"), QStringLiteral("debugfs.reiser4"), QStringLiteral("fsck.reiser4"), QStringLiteral("mkfs.reiser4"), QStringLiteral("debugreiserfs"), QStringLiteral("reiserfstune"), QStringLiteral("fsck.reiserfs"), QStringLiteral("mkfs.reiserfs"), QStringLiteral("resize_reiserfs"), QStringLiteral("mkudffs"), QStringLiteral("udfinfo"), QStringLiteral("udflabel"), QStringLiteral("xfs_db"), QStringLiteral("xfs_repair"), QStringLiteral("mkfs.xfs"), QStringLiteral("xfs_copy"), QStringLiteral("xfs_growfs"), QStringLiteral("zpool"), QStringLiteral("kpmcore_mdadmupdateconf") }; #endif diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp index 008c4c9..7330232 100644 --- a/src/util/externalcommandhelper.cpp +++ b/src/util/externalcommandhelper.cpp @@ -1,387 +1,387 @@ /************************************************************************* * 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 "externalcommand_interface.h" #include "externalcommand_whitelist.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 a DBus service that we monitor for changes. * If helper is not busy then it exits when the client services gets * unregistered. Otherwise, * we wait for the current job to finish before exiting, so even in case * of main application crash, we do not leave partially moved data. * * 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()); m_loop = std::make_unique(); HelperSupport::progressStep(QVariantMap()); // End the loop and return only once the client is done using us. auto serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"), QDBusConnection::systemBus(), QDBusServiceWatcher::WatchForUnregistration, this); connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, [this]() { m_loop->exit(); }); 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) +bool ExternalCommandHelper::readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const 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 %2.", offset, 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) +bool ExternalCommandHelper::writeData(const QString &targetDevice, const QByteArray& buffer, const 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 %2.", offset, targetDevice); return false; } if (device.write(buffer) != buffer.size()) { qCritical() << xi18n("Could not write to device %1.", targetDevice); return false; } return true; } // If targetDevice is empty then return QByteArray with data that was read from disk. QVariantMap 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) { 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(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"); reply[QStringLiteral("success")] = false; return reply; } 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 && !targetDevice.isEmpty()) { 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) { if (targetDevice.isEmpty()) reply[QStringLiteral("targetByteArray")] = buffer; else rval = writeData(targetDevice, buffer, lastBlockWriteOffset); } if (rval) { HelperSupport::progressStep(100); bytesWritten += buffer.size(); } } report[QStringLiteral("report")] = xi18ncp("@info:progress argument 2 is a string such as 7 bytes (localized accordingly)", "Copying 1 block (%2) finished.", "Copying %1 blocks (%2) finished.", blocksCopied, i18np("1 byte", "%1 bytes", bytesWritten)); HelperSupport::progressStep(report); reply[QStringLiteral("success")] = rval; return reply; } bool ExternalCommandHelper::writeData(const QByteArray& signature, const quint64 nonce, const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte) { if (m_Nonces.find(nonce) != m_Nonces.end()) m_Nonces.erase( nonce ); else return false; QByteArray request; request.setNum(nonce); request.append(buffer); request.append(targetDevice.toUtf8()); request.append(QByteArray::number(targetFirstByte)); // Do not allow using this helper for writing to arbitrary location if ( targetDevice.left(5) != QStringLiteral("/dev/") && !targetDevice.contains(QStringLiteral("/etc/fstab"))) return false; QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); if (!m_publicKey.verifyMessage(hash, signature, QCA::EMSA3_Raw)) { qCritical() << xi18n("Invalid cryptographic signature"); return false; } return writeData(targetDevice, buffer, targetFirstByte); } 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; } if (command.isEmpty()) { 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; } // Compare with command whitelist QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1); if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) { // TODO: notify the user m_loop->exit(); 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 index 6cb14c8..508dd23 100644 --- a/src/util/externalcommandhelper.h +++ b/src/util/externalcommandhelper.h @@ -1,71 +1,71 @@ /************************************************************************* * 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 #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); + bool readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const qint64 size); + bool writeData(const QString& targetDevice, const QByteArray& buffer, const 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 QVariantMap 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 bool writeData(const QByteArray& signature, const quint64 nonce, const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte); Q_SCRIPTABLE void exit(const QByteArray& signature, const quint64 nonce); private: void onReadOutput(); std::unique_ptr 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/org.kde.kpmcore.externalcommand.actions b/src/util/org.kde.kpmcore.externalcommand.actions index 4117d5f..8ef3251 100644 --- a/src/util/org.kde.kpmcore.externalcommand.actions +++ b/src/util/org.kde.kpmcore.externalcommand.actions @@ -1,49 +1,54 @@ [Domain] Icon=partitionmanager [org.kde.kpmcore.externalcommand.init] Name=Start external command daemon Name[ca]=Inicia el dimoni d'ordres externes Name[ca@valencia]=Inicia el dimoni d'ordres externes Name[cs]=Spustit démona externích příkazů +Name[da]=Start ekstern kommando-dæmon Name[de]=Externen Befehlsdienst starten Name[el]=Εκκίνηση διεργασίας με εξωτερική εντολή Name[en_GB]=Start external command daemon Name[es]=Iniciar el demonio de órdenes externas Name[fi]=Käynnistä ulkoinen komentopalvelu Name[fr]=Lancer le démon de la commande externe Name[gl]=Iniciar o servizo de orde externa Name[it]=Avvia demone per comando esterno Name[ko]=외부 명령 데몬 시작 Name[lt]=Paleisti išorinių komandų tarnybą Name[nl]=Start externe opdrachtdaemon Name[pl]=Rozpocznij usługę zewnętrznego polecenia Name[pt]=Iniciar o servidor de comandos externos +Name[pt_BR]=Iniciar comando externo do daemon Name[sk]=Spustiť externé démony príkazov Name[sv]=Starta extern kommandodemon Name[uk]=Запуск фонової служби зовнішньої команди Name[x-test]=xxStart external command daemonxx +Name[zh_TW]=啟動外部指令守護程式 Description=Administrative privileges are required to manage disks Description[ast]=Ríquense los privilexos alministrativos pa xestionar discos Description[ca]=Es requereixen privilegis d'administrador per gestionar discs Description[ca@valencia]=Es requereixen privilegis d'administrador per gestionar discs Description[cs]=Pro správu disků jsou potřeba práva administrátora +Description[da]=Der kræves administrative rettigheder for at håndtere diske Description[de]=Systemverwalterrechte sind zur Verwaltung von Festplatten erforderlich Description[el]=Απαιτούνται δικαιώματα διαχειριστή για τη διαχείριση δίσκων Description[en_GB]=Administrative privileges are required to manage disks Description[es]=Se necesitan permisos de administrador para gestionar discos Description[fi]=Levyjen hallinta vaatii pääkäyttäjäoikeuksia Description[fr]=Vous devez disposer des privilèges d'administrateur pour gérer les disques Description[gl]=Requírense privilexios de administración para xestionar discos. Description[it]=Per gestire il disco sono richiesti privilegi di amministratore Description[ko]=디스크를 관리하려면 권한이 필요함 Description[lt]=Diskų tvarkymui reikalingos administratoriaus teisės Description[nl]=Er zijn administratieve rechten vereist om schijven te beheren Description[pl]=Do zarządzania dyskami wymagane są uprawnienia administratora Description[pt]=São necessários privilégios de administrador para gerir os discos Description[pt_BR]=São necessários privilégios administrativos para gerenciar discos Description[sv]=Administratörsprivilegier krävs för att hantera diskar Description[uk]=Для керування дисками потрібні права доступу адміністратора (root) Description[x-test]=xxAdministrative privileges are required to manage disksxx +Description[zh_TW]=管理硬碟需要管理員權限 Policy=auth_admin Persistence=session diff --git a/test/testexternalcommand.cpp b/test/testexternalcommand.cpp index aec3d31..a8d48f9 100644 --- a/test/testexternalcommand.cpp +++ b/test/testexternalcommand.cpp @@ -1,66 +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() +void run() override { ExternalCommand blkidCmd(QStringLiteral("blkid"), {}); blkidCmd.run(); qDebug().noquote() << blkidCmd.output(); } }; class runcmd2 : public QThread { public: -void run() +void run() override { 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/testlist.cpp b/test/testlist.cpp index 1960e00..703283b 100644 --- a/test/testlist.cpp +++ b/test/testlist.cpp @@ -1,112 +1,112 @@ /************************************************************************* * Copyright 2017 by Adriaan de Groot * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ // SPDX-License-Identifier: GPL-3.0+ // Lists devices #include "helpers.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/device.h" #include "core/partition.h" #include "util/capacity.h" #include #include #include #include using PartitionList = QList; // Recursive helper for flatten(), adds partitions that // are children of @p n to the list @p l. void _flatten(PartitionList& l, PartitionNode *n) { for (const auto &p : n->children()) { l.append(p); if (p->roles().has(PartitionRole::Extended)) { _flatten(l, p); } } } /** * Recursively walk the partition table and collect all the partitions * in it (also in extended partitions). Produces a sorted list. */ PartitionList flatten(PartitionTable *table) { PartitionList l; _flatten(l, table); std::sort(l.begin(), l.end(), [](const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); }); return l; } int main( int argc, char **argv ) { QCoreApplication app(argc, argv); std::unique_ptr i; if (argc != 2) { i = std::make_unique(); if (!i->isValid()) return 1; } else { i = std::make_unique( argv[1] ); if (!i->isValid()) return 1; } auto backend = CoreBackendManager::self()->backend(); if (!backend) { qWarning() << "Could not get backend."; return 1; } - const auto devices = backend->scanDevices(); + const auto devices = backend->scanDevices(ScanFlag::includeLoopback); qDebug() << "Found" << devices.length() << "devices."; for (const auto pdev : devices) { qDebug() << "Device @" << (void *)pdev; qDebug() << " " << pdev->prettyName(); const auto partitiontable = pdev->partitionTable(); qDebug() << " Partition Table @" << (void *)partitiontable << '(' << (partitiontable ? partitiontable->typeName() : QLatin1String("null")) << ')'; const auto partitionlist = flatten(partitiontable); for (const auto &p : partitionlist) qDebug() << " " << p->number() << p->partitionPath() << p->label() << Capacity::formatByteSize(p->capacity()) << p->fileSystem().name(); } return 0; }