diff --git a/src/backend/corebackend.h b/src/backend/corebackend.h index 59725a2..7829ce2 100644 --- a/src/backend/corebackend.h +++ b/src/backend/corebackend.h @@ -1,179 +1,193 @@ /************************************************************************* * 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 class CoreBackendManager; class CoreBackendDevice; class Device; class PartitionTable; class QString; /** * 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 m_id; } /** * Return the plugin's version from JSON metadata * @return the plugin's version from JSON metadata */ QString version() { return m_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. * 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; /** * Scan a single device in the system. - * @param deviceNode The path to the device that is to be scanned (e.g. /dev/sda) + * @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. If a pointer to * an instance is returned, it's the caller's responsibility to delete the * object. */ virtual CoreBackendDevice* openDevice(const Device& d) = 0; /** * Open a device in exclusive mode for writing. * @param deviceNode The path of the device that is to be opened (e.g. /dev/sda) * @return a pointer to a CoreBackendDevice or nullptr if the open failed. If a pointer to * an instance is returned, it's the caller's responsibility to delete the * object. */ virtual CoreBackendDevice* openDeviceExclusive(const Device& d) = 0; /** * Close a CoreBackendDevice that has previously been opened. * @param core_device Pointer to the CoreBackendDevice to be closed. Must not be nullptr. * @return true if closing the CoreBackendDevice succeeded, otherwise false. * * This method does not delete the object. */ virtual bool closeDevice(CoreBackendDevice* core_device) = 0; /** * 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) { m_id = id; } void setVersion(const QString& version) { m_version = version; } private: QString m_id, m_version; class CoreBackendPrivate; CoreBackendPrivate* d; }; #endif diff --git a/src/backend/corebackenddevice.h b/src/backend/corebackenddevice.h index 2928b0c..600adc6 100644 --- a/src/backend/corebackenddevice.h +++ b/src/backend/corebackenddevice.h @@ -1,123 +1,121 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_COREBACKENDDEVICE_H) #define KPMCORE_COREBACKENDDEVICE_H -#include "util/libpartitionmanagerexport.h" - #include class CoreBackendPartition; class CoreBackendPartitionTable; class Partition; class PartitionTable; class Report; /** * Interface class for devices in the backend plugin. * For a device description, see Device. This * CoreBackendDevice can be used for (read- and) write * operations on the raw device. * * @author Volker Lanz */ -class LIBKPMCORE_EXPORT CoreBackendDevice +class CoreBackendDevice { public: CoreBackendDevice(const QString& deviceNode); virtual ~CoreBackendDevice() {} public: /** * Get the device path for this device (e.g. "/dev/sda") * @return the device path */ virtual const QString& deviceNode() const { return m_DeviceNode; } /** * Determine if this device is opened in exclusive mode. * @return true if it is opened in exclusive mode, otherwise false */ virtual bool isExclusive() const { return m_Exclusive; } /** * Open the backend device * @return true if successful */ virtual bool open() = 0; /** * Open the backend device in exclusive mode * @return true if successful */ virtual bool openExclusive() = 0; /** * Close the backend device * @return true if successful */ virtual bool close() = 0; /** * Open this backend device's partition table * @return a pointer to the CoreBackendPartitionTable for this device or nullptr in case * of errors */ virtual CoreBackendPartitionTable* openPartitionTable() = 0; /** * Create a new partition table on this device. * @param report the Report to write information to * @param ptable the PartitionTable to create on this backend device * @return true if successful */ virtual bool createPartitionTable(Report& report, const PartitionTable& ptable) = 0; /** * Read data from an opened device into a buffer. * @param buffer the buffer to write the read data to * @param offset offset byte where to start reading on the device * @param size the number of bytes to read * @return true on success */ virtual bool readData(QByteArray& buffer, qint64 offset, qint64 size) = 0; /** * Write data from a buffer to an exclusively opened device. * @param buffer the buffer with the data * @param offset offset byte where to start writing to the device * @return true on success */ virtual bool writeData(QByteArray& buffer, qint64 offset) = 0; protected: void setExclusive(bool b) { m_Exclusive = b; } private: const QString m_DeviceNode; bool m_Exclusive; }; #endif diff --git a/src/backend/corebackendpartitiontable.h b/src/backend/corebackendpartitiontable.h index 1e402ac..aae5fca 100644 --- a/src/backend/corebackendpartitiontable.h +++ b/src/backend/corebackendpartitiontable.h @@ -1,126 +1,125 @@ /************************************************************************* * Copyright (C) 2010,2011 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_COREBACKENDPARTITIONTABLE_H) #define KPMCORE_COREBACKENDPARTITIONTABLE_H #include "core/partitiontable.h" #include "fs/filesystem.h" -#include "util/libpartitionmanagerexport.h" #include class CoreBackendPartition; class Report; class Partition; /** * Interface class to represent a partition table in the backend. * @author Volker Lanz */ -class LIBKPMCORE_EXPORT CoreBackendPartitionTable +class CoreBackendPartitionTable { public: virtual ~CoreBackendPartitionTable() {} public: /** * Open the partition table * @return true on success */ virtual bool open() = 0; /** * Commit changes to the partition table to disk and to the OS. * @param timeout timeout in seconds to wait for the commit to succeed * @return true on success */ virtual bool commit(quint32 timeout = 10) = 0; /** * Delete a partition. * @param report the report to write information to * @param partition the Partition to delete * @return true on success */ virtual bool deletePartition(Report& report, const Partition& partition) = 0; /** * Delete a file system on disk so it cannot be detected anymore. * @param report the report to write information to * @param partition the Partition for which to clobber the file system * @return true on success */ virtual bool clobberFileSystem(Report& report, const Partition& partition) = 0; /** * Resize a file system to a new length. * @param report the report to write information to * @param partition the partition the FileSystem to resize is on * @param newLength the new length for the FileSystem in sectors * @return true on success */ virtual bool resizeFileSystem(Report& report, const Partition& partition, qint64 newLength) = 0; /** * Detect which FileSystem is present at a given start sector. * @param report the report to write information to * @param device the Device on which the FileSystem resides * @param sector the sector where to look for a FileSystem * @return the detected FileSystem::Type */ virtual FileSystem::Type detectFileSystemBySector(Report& report, const Device& device, qint64 sector) = 0; /** * Create a new partition. * @param report the report to write information to * @param partition the new partition to create on disk * @return the new number the OS sees the partition under (e.g. 7 for "/dev/sda7") or -1 on failure */ virtual QString createPartition(Report& report, const Partition& partition) = 0; /** * Update the geometry for a partition in the partition table. * @param report the report to write information to * @param partition the partition to update the geometry for * @param sector_start the new start sector for the partition * @param sector_end the new last sector for the partition * @return true on success */ virtual bool updateGeometry(Report& report, const Partition& partition, qint64 sector_start, qint64 sector_end) = 0; /** * Set the system type (e.g. 83 for Linux) of a partition. The type to set is taken from * the partition's file system. * @param report the report to write information to * @param partition the partition to set the system type for * @return true on success */ virtual bool setPartitionSystemType(Report& report, const Partition& partition) = 0; /** * Set a flag for the partition * @param report the Report to write information to * @param flag the flag to set * @param state the state to set the flag to (i.e., on or off) * @return true on success */ virtual bool setFlag(Report& report, const Partition& partition, PartitionTable::Flag flag, bool state) = 0; }; #endif diff --git a/src/core/operationrunner.h b/src/core/operationrunner.h index 06aee79..f91468d 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(); - LIBKPMCORE_EXPORT qint32 numJobs() const; - LIBKPMCORE_EXPORT qint32 numOperations() const; - LIBKPMCORE_EXPORT qint32 numProgressSub() const; + 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/fs/btrfs.cpp b/src/fs/btrfs.cpp index bc81b9a..e202213 100644 --- a/src/fs/btrfs.cpp +++ b/src/fs/btrfs.cpp @@ -1,197 +1,197 @@ /************************************************************************* * 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/btrfs.h" #include "util/externalcommand.h" #include "util/capacity.h" #include "util/report.h" #include #include #include #include namespace FS { FileSystem::CommandSupportType btrfs::m_GetUsed = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_GetLabel = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Create = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Grow = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Shrink = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Move = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Check = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Copy = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_Backup = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_SetLabel = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType btrfs::m_GetUUID = FileSystem::cmdSupportNone; btrfs::btrfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Btrfs) { } void btrfs::init() { m_Create = findExternal(QStringLiteral("mkfs.btrfs")) ? cmdSupportFileSystem : cmdSupportNone; - m_Check = findExternal(QStringLiteral("btrfsck"), QStringList(), 1) ? cmdSupportFileSystem : cmdSupportNone; - m_Grow = (m_Check != cmdSupportNone && findExternal(QStringLiteral("btrfs"))) ? cmdSupportFileSystem : cmdSupportNone; - m_GetUsed = findExternal(QStringLiteral("btrfs")) ? cmdSupportFileSystem : cmdSupportNone; + m_Check = findExternal(QStringLiteral("btrfs")) ? cmdSupportFileSystem : cmdSupportNone; + m_Grow = m_Check; + m_GetUsed = m_Check; m_Shrink = (m_Grow != cmdSupportNone && m_GetUsed != cmdSupportNone) ? cmdSupportFileSystem : cmdSupportNone; - m_SetLabel = findExternal(QStringLiteral("btrfs")) ? cmdSupportFileSystem : cmdSupportNone; + m_SetLabel = m_Check; m_UpdateUUID = findExternal(QStringLiteral("btrfstune")) ? cmdSupportFileSystem : cmdSupportNone; m_Copy = (m_Check != cmdSupportNone) ? cmdSupportCore : cmdSupportNone; m_Move = (m_Check != cmdSupportNone) ? cmdSupportCore : cmdSupportNone; m_GetLabel = cmdSupportCore; m_Backup = cmdSupportCore; m_GetUUID = cmdSupportCore; } bool btrfs::supportToolFound() const { return m_GetUsed != cmdSupportNone && m_GetLabel != cmdSupportNone && m_SetLabel != cmdSupportNone && m_Create != cmdSupportNone && m_Check != cmdSupportNone && // m_UpdateUUID != cmdSupportNone && m_Grow != cmdSupportNone && m_Shrink != cmdSupportNone && m_Copy != cmdSupportNone && m_Move != cmdSupportNone && m_Backup != cmdSupportNone && m_GetUUID != cmdSupportNone; } FileSystem::SupportTool btrfs::supportToolName() const { return SupportTool(QStringLiteral("btrfs-tools"), QUrl(QStringLiteral("http://btrfs.wiki.kernel.org/"))); } qint64 btrfs::minCapacity() const { return 256 * Capacity::unitFactor(Capacity::Byte, Capacity::MiB); } qint64 btrfs::maxCapacity() const { return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); } int btrfs::maxLabelLength() const { return 255; } qint64 btrfs::readUsedCapacity(const QString& deviceNode) const { ExternalCommand cmd(QStringLiteral("btrfs"), { QStringLiteral("filesystem"), QStringLiteral("show"), QStringLiteral("--raw"), deviceNode }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral(" used (\\d+) path ") + deviceNode); QRegularExpressionMatch reBytesUsed = re.match(cmd.output()); if (reBytesUsed.hasMatch()) return reBytesUsed.captured(1).toLongLong(); } return -1; } bool btrfs::check(Report& report, const QString& deviceNode) const { - ExternalCommand cmd(report, QStringLiteral("btrfsck"), { deviceNode }); + ExternalCommand cmd(report, QStringLiteral("btrfs"), { QStringLiteral("check"), QStringLiteral("--repair"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool btrfs::create(Report& report, const QString& deviceNode) { ExternalCommand cmd(report, QStringLiteral("mkfs.btrfs"), { QStringLiteral("--force"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool btrfs::resize(Report& report, const QString& deviceNode, qint64 length) const { QTemporaryDir tempDir; if (!tempDir.isValid()) { report.line() << xi18nc("@info:progress", "Resizing Btrfs file system on partition %1 failed: Could not create temp dir.", deviceNode); return false; } bool rval = false; ExternalCommand mountCmd(report, QStringLiteral("mount"), { QStringLiteral("--verbose"), QStringLiteral("--types"), QStringLiteral("btrfs"), deviceNode, tempDir.path() }); if (mountCmd.run(-1) && mountCmd.exitCode() == 0) { ExternalCommand resizeCmd(report, QStringLiteral("btrfs"), { QStringLiteral("filesystem"), QStringLiteral("resize"), QString::number(length), tempDir.path() }); if (resizeCmd.run(-1) && resizeCmd.exitCode() == 0) rval = true; else report.line() << xi18nc("@info:progress", "Resizing Btrfs file system on partition %1 failed: btrfs file system resize failed.", deviceNode); ExternalCommand unmountCmd(report, QStringLiteral("umount"), { tempDir.path() }); if (!unmountCmd.run(-1) && unmountCmd.exitCode() == 0) report.line() << xi18nc("@info:progress", "Resizing Btrfs file system on partition %1: Unmount failed.", deviceNode); } else report.line() << xi18nc("@info:progress", "Resizing Btrfs file system on partition %1 failed: Initial mount failed.", deviceNode); return rval; } bool btrfs::resizeOnline(Report& report, const QString& deviceNode, const QString& mountPoint, qint64 length) const { ExternalCommand resizeCmd(report, QStringLiteral("btrfs"), { QStringLiteral("filesystem"), QStringLiteral("resize"), QString::number(length), mountPoint }); if (resizeCmd.run(-1) && resizeCmd.exitCode() == 0) return true; report.line() << xi18nc("@info:progress", "Resizing Btrfs file system on partition %1 failed: btrfs file system resize failed.", deviceNode); return false; } bool btrfs::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) { ExternalCommand cmd(report, QStringLiteral("btrfs"), { QStringLiteral("filesystem"), QStringLiteral("label"), deviceNode, newLabel }); return cmd.run(-1) && cmd.exitCode() == 0; } bool btrfs::writeLabelOnline(Report& report, const QString& deviceNode, const QString& mountPoint, const QString& newLabel) { Q_UNUSED(deviceNode) ExternalCommand cmd(report, QStringLiteral("btrfs"), { QStringLiteral("filesystem"), QStringLiteral("label"), mountPoint, newLabel }); return cmd.run(-1) && cmd.exitCode() == 0; } bool btrfs::updateUUID(Report& report, const QString& deviceNode) const { ExternalCommand cmd(report, QStringLiteral("btrfstune"), { QStringLiteral("-f"), QStringLiteral("-u"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } } diff --git a/src/fs/filesystem.cpp b/src/fs/filesystem.cpp index 117dd26..fa4e640 100644 --- a/src/fs/filesystem.cpp +++ b/src/fs/filesystem.cpp @@ -1,574 +1,551 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/fstab.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::array< QColor, FileSystem::__lastType > 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 } }; /** Creates a new FileSystem object @param firstsector the first sector used by this FileSystem on the Device @param lastsector the last sector used by this FileSystem on the Device @param sectorsused the number of sectors in use on the FileSystem @param l the FileSystem label @param t the FileSystem type */ FileSystem::FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& l, FileSystem::Type t) : m_Type(t), m_FirstSector(firstsector), m_LastSector(lastsector), m_SectorsUsed(sectorsused), m_Label(l), m_UUID() { } /** 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; } -static QString readBlkIdValue(const QString& deviceNode, const QString& tag) -{ - blkid_cache cache; - QString rval; - - if (blkid_get_cache(&cache, nullptr) == 0) { - blkid_dev dev; - - char* label = nullptr; - if ((dev = blkid_get_dev(cache, deviceNode.toLocal8Bit().constData(), BLKID_DEV_NORMAL)) != nullptr && - (label = blkid_get_tag_value(cache, tag.toLocal8Bit().constData(), deviceNode.toLocal8Bit().constData()))) { - rval = QString::fromLocal8Bit(label); - free(label); - } - - blkid_put_cache(cache); - } - - return rval; -} - 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::Lvm2_PV) return FS::lvm2_pv::getVGName(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::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 readBlkIdValue(deviceNode, QStringLiteral("LABEL")); + 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 readBlkIdValue(deviceNode, QStringLiteral("UUID")); + 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::Byte, Capacity::MiB); } /** @return the maximum capacity valid for this FileSystem in bytes */ qint64 FileSystem::maxCapacity() const { return Capacity::unitFactor(Capacity::Byte, Capacity::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); } /** @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") }; 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 >= 0); Q_ASSERT(t < __lastType); return typeNames()[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 < __lastType; i++) if (typeNames()[i].toString(languages) == s) return static_cast(i); return Unknown; } /** @return a QList of all known types */ QList FileSystem::types() { QList result; int i = Ext2; // first "real" filesystem while (i != __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; } // FIXME: args and expectedCode is now unused. bool FileSystem::findExternal(const QString& cmdName, const QStringList& args, int expectedCode) { QString cmd = QStandardPaths::findExecutable(cmdName); if (cmd.isEmpty()) cmd = QStandardPaths::findExecutable(cmdName, { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") }); return !cmd.isEmpty(); } bool FileSystem::supportToolFound() const { return false; } FileSystem::SupportTool FileSystem::supportToolName() const { return SupportTool(); } diff --git a/src/fs/filesystem.h b/src/fs/filesystem.h index cdce228..27451a7 100644 --- a/src/fs/filesystem.h +++ b/src/fs/filesystem.h @@ -1,297 +1,297 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_FILESYSTEM_H) - #define KPMCORE_FILESYSTEM_H + #include "util/libpartitionmanagerexport.h" #include #include #include #include #include #include #include class QValidator; class Device; class Report; /** 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 { Unknown = 0, Extended = 1, Ext2 = 2, Ext3 = 3, Ext4 = 4, LinuxSwap = 5, Fat16 = 6, Fat32 = 7, Ntfs = 8, ReiserFS = 9, Reiser4 = 10, Xfs = 11, Jfs = 12, Hfs = 13, HfsPlus = 14, Ufs = 15, Unformatted = 16, Btrfs = 17, Hpfs = 18, Luks = 19, Ocfs2 = 20, Zfs = 21, Exfat = 22, Nilfs2 = 23, Lvm2_PV = 24, F2fs = 25, Udf = 26, Iso9660 = 27, Luks2 = 28, Fat12 = 29, __lastType = 30 }; /** 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::array< QColor, __lastType > defaultColorCode; Q_DECLARE_FLAGS(CommandSupportTypes, CommandSupportType) protected: FileSystem(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, FileSystem::Type t); 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; virtual FileSystem::Type type() const { return m_Type; /**< @return the FileSystem's type */ } /** * 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); qint64 firstSector() const { return m_FirstSector; /**< @return the FileSystem's first sector */ } qint64 lastSector() const { return m_LastSector; /**< @return the FileSystem's last sector */ } 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 */ } void setFirstSector(qint64 s) { m_FirstSector = s; /**< @param s the new first sector */ } void setLastSector(qint64 s) { m_LastSector = s; /**< @param s the new last sector */ } void move(qint64 newStartSector); const QString& label() const { return m_Label; /**< @return the FileSystem's label */ } qint64 sectorSize() const { return m_SectorSize; /**< @return the sector size in the underlying Device */ } qint64 sectorsUsed() const { return m_SectorsUsed; /**< @return the sectors in use on the FileSystem */ } const QString& uuid() const { return m_UUID; /**< @return the FileSystem's UUID */ } void setSectorSize(qint64 s) { m_SectorSize = s; /**< @param s the new value for sector size */ } void setSectorsUsed(qint64 s) { m_SectorsUsed = s; /**< @param s the new value for sectors in use */ } void setLabel(const QString& s) { m_Label = s; /**< @param s the new label */ } void setUUID(const QString& s) { m_UUID = s; /**< @param s the new UUID */ } protected: static bool findExternal(const QString& cmdName, const QStringList& args = QStringList(), int exptectedCode = 1); protected: FileSystem::Type m_Type; qint64 m_FirstSector; qint64 m_LastSector; qint64 m_SectorSize; qint64 m_SectorsUsed; QString m_Label; QString m_UUID; }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileSystem::CommandSupportTypes) #endif diff --git a/src/plugins/dummy/CMakeLists.txt b/src/plugins/dummy/CMakeLists.txt index b935d6e..e4895ff 100644 --- a/src/plugins/dummy/CMakeLists.txt +++ b/src/plugins/dummy/CMakeLists.txt @@ -1,29 +1,30 @@ # Copyright (C) 2010 by Volker Lanz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set (pmdummybackendplugin_SRCS dummybackend.cpp dummydevice.cpp dummypartitiontable.cpp + ${CMAKE_SOURCE_DIR}/src/backend/corebackenddevice.cpp ) add_library(pmdummybackendplugin SHARED ${pmdummybackendplugin_SRCS}) target_link_libraries(pmdummybackendplugin kpmcore) install(TARGETS pmdummybackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) kcoreaddons_desktop_to_json(pmdummybackendplugin pmdummybackendplugin.desktop DEFAULT_SERVICE_TYPE) install(FILES pmdummybackendplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/src/plugins/dummy/dummybackend.cpp b/src/plugins/dummy/dummybackend.cpp index 7e54880..5abc6f2 100644 --- a/src/plugins/dummy/dummybackend.cpp +++ b/src/plugins/dummy/dummybackend.cpp @@ -1,109 +1,123 @@ /************************************************************************* * 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) { Q_UNUSED(excludeLoop) QList result; result.append(scanDevice(QStringLiteral("/dev/sda"))); emitScanProgress(QStringLiteral("/dev/sda"), 100); return result; } 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::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(); +} + CoreBackendDevice* DummyBackend::openDevice(const Device& d) { DummyDevice* device = new DummyDevice(d.deviceNode()); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* DummyBackend::openDeviceExclusive(const Device& d) { DummyDevice* device = new DummyDevice(d.deviceNode()); if (device == nullptr || !device->openExclusive()) { delete device; device = nullptr; } return device; } bool DummyBackend::closeDevice(CoreBackendDevice* coreDevice) { return coreDevice->close(); } #include "dummybackend.moc" diff --git a/src/plugins/dummy/dummybackend.h b/src/plugins/dummy/dummybackend.h index d027171..12aad6b 100644 --- a/src/plugins/dummy/dummybackend.h +++ b/src/plugins/dummy/dummybackend.h @@ -1,55 +1,57 @@ /************************************************************************* * 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; CoreBackendDevice* openDevice(const Device& d) override; CoreBackendDevice* openDeviceExclusive(const Device& d) override; bool closeDevice(CoreBackendDevice* 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/libparted/CMakeLists.txt b/src/plugins/libparted/CMakeLists.txt index a123e44..6e21439 100644 --- a/src/plugins/libparted/CMakeLists.txt +++ b/src/plugins/libparted/CMakeLists.txt @@ -1,40 +1,41 @@ # Copyright (C) 2010 by Volker Lanz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . find_package(LIBPARTED REQUIRED) if (LIBPARTED_FILESYSTEM_SUPPORT) add_definitions(-DLIBPARTED_FILESYSTEM_SUPPORT) endif (LIBPARTED_FILESYSTEM_SUPPORT) if (LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) add_definitions(-DLIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) endif (LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT) include_directories(${LIBPARTED_INCLUDE_DIR}) set (pmlibpartedbackendplugin_SRCS libpartedbackend.cpp libparteddevice.cpp libpartedpartitiontable.cpp + ${CMAKE_SOURCE_DIR}/src/backend/corebackenddevice.cpp ) add_library(pmlibpartedbackendplugin SHARED ${pmlibpartedbackendplugin_SRCS}) target_link_libraries(pmlibpartedbackendplugin kpmcore ${LIBPARTED_LIBS} KF5::I18n) install(TARGETS pmlibpartedbackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) kcoreaddons_desktop_to_json(pmlibpartedbackendplugin pmlibpartedbackendplugin.desktop DEFAULT_SERVICE_TYPE) install(FILES pmlibpartedbackendplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/src/plugins/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp index 582fa0f..7c0e59a 100644 --- a/src/plugins/libparted/libpartedbackend.cpp +++ b/src/plugins/libparted/libpartedbackend.cpp @@ -1,570 +1,601 @@ /************************************************************************* * Copyright (C) 2008-2012 by Volker Lanz * * Copyright (C) 2015-2016 by Teo Mrnjavac * * Copyright (C) 2016-2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ /** @file */ #include "plugins/libparted/libpartedbackend.h" #include "plugins/libparted/libparteddevice.h" #include "plugins/libparted/pedflags.h" #include "core/lvmdevice.h" #include "core/partition.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "fs/fat16.h" #include "fs/hfs.h" #include "fs/hfsplus.h" #include "fs/luks.h" #include "fs/lvm2_pv.h" #include "util/globallog.h" #include "util/externalcommand.h" #include "util/helpers.h" #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin();) static QString s_lastPartedExceptionMessage; /** Callback to handle exceptions from libparted @param e the libparted exception to handle */ static PedExceptionOption pedExceptionHandler(PedException* e) { Log(Log::error) << xi18nc("@info:status", "LibParted Exception: %1", QString::fromLocal8Bit(e->message)); s_lastPartedExceptionMessage = QString::fromLocal8Bit(e->message); return PED_EXCEPTION_UNHANDLED; } // -------------------------------------------------------------------------- // The following structs and the typedef come from libparted's internal gpt sources. // It's very unfortunate there is no public API to get at the first and last usable // sector for GPT a partition table, so this is the only (libparted) way to get that // information (another way would be to read the GPT header and parse the // information ourselves; if the libparted devs begin changing these internal // structs for each point release and break our code, we'll have to do just that). typedef struct { uint32_t time_low; uint16_t time_mid; uint16_t time_hi_and_version; uint8_t clock_seq_hi_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; } /* __attribute__ ((packed)) */ efi_guid_t; struct __attribute__((packed)) _GPTDiskData { PedGeometry data_area; int entry_count; efi_guid_t uuid; }; typedef struct _GPTDiskData GPTDiskData; // -------------------------------------------------------------------------- /** Get the first sector a Partition may cover on a given Device @param d the Device in question @return the first sector usable by a Partition */ static qint64 firstUsableSector(const Device& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLocal8Bit().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; qint64 rval = 0; if (pedDisk) rval = static_cast(pedDisk->dev->bios_geom.sectors); if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); if (geom) rval = static_cast(geom->start); else rval += 32; } ped_disk_destroy(pedDisk); return rval; } /** Get the last sector a Partition may cover on a given Device @param d the Device in question @return the last sector usable by a Partition */ static qint64 lastUsableSector(const Device& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLocal8Bit().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; qint64 rval = 0; if (pedDisk) rval = static_cast< qint64 >( pedDisk->dev->bios_geom.sectors ) * static_cast< qint64 >( pedDisk->dev->bios_geom.heads ) * static_cast< qint64 >( pedDisk->dev->bios_geom.cylinders - 1 ); if (pedDisk && strcmp(pedDisk->type->name, "gpt") == 0) { GPTDiskData* gpt_disk_data = reinterpret_cast(pedDisk->disk_specific); PedGeometry* geom = reinterpret_cast(&gpt_disk_data->data_area); if (geom) rval = geom->end; else rval -= 32; } ped_disk_destroy(pedDisk); return rval; } /** Reads sectors used on a FileSystem using libparted functions. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @return the number of sectors used */ #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT static qint64 readSectorsUsedLibParted(PedDisk* pedDisk, const Partition& p) { Q_ASSERT(pedDisk); qint64 rval = -1; PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, p.firstSector()); if (pedPartition) { PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom); if (pedFileSystem) { if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem)) { rval = pedConstraint->min_size; ped_constraint_destroy(pedConstraint); } ped_file_system_close(pedFileSystem); } } return rval; } #endif /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param pedDisk pointer to pedDisk where the Partition and its FileSystem are @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ static void readSectorsUsed(PedDisk* pedDisk, const Device& d, Partition& p, const QString& mountPoint) { if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::LinuxSwap && p.fileSystem().type() != FileSystem::Lvm2_PV) { const QStorageInfo storage = QStorageInfo(mountPoint); if (p.isMounted() && storage.isValid()) p.fileSystem().setSectorsUsed( (storage.bytesTotal() - storage.bytesFree()) / d.logicalSize()); } else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSize()); #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportCore) p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(pedDisk, p)); #else Q_UNUSED(pedDisk); #endif } static PartitionTable::Flags activeFlags(PedPartition* p) { PartitionTable::Flags flags = PartitionTable::FlagNone; // We might get here with a pedPartition just picked up from libparted that is // unallocated. Libparted doesn't like it if we ask for flags for unallocated // space. if (p->num <= 0) return flags; for (const auto &flag : flagmap) if (ped_partition_is_flag_available(p, flag.pedFlag) && ped_partition_get_flag(p, flag.pedFlag)) flags |= flag.flag; return flags; } static PartitionTable::Flags availableFlags(PedPartition* p) { PartitionTable::Flags flags; // see above. if (p->num <= 0) return flags; for (const auto &flag : flagmap) { if (ped_partition_is_flag_available(p, flag.pedFlag)) { // Workaround: libparted claims the hidden flag is available for extended partitions, but // throws an error when we try to set or clear it. So skip this combination. Also see setFlag. if (p->type != PED_PARTITION_EXTENDED || flag.flag != PartitionTable::FlagHidden) flags |= flag.flag; } } return flags; } /** Constructs a LibParted object. */ LibPartedBackend::LibPartedBackend(QObject*, const QList&) : CoreBackend() { ped_exception_set_handler(pedExceptionHandler); } void LibPartedBackend::initFSSupport() { #if defined LIBPARTED_FS_RESIZE_LIBRARY_SUPPORT if (FS::fat16::m_Shrink == FileSystem::cmdSupportNone) FS::fat16::m_Shrink = FileSystem::cmdSupportBackend; if (FS::fat16::m_Grow == FileSystem::cmdSupportNone) FS::fat16::m_Grow = FileSystem::cmdSupportBackend; if (FS::hfs::m_Shrink == FileSystem::cmdSupportNone) FS::hfs::m_Shrink = FileSystem::cmdSupportBackend; if (FS::hfsplus::m_Shrink == FileSystem::cmdSupportNone) FS::hfsplus::m_Shrink = FileSystem::cmdSupportBackend; if (FS::hfs::m_GetUsed == FileSystem::cmdSupportNone) FS::hfs::m_GetUsed = FileSystem::cmdSupportBackend; if (FS::hfsplus::m_GetUsed == FileSystem::cmdSupportNone) FS::hfsplus::m_GetUsed = FileSystem::cmdSupportBackend; #endif } /** Scans a Device for Partitions. This method will scan a Device for all Partitions on it, detect the FileSystem for each Partition, try to determine the FileSystem usage, read the FileSystem label and store it all in newly created objects that are in the end added to the Device's PartitionTable. @param d Device @param pedDisk libparted pointer to the partition table */ void LibPartedBackend::scanDevicePartitions(Device& d, PedDisk* pedDisk) { Q_ASSERT(pedDisk); Q_ASSERT(d.partitionTable()); PedPartition* pedPartition = nullptr; QList partitions; while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { if (pedPartition->num < 1) continue; PartitionRole::Roles r = PartitionRole::None; FileSystem::Type type = FileSystem::Unknown; char* pedPath = ped_partition_get_path(pedPartition); const QString partitionNode = pedPath ? QString::fromLocal8Bit(pedPath) : QString(); free(pedPath); type = detectFileSystem(partitionNode); switch (pedPartition->type) { case PED_PARTITION_NORMAL: r = PartitionRole::Primary; break; case PED_PARTITION_EXTENDED: r = PartitionRole::Extended; type = FileSystem::Extended; break; case PED_PARTITION_LOGICAL: r = PartitionRole::Logical; break; default: continue; } // Find an extended partition this partition is in. PartitionNode* parent = d.partitionTable()->findPartitionBySector(pedPartition->geom.start, PartitionRole(PartitionRole::Extended)); // None found, so it's a primary in the device's partition table. if (parent == nullptr) parent = d.partitionTable(); FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end, d.logicalSize()); fs->scan(partitionNode); QString mountPoint; bool mounted; // libparted does not handle LUKS partitions if (fs->type() == FileSystem::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = static_cast(fs); luksFs->initLUKS(); QString mapperNode = luksFs->mapperName(); mountPoint = FileSystem::detectMountPoint(fs, mapperNode); mounted = FileSystem::detectMountStatus(fs, mapperNode); } else { mountPoint = FileSystem::detectMountPoint(fs, partitionNode); mounted = FileSystem::detectMountStatus(fs, partitionNode); } Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end, partitionNode, availableFlags(pedPartition), mountPoint, mounted, activeFlags(pedPartition)); if (!part->roles().has(PartitionRole::Luks)) readSectorsUsed(pedDisk, d, *part, mountPoint); if (fs->supportGetLabel() != FileSystem::cmdSupportNone) fs->setLabel(fs->readLabel(part->deviceNode())); // GPT partitions support partition labels and partition UUIDs if(d.partitionTable()->type() == PartitionTable::TableType::gpt) part->setLabel(QLatin1String(ped_partition_get_name(pedPartition))); if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(part->deviceNode())); parent->append(part); partitions.append(part); } d.partitionTable()->updateUnallocated(d); if (d.partitionTable()->isSectorBased(d)) d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased); for (const Partition * part : qAsConst(partitions)) PartitionAlignment::isAligned(d, *part); } /** Create a Device for the given device_node and scan it for partitions. @param deviceNode the device node (e.g. "/dev/sda") @return the created Device object. callers need to free this. */ DiskDevice* LibPartedBackend::scanDevice(const QString& deviceNode) { PedDevice* pedDevice = ped_device_get(deviceNode.toLocal8Bit().constData()); if (pedDevice == nullptr) { Log(Log::warning) << xi18nc("@info:status", "Could not access device %1", deviceNode); return nullptr; } Log(Log::information) << xi18nc("@info:status", "Device found: %1", QString::fromLocal8Bit(pedDevice->model)); DiskDevice* d = new DiskDevice(QString::fromLocal8Bit(pedDevice->model), QString::fromLocal8Bit(pedDevice->path), pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size); PedDisk* pedDisk = ped_disk_new(pedDevice); if (pedDisk) { const PartitionTable::TableType type = PartitionTable::nameToTableType(QString::fromLocal8Bit(pedDisk->type->name)); CoreBackend::setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector(*d), lastUsableSector(*d))); CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), ped_disk_get_max_primary_partition_count(pedDisk)); scanDevicePartitions(*d, pedDisk); } ped_device_destroy(pedDevice); return d; } QList LibPartedBackend::scanDevices(bool excludeReadOnly) { // TODO: add another bool option for loopDevices QList result; QStringList deviceNodes; ExternalCommand cmd(QStringLiteral("lsblk"), { QStringLiteral("--nodeps"), QStringLiteral("--paths"), QStringLiteral("--sort"), QStringLiteral("name"), QStringLiteral("--json"), QStringLiteral("--output"), QStringLiteral("type,name") }); if (cmd.run(-1) && cmd.exitCode() == 0) { const QJsonDocument jsonDocument = QJsonDocument::fromJson(cmd.rawOutput()); QJsonObject jsonObject = jsonDocument.object(); const QJsonArray jsonArray = jsonObject[QLatin1String("blockdevices")].toArray(); for (const auto &deviceLine : jsonArray) { QJsonObject deviceObject = deviceLine.toObject(); if (deviceObject[QLatin1String("type")].toString() != QLatin1String("disk")) continue; const QString deviceNode = deviceObject[QLatin1String("name")].toString(); if (excludeReadOnly) { QString deviceName = deviceNode; deviceName.remove(QStringLiteral("/dev/")); QFile f(QStringLiteral("/sys/block/%1/ro").arg(deviceName)); if (f.open(QIODevice::ReadOnly)) if (f.readLine().trimmed().toInt() == 1) continue; } deviceNodes << deviceNode; } int totalDevices = deviceNodes.length(); for (int i = 0; i < totalDevices; ++i) { const QString deviceNode = deviceNodes[i]; emitScanProgress(deviceNode, i * 100 / totalDevices); Device* device = scanDevice(deviceNode); if(device != nullptr) { result.append(device); } } LvmDevice::scanSystemLVM(result); } return result; } /** Detects the type of a FileSystem given a PedDevice and a PedPartition @param partitionPath path to the partition @return the detected FileSystem type (FileSystem::Unknown if not detected) */ FileSystem::Type LibPartedBackend::detectFileSystem(const QString& partitionPath) { FileSystem::Type rval = FileSystem::Unknown; blkid_cache cache; if (blkid_get_cache(&cache, nullptr) == 0) { blkid_dev dev; if ((dev = blkid_get_dev(cache, partitionPath.toLocal8Bit().constData(), BLKID_DEV_NORMAL)) != nullptr) { char *string = blkid_get_tag_value(cache, "TYPE", partitionPath.toLocal8Bit().constData()); QString s = QString::fromLocal8Bit(string); free(string); if (s == QStringLiteral("ext2")) rval = FileSystem::Ext2; else if (s == QStringLiteral("ext3")) rval = FileSystem::Ext3; else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Ext4; else if (s == QStringLiteral("swap")) rval = FileSystem::LinuxSwap; else if (s == QStringLiteral("ntfs")) rval = FileSystem::Ntfs; else if (s == QStringLiteral("reiserfs")) rval = FileSystem::ReiserFS; else if (s == QStringLiteral("reiser4")) rval = FileSystem::Reiser4; else if (s == QStringLiteral("xfs")) rval = FileSystem::Xfs; else if (s == QStringLiteral("jfs")) rval = FileSystem::Jfs; else if (s == QStringLiteral("hfs")) rval = FileSystem::Hfs; else if (s == QStringLiteral("hfsplus")) rval = FileSystem::HfsPlus; else if (s == QStringLiteral("ufs")) rval = FileSystem::Ufs; else if (s == QStringLiteral("vfat")) { // libblkid uses SEC_TYPE to distinguish between FAT16 and FAT32 string = blkid_get_tag_value(cache, "SEC_TYPE", partitionPath.toLocal8Bit().constData()); QString st = QString::fromLocal8Bit(string); free(string); if (st == QStringLiteral("msdos")) rval = FileSystem::Fat16; else rval = FileSystem::Fat32; } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Btrfs; else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Ocfs2; else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Zfs; else if (s == QStringLiteral("hpfs")) rval = FileSystem::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) rval = FileSystem::Luks; else if (s == QStringLiteral("exfat")) rval = FileSystem::Exfat; else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; else if (s == QStringLiteral("udf")) rval = FileSystem::Udf; else if (s == QStringLiteral("iso9660")) rval = FileSystem::Iso9660; else qWarning() << "blkid: unknown file system type " << s << " on " << partitionPath; } blkid_put_cache(cache); } return rval; } +static QString readBlkIdValue(const QString& deviceNode, const QString& tag) +{ + blkid_cache cache; + QString rval; + + if (blkid_get_cache(&cache, nullptr) == 0) { + blkid_dev dev; + + char* label = nullptr; + if ((dev = blkid_get_dev(cache, deviceNode.toLocal8Bit().constData(), BLKID_DEV_NORMAL)) != nullptr && + (label = blkid_get_tag_value(cache, tag.toLocal8Bit().constData(), deviceNode.toLocal8Bit().constData()))) { + rval = QString::fromLocal8Bit(label); + free(label); + } + + blkid_put_cache(cache); + } + + return rval; +} + +QString LibPartedBackend::readLabel(const QString& deviceNode) const +{ + return readBlkIdValue(deviceNode, QStringLiteral("LABEL")); +} + +QString LibPartedBackend::readUUID(const QString& deviceNode) const +{ + return readBlkIdValue(deviceNode, QStringLiteral("UUID")); +} + CoreBackendDevice* LibPartedBackend::openDevice(const Device& d) { LibPartedDevice* device = new LibPartedDevice(d.deviceNode()); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const Device& d) { LibPartedDevice* device = new LibPartedDevice(d.deviceNode()); if (device == nullptr || !device->openExclusive()) { delete device; device = nullptr; } return device; } bool LibPartedBackend::closeDevice(CoreBackendDevice* core_device) { return core_device->close(); } PedPartitionFlag LibPartedBackend::getPedFlag(PartitionTable::Flag flag) { for (const auto &f : flagmap) if (f.flag == flag) return f.pedFlag; return static_cast(-1); } QString LibPartedBackend::lastPartedExceptionMessage() { return s_lastPartedExceptionMessage; } #include "libpartedbackend.moc" diff --git a/src/plugins/libparted/libpartedbackend.h b/src/plugins/libparted/libpartedbackend.h index a5d5450..93ed320 100644 --- a/src/plugins/libparted/libpartedbackend.h +++ b/src/plugins/libparted/libpartedbackend.h @@ -1,78 +1,80 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_LIBPARTED_H) #define KPMCORE_LIBPARTED_H #include "backend/corebackend.h" #include "core/partitiontable.h" #include "core/diskdevice.h" #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include #include #include #include class LibPartedDevice; class LibPartedPartitionTable; class LibPartedPartition; class OperationStack; class KPluginFactory; class QString; /** Backend plugin for libparted. @author Volker Lanz */ class LibPartedBackend : public CoreBackend { friend class KPluginFactory; friend class LibPartedPartition; friend class LibPartedDevice; friend class LibPartedPartitionTable; Q_DISABLE_COPY(LibPartedBackend) private: LibPartedBackend(QObject* parent, const QList& args); public: void initFSSupport() override; CoreBackendDevice* openDevice(const Device& d) override; CoreBackendDevice* openDeviceExclusive(const Device& d) override; bool closeDevice(CoreBackendDevice* core_device) override; DiskDevice* scanDevice(const QString& deviceNode) override; QList scanDevices(bool excludeReadOnly = false) override; FileSystem::Type detectFileSystem(const QString& partitionPath) override; + QString readLabel(const QString& deviceNode) const override; + QString readUUID(const QString& deviceNode) const override; static QString lastPartedExceptionMessage(); private: static PedPartitionFlag getPedFlag(PartitionTable::Flag flag); void scanDevicePartitions(Device& d, PedDisk* pedDisk); }; #endif diff --git a/src/plugins/sfdisk/CMakeLists.txt b/src/plugins/sfdisk/CMakeLists.txt index 6b21c07..7c03787 100644 --- a/src/plugins/sfdisk/CMakeLists.txt +++ b/src/plugins/sfdisk/CMakeLists.txt @@ -1,28 +1,29 @@ # Copyright (C) 2010 by Volker Lanz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set (pmsfdiskbackendplugin_SRCS sfdiskbackend.cpp sfdiskdevice.cpp sfdiskpartitiontable.cpp + ${CMAKE_SOURCE_DIR}/src/backend/corebackenddevice.cpp ) add_library(pmsfdiskbackendplugin SHARED ${pmsfdiskbackendplugin_SRCS}) target_link_libraries(pmsfdiskbackendplugin kpmcore) install(TARGETS pmsfdiskbackendplugin DESTINATION ${KDE_INSTALL_PLUGINDIR}) kcoreaddons_desktop_to_json(pmsfdiskbackendplugin pmsfdiskbackendplugin.desktop) install(FILES pmsfdiskbackendplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop b/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop index 18d85e7..10857d0 100644 --- a/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop +++ b/src/plugins/sfdisk/pmsfdiskbackendplugin.desktop @@ -1,45 +1,49 @@ [Desktop Entry] Encoding=UTF-8 Name=KDE Partition Manager sfdisk Backend Name[ca]=Dorsal «sfdisk» del gestor de particions del KDE Name[ca@valencia]=Dorsal «sfdisk» del gestor de particions del KDE Name[de]=KDE-Partitionsverwaltung sfdisk-Backend +Name[es]=Motor sfdisk para el gestor de particiones de KDE Name[gl]=Infraestrutura de sfdisk do xestor de particións de KDE Name[nl]=Sfdisk-backend van KDE-partitiebeheerder Name[pl]=Silnik sfdisk zarządzania partycjami KDE Name[pt]=Infra-Estrutura do 'sfdisk' do Gestor de Partições do KDE +Name[sl]=Zaledje sfdisk za upravljalnik razdelkov za KDE Name[sr]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција Name[sr@ijekavian]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција Name[sr@ijekavianlatin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija Name[sr@latin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija Name[sv]=KDE:s partitionshanterare sfdisk bakgrundsprogram Name[uk]=Додаток sfdisk сервера Керування розділами KDE Name[x-test]=xxKDE Partition Manager sfdisk Backendxx Comment=A KDE Partition Manager sfdisk backend. Comment[ca]=Un dorsal «sfdisk» del gestor de particions del KDE. Comment[ca@valencia]=Un dorsal «sfdisk» del gestor de particions del KDE. Comment[de]=Ein sfdisk-Backend für die KDE-Partitionsverwaltung. +Comment[es]=Motor sfdisk para el gestor de particiones de KDE. Comment[gl]=Unha infraestrutura de sfdisk do xestor de particións de KDE. Comment[nl]=Sfdisk-backend van KDE-partitiebeheerder Comment[pl]=Silnik sfdisk zarządzania partycjami KDE. Comment[pt]=A infra-estrutura do 'sfdisk' do Gestor de Partições do KDE. +Comment[sl]=Zaledje sfdisk za upravljalnik razdelkov za KDE Comment[sr]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција Comment[sr@ijekavian]=Позадина sfdisk‑а за КДЕ‑ов менаџер партиција Comment[sr@ijekavianlatin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija Comment[sr@latin]=Pozadina sfdisk‑a za KDE‑ov menadžer particija Comment[sv]=KDE:s partitionshanterare sfdisk bakgrundsprogram Comment[uk]=Додаток sfdisk сервера Керування розділами KDE. Comment[x-test]=xxA KDE Partition Manager sfdisk backend.xx Type=Service ServiceTypes=PartitionManager/Plugin Icon=preferences-plugin X-KDE-Library=pmsfdiskbackendplugin X-KDE-PluginInfo-Name=pmsfdiskbackendplugin X-KDE-PluginInfo-Author=Andrius Štikonas X-KDE-PluginInfo-Email=andrius@stikonas.eu X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=BackendPlugin X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-Version=1 X-KDE-PluginInfo-Website=http://www.partitionmanager.org diff --git a/src/plugins/sfdisk/sfdiskbackend.cpp b/src/plugins/sfdisk/sfdiskbackend.cpp index 9849628..33ef5ff 100644 --- a/src/plugins/sfdisk/sfdiskbackend.cpp +++ b/src/plugins/sfdisk/sfdiskbackend.cpp @@ -1,405 +1,434 @@ /************************************************************************* * 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/diskdevice.h" #include "core/lvmdevice.h" #include "core/partitiontable.h" #include "core/partitionalignment.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 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")) continue; const QString deviceNode = deviceObject[QLatin1String("name")].toString(); if (excludeReadOnly) { QString deviceName = deviceNode; deviceName.remove(QStringLiteral("/dev/")); QFile f(QStringLiteral("/sys/block/%1/ro").arg(deviceName)); if (f.open(QIODevice::ReadOnly)) if (f.readLine().trimmed().toInt() == 1) continue; } deviceNodes << deviceNode; } int totalDevices = deviceNodes.length(); for (int i = 0; i < totalDevices; ++i) { const QString deviceNode = deviceNodes[i]; emitScanProgress(deviceNode, i * 100 / totalDevices); Device* device = scanDevice(deviceNode); if (device != nullptr) { result.append(device); } } LvmDevice::scanSystemLVM(result); } return result; } /** 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 } ); if ( modelCommand.run(-1) && modelCommand.exitCode() == 0 && sizeCommand.run(-1) && sizeCommand.exitCode() == 0 && sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0 && jsonCommand.run(-1) ) { QString modelName = modelCommand.output(); modelName = modelName.left(modelName.length() - 1); qint64 deviceSize = sizeCommand.output().trimmed().toLongLong(); Log(Log::information) << xi18nc("@info:status", "Device found: %1", modelName); int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong(); DiskDevice* d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize); if (jsonCommand.exitCode() != 0) return d; const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object(); const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject(); QString tableType = partitionTable[QLatin1String("label")].toString(); const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType); qint64 firstUsableSector = 0, lastUsableSector = d->totalSectors(); if (type == PartitionTable::gpt) { firstUsableSector = partitionTable[QLatin1String("firstlba")].toVariant().toLongLong(); lastUsableSector = partitionTable[QLatin1String("lastlba")].toVariant().toLongLong(); } if (lastUsableSector < firstUsableSector) { return nullptr; } setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector)); switch (type) { case PartitionTable::gpt: { // Read the maximum number of GPT partitions qint32 maxEntries; ExternalCommand ddCommand(QStringLiteral("dd"), { QStringLiteral("skip=1"), QStringLiteral("count=1"), QStringLiteral("if=") + deviceNode}, QProcess::SeparateChannels); if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) { QByteArray gptHeader = ddCommand.rawOutput(); QByteArray gptMaxEntries = gptHeader.mid(80, 4); QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly); stream.setByteOrder(QDataStream::LittleEndian); stream >> maxEntries; } else maxEntries = 128; CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxEntries); } default: break; } scanDevicePartitions(*d, partitionTable[QLatin1String("partitions")].toArray()); return d; } 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::Flag activeFlags = partitionObject[QLatin1String("bootable")].toBool() ? PartitionTable::FlagBoot : PartitionTable::FlagNone; if (partitionType == QStringLiteral("C12A7328-F81F-11D2-BA4B-00A0C93EC93B")) activeFlags = PartitionTable::FlagEsp; else if (partitionType == QStringLiteral("21686148-6449-6E6F-744E-656564454649")) activeFlags = PartitionTable::FlagBiosGrub; FileSystem::Type type = FileSystem::Unknown; type = detectFileSystem(partitionNode); PartitionRole::Roles r = PartitionRole::Primary; if ( (d.partitionTable()->type() == PartitionTable::msdos || d.partitionTable()->type() == PartitionTable::msdos_sectorbased) && partitionType.toInt() == 5 ) { r = PartitionRole::Extended; type = FileSystem::Extended; } // 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::Luks || fs->type() == FileSystem::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); } /** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object. @param p the Partition the FileSystem is on @param mountPoint mount point of the partition in question */ void SfdiskBackend::readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint) { if (!mountPoint.isEmpty() && p.fileSystem().type() != FileSystem::LinuxSwap && p.fileSystem().type() != FileSystem::Lvm2_PV) { 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::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::Ext2; else if (s == QStringLiteral("ext3")) rval = FileSystem::Ext3; else if (s.startsWith(QStringLiteral("ext4"))) rval = FileSystem::Ext4; else if (s == QStringLiteral("swap")) rval = FileSystem::LinuxSwap; else if (s == QStringLiteral("ntfs-3g")) rval = FileSystem::Ntfs; else if (s == QStringLiteral("reiserfs")) rval = FileSystem::ReiserFS; else if (s == QStringLiteral("reiser4")) rval = FileSystem::Reiser4; else if (s == QStringLiteral("xfs")) rval = FileSystem::Xfs; else if (s == QStringLiteral("jfs")) rval = FileSystem::Jfs; else if (s == QStringLiteral("hfs")) rval = FileSystem::Hfs; else if (s == QStringLiteral("hfsplus")) rval = FileSystem::HfsPlus; else if (s == QStringLiteral("ufs")) rval = FileSystem::Ufs; else if (s == QStringLiteral("vfat")) { if (version == QStringLiteral("FAT32")) rval = FileSystem::Fat32; else if (version == QStringLiteral("FAT16")) rval = FileSystem::Fat16; else if (version == QStringLiteral("FAT12")) rval = FileSystem::Fat12; } else if (s == QStringLiteral("btrfs")) rval = FileSystem::Btrfs; else if (s == QStringLiteral("ocfs2")) rval = FileSystem::Ocfs2; else if (s == QStringLiteral("zfs_member")) rval = FileSystem::Zfs; else if (s == QStringLiteral("hpfs")) rval = FileSystem::Hpfs; else if (s == QStringLiteral("crypto_LUKS")) { if (version == QStringLiteral("1")) rval = FileSystem::Luks; else if (version == QStringLiteral("2")) { rval = FileSystem::Luks2; } } else if (s == QStringLiteral("exfat")) rval = FileSystem::Exfat; else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; else if (s == QStringLiteral("udf")) rval = FileSystem::Udf; else if (s == QStringLiteral("iso9660")) rval = FileSystem::Iso9660; else 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 }); + + QRegularExpression re(QStringLiteral("ID_FS_LABEL=(\\w+)")); + 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 }); + QRegularExpression re(QStringLiteral("ID_FS_UUID=(\\w+)")); + 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::FlagBiosGrub | PartitionTable::FlagEsp; } else if (type == PartitionTable::msdos || type == PartitionTable::msdos_sectorbased) flags = PartitionTable::FlagBoot; return flags; } CoreBackendDevice* SfdiskBackend::openDevice(const Device& d) { SfdiskDevice* device = new SfdiskDevice(d); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* SfdiskBackend::openDeviceExclusive(const Device& d) { SfdiskDevice* device = new SfdiskDevice(d); if (device == nullptr || !device->openExclusive()) { delete device; device = nullptr; } return device; } bool SfdiskBackend::closeDevice(CoreBackendDevice* coreDevice) { return coreDevice->close(); } #include "sfdiskbackend.moc" diff --git a/src/plugins/sfdisk/sfdiskbackend.h b/src/plugins/sfdisk/sfdiskbackend.h index ad421f7..5cb3291 100644 --- a/src/plugins/sfdisk/sfdiskbackend.h +++ b/src/plugins/sfdisk/sfdiskbackend.h @@ -1,62 +1,64 @@ /************************************************************************* * 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; CoreBackendDevice* openDevice(const Device& d) override; CoreBackendDevice* openDeviceExclusive(const Device& d) override; bool closeDevice(CoreBackendDevice* 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); static PartitionTable::Flags availableFlags(PartitionTable::TableType type); }; #endif diff --git a/src/plugins/sfdisk/sfdiskpartitiontable.cpp b/src/plugins/sfdisk/sfdiskpartitiontable.cpp index 58e1e0e..79c2a55 100644 --- a/src/plugins/sfdisk/sfdiskpartitiontable.cpp +++ b/src/plugins/sfdisk/sfdiskpartitiontable.cpp @@ -1,253 +1,254 @@ /************************************************************************* * 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 "fs/filesystem.h" #include "util/report.h" #include "util/externalcommand.h" #include #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 ( !(ExternalCommand(QStringLiteral("udevadm"), { QStringLiteral("settle"), QStringLiteral("--timeout=") + QString::number(timeout) }).run() && ExternalCommand(QStringLiteral("partx"), { QStringLiteral("--update"), m_device->deviceNode() }).run()) ) sleep(timeout); 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) && createCommand.waitFor() ) { QRegularExpression re(QStringLiteral("Created a new partition (\\d)")); QRegularExpressionMatch rem = re.match(createCommand.output()); if (rem.hasMatch()) 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.waitFor() && 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::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::Btrfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Ext2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Ext3, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Ext4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::LinuxSwap, { QLatin1String("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"), QLatin1String("82") } }, { FileSystem::Fat12, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Fat16, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("6") } }, { FileSystem::Fat32, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Nilfs2, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Ntfs, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::Exfat, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } }, { FileSystem::ReiserFS, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Reiser4, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Xfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Jfs, { QLatin1String("0FC63DAF-8483-4772-8E79-3D69D8477DE4"), QLatin1String("83") } }, { FileSystem::Hfs, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af")} }, { FileSystem::HfsPlus, { QLatin1String("48465300-0000-11AA-AA11-00306543ECAC"), QLatin1String("af") } }, { FileSystem::Udf, { QLatin1String("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"), QLatin1String("7") } } // Add ZFS too }; static QLatin1String getPartitionType(FileSystem::Type t, PartitionTable::TableType tableType) { quint8 type; switch (tableType) { case PartitionTable::gpt: type = 0; break; case PartitionTable::msdos: case PartitionTable::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) { // We only allow setting one active partition per device if (flag == PartitionTable::FlagBoot && 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::FlagBoot && state == false) { ExternalCommand sfdiskCommand(report, QStringLiteral("sfdisk"), { QStringLiteral("--activate"), m_device->deviceNode(), QStringLiteral("-") } ); if (sfdiskCommand.run(-1) && sfdiskCommand.exitCode() == 0) return true; // FIXME: Do not return false since we have no way of checking if partition table is MBR } if (flag == PartitionTable::FlagEsp && 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::FlagEsp && state == false) setPartitionSystemType(report, partition); if (flag == PartitionTable::FlagBiosGrub && 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::FlagBiosGrub && state == false) setPartitionSystemType(report, partition); return true; }