diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 73e742f..60b420b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,41 +1,43 @@ set(CORE_SRC core/copysourceshred.cpp core/copysource.cpp core/partition.cpp core/mountentry.cpp core/copytargetdevice.cpp core/copytarget.cpp core/copysourcedevice.cpp core/operationrunner.cpp core/partitiontable.cpp core/copytargetfile.cpp core/smartstatus.cpp core/copysourcefile.cpp core/smartattribute.cpp core/devicescanner.cpp core/partitionnode.cpp core/partitionalignment.cpp + core/diskdevice.cpp core/device.cpp core/operationstack.cpp core/partitionrole.cpp ) set(CORE_LIB_HDRS core/copysource.h core/copysourcedevice.h core/copytarget.h core/copytargetdevice.h + core/diskdevice.h core/device.h core/devicescanner.h core/mountentry.h core/operationrunner.h core/operationstack.h core/partition.h core/partitionalignment.h core/partitionnode.h core/partitionrole.h core/partitiontable.h core/smartattribute.h core/smartstatus.h ) diff --git a/src/core/copysourcedevice.cpp b/src/core/copysourcedevice.cpp index 34a5e69..d147429 100644 --- a/src/core/copysourcedevice.cpp +++ b/src/core/copysourcedevice.cpp @@ -1,113 +1,113 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/copysourcedevice.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "backend/corebackenddevice.h" #include "core/copytarget.h" #include "core/copytargetdevice.h" #include "core/device.h" /** Constructs a CopySource on the given Device @param d Device from which to copy @param firstsector First sector that will be copied @param lastsector Last sector that will be copied */ CopySourceDevice::CopySourceDevice(Device& d, qint64 firstsector, qint64 lastsector) : CopySource(), m_Device(d), m_FirstSector(firstsector), m_LastSector(lastsector), m_BackendDevice(nullptr) { } /** Destructs a CopySourceDevice */ CopySourceDevice::~CopySourceDevice() { delete m_BackendDevice; } /** Opens the Device @return true if the Device could be successfully opened */ bool CopySourceDevice::open() { m_BackendDevice = CoreBackendManager::self()->backend()->openDeviceExclusive(device().deviceNode()); return m_BackendDevice != nullptr; } /** Returns the Device's sector size @return the sector size */ qint32 CopySourceDevice::sectorSize() const { - return device().logicalSectorSize(); + return device().logicalSize(); } /** Returns the length of this CopySource @return length of the copy source */ qint64 CopySourceDevice::length() const { return lastSector() - firstSector() + 1; } /** Reads a given number of sectors from the Device into the given buffer. Note that @p readOffset must be greater or equal than zero. @param buffer the buffer to store the read sectors in @param readOffset the offset to begin reading @param numSectors the number of sector to read @return true if successful */ bool CopySourceDevice::readSectors(void* buffer, qint64 readOffset, qint64 numSectors) { Q_ASSERT(readOffset >= 0); return m_BackendDevice->readSectors(buffer, readOffset, numSectors); } /** Checks if this CopySourceDevice overlaps with the given CopyTarget @param target the CopyTarget to check overlapping with @return true if overlaps */ bool CopySourceDevice::overlaps(const CopyTarget& target) const { try { const CopyTargetDevice& t = dynamic_cast(target); if (device().deviceNode() != t.device().deviceNode()) return false; // overlapping at the front? if (firstSector() <= t.firstSector() && lastSector() >= t.firstSector()) return true; // overlapping at the back? if (firstSector() <= t.lastSector() && lastSector() >= t.lastSector()) return true; } catch (...) { } return false; } diff --git a/src/core/copytargetdevice.cpp b/src/core/copytargetdevice.cpp index ec6b0f2..b53004a 100644 --- a/src/core/copytargetdevice.cpp +++ b/src/core/copytargetdevice.cpp @@ -1,80 +1,80 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/copytargetdevice.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "backend/corebackenddevice.h" #include "core/device.h" /** Constructs a device to copy to. @param d the Device to copy to @param firstsector the first sector on the Device to write to @param lastsector the last sector on the Device to write to */ CopyTargetDevice::CopyTargetDevice(Device& d, qint64 firstsector, qint64 lastsector) : CopyTarget(), m_Device(d), m_BackendDevice(nullptr), m_FirstSector(firstsector), m_LastSector(lastsector) { } /** Destructs a CopyTargetDevice */ CopyTargetDevice::~CopyTargetDevice() { delete m_BackendDevice; } /** Opens a CopyTargetDevice for writing to. @return true on success */ bool CopyTargetDevice::open() { m_BackendDevice = CoreBackendManager::self()->backend()->openDeviceExclusive(device().deviceNode()); return m_BackendDevice != nullptr; } /** @return the Device's sector size */ qint32 CopyTargetDevice::sectorSize() const { - return device().logicalSectorSize(); + return device().logicalSize(); } /** Writes the given number of sectors to the Device. Note that @p writeOffset must be greater or equal than zero. @param buffer the data to write @param writeOffset where to start writing on the Device @param numSectors the number of sectors in @p buffer @return true on success */ bool CopyTargetDevice::writeSectors(void* buffer, qint64 writeOffset, qint64 numSectors) { Q_ASSERT(writeOffset >= 0); bool rval = m_BackendDevice->writeSectors(buffer, writeOffset, numSectors); if (rval) setSectorsWritten(sectorsWritten() + numSectors); return rval; } diff --git a/src/core/device.cpp b/src/core/device.cpp index 46bdc86..8f6e377 100644 --- a/src/core/device.cpp +++ b/src/core/device.cpp @@ -1,117 +1,68 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/device.h" - #include "core/partitiontable.h" #include "core/smartstatus.h" #include "util/capacity.h" #include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if !defined(BLKPBSZGET) -#define BLKPBSZGET _IO(0x12,123)/* get block physical sector size */ -#endif - -static qint32 getPhysicalSectorSize(const QString& device_node) -{ - /* - * possible ways of getting the physical sector size for a drive: - * - ioctl(BLKPBSZGET) -- supported with Linux 2.6.32 and later - * - /sys/block/sda/queue/physical_block_size - * - libblkid from util-linux-ng 2.17 or later - * TODO: implement the blkid method - */ - -#if defined(BLKPBSZGET) - int phSectorSize = -1; - int fd = open(device_node.toLocal8Bit().constData(), O_RDONLY); - if (fd != -1) { - if (ioctl(fd, BLKPBSZGET, &phSectorSize) >= 0) { - close(fd); - return phSectorSize; - } - - close(fd); - } -#endif - - QFile f(QStringLiteral("/sys/block/%1/queue/physical_block_size").arg(QString(device_node).remove(QStringLiteral("/dev/")))); - - if (f.open(QIODevice::ReadOnly)) { - QByteArray a = f.readLine(); - return a.trimmed().toInt(); - } - - return -1; -} - /** Constructs a Device with an empty PartitionTable. @param name the Device's name, usually some string defined by the manufacturer @param devicenode the Device's node, for example "/dev/sda" - @param heads the number of heads in CHS notation - @param numSectors the number of sectors in CHS notation - @param cylinders the number of cylinders in CHS notation - @param sectorSize the size of a sector in bytes */ -Device::Device(const QString& name, const QString& devicenode, qint32 heads, qint32 numSectors, qint32 cylinders, qint64 sectorSize, const QString& iconname) : - QObject(), - m_Name(name.length() > 0 ? name : i18n("Unknown Device")), - m_DeviceNode(devicenode), - m_PartitionTable(nullptr), - m_Heads(heads), - m_SectorsPerTrack(numSectors), - m_Cylinders(cylinders), - m_LogicalSectorSize(sectorSize), - m_PhysicalSectorSize(getPhysicalSectorSize(devicenode)), - m_IconName(iconname.isEmpty() ? QStringLiteral("drive-harddisk") : iconname), - m_SmartStatus(new SmartStatus(devicenode)) +Device::Device(const QString& name, + const QString& devicenode, + const qint32 logicalSize, + const qint64 totalLogical, + const QString& iconname, + Device::Type type) + : QObject() + , m_Name(name.length() > 0 ? name : i18n("Unknown Device")) + , m_DeviceNode(devicenode) + , m_LogicalSize(logicalSize) + , m_TotalLogical(totalLogical) + , m_PartitionTable(nullptr) + , m_IconName(iconname.isEmpty() ? QStringLiteral("drive-harddisk") : iconname) + , m_SmartStatus(new SmartStatus(devicenode)) + , m_Type(type) { } /** Destructs a Device. */ Device::~Device() { delete m_PartitionTable; } bool Device::operator==(const Device& other) const { return m_DeviceNode == other.m_DeviceNode; } bool Device::operator!=(const Device& other) const { return !(other == *this); } QString Device::prettyName() const { return i18nc("Device name – Capacity (device node)", "%1 – %2 (%3)", name(), Capacity::formatByteSize(capacity()), deviceNode()); } diff --git a/src/core/device.h b/src/core/device.h index 9b440e3..1190fec 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -1,128 +1,128 @@ /************************************************************************* * 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(DEVICE__H) #define DEVICE__H #include "util/libpartitionmanagerexport.h" #include #include #include class PartitionTable; class CreatePartitionTableOperation; class CoreBackend; class SmartStatus; -/** A device. +/** A abstract device interface. Represents a device like /dev/sda. Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. @see PartitionTable, Partition @author Volker Lanz */ class LIBKPMCORE_EXPORT Device : public QObject { Q_DISABLE_COPY(Device) friend class CreatePartitionTableOperation; friend class CoreBackend; public: - Device(const QString& name, const QString& devicenode, qint32 heads, qint32 numSectors, qint32 cylinders, qint64 sectorSize, const QString& iconname = QString()); - ~Device(); + enum Type { + Disk_Device = 0, + LVM_Device = 1, /* VG */ + RAID_Device = 2, /* software RAID device */ + Unknown_Device = 4 + }; + +protected: + Device(const QString& name, const QString& devicenode, const qint32 logicalSize, const qint64 totalLogical, const QString& iconname = QString(), Device::Type type = Device::Disk_Device); + +public: + virtual ~Device(); public: - bool operator==(const Device& other) const; - bool operator!=(const Device& other) const; + virtual bool operator==(const Device& other) const; + virtual bool operator!=(const Device& other) const; - const QString& name() const { + virtual const QString& name() const { return m_Name; /**< @return the Device's name, usually some manufacturer string */ } - const QString& deviceNode() const { + virtual const QString& deviceNode() const { return m_DeviceNode; /**< @return the Device's node, for example "/dev/sda" */ } - PartitionTable* partitionTable() { + + virtual PartitionTable* partitionTable() { return m_PartitionTable; /**< @return the Device's PartitionTable */ } - const PartitionTable* partitionTable() const { + virtual const PartitionTable* partitionTable() const { return m_PartitionTable; /**< @return the Device's PartitionTable */ } - qint32 heads() const { - return m_Heads; /**< @return the number of heads on the Device in CHS notation */ - } - qint32 cylinders() const { - return m_Cylinders; /**< @return the number of cylinders on the Device in CHS notation */ - } - qint32 sectorsPerTrack() const { - return m_SectorsPerTrack; /**< @return the number of sectors on the Device in CHS notation */ - } - qint32 physicalSectorSize() const { - return m_PhysicalSectorSize; /**< @return the physical sector size the Device uses or -1 if unknown */ - } - qint32 logicalSectorSize() const { - return m_LogicalSectorSize; /**< @return the logical sector size the Device uses */ - } - qint64 totalSectors() const { - return static_cast(heads()) * cylinders() * sectorsPerTrack(); /**< @return the total number of sectors on the device */ - } - qint64 capacity() const { - return totalSectors() * logicalSectorSize(); /**< @return the Device's capacity in bytes */ - } - qint64 cylinderSize() const { - return static_cast(heads()) * sectorsPerTrack(); /**< @return the size of a cylinder on this Device in sectors */ + + virtual qint64 capacity() const { /**< @return the Device's capacity in bytes */ + return logicalSize() * totalLogical(); } - void setIconName(const QString& name) { + virtual void setIconName(const QString& name) { m_IconName = name; } - const QString& iconName() const { + virtual const QString& iconName() const { return m_IconName; /**< @return suggested icon name for this Device */ } - SmartStatus& smartStatus() { + virtual SmartStatus& smartStatus() { return *m_SmartStatus; } - const SmartStatus& smartStatus() const { + virtual const SmartStatus& smartStatus() const { return *m_SmartStatus; } - QString prettyName() const; - - void setPartitionTable(PartitionTable* ptable) { + virtual void setPartitionTable(PartitionTable* ptable) { m_PartitionTable = ptable; } -private: + virtual qint32 logicalSize() const { + return m_LogicalSize; + } + virtual qint64 totalLogical() const { + return m_TotalLogical; + } + + virtual Device::Type type() const { + return m_Type; + } + + virtual QString prettyName() const; + +protected: QString m_Name; QString m_DeviceNode; + qint32 m_LogicalSize; + qint32 m_TotalLogical; PartitionTable* m_PartitionTable; - qint32 m_Heads; - qint32 m_SectorsPerTrack; - qint32 m_Cylinders; - qint32 m_LogicalSectorSize; - qint32 m_PhysicalSectorSize; QString m_IconName; SmartStatus* m_SmartStatus; + Device::Type m_Type; }; #endif diff --git a/src/core/devicescanner.cpp b/src/core/devicescanner.cpp index e319aad..74ac0c8 100644 --- a/src/core/devicescanner.cpp +++ b/src/core/devicescanner.cpp @@ -1,65 +1,66 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/devicescanner.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/operationstack.h" #include "core/device.h" /** Constructs a DeviceScanner @param ostack the OperationStack where the devices will be created */ DeviceScanner::DeviceScanner(QObject* parent, OperationStack& ostack) : QThread(parent), m_OperationStack(ostack) { setupConnections(); } void DeviceScanner::setupConnections() { connect(CoreBackendManager::self()->backend(), &CoreBackend::scanProgress, this, &DeviceScanner::progress); } void DeviceScanner::clear() { operationStack().clearOperations(); operationStack().clearDevices(); } void DeviceScanner::run() { scan(); } void DeviceScanner::scan() { emit progress(QString(), 0); clear(); QList deviceList = CoreBackendManager::self()->backend()->scanDevices(); + //TODO: Scan for LVM here and add to the list. foreach(Device * d, deviceList) operationStack().addDevice(d); operationStack().sortDevices(); } diff --git a/src/core/device.cpp b/src/core/diskdevice.cpp similarity index 71% copy from src/core/device.cpp copy to src/core/diskdevice.cpp index 46bdc86..e1026d4 100644 --- a/src/core/device.cpp +++ b/src/core/diskdevice.cpp @@ -1,117 +1,95 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ -#include "core/device.h" +#include "core/diskdevice.h" #include "core/partitiontable.h" #include "core/smartstatus.h" -#include "util/capacity.h" - #include #include #include #include #include #include #include #include #include #if !defined(BLKPBSZGET) #define BLKPBSZGET _IO(0x12,123)/* get block physical sector size */ #endif static qint32 getPhysicalSectorSize(const QString& device_node) { /* * possible ways of getting the physical sector size for a drive: * - ioctl(BLKPBSZGET) -- supported with Linux 2.6.32 and later * - /sys/block/sda/queue/physical_block_size * - libblkid from util-linux-ng 2.17 or later * TODO: implement the blkid method */ #if defined(BLKPBSZGET) int phSectorSize = -1; int fd = open(device_node.toLocal8Bit().constData(), O_RDONLY); if (fd != -1) { if (ioctl(fd, BLKPBSZGET, &phSectorSize) >= 0) { close(fd); return phSectorSize; } close(fd); } #endif QFile f(QStringLiteral("/sys/block/%1/queue/physical_block_size").arg(QString(device_node).remove(QStringLiteral("/dev/")))); if (f.open(QIODevice::ReadOnly)) { QByteArray a = f.readLine(); return a.trimmed().toInt(); } return -1; } -/** Constructs a Device with an empty PartitionTable. +/** Constructs a Disk Device with an empty PartitionTable. @param name the Device's name, usually some string defined by the manufacturer @param devicenode the Device's node, for example "/dev/sda" @param heads the number of heads in CHS notation @param numSectors the number of sectors in CHS notation @param cylinders the number of cylinders in CHS notation @param sectorSize the size of a sector in bytes */ -Device::Device(const QString& name, const QString& devicenode, qint32 heads, qint32 numSectors, qint32 cylinders, qint64 sectorSize, const QString& iconname) : - QObject(), - m_Name(name.length() > 0 ? name : i18n("Unknown Device")), - m_DeviceNode(devicenode), - m_PartitionTable(nullptr), - m_Heads(heads), - m_SectorsPerTrack(numSectors), - m_Cylinders(cylinders), - m_LogicalSectorSize(sectorSize), - m_PhysicalSectorSize(getPhysicalSectorSize(devicenode)), - m_IconName(iconname.isEmpty() ? QStringLiteral("drive-harddisk") : iconname), - m_SmartStatus(new SmartStatus(devicenode)) -{ -} - -/** Destructs a Device. */ -Device::~Device() -{ - delete m_PartitionTable; -} - -bool Device::operator==(const Device& other) const -{ - return m_DeviceNode == other.m_DeviceNode; -} - -bool Device::operator!=(const Device& other) const -{ - return !(other == *this); -} - -QString Device::prettyName() const +DiskDevice::DiskDevice(const QString& name, + const QString& devicenode, + qint32 heads, + qint32 numSectors, + qint32 cylinders, + qint32 sectorSize, + const QString& iconname) + : Device(name, devicenode, sectorSize, (static_cast(heads) * cylinders * numSectors), iconname, Device::Disk_Device) + , m_Heads(heads) + , m_SectorsPerTrack(numSectors) + , m_Cylinders(cylinders) + , m_LogicalSectorSize(sectorSize) + , m_PhysicalSectorSize(getPhysicalSectorSize(devicenode)) { - return i18nc("Device name – Capacity (device node)", "%1 – %2 (%3)", name(), Capacity::formatByteSize(capacity()), deviceNode()); } diff --git a/src/core/device.h b/src/core/diskdevice.h similarity index 63% copy from src/core/device.h copy to src/core/diskdevice.h index 9b440e3..0ecb28e 100644 --- a/src/core/device.h +++ b/src/core/diskdevice.h @@ -1,128 +1,84 @@ /************************************************************************* * 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(DEVICE__H) +#if !defined(DISKDEVICE__H) -#define DEVICE__H +#define DISKDEVICE__H #include "util/libpartitionmanagerexport.h" +#include "core/device.h" #include #include #include class PartitionTable; class CreatePartitionTableOperation; class CoreBackend; class SmartStatus; -/** A device. +/** A disk device. Represents a device like /dev/sda. Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. @see PartitionTable, Partition @author Volker Lanz */ -class LIBKPMCORE_EXPORT Device : public QObject +class LIBKPMCORE_EXPORT DiskDevice : public Device { - Q_DISABLE_COPY(Device) + Q_DISABLE_COPY(DiskDevice) friend class CreatePartitionTableOperation; friend class CoreBackend; public: - Device(const QString& name, const QString& devicenode, qint32 heads, qint32 numSectors, qint32 cylinders, qint64 sectorSize, const QString& iconname = QString()); - ~Device(); + DiskDevice(const QString& name, const QString& devicenode, qint32 heads, qint32 numSectors, qint32 cylinders, qint32 sectorSize, const QString& iconname = QString()); public: - bool operator==(const Device& other) const; - bool operator!=(const Device& other) const; - - const QString& name() const { - return m_Name; /**< @return the Device's name, usually some manufacturer string */ - } - const QString& deviceNode() const { - return m_DeviceNode; /**< @return the Device's node, for example "/dev/sda" */ - } - PartitionTable* partitionTable() { - return m_PartitionTable; /**< @return the Device's PartitionTable */ - } - const PartitionTable* partitionTable() const { - return m_PartitionTable; /**< @return the Device's PartitionTable */ - } qint32 heads() const { return m_Heads; /**< @return the number of heads on the Device in CHS notation */ } qint32 cylinders() const { return m_Cylinders; /**< @return the number of cylinders on the Device in CHS notation */ } qint32 sectorsPerTrack() const { return m_SectorsPerTrack; /**< @return the number of sectors on the Device in CHS notation */ } qint32 physicalSectorSize() const { return m_PhysicalSectorSize; /**< @return the physical sector size the Device uses or -1 if unknown */ } qint32 logicalSectorSize() const { return m_LogicalSectorSize; /**< @return the logical sector size the Device uses */ } qint64 totalSectors() const { return static_cast(heads()) * cylinders() * sectorsPerTrack(); /**< @return the total number of sectors on the device */ } - qint64 capacity() const { - return totalSectors() * logicalSectorSize(); /**< @return the Device's capacity in bytes */ - } qint64 cylinderSize() const { return static_cast(heads()) * sectorsPerTrack(); /**< @return the size of a cylinder on this Device in sectors */ } - void setIconName(const QString& name) { - m_IconName = name; - } - const QString& iconName() const { - return m_IconName; /**< @return suggested icon name for this Device */ - } - - SmartStatus& smartStatus() { - return *m_SmartStatus; - } - const SmartStatus& smartStatus() const { - return *m_SmartStatus; - } - - QString prettyName() const; - - void setPartitionTable(PartitionTable* ptable) { - m_PartitionTable = ptable; - } - private: - QString m_Name; - QString m_DeviceNode; - PartitionTable* m_PartitionTable; qint32 m_Heads; qint32 m_SectorsPerTrack; qint32 m_Cylinders; qint32 m_LogicalSectorSize; qint32 m_PhysicalSectorSize; - QString m_IconName; - SmartStatus* m_SmartStatus; }; #endif - diff --git a/src/core/partition.cpp b/src/core/partition.cpp index a3e4532..b45204d 100644 --- a/src/core/partition.cpp +++ b/src/core/partition.cpp @@ -1,387 +1,387 @@ /************************************************************************* * Copyright (C) 2008 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/partition.h" #include "core/device.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/externalcommand.h" #include "util/report.h" #include #include #include #include #include #include /** Creates a new Partition object. @param parent the Partition's parent. May be another Partition (for logicals) or a PartitionTable. Must not be nullptr. @param device the Device this Partition is on. @param role the Partition's role(s) @param fs pointer to the Partition's FileSystem object. The Partition object will take ownership of this. @param sectorStart the first sector of the Partition on its Device @param sectorEnd the last sector of the Partition on its Device @param partitionPath the Partition's path, e.g. /dev/sda4 or /dev/mmcblk0p1 @param availableFlags the flags available for this Partition @param mountPoint mount point for this Partition @param mounted true if the Partition is mounted @param activeFlags active flags for this Partition @param state the Partition's state */ Partition::Partition(PartitionNode* parent, const Device& device, const PartitionRole& role, FileSystem* fs, qint64 sectorStart, qint64 sectorEnd, QString partitionPath, PartitionTable::Flags availableFlags, const QString& mountPoint, bool mounted, PartitionTable::Flags activeFlags, State state) : PartitionNode(), m_Children(), m_Parent(parent), m_FileSystem(fs), m_Roles(role), m_FirstSector(sectorStart), m_LastSector(sectorEnd), m_DevicePath(device.deviceNode()), m_MountPoint(mountPoint), m_AvailableFlags(availableFlags), m_ActiveFlags(activeFlags), m_IsMounted(mounted), - m_SectorSize(device.logicalSectorSize()), m_State(state) { setPartitionPath(partitionPath); Q_ASSERT(m_Parent); + m_SectorSize = device.logicalSize(); } /** Destroys a Partition, destroying its children and its FileSystem */ Partition::~Partition() { // FIXME: Design flaw: Currently, there are two ways a partition node can get children: Either // they're created and inserted as unallocated in PartitionTable (these unallocated then get // "converted" to real, new partitions in the GUI) or they're created and appended in the // backend plugin. There is however no defined way to remove partitions from parents. This might // either cause leaks (a partition is removed from the parent's list of children but never // deleted) or, worse, crashes (a partition is deleted but not removed from the parent's // list of children). As a workaround, always remove a partition from its parent here in the dtor. // This presumably fixes 232092, but backporting is too risky until we're sure this doesn't cause // side-effects. if (m_Parent) parent()->remove(this); clearChildren(); deleteFileSystem(); } /** @param other Partition to copy */ Partition::Partition(const Partition& other) : PartitionNode(), m_Children(), m_Parent(other.m_Parent), m_FileSystem(FileSystemFactory::create(other.fileSystem())), m_Roles(other.m_Roles), m_FirstSector(other.m_FirstSector), m_LastSector(other.m_LastSector), m_DevicePath(other.m_DevicePath), m_MountPoint(other.m_MountPoint), m_AvailableFlags(other.m_AvailableFlags), m_ActiveFlags(other.m_ActiveFlags), m_IsMounted(other.m_IsMounted), m_SectorSize(other.m_SectorSize), m_State(other.m_State) { setPartitionPath(other.m_PartitionPath); foreach(const Partition * child, other.children()) { Partition* p = new Partition(*child); p->setParent(this); m_Children.append(p); } } /** @param other Partition to assign from */ Partition& Partition::operator=(const Partition& other) { if (&other == this) return *this; clearChildren(); foreach(const Partition * child, other.children()) { Partition* p = new Partition(*child); p->setParent(this); m_Children.append(p); } m_Number = other.m_Number; m_FileSystem = FileSystemFactory::create(other.fileSystem()); m_Roles = other.m_Roles; m_FirstSector = other.m_FirstSector; m_LastSector = other.m_LastSector; m_DevicePath = other.m_DevicePath; m_PartitionPath = other.m_PartitionPath; m_MountPoint = other.m_MountPoint; m_AvailableFlags = other.m_AvailableFlags; m_ActiveFlags = other.m_ActiveFlags; m_IsMounted = other.m_IsMounted; m_SectorSize = other.m_SectorSize; m_State = other.m_State; return *this; } bool Partition::operator==(const Partition& other) const { return other.deviceNode() == deviceNode(); } bool Partition::operator!=(const Partition& other) const { return !(other == *this); } /** @return a short descriptive text or, in case the Partition has StateNone, its device node. */ QString Partition::deviceNode() const { if (roles().has(PartitionRole::None) || roles().has(PartitionRole::Unallocated)) return i18nc("@item partition name", "unallocated"); if (state() == StateNew) return i18nc("@item partition name", "New Partition"); if (state() == StateRestore) return i18nc("@item partition name", "Restored Partition"); if (state() == StateCopy) return i18nc("@item partition name", "Copy of %1", partitionPath()); return partitionPath(); } /** @return the sectors used in the Partition's FileSystem or, in case of an extended partition, the sum of used sectors of the Partition's children */ qint64 Partition::sectorsUsed() const { if (!roles().has(PartitionRole::Extended)) return fileSystem().sectorsUsed(); qint64 result = 0; foreach(const Partition * p, children()) if (!p->roles().has(PartitionRole::Unallocated)) result += p->length(); return result; } /** @return the minimum number of sectors this Partition must be long */ qint64 Partition::minimumSectors() const { if (roles().has(PartitionRole::Luks)) return fileSystem().minCapacity() / sectorSize() + 4096; // 4096 is the default cryptsetup payload offset return fileSystem().minCapacity() / sectorSize(); } /** @return the maximum number of sectors this Partition may be long */ qint64 Partition::maximumSectors() const { return fileSystem().maxCapacity() / sectorSize(); } /** Adjusts the numbers of logical Partitions for an extended Partition. This is required if a logical Partition is deleted or inserted because logicals must be numberd from 5 onwards without a gap. So if the user deletes Partition number 7 and there is a number 8, 8 becomes the "new" 7. And since this happens somewhere in the middle of a DeleteOperation, we have to adjust to that so the next Job still finds the Partition it wants to deal with. @param deletedNumber the number of a deleted logical or -1 if none has been deleted @param insertedNumber the number of an inserted logical or -1 if none has been inserted */ void Partition::adjustLogicalNumbers(qint32 deletedNumber, qint32 insertedNumber) { if (!roles().has(PartitionRole::Extended)) return; foreach(Partition * p, children()) { QString path = p->partitionPath(); path.remove(QRegularExpression(QStringLiteral("(\\d+$)"))); if (deletedNumber > 4 && p->number() > deletedNumber) p->setPartitionPath(path + QString::number(p->number() - 1)); else if (insertedNumber > 4 && p->number() >= insertedNumber) p->setPartitionPath(path + QString::number(p->number() + 1)); } } /** @return the highest sector number an extended Partition can begin at */ qint64 Partition::maxFirstSector() const { qint64 rval = -1; foreach(const Partition * child, children()) if (!child->roles().has(PartitionRole::Unallocated) && (child->firstSector() < rval || rval == -1)) rval = child->firstSector(); return rval; } /** @return the lowest sector number an extended Partition can end at */ qint64 Partition::minLastSector() const { qint64 rval = -1; foreach(const Partition * child, children()) if (!child->roles().has(PartitionRole::Unallocated) && child->lastSector() > rval) rval = child->lastSector(); return rval; } /** @return true if the Partition has children */ bool Partition::hasChildren() const { foreach(const Partition * child, children()) if (!child->roles().has(PartitionRole::Unallocated)) return true; return false; } /** Sets an extended Partition to mounted if any of its children are mounted */ void Partition::checkChildrenMounted() { setMounted(isChildMounted()); } /** @return true if this Partition can be mounted */ bool Partition::canMount() const { // cannot mount if already mounted if (isMounted()) { return false; } if (fileSystem().canMount(deviceNode(), mountPoint())) { return true; } return false; } /** @return true if this Partition can be unmounted */ bool Partition::canUnmount() const { return !roles().has(PartitionRole::Extended) && isMounted(); } /** Tries to mount a Partition. @return true on success */ bool Partition::mount(Report& report) { if (isMounted()) return false; bool success = false; if (fileSystem().canMount(deviceNode(), mountPoint())) { success = fileSystem().mount(report, deviceNode(), mountPoint()); } setMounted(success); return success; } /** Tries to unmount a Partition. @return true on success */ bool Partition::unmount(Report& report) { if (!isMounted()) return false; bool success = true; while (success) { if (fileSystem().canUnmount(deviceNode())) { success = fileSystem().unmount(report, deviceNode()); } KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); if (!mountPoints.findByDevice(deviceNode())) break; } setMounted(!success); return success; } void Partition::deleteFileSystem() { delete m_FileSystem; m_FileSystem = nullptr; } void Partition::setPartitionPath(const QString& s) { m_PartitionPath = s; QRegularExpression re(QStringLiteral("(\\d+$)")); QRegularExpressionMatch rePartitionNumber = re.match(partitionPath()); if (rePartitionNumber.hasMatch()) { setNumber(rePartitionNumber.captured().toInt()); return; } setNumber(-1); } void Partition::setFileSystem(FileSystem* fs) { m_FileSystem = fs; } void Partition::move(qint64 newStartSector) { const qint64 savedLength = length(); setFirstSector(newStartSector); setLastSector(newStartSector + savedLength - 1); } QTextStream& operator<<(QTextStream& stream, const Partition& p) { QStringList flagList; foreach(const PartitionTable::Flag & f, PartitionTable::flagList()) { if (p.activeFlags() & f) flagList.append(PartitionTable::flagName(f)); } const QString sep(QStringLiteral(";")); // number - start - end - type - roles - label - flags stream << p.number() << sep << p.firstSector() << sep << p.lastSector() << sep << p.fileSystem().name() << sep << p.roles().toString() << sep << "\"" << p.fileSystem().label() << QStringLiteral("\"") << sep << "\"" << flagList.join(QStringLiteral(",")) << QStringLiteral("\"") << "\n"; return stream; } diff --git a/src/core/partitionalignment.cpp b/src/core/partitionalignment.cpp index 86f212d..f13c091 100644 --- a/src/core/partitionalignment.cpp +++ b/src/core/partitionalignment.cpp @@ -1,151 +1,156 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/partitionalignment.h" #include "core/partition.h" #include "core/partitiontable.h" #include "core/device.h" +#include "core/diskdevice.h" #include "fs/filesystem.h" #include "util/globallog.h" #include int PartitionAlignment::s_sectorAlignment = 2048; qint64 PartitionAlignment::firstDelta(const Device& d, const Partition& p, qint64 s) { + //TODO: make sure things work for LVM if (d.partitionTable()->type() == PartitionTable::msdos) { - if (p.roles().has(PartitionRole::Logical) && s == 2 * d.sectorsPerTrack()) - return (s - (2 * d.sectorsPerTrack())) % sectorAlignment(d); + const DiskDevice& diskDevice = dynamic_cast(d); + if (p.roles().has(PartitionRole::Logical) && s == 2 * diskDevice.sectorsPerTrack()) + return (s - (2 * diskDevice.sectorsPerTrack())) % sectorAlignment(d); - if (p.roles().has(PartitionRole::Logical) || s == d.sectorsPerTrack()) - return (s - d.sectorsPerTrack()) % sectorAlignment(d); + if (p.roles().has(PartitionRole::Logical) || s == diskDevice.sectorsPerTrack()) + return (s - diskDevice.sectorsPerTrack()) % sectorAlignment(d); } return s % sectorAlignment(d); } qint64 PartitionAlignment::lastDelta(const Device& d, const Partition&, qint64 s) { return (s + 1) % sectorAlignment(d); } bool PartitionAlignment::isLengthAligned(const Device& d, const Partition& p) { + //TODO: make sure things work for LVM if (d.partitionTable()->type() == PartitionTable::msdos) { - if (p.roles().has(PartitionRole::Logical) && p.firstSector() == 2 * d.sectorsPerTrack()) - return (p.length() + (2 * d.sectorsPerTrack())) % sectorAlignment(d) == 0; + const DiskDevice& diskDevice = dynamic_cast(d); + if (p.roles().has(PartitionRole::Logical) && p.firstSector() == 2 * diskDevice.sectorsPerTrack()) + return (p.length() + (2 * diskDevice.sectorsPerTrack())) % sectorAlignment(d) == 0; - if (p.roles().has(PartitionRole::Logical) || p.firstSector() == d.sectorsPerTrack()) - return (p.length() + d.sectorsPerTrack()) % sectorAlignment(d) == 0; + if (p.roles().has(PartitionRole::Logical) || p.firstSector() == diskDevice.sectorsPerTrack()) + return (p.length() + diskDevice.sectorsPerTrack()) % sectorAlignment(d) == 0; } return p.length() % sectorAlignment(d) == 0; } /** Checks if the Partition is properly aligned to the PartitionTable's alignment requirements. Will print warning messages to GlobalLog if the Partition's first sector is not aligned and another one if the last sector is not aligned. This can be suppressed with setting the @p quiet to true. @see alignPartition(), canAlignToSector() @param d device the partition is on @param p the partition to check @param quiet if true, will not print warning @return true if propertly aligned */ bool PartitionAlignment::isAligned(const Device& d, const Partition& p, bool quiet) { return isAligned(d, p, p.firstSector(), p.lastSector(), quiet); } bool PartitionAlignment::isAligned(const Device& d, const Partition& p, qint64 newFirst, qint64 newLast, bool quiet) { if (firstDelta(d, p, newFirst) && !quiet) Log(Log::warning) << xi18nc("@info/plain", "Partition %1 is not properly aligned (first sector: %2, modulo: %3).", p.deviceNode(), newFirst, firstDelta(d, p, newFirst)); if (lastDelta(d, p, newLast) && !quiet) Log(Log::warning) << xi18nc("@info/plain", "Partition %1 is not properly aligned (last sector: %2, modulo: %3).", p.deviceNode(), newLast, lastDelta(d, p, newLast)); return firstDelta(d, p, newFirst) == 0 && lastDelta(d, p, newLast) == 0; } /** @return the sector size to align the partition start and end to */ qint64 PartitionAlignment::sectorAlignment(const Device& d) { Q_UNUSED(d) return s_sectorAlignment; } void PartitionAlignment::setSectorAlignment(int sectorAlignment) { s_sectorAlignment = sectorAlignment; } qint64 PartitionAlignment::alignedFirstSector(const Device& d, const Partition& p, qint64 s, qint64 min_first, qint64 max_first, qint64 min_length, qint64 max_length) { if (firstDelta(d, p, s) == 0) return s; /** @todo Don't assume we always want to align to the front. Always trying to align to the front solves the problem that a partition does get too small to take another one that's copied to it, but it introduces a new bug: The user might create a partition aligned at the end of a device, extended partition or at the start of the next one, but we align to the back and leave some space in between. */ // We always want to make the partition larger, not smaller. Making it smaller // might, in case it's a partition that another is being copied to, mean the partition // ends up too small. So try to move the start to the front first. s = s - firstDelta(d, p, s); while (s < d.partitionTable()->firstUsable() || s < min_first || (max_length > -1 && p.lastSector() - s + 1 > max_length)) s += sectorAlignment(d); while (s > d.partitionTable()->lastUsable() || (max_first > -1 && s > max_first) || p.lastSector() - s + 1 < min_length) s -= sectorAlignment(d); return s; } qint64 PartitionAlignment::alignedLastSector(const Device& d, const Partition& p, qint64 s, qint64 min_last, qint64 max_last, qint64 min_length, qint64 max_length, qint64 original_length, bool original_aligned) { if (lastDelta(d, p, s) == 0) return s; s = s + sectorAlignment(d) - lastDelta(d, p, s); // if we can retain the partition length exactly by aligning to the front, do that if (original_aligned && p.length() - original_length == lastDelta(d, p, s)) s -= sectorAlignment(d); while (s < d.partitionTable()->firstUsable() || s < min_last || s - p.firstSector() + 1 < min_length) s += sectorAlignment(d); while (s > d.partitionTable()->lastUsable() || (max_last > -1 && s > max_last) || (max_length > -1 && s - p.firstSector() + 1 > max_length)) s -= sectorAlignment(d); return s; } diff --git a/src/core/partitionrole.h b/src/core/partitionrole.h index 39c2cb2..26df5f1 100644 --- a/src/core/partitionrole.h +++ b/src/core/partitionrole.h @@ -1,76 +1,77 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(PARTITIONROLE__H) #define PARTITIONROLE__H #include "util/libpartitionmanagerexport.h" #include class QString; /** A Partition's role. Each Partition has a PartitionRole: It can be primary, extended, logical or represent unallocated space on the Device. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartitionRole { public: /** A Partition's role: What kind of Partition is it? */ enum Role { None = 0, /**< None at all */ Primary = 1, /**< Primary */ Extended = 2, /**< Extended */ Logical = 4, /**< Logical inside an extended */ Unallocated = 8, /**< No real Partition, just unallocated space */ Luks = 16, /**< Encrypted partition with LUKS key management */ + Lvm_Lv = 32, /**< Logical Volume of LVM */ Any = 255 /**< In case we're looking for a Partition with a PartitionRole, any will do */ }; Q_DECLARE_FLAGS(Roles, Role) public: explicit PartitionRole(Roles r) : m_Roles(r) {} /**< Creates a new PartitionRole object */ Roles roles() const { return m_Roles; /**< @return the roles as bitfield */ } bool has(Role r) const { return roles() & r; /**< @param r the role to check @return true if the role is set */ } bool operator==(const PartitionRole& other) const { return m_Roles == other.m_Roles; /**< @param other object to compare with @return true if the same */ } bool operator!=(const PartitionRole& other) const { return !operator==(other); /**< @param other object to compare with @return true if not the same */ } QString toString() const; private: Roles m_Roles; }; Q_DECLARE_OPERATORS_FOR_FLAGS(PartitionRole::Roles) #endif diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index 2c6d3eb..bd139c1 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -1,516 +1,531 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * Copyright (C) 2016 by Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ /** @file */ #include "core/partitiontable.h" #include "core/partition.h" #include "core/device.h" +#include "core/diskdevice.h" #include "core/partitionalignment.h" - #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/globallog.h" #include #include #include /** Creates a new PartitionTable object with type MSDOS @param type name of the PartitionTable type (e.g. "msdos" or "gpt") */ PartitionTable::PartitionTable(TableType type, qint64 first_usable, qint64 last_usable) : PartitionNode(), m_Children(), m_MaxPrimaries(maxPrimariesForTableType(type)), m_Type(type), m_FirstUsable(first_usable), m_LastUsable(last_usable) { } /** Destroys a PartitionTable object, destroying all children */ PartitionTable::~PartitionTable() { clearChildren(); } /** Gets the number of free sectors before a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors before @returns the number of free sectors before the Partition */ qint64 PartitionTable::freeSectorsBefore(const Partition& p) const { const Partition* pred = predecessor(p); // due to the space required for extended boot records the // below is NOT the same as pred->length() if (pred && pred->roles().has(PartitionRole::Unallocated)) return p.firstSector() - pred->firstSector(); return 0; } /** Gets the number of free sectors after a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors after @returns the number of free sectors after the Partition */ qint64 PartitionTable::freeSectorsAfter(const Partition& p) const { const Partition* succ = successor(p); // due to the space required for extended boot records the // below is NOT the same as succ->length() if (succ && succ->roles().has(PartitionRole::Unallocated)) return succ->lastSector() - p.lastSector(); return 0; } /** @return true if the PartitionTable has an extended Partition */ bool PartitionTable::hasExtended() const { for (int i = 0; i < children().size(); i++) if (children()[i]->roles().has(PartitionRole::Extended)) return true; return false; } /** @return pointer to the PartitionTable's extended Partition or nullptr if none exists */ Partition* PartitionTable::extended() const { for (int i = 0; i < children().size(); i++) if (children()[i]->roles().has(PartitionRole::Extended)) return children()[i]; return nullptr; } /** Gets valid PartitionRoles for a Partition @param p the Partition @return valid roles for the given Partition */ PartitionRole::Roles PartitionTable::childRoles(const Partition& p) const { Q_ASSERT(p.parent()); PartitionRole::Roles r = p.parent()->isRoot() ? PartitionRole::Primary : PartitionRole::Logical; if (r == PartitionRole::Primary && hasExtended() == false && tableTypeSupportsExtended(type())) r |= PartitionRole::Extended; return r; } /** @return the number of primaries in this PartitionTable */ int PartitionTable::numPrimaries() const { int result = 0; foreach(const Partition * p, children()) if (p->roles().has(PartitionRole::Primary) || p->roles().has(PartitionRole::Extended)) result++; return result; } /** Appends a Partition to this PartitionTable @param partition pointer of the partition to append. Must not be nullptr. */ void PartitionTable::append(Partition* partition) { children().append(partition); } /** @param f the flag to get the name for @returns the flags name or an empty QString if the flag is not known */ QString PartitionTable::flagName(Flag f) { switch (f) { case PartitionTable::FlagBoot: return i18nc("@item partition flag", "boot"); case PartitionTable::FlagRoot: return i18nc("@item partition flag", "root"); case PartitionTable::FlagSwap: return i18nc("@item partition flag", "swap"); case PartitionTable::FlagHidden: return i18nc("@item partition flag", "hidden"); case PartitionTable::FlagRaid: return i18nc("@item partition flag", "raid"); case PartitionTable::FlagLvm: return i18nc("@item partition flag", "lvm"); case PartitionTable::FlagLba: return i18nc("@item partition flag", "lba"); case PartitionTable::FlagHpService: return i18nc("@item partition flag", "hpservice"); case PartitionTable::FlagPalo: return i18nc("@item partition flag", "palo"); case PartitionTable::FlagPrep: return i18nc("@item partition flag", "prep"); case PartitionTable::FlagMsftReserved: return i18nc("@item partition flag", "msft-reserved"); case PartitionTable::FlagBiosGrub: return i18nc("@item partition flag", "bios-grub"); case PartitionTable::FlagAppleTvRecovery: return i18nc("@item partition flag", "apple-tv-recovery"); case PartitionTable::FlagDiag: return i18nc("@item partition flag", "diag"); case PartitionTable::FlagLegacyBoot: return i18nc("@item partition flag", "legacy-boot"); case PartitionTable::FlagMsftData: return i18nc("@item partition flag", "msft-data"); case PartitionTable::FlagIrst: return i18nc("@item partition flag", "irst"); case PartitionTable::FlagEsp: return i18nc("@item partition flag", "esp"); default: break; } return QString(); } /** @return list of all flags */ QList PartitionTable::flagList() { QList rval; rval.append(PartitionTable::FlagBoot); rval.append(PartitionTable::FlagRoot); rval.append(PartitionTable::FlagSwap); rval.append(PartitionTable::FlagHidden); rval.append(PartitionTable::FlagRaid); rval.append(PartitionTable::FlagLvm); rval.append(PartitionTable::FlagLba); rval.append(PartitionTable::FlagHpService); rval.append(PartitionTable::FlagPalo); rval.append(PartitionTable::FlagPrep); rval.append(PartitionTable::FlagMsftReserved); rval.append(PartitionTable::FlagBiosGrub); rval.append(PartitionTable::FlagAppleTvRecovery); rval.append(PartitionTable::FlagDiag); rval.append(PartitionTable::FlagLegacyBoot); rval.append(PartitionTable::FlagMsftData); rval.append(PartitionTable::FlagIrst); rval.append(PartitionTable::FlagEsp); return rval; } /** @param flags the flags to get the names for @returns QStringList of the flags' names */ QStringList PartitionTable::flagNames(Flags flags) { QStringList rval; int f = 1; QString s; while (!(s = flagName(static_cast(f))).isEmpty()) { if (flags & f) rval.append(s); f <<= 1; } return rval; } -bool PartitionTable::getUnallocatedRange(const Device& device, PartitionNode& parent, qint64& start, qint64& end) +bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end) { - if (!parent.isRoot()) { - Partition* extended = dynamic_cast(&parent); - - if (extended == nullptr) { - qWarning() << "extended is null. start: " << start << ", end: " << end << ", device: " << device.deviceNode(); - return false; + //TODO: alignment for LVM device + if (d.type() == Device::Disk_Device) { + const DiskDevice& device = dynamic_cast(d); + if (!parent.isRoot()) { + Partition* extended = dynamic_cast(&parent); + + if (extended == nullptr) { + qWarning() << "extended is null. start: " << start << ", end: " << end << ", device: " << device.deviceNode(); + return false; + } + + // Leave a track (cylinder aligned) or sector alignment sectors (sector based) free at the + // start for a new partition's metadata + start += device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); + + // .. and also at the end for the metadata for a partition to follow us, if we're not + // at the end of the extended partition + if (end < extended->lastSector()) + end -= device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); } - // Leave a track (cylinder aligned) or sector alignment sectors (sector based) free at the - // start for a new partition's metadata - start += device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); - - // .. and also at the end for the metadata for a partition to follow us, if we're not - // at the end of the extended partition - if (end < extended->lastSector()) - end -= device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); + return end - start + 1 >= PartitionAlignment::sectorAlignment(device); + } else if (d.type() == Device::LVM_Device) { + return false; } - - return end - start + 1 >= PartitionAlignment::sectorAlignment(device); + return false; } + + /** Creates a new unallocated Partition on the given Device. @param device the Device to create the new Partition on @param parent the parent PartitionNode for the new Partition @param start the new Partition's start sector @param end the new Partition's end sector @return pointer to the newly created Partition object or nullptr if the Partition could not be created */ Partition* createUnallocated(const Device& device, PartitionNode& parent, qint64 start, qint64 end) { PartitionRole::Roles r = PartitionRole::Unallocated; if (!parent.isRoot()) r |= PartitionRole::Logical; if (!PartitionTable::getUnallocatedRange(device, parent, start, end)) return nullptr; return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Unknown, start, end), start, end, QString()); } /** Removes all unallocated children from a PartitionNode @param p pointer to the parent to remove unallocated children from */ void PartitionTable::removeUnallocated(PartitionNode* p) { Q_ASSERT(p); qint32 i = 0; while (i < p->children().size()) { Partition* child = p->children()[i]; if (child->roles().has(PartitionRole::Unallocated)) { p->remove(child); delete child; continue; } if (child->roles().has(PartitionRole::Extended)) removeUnallocated(child); i++; } } /** @overload */ void PartitionTable::removeUnallocated() { removeUnallocated(this); } /** Inserts unallocated children for a Device's PartitionTable with the given parent. This method inserts unallocated Partitions for a parent, usually the Device this PartitionTable is on. It will also insert unallocated Partitions in any extended Partitions it finds. @warning This method assumes that no unallocated Partitions exist when it is called. @param d the Device this PartitionTable and @p p are on @param p the parent PartitionNode (may be this or an extended Partition) @param start the first sector to begin looking for free space */ void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 start) const { Q_ASSERT(p); qint64 lastEnd = start; foreach(Partition * child, p->children()) { p->insert(createUnallocated(d, *p, lastEnd, child->firstSector() - 1)); if (child->roles().has(PartitionRole::Extended)) insertUnallocated(d, child, child->firstSector()); lastEnd = child->lastSector() + 1; } // Take care of the free space between the end of the last child and the end // of the device or the extended partition. qint64 parentEnd = lastUsable(); if (!p->isRoot()) { Partition* extended = dynamic_cast(p); parentEnd = extended ? extended->lastSector() : -1; Q_ASSERT(extended); } if (parentEnd >= firstUsable()) p->insert(createUnallocated(d, *p, lastEnd, parentEnd)); } /** Updates the unallocated Partitions for this PartitionTable. @param d the Device this PartitionTable is on */ void PartitionTable::updateUnallocated(const Device& d) { removeUnallocated(); insertUnallocated(d, this, firstUsable()); } qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t) { Q_UNUSED(t) - return PartitionAlignment::sectorAlignment(d); + const DiskDevice& diskDevice = dynamic_cast(d); + return PartitionAlignment::sectorAlignment(diskDevice); } qint64 PartitionTable::defaultLastUsable(const Device& d, TableType t) { if (t == gpt) - return d.totalSectors() - 1 - 32 - 1; + return d.totalLogical() - 1 - 32 - 1; - return d.totalSectors() - 1; + return d.totalLogical() - 1; } static struct { const QString name; /**< name of partition table type */ quint32 maxPrimaries; /**< max numbers of primary partitions supported */ bool canHaveExtended; /**< does partition table type support extended partitions */ bool isReadOnly; /**< does KDE Partition Manager support this only in read only mode */ PartitionTable::TableType type; /**< enum type */ } tableTypes[] = { { QStringLiteral("aix"), 4, false, true, PartitionTable::aix }, { QStringLiteral("bsd"), 8, false, true, PartitionTable::bsd }, { QStringLiteral("dasd"), 1, false, true, PartitionTable::dasd }, { QStringLiteral("msdos"), 4, true, false, PartitionTable::msdos }, { QStringLiteral("msdos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QStringLiteral("dvh"), 16, true, true, PartitionTable::dvh }, { QStringLiteral("gpt"), 128, false, false, PartitionTable::gpt }, { QStringLiteral("loop"), 1, false, true, PartitionTable::loop }, { QStringLiteral("mac"), 0xffff, false, true, PartitionTable::mac }, { QStringLiteral("pc98"), 16, false, true, PartitionTable::pc98 }, { QStringLiteral("amiga"), 128, false, true, PartitionTable::amiga }, { QStringLiteral("sun"), 8, false, true, PartitionTable::sun } }; PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (n == tableTypes[i].name) return tableTypes[i].type; return PartitionTable::unknownTableType; } QString PartitionTable::tableTypeToName(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].name; return i18nc("@item partition table name", "unknown"); } qint64 PartitionTable::maxPrimariesForTableType(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].maxPrimaries; return 1; } bool PartitionTable::tableTypeSupportsExtended(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].canHaveExtended; return false; } bool PartitionTable::tableTypeIsReadOnly(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].isReadOnly; return false; } /** Simple heuristic to determine if the PartitionTable is sector aligned (i.e. if its Partitions begin at sectors evenly divisable by PartitionAlignment::sectorAlignment(). @return true if is sector aligned, otherwise false */ bool PartitionTable::isSectorBased(const Device& d) const { - if (type() == PartitionTable::msdos) { - // the default for empty partition tables is sector based - if (numPrimaries() == 0) - return true; - - quint32 numCylinderAligned = 0; - quint32 numSectorAligned = 0; - - // see if we have more cylinder aligned partitions than sector - // aligned ones. - foreach(const Partition * p, children()) - if (p->firstSector() % PartitionAlignment::sectorAlignment(d) == 0) - numSectorAligned++; - else if (p->firstSector() % d.cylinderSize() == 0) - numCylinderAligned++; - - return numSectorAligned >= numCylinderAligned; + if (d.type() == Device::Disk_Device) { + const DiskDevice& diskDevice = dynamic_cast(d); + + if (type() == PartitionTable::msdos) { + // the default for empty partition tables is sector based + if (numPrimaries() == 0) + return true; + + quint32 numCylinderAligned = 0; + quint32 numSectorAligned = 0; + + // see if we have more cylinder aligned partitions than sector + // aligned ones. + foreach(const Partition * p, children()) + if (p->firstSector() % PartitionAlignment::sectorAlignment(diskDevice) == 0) + numSectorAligned++; + else if (p->firstSector() % diskDevice.cylinderSize() == 0) + numCylinderAligned++; + + return numSectorAligned >= numCylinderAligned; + } + return type() == PartitionTable::msdos_sectorbased; } - return type() == PartitionTable::msdos_sectorbased; + return false; } void PartitionTable::setType(const Device& d, TableType t) { setFirstUsableSector(defaultFirstUsable(d, t)); setLastUsableSector(defaultLastUsable(d, t)); m_Type = t; updateUnallocated(d); } static bool isPartitionLessThan(const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); } QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable) { stream << "type: \"" << ptable.typeName() << "\"\n" << "align: \"" << (ptable.type() == PartitionTable::msdos ? "cylinder" : "sector") << "\"\n" << "\n# number start end type roles label flags\n"; QList partitions; foreach(const Partition * p, ptable.children()) if (!p->roles().has(PartitionRole::Unallocated)) { partitions.append(p); if (p->roles().has(PartitionRole::Extended)) foreach(const Partition * child, p->children()) if (!child->roles().has(PartitionRole::Unallocated)) partitions.append(child); } qSort(partitions.begin(), partitions.end(), isPartitionLessThan); foreach(const Partition * p, partitions) stream << *p; return stream; } diff --git a/src/core/partitiontable.h b/src/core/partitiontable.h index a0ddeef..e391156 100644 --- a/src/core/partitiontable.h +++ b/src/core/partitiontable.h @@ -1,199 +1,200 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * Copyright (C) 2016 by Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(PARTITIONTABLE__H) #define PARTITIONTABLE__H #include "util/libpartitionmanagerexport.h" #include "core/partitionnode.h" #include "core/partitionrole.h" #include #include class Device; class Partition; class CoreBackend; class QTextStream; /** The partition table (a.k.a Disk Label) PartitionTable represents a partition table (or disk label). PartitionTable has child nodes that represent Partitions. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartitionTable : public PartitionNode { Q_DISABLE_COPY(PartitionTable) friend class CoreBackend; friend LIBKPMCORE_EXPORT QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); public: enum TableType : qint8 { unknownTableType = -1, aix, bsd, dasd, msdos, msdos_sectorbased, dvh, gpt, loop, mac, pc98, amiga, - sun + sun, + vmd /* Volume Manager Device */ }; /** Partition flags */ enum Flag : qint32 { FlagNone = 0, FlagBoot = 1, FlagRoot = 2, FlagSwap = 4, FlagHidden = 8, FlagRaid = 16, FlagLvm = 32, FlagLba = 64, FlagHpService = 128, FlagPalo = 256, FlagPrep = 512, FlagMsftReserved = 1024, FlagBiosGrub = 2048, FlagAppleTvRecovery = 4096, FlagDiag = 8192, FlagLegacyBoot = 16384, FlagMsftData = 32768, FlagIrst = 65536, FlagEsp = 131072 }; Q_DECLARE_FLAGS(Flags, Flag) public: PartitionTable(TableType type, qint64 first_usable, qint64 last_usable); ~PartitionTable(); public: PartitionNode* parent() override { return nullptr; /**< @return always nullptr for PartitionTable */ } const PartitionNode* parent() const override { return nullptr; /**< @return always nullptr for PartitionTable */ } bool isRoot() const override { return true; /**< @return always true for PartitionTable */ } bool isReadOnly() const { return tableTypeIsReadOnly(type()); /**< @return true if the PartitionTable is read only */ } Partitions& children() override { return m_Children; /**< @return the children in this PartitionTable */ } const Partitions& children() const override { return m_Children; /**< @return the children in this PartitionTable */ } void setType(const Device& d, TableType t); void append(Partition* partition) override; qint64 freeSectorsBefore(const Partition& p) const; qint64 freeSectorsAfter(const Partition& p) const; bool hasExtended() const; Partition* extended() const; PartitionRole::Roles childRoles(const Partition& p) const; int numPrimaries() const; int maxPrimaries() const { return m_MaxPrimaries; /**< @return max number of primary partitions this PartitionTable can handle */ } PartitionTable::TableType type() const { return m_Type; /**< @return the PartitionTable's type */ } const QString typeName() const { return tableTypeToName(type()); /**< @return the name of this PartitionTable type */ } qint64 firstUsable() const { return m_FirstUsable; } qint64 lastUsable() const { return m_LastUsable; } void updateUnallocated(const Device& d); void insertUnallocated(const Device& d, PartitionNode* p, qint64 start) const; bool isSectorBased(const Device& d) const; static QList flagList(); static QString flagName(Flag f); static QStringList flagNames(Flags f); static bool getUnallocatedRange(const Device& device, PartitionNode& parent, qint64& start, qint64& end); static void removeUnallocated(PartitionNode* p); void removeUnallocated(); static qint64 defaultFirstUsable(const Device& d, TableType t); static qint64 defaultLastUsable(const Device& d, TableType t); static PartitionTable::TableType nameToTableType(const QString& n); static QString tableTypeToName(TableType l); static qint64 maxPrimariesForTableType(TableType l); static bool tableTypeSupportsExtended(TableType l); static bool tableTypeIsReadOnly(TableType l); protected: void setMaxPrimaries(qint32 n) { m_MaxPrimaries = n; } void setFirstUsableSector(qint64 s) { m_FirstUsable = s; } void setLastUsableSector(qint64 s) { m_LastUsable = s; } private: Partitions m_Children; qint32 m_MaxPrimaries; TableType m_Type; qint64 m_FirstUsable; qint64 m_LastUsable; }; Q_DECLARE_OPERATORS_FOR_FLAGS(PartitionTable::Flags) QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); #endif diff --git a/src/jobs/backupfilesystemjob.cpp b/src/jobs/backupfilesystemjob.cpp index 5dcedfc..b4c04c6 100644 --- a/src/jobs/backupfilesystemjob.cpp +++ b/src/jobs/backupfilesystemjob.cpp @@ -1,78 +1,78 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "jobs/backupfilesystemjob.h" #include "core/partition.h" #include "core/device.h" #include "core/copysourcedevice.h" #include "core/copytargetfile.h" #include "fs/filesystem.h" #include "util/report.h" #include /** Creates a new BackupFileSystemJob @param sourcedevice the device the FileSystem to back up is on @param sourcepartition the Partition the FileSystem to back up is on @param filename name of the file to backup to */ BackupFileSystemJob::BackupFileSystemJob(Device& sourcedevice, Partition& sourcepartition, const QString& filename) : Job(), m_SourceDevice(sourcedevice), m_SourcePartition(sourcepartition), m_FileName(filename) { } qint32 BackupFileSystemJob::numSteps() const { return 100; } bool BackupFileSystemJob::run(Report& parent) { bool rval = false; Report* report = jobStarted(parent); if (sourcePartition().fileSystem().supportBackup() == FileSystem::cmdSupportFileSystem) rval = sourcePartition().fileSystem().backup(*report, sourceDevice(), sourcePartition().deviceNode(), fileName()); else if (sourcePartition().fileSystem().supportBackup() == FileSystem::cmdSupportCore) { CopySourceDevice copySource(sourceDevice(), sourcePartition().fileSystem().firstSector(), sourcePartition().fileSystem().lastSector()); - CopyTargetFile copyTarget(fileName(), sourceDevice().logicalSectorSize()); + CopyTargetFile copyTarget(fileName(), sourceDevice().logicalSize()); if (!copySource.open()) report->line() << xi18nc("@info/plain", "Could not open file system on source partition %1 for backup.", sourcePartition().deviceNode()); else if (!copyTarget.open()) report->line() << xi18nc("@info/plain", "Could not create backup file %1.", fileName()); else rval = copyBlocks(*report, copyTarget, copySource); } jobFinished(*report, rval); return rval; } QString BackupFileSystemJob::description() const { return xi18nc("@info/plain", "Back up file system on partition %1 to %2", sourcePartition().deviceNode(), fileName()); } diff --git a/src/jobs/createpartitionjob.cpp b/src/jobs/createpartitionjob.cpp index a0b3ea5..5789560 100644 --- a/src/jobs/createpartitionjob.cpp +++ b/src/jobs/createpartitionjob.cpp @@ -1,87 +1,91 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "jobs/createpartitionjob.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "backend/corebackenddevice.h" #include "backend/corebackendpartitiontable.h" #include "core/partition.h" #include "core/device.h" #include "util/report.h" #include /** Creates a new CreatePartitionJob @param d the Device the Partition to be created will be on @param p the Partition to create */ CreatePartitionJob::CreatePartitionJob(Device& d, Partition& p) : Job(), m_Device(d), m_Partition(p) { } bool CreatePartitionJob::run(Report& parent) { Q_ASSERT(partition().devicePath() == device().deviceNode()); bool rval = false; Report* report = jobStarted(parent); - CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device().deviceNode()); + if (device().type() == Device::Disk_Device) { + CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device().deviceNode()); - if (backendDevice) { - CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); + if (backendDevice) { + CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); - if (backendPartitionTable) { - QString partitionPath = backendPartitionTable->createPartition(*report, partition()); + if (backendPartitionTable) { + QString partitionPath = backendPartitionTable->createPartition(*report, partition()); - if (partitionPath != QString()) { - rval = true; - partition().setPartitionPath(partitionPath); - partition().setState(Partition::StateNone); - backendPartitionTable->commit(); + if (partitionPath != QString()) { + rval = true; + partition().setPartitionPath(partitionPath); + partition().setState(Partition::StateNone); + backendPartitionTable->commit(); + } else + report->line() << xi18nc("@info/plain", "Failed to add partition %1 to device %2.", partition().deviceNode(), device().deviceNode()); + + delete backendPartitionTable; } else - report->line() << xi18nc("@info/plain", "Failed to add partition %1 to device %2.", partition().deviceNode(), device().deviceNode()); + report->line() << xi18nc("@info/plain", "Could not open partition table on device %1 to create new partition %2.", device().deviceNode(), partition().deviceNode()); - delete backendPartitionTable; + delete backendDevice; } else - report->line() << xi18nc("@info/plain", "Could not open partition table on device %1 to create new partition %2.", device().deviceNode(), partition().deviceNode()); + report->line() << xi18nc("@info/plain", "Could not open device %1 to create new partition %2.", device().deviceNode(), partition().deviceNode()); + } else if (device().type() == Device::LVM_Device) { - delete backendDevice; - } else - report->line() << xi18nc("@info/plain", "Could not open device %1 to create new partition %2.", device().deviceNode(), partition().deviceNode()); + } jobFinished(*report, rval); return rval; } QString CreatePartitionJob::description() const { if (partition().number() > 0) return xi18nc("@info/plain", "Create new partition %1", partition().deviceNode()); return xi18nc("@info/plain", "Create new partition on device %1", device().deviceNode()); } diff --git a/src/jobs/resizefilesystemjob.cpp b/src/jobs/resizefilesystemjob.cpp index 8cd4403..9885ef8 100644 --- a/src/jobs/resizefilesystemjob.cpp +++ b/src/jobs/resizefilesystemjob.cpp @@ -1,144 +1,144 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "jobs/resizefilesystemjob.h" #include "core/partition.h" #include "core/device.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "backend/corebackenddevice.h" #include "backend/corebackendpartitiontable.h" #include "fs/filesystem.h" #include "util/report.h" #include "util/capacity.h" #include #include /** Creates a new ResizeFileSystemJob @param d the Device the FileSystem to be resized is on @param p the Partition the FileSystem to be resized is on @param newlength the new length for the FileSystem; if -1, the FileSystem will be resized to fill the entire Partition */ ResizeFileSystemJob::ResizeFileSystemJob(Device& d, Partition& p, qint64 newlength) : Job(), m_Device(d), m_Partition(p), m_Maximize(newlength == -1), m_NewLength(isMaximizing() ? partition().length() : newlength) { } qint32 ResizeFileSystemJob::numSteps() const { return 100; } bool ResizeFileSystemJob::run(Report& parent) { Q_ASSERT(partition().fileSystem().firstSector() != -1); Q_ASSERT(partition().fileSystem().lastSector() != -1); Q_ASSERT(newLength() <= partition().length()); if (partition().fileSystem().firstSector() == -1 || partition().fileSystem().lastSector() == -1 || newLength() > partition().length()) { qWarning() << "file system first sector: " << partition().fileSystem().firstSector() << ", last sector: " << partition().fileSystem().lastSector() << ", new length: " << newLength() << ", partition length: " << partition().length(); return false; } bool rval = false; Report* report = jobStarted(parent); if (partition().fileSystem().length() == newLength()) { report->line() << xi18ncp("@info/plain", "The file system on partition %2 already has the requested length of 1 sector.", "The file system on partition %2 already has the requested length of %1 sectors.", newLength(), partition().deviceNode()); rval = true; } else { report->line() << i18nc("@info/plain", "Resizing file system from %1 to %2 sectors.", partition().fileSystem().length(), newLength()); FileSystem::CommandSupportType support = (newLength() < partition().fileSystem().length()) ? partition().fileSystem().supportShrink() : partition().fileSystem().supportGrow(); switch (support) { case FileSystem::cmdSupportBackend: { Report* childReport = report->newChild(); childReport->line() << i18nc("@info/plain", "Resizing a %1 file system using internal backend functions.", partition().fileSystem().name()); rval = resizeFileSystemBackend(*childReport); break; } case FileSystem::cmdSupportFileSystem: { - const qint64 newLengthInByte = Capacity(newLength() * device().logicalSectorSize()).toInt(Capacity::Byte); + const qint64 newLengthInByte = Capacity(newLength() * device().logicalSize()).toInt(Capacity::Byte); rval = partition().fileSystem().resize(*report, partition().deviceNode(), newLengthInByte); break; } default: report->line() << xi18nc("@info/plain", "The file system on partition %1 cannot be resized because there is no support for it.", partition().deviceNode()); break; } if (rval) partition().fileSystem().setLastSector(partition().fileSystem().firstSector() + newLength() - 1); } jobFinished(*report, rval); return rval; } bool ResizeFileSystemJob::resizeFileSystemBackend(Report& report) { bool rval = false; CoreBackendDevice* backendDevice = CoreBackendManager::self()->backend()->openDevice(device().deviceNode()); if (backendDevice) { CoreBackendPartitionTable* backendPartitionTable = backendDevice->openPartitionTable(); if (backendPartitionTable) { connect(CoreBackendManager::self()->backend(), &CoreBackend::progress, this, &ResizeFileSystemJob::progress); rval = backendPartitionTable->resizeFileSystem(report, partition(), newLength()); disconnect(CoreBackendManager::self()->backend(), &CoreBackend::progress, this, &ResizeFileSystemJob::progress); if (rval) { report.line() << i18nc("@info/plain", "Successfully resized file system using internal backend functions."); backendPartitionTable->commit(); } delete backendPartitionTable; } else report.line() << xi18nc("@info/plain", "Could not open partition %1 while trying to resize the file system.", partition().deviceNode()); delete backendDevice; } else report.line() << xi18nc("@info/plain", "Could not read geometry for partition %1 while trying to resize the file system.", partition().deviceNode()); return rval; } QString ResizeFileSystemJob::description() const { if (isMaximizing()) return xi18nc("@info/plain", "Maximize file system on %1 to fill the partition", partition().deviceNode()); return xi18ncp("@info/plain", "Resize file system on partition %2 to 1 sector", "Resize file system on partition %2 to %1 sectors", newLength(), partition().deviceNode()); } diff --git a/src/ops/copyoperation.cpp b/src/ops/copyoperation.cpp index 95f91d2..22a1e09 100644 --- a/src/ops/copyoperation.cpp +++ b/src/ops/copyoperation.cpp @@ -1,323 +1,323 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "ops/copyoperation.h" #include "core/partition.h" #include "core/device.h" #include "jobs/createpartitionjob.h" #include "jobs/deletepartitionjob.h" #include "jobs/checkfilesystemjob.h" #include "jobs/copyfilesystemjob.h" #include "jobs/resizefilesystemjob.h" #include "fs/filesystemfactory.h" #include "util/capacity.h" #include "util/report.h" #include #include #include /** Creates a new CopyOperation. @param targetdevice the Device to copy the Partition to @param copiedpartition pointer to the new Partition object on the target Device. May not be nullptr. @param sourcedevice the Device where to copy from @param sourcepartition pointer to the Partition to copy from. May not be nullptr. */ CopyOperation::CopyOperation(Device& targetdevice, Partition* copiedpartition, Device& sourcedevice, Partition* sourcepartition) : Operation(), m_TargetDevice(targetdevice), m_CopiedPartition(copiedpartition), m_SourceDevice(sourcedevice), m_SourcePartition(sourcepartition), m_OverwrittenPartition(nullptr), m_MustDeleteOverwritten(false), m_CheckSourceJob(nullptr), m_CreatePartitionJob(nullptr), m_CopyFSJob(nullptr), m_CheckTargetJob(nullptr), m_MaximizeJob(nullptr), m_Description(updateDescription()) { Q_ASSERT(targetDevice().partitionTable()); Partition* dest = targetDevice().partitionTable()->findPartitionBySector(copiedPartition().firstSector(), PartitionRole(PartitionRole::Primary | PartitionRole::Logical | PartitionRole::Unallocated)); if (dest == nullptr) qWarning() << "destination partition not found at sector " << copiedPartition().firstSector(); Q_ASSERT(dest); if (dest && !dest->roles().has(PartitionRole::Unallocated)) { copiedPartition().setLastSector(dest->lastSector()); setOverwrittenPartition(dest); } addJob(m_CheckSourceJob = new CheckFileSystemJob(sourcePartition())); if (overwrittenPartition() == nullptr) addJob(m_CreatePartitionJob = new CreatePartitionJob(targetDevice(), copiedPartition())); addJob(m_CopyFSJob = new CopyFileSystemJob(targetDevice(), copiedPartition(), sourceDevice(), sourcePartition())); addJob(m_CheckTargetJob = new CheckFileSystemJob(copiedPartition())); addJob(m_MaximizeJob = new ResizeFileSystemJob(targetDevice(), copiedPartition())); } CopyOperation::~CopyOperation() { if (status() == StatusPending) delete m_CopiedPartition; if (status() == StatusFinishedSuccess || status() == StatusFinishedWarning || status() == StatusError) cleanupOverwrittenPartition(); } bool CopyOperation::targets(const Device& d) const { return d == targetDevice(); } bool CopyOperation::targets(const Partition& p) const { return p == copiedPartition(); } void CopyOperation::preview() { if (overwrittenPartition()) removePreviewPartition(targetDevice(), *overwrittenPartition()); insertPreviewPartition(targetDevice(), copiedPartition()); } void CopyOperation::undo() { removePreviewPartition(targetDevice(), copiedPartition()); if (overwrittenPartition()) insertPreviewPartition(targetDevice(), *overwrittenPartition()); } bool CopyOperation::execute(Report& parent) { bool rval = false; bool warning = false; Report* report = parent.newChild(description()); // check the source first if ((rval = checkSourceJob()->run(*report))) { // At this point, if the target partition is to be created and not overwritten, it // will still have the wrong device path (the one of the source device). We need // to adjust that before we're creating it. copiedPartition().setDevicePath(targetDevice().deviceNode()); // either we have no partition to create (because we're overwriting) or creating // must be successful if (!createPartitionJob() || (rval = createPartitionJob()->run(*report))) { // set the state of the target partition from StateCopy to StateNone or checking // it will fail (because its deviceNode() will still be "Copy of sdXn"). This is // only required for overwritten partitions, but doesn't hurt in any case. copiedPartition().setState(Partition::StateNone); // if we have overwritten a partition, reset device path and number if (overwrittenPartition()) { copiedPartition().setDevicePath(overwrittenPartition()->devicePath()); copiedPartition().setPartitionPath(overwrittenPartition()->partitionPath()); } // now run the copy job itself if ((rval = copyFSJob()->run(*report))) { // and if the copy job succeeded, check the target if ((rval = checkTargetJob()->run(*report))) { // ok, everything went well rval = true; // if maximizing doesn't work, just warn the user, don't fail if (!maximizeJob()->run(*report)) { report->line() << xi18nc("@info/plain", "Warning: Maximizing file system on target partition %1 to the size of the partition failed.", copiedPartition().deviceNode()); warning = true; } } else report->line() << xi18nc("@info/plain", "Checking target partition %1 after copy failed.", copiedPartition().deviceNode()); } else { if (createPartitionJob()) { DeletePartitionJob deleteJob(targetDevice(), copiedPartition()); deleteJob.run(*report); } report->line() << i18nc("@info/plain", "Copying source to target partition failed."); } } else report->line() << i18nc("@info/plain", "Creating target partition for copying failed."); } else report->line() << xi18nc("@info/plain", "Checking source partition %1 failed.", sourcePartition().deviceNode()); if (rval) setStatus(warning ? StatusFinishedWarning : StatusFinishedSuccess); else setStatus(StatusError); report->setStatus(i18nc("@info/plain status (success, error, warning...) of operation", "%1: %2", description(), statusText())); return rval; } QString CopyOperation::updateDescription() const { if (overwrittenPartition()) { if (copiedPartition().length() == overwrittenPartition()->length()) return xi18nc("@info/plain", "Copy partition %1 (%2, %3) to %4 (%5, %6)", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), overwrittenPartition()->deviceNode(), Capacity::formatByteSize(overwrittenPartition()->capacity()), overwrittenPartition()->fileSystem().name() ); return xi18nc("@info/plain", "Copy partition %1 (%2, %3) to %4 (%5, %6) and grow it to %7", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), overwrittenPartition()->deviceNode(), Capacity::formatByteSize(overwrittenPartition()->capacity()), overwrittenPartition()->fileSystem().name(), Capacity::formatByteSize(copiedPartition().capacity()) ); } if (copiedPartition().length() == sourcePartition().length()) return xi18nc("@info/plain", "Copy partition %1 (%2, %3) to unallocated space (starting at %4) on %5", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), - Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSectorSize()), + Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSize()), targetDevice().deviceNode() ); return xi18nc("@info/plain", "Copy partition %1 (%2, %3) to unallocated space (starting at %4) on %5 and grow it to %6", sourcePartition().deviceNode(), Capacity::formatByteSize(sourcePartition().capacity()), sourcePartition().fileSystem().name(), - Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSectorSize()), + Capacity::formatByteSize(copiedPartition().firstSector() * targetDevice().logicalSize()), targetDevice().deviceNode(), Capacity::formatByteSize(copiedPartition().capacity()) ); } void CopyOperation::setOverwrittenPartition(Partition* p) { // this code is also in RestoreOperation. cleanupOverwrittenPartition(); m_OverwrittenPartition = p; // If the overwritten partition has no other operation that owns it (e.g., an OperationNew or // an OperationRestore), we're the new owner. So remember that, because after the operations all // have executed and we're asked to clean up after ourselves, the state of the overwritten partition // might have changed: If it was a new one and the NewOperation has successfully run, the state will // then be StateNone. m_MustDeleteOverwritten = (p && p->state() == Partition::StateNone); } void CopyOperation::cleanupOverwrittenPartition() { if (mustDeleteOverwritten()) { delete overwrittenPartition(); m_OverwrittenPartition = nullptr; } } /** Creates a new copied Partition. @param target the target Partition to copy to (may be unallocated) @param source the source Partition to copy @return pointer to the newly created Partition object */ Partition* CopyOperation::createCopy(const Partition& target, const Partition& source) { Partition* p = target.roles().has(PartitionRole::Unallocated) ? new Partition(source) : new Partition(target); p->setDevicePath(source.devicePath()); p->setPartitionPath(source.partitionPath()); p->setState(Partition::StateCopy); p->deleteFileSystem(); p->setFileSystem(FileSystemFactory::create(source.fileSystem())); p->fileSystem().setFirstSector(p->firstSector()); p->fileSystem().setLastSector(p->lastSector()); p->setFlags(PartitionTable::FlagNone); return p; } /** Can a Partition be copied? @param p the Partition in question, may be nullptr. @return true if @p p can be copied. */ bool CopyOperation::canCopy(const Partition* p) { if (p == nullptr) return false; if (p->state() == Partition::StateNew && p->roles().has(PartitionRole::Luks)) return false; if (p->isMounted()) return false; // Normally, copying partitions that have not been written to disk yet should // be forbidden here. The operation stack, however, will take care of these // problematic cases when pushing the CopyOperation onto the stack. return p->fileSystem().supportCopy() != FileSystem::cmdSupportNone; } /** Can a Partition be pasted on another one? @param p the Partition to be pasted to, may be nullptr @param source the Partition to be pasted, may be nullptr @return true if @p source can be pasted on @p p */ bool CopyOperation::canPaste(const Partition* p, const Partition* source) { if (p == nullptr || source == nullptr) return false; if (p->isMounted()) return false; if (p->roles().has(PartitionRole::Extended)) return false; if (p == source) return false; if (source->length() > p->length()) return false; if (!p->roles().has(PartitionRole::Unallocated) && p->capacity() > source->fileSystem().maxCapacity()) return false; return true; } diff --git a/src/ops/resizeoperation.cpp b/src/ops/resizeoperation.cpp index eda1019..81ac08c 100644 --- a/src/ops/resizeoperation.cpp +++ b/src/ops/resizeoperation.cpp @@ -1,379 +1,379 @@ /************************************************************************* * Copyright (C) 2008, 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 "ops/resizeoperation.h" #include "core/partition.h" #include "core/device.h" #include "core/partitiontable.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" #include "jobs/checkfilesystemjob.h" #include "jobs/setpartgeometryjob.h" #include "jobs/resizefilesystemjob.h" #include "jobs/movefilesystemjob.h" #include "fs/filesystem.h" #include "util/capacity.h" #include "util/report.h" #include #include #include /** Creates a new ResizeOperation. @param d the Device to resize a Partition on @param p the Partition to resize @param newfirst the new first sector of the Partition @param newlast the new last sector of the Partition */ ResizeOperation::ResizeOperation(Device& d, Partition& p, qint64 newfirst, qint64 newlast) : Operation(), m_TargetDevice(d), m_Partition(p), m_OrigFirstSector(partition().firstSector()), m_OrigLastSector(partition().lastSector()), m_NewFirstSector(newfirst), m_NewLastSector(newlast), m_CheckOriginalJob(new CheckFileSystemJob(partition())), m_MoveExtendedJob(nullptr), m_ShrinkResizeJob(nullptr), m_ShrinkSetGeomJob(nullptr), m_MoveSetGeomJob(nullptr), m_MoveFileSystemJob(nullptr), m_GrowResizeJob(nullptr), m_GrowSetGeomJob(nullptr), m_CheckResizedJob(nullptr) { addJob(checkOriginalJob()); if (partition().roles().has(PartitionRole::Extended)) { m_MoveExtendedJob = new SetPartGeometryJob(targetDevice(), partition(), newFirstSector(), newLength()); addJob(moveExtendedJob()); } else { if (resizeAction() & Shrink) { m_ShrinkResizeJob = new ResizeFileSystemJob(targetDevice(), partition(), newLength()); m_ShrinkSetGeomJob = new SetPartGeometryJob(targetDevice(), partition(), partition().firstSector(), newLength()); addJob(shrinkResizeJob()); addJob(shrinkSetGeomJob()); } if ((resizeAction() & MoveLeft) || (resizeAction() & MoveRight)) { // At this point, we need to set the partition's length to either the resized length, if it has already been // shrunk, or to the original length (it may or may not then later be grown, we don't care here) const qint64 currentLength = (resizeAction() & Shrink) ? newLength() : partition().length(); m_MoveSetGeomJob = new SetPartGeometryJob(targetDevice(), partition(), newFirstSector(), currentLength); m_MoveFileSystemJob = new MoveFileSystemJob(targetDevice(), partition(), newFirstSector()); addJob(moveSetGeomJob()); addJob(moveFileSystemJob()); } if (resizeAction() & Grow) { m_GrowSetGeomJob = new SetPartGeometryJob(targetDevice(), partition(), newFirstSector(), newLength()); m_GrowResizeJob = new ResizeFileSystemJob(targetDevice(), partition(), newLength()); addJob(growSetGeomJob()); addJob(growResizeJob()); } m_CheckResizedJob = new CheckFileSystemJob(partition()); addJob(checkResizedJob()); } } bool ResizeOperation::targets(const Device& d) const { return d == targetDevice(); } bool ResizeOperation::targets(const Partition& p) const { return p == partition(); } void ResizeOperation::preview() { // If the operation has already been executed, the partition will of course have newFirstSector and // newLastSector as first and last sector. But to remove it from its original position, we need to // temporarily set these values back to where they were before the operation was executed. if (partition().firstSector() == newFirstSector() && partition().lastSector() == newLastSector()) { partition().setFirstSector(origFirstSector()); partition().setLastSector(origLastSector()); } removePreviewPartition(targetDevice(), partition()); partition().setFirstSector(newFirstSector()); partition().setLastSector(newLastSector()); insertPreviewPartition(targetDevice(), partition()); } void ResizeOperation::undo() { removePreviewPartition(targetDevice(), partition()); partition().setFirstSector(origFirstSector()); partition().setLastSector(origLastSector()); insertPreviewPartition(targetDevice(), partition()); } bool ResizeOperation::execute(Report& parent) { bool rval = false; Report* report = parent.newChild(description()); if ((rval = checkOriginalJob()->run(*report))) { // Extended partitions are a special case: They don't have any file systems and so there's no // need to move, shrink or grow their contents before setting the new geometry. In fact, trying // to first shrink THEN move would not work for an extended partition that has children, because // they might temporarily be outside the extended partition and the backend would not let us do that. if (moveExtendedJob()) { if (!(rval = moveExtendedJob()->run(*report))) report->line() << xi18nc("@info/plain", "Moving extended partition %1 failed.", partition().deviceNode()); } else { // We run all three methods. Any of them returns true if it has nothing to do. rval = shrink(*report) && move(*report) && grow(*report); if (rval) { if (!(rval = checkResizedJob()->run(*report))) report->line() << xi18nc("@info/plain", "Checking partition %1 after resize/move failed.", partition().deviceNode()); } else report->line() << xi18nc("@info/plain", "Resizing/moving partition %1 failed.", partition().deviceNode()); } } else report->line() << xi18nc("@info/plain", "Checking partition %1 before resize/move failed.", partition().deviceNode()); setStatus(rval ? StatusFinishedSuccess : StatusError); report->setStatus(i18nc("@info/plain status (success, error, warning...) of operation", "%1: %2", description(), statusText())); return rval; } QString ResizeOperation::description() const { // There are eight possible things a resize operation might do: // 1) Move a partition to the left (closer to the start of the disk) // 2) Move a partition to the right (closer to the end of the disk) // 3) Grow a partition // 4) Shrink a partition // 5) Move a partition to the left and grow it // 6) Move a partition to the right and grow it // 7) Move a partition to the left and shrink it // 8) Move a partition to the right and shrink it // Each of these needs a different description. And for reasons of i18n, we cannot // just concatenate strings together... - const QString moveDelta = Capacity::formatByteSize(qAbs(newFirstSector() - origFirstSector()) * targetDevice().logicalSectorSize()); + const QString moveDelta = Capacity::formatByteSize(qAbs(newFirstSector() - origFirstSector()) * targetDevice().logicalSize()); - const QString origCapacity = Capacity::formatByteSize(origLength() * targetDevice().logicalSectorSize()); - const QString newCapacity = Capacity::formatByteSize(newLength() * targetDevice().logicalSectorSize()); + const QString origCapacity = Capacity::formatByteSize(origLength() * targetDevice().logicalSize()); + const QString newCapacity = Capacity::formatByteSize(newLength() * targetDevice().logicalSize()); switch (resizeAction()) { case MoveLeft: return xi18nc("@info/plain describe resize/move action", "Move partition %1 to the left by %2", partition().deviceNode(), moveDelta); case MoveRight: return xi18nc("@info/plain describe resize/move action", "Move partition %1 to the right by %2", partition().deviceNode(), moveDelta); case Grow: return xi18nc("@info/plain describe resize/move action", "Grow partition %1 from %2 to %3", partition().deviceNode(), origCapacity, newCapacity); case Shrink: return xi18nc("@info/plain describe resize/move action", "Shrink partition %1 from %2 to %3", partition().deviceNode(), origCapacity, newCapacity); case MoveLeftGrow: return xi18nc("@info/plain describe resize/move action", "Move partition %1 to the left by %2 and grow it from %3 to %4", partition().deviceNode(), moveDelta, origCapacity, newCapacity); case MoveRightGrow: return xi18nc("@info/plain describe resize/move action", "Move partition %1 to the right by %2 and grow it from %3 to %4", partition().deviceNode(), moveDelta, origCapacity, newCapacity); case MoveLeftShrink: return xi18nc("@info/plain describe resize/move action", "Move partition %1 to the left by %2 and shrink it from %3 to %4", partition().deviceNode(), moveDelta, origCapacity, newCapacity); case MoveRightShrink: return xi18nc("@info/plain describe resize/move action", "Move partition %1 to the right by %2 and shrink it from %3 to %4", partition().deviceNode(), moveDelta, origCapacity, newCapacity); default: qWarning() << "Could not determine what to do with partition " << partition().deviceNode() << "."; break; } return i18nc("@info/plain describe resize/move action", "Unknown resize/move action."); } ResizeOperation::ResizeAction ResizeOperation::resizeAction() const { ResizeAction action = None; // Grow? if (newLength() > origLength()) action = Grow; // Shrink? if (newLength() < origLength()) action = Shrink; // Move to the right? if (newFirstSector() > origFirstSector()) action = static_cast(action | MoveRight); // Move to the left? if (newFirstSector() < origFirstSector()) action = static_cast(action | MoveLeft); return action; } bool ResizeOperation::shrink(Report& report) { if (shrinkResizeJob() && !shrinkResizeJob()->run(report)) { report.line() << xi18nc("@info/plain", "Resize/move failed: Could not resize file system to shrink partition %1.", partition().deviceNode()); return false; } if (shrinkSetGeomJob() && !shrinkSetGeomJob()->run(report)) { report.line() << xi18nc("@info/plain", "Resize/move failed: Could not shrink partition %1.", partition().deviceNode()); return false; /** @todo if this fails, no one undoes the shrinking of the file system above, because we rely upon there being a maximize job at the end, but that's no longer the case. */ } return true; } bool ResizeOperation::move(Report& report) { // We must make sure not to overwrite the partition's metadata if it's a logical partition // and we're moving to the left. The easiest way to achieve this is to move the // partition itself first (it's the backend's responsibility to then move the metadata) and // only afterwards copy the filesystem. Disadvantage: We need to move the partition // back to its original position if copyBlocks fails. const qint64 oldStart = partition().firstSector(); if (moveSetGeomJob() && !moveSetGeomJob()->run(report)) { report.line() << xi18nc("@info/plain", "Moving partition %1 failed.", partition().deviceNode()); return false; } if (moveFileSystemJob() && !moveFileSystemJob()->run(report)) { report.line() << xi18nc("@info/plain", "Moving the filesystem for partition %1 failed. Rolling back.", partition().deviceNode()); // see above: We now have to move back the partition itself. if (!SetPartGeometryJob(targetDevice(), partition(), oldStart, partition().length()).run(report)) report.line() << xi18nc("@info/plain", "Moving back partition %1 to its original position failed.", partition().deviceNode()); return false; } return true; } bool ResizeOperation::grow(Report& report) { const qint64 oldLength = partition().length(); if (growSetGeomJob() && !growSetGeomJob()->run(report)) { report.line() << xi18nc("@info/plain", "Resize/move failed: Could not grow partition %1.", partition().deviceNode()); return false; } if (growResizeJob() && !growResizeJob()->run(report)) { report.line() << xi18nc("@info/plain", "Resize/move failed: Could not resize the file system on partition %1", partition().deviceNode()); if (!SetPartGeometryJob(targetDevice(), partition(), partition().firstSector(), oldLength).run(report)) report.line() << xi18nc("@info/plain", "Could not restore old partition size for partition %1.", partition().deviceNode()); return false; } return true; } /** Can a Partition be grown, i.e. increased in size? @param p the Partition in question, may be nullptr. @return true if @p p can be grown. */ bool ResizeOperation::canGrow(const Partition* p) { if (p == nullptr) return false; // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::StateNew && !p->roles().has(PartitionRole::Luks)) return true; if (p->isMounted()) return false; return p->fileSystem().supportGrow() != FileSystem::cmdSupportNone; } /** Can a Partition be shrunk, i.e. decreased in size? @param p the Partition in question, may be nullptr. @return true if @p p can be shrunk. */ bool ResizeOperation::canShrink(const Partition* p) { if (p == nullptr) return false; // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::StateNew && !p->roles().has(PartitionRole::Luks)) return true; if (p->state() == Partition::StateCopy) return false; if (p->isMounted()) return false; return p->fileSystem().supportShrink() != FileSystem::cmdSupportNone; } /** Can a Partition be moved? @param p the Partition in question, may be nullptr. @return true if @p p can be moved. */ bool ResizeOperation::canMove(const Partition* p) { if (p == nullptr) return false; // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::StateNew) // too many bad things can happen for LUKS partitions return p->roles().has(PartitionRole::Luks) ? false : true; if (p->isMounted()) return false; // no moving of extended partitions if they have logicals if (p->roles().has(PartitionRole::Extended) && p->hasChildren()) return false; return p->fileSystem().supportMove() != FileSystem::cmdSupportNone; } diff --git a/src/ops/restoreoperation.cpp b/src/ops/restoreoperation.cpp index 56d98ee..25e4adf 100644 --- a/src/ops/restoreoperation.cpp +++ b/src/ops/restoreoperation.cpp @@ -1,231 +1,231 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "ops/restoreoperation.h" #include "core/partition.h" #include "core/device.h" #include "core/partitiontable.h" #include "core/partitionnode.h" #include "jobs/createpartitionjob.h" #include "jobs/deletepartitionjob.h" #include "jobs/checkfilesystemjob.h" #include "jobs/restorefilesystemjob.h" #include "jobs/resizefilesystemjob.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "fs/luks.h" #include "util/capacity.h" #include "util/report.h" #include #include #include #include /** Creates a new RestoreOperation. @param d the Device to restore the Partition to @param p pointer to the Partition that will be restored. May not be nullptr. @param filename name of the image file to restore from */ RestoreOperation::RestoreOperation(Device& d, Partition* p, const QString& filename) : Operation(), m_TargetDevice(d), m_RestorePartition(p), m_FileName(filename), m_OverwrittenPartition(nullptr), m_MustDeleteOverwritten(false), m_ImageLength(QFileInfo(filename).size() / 512), // 512 being the "sector size" of an image file. m_CreatePartitionJob(nullptr), m_RestoreJob(nullptr), m_CheckTargetJob(nullptr), m_MaximizeJob(nullptr) { restorePartition().setState(Partition::StateRestore); Q_ASSERT(targetDevice().partitionTable()); Partition* dest = targetDevice().partitionTable()->findPartitionBySector(restorePartition().firstSector(), PartitionRole(PartitionRole::Primary | PartitionRole::Logical | PartitionRole::Unallocated)); if (dest == nullptr) qWarning() << "destination partition not found at sector " << restorePartition().firstSector(); Q_ASSERT(dest); if (dest && !dest->roles().has(PartitionRole::Unallocated)) { restorePartition().setLastSector(dest->lastSector()); setOverwrittenPartition(dest); removePreviewPartition(targetDevice(), *dest); } if (!overwrittenPartition()) addJob(m_CreatePartitionJob = new CreatePartitionJob(targetDevice(), restorePartition())); addJob(m_RestoreJob = new RestoreFileSystemJob(targetDevice(), restorePartition(), fileName())); addJob(m_CheckTargetJob = new CheckFileSystemJob(restorePartition())); addJob(m_MaximizeJob = new ResizeFileSystemJob(targetDevice(), restorePartition())); } RestoreOperation::~RestoreOperation() { if (status() == StatusPending) delete m_RestorePartition; if (status() == StatusFinishedSuccess || status() == StatusFinishedWarning || status() == StatusError) cleanupOverwrittenPartition(); } bool RestoreOperation::targets(const Device& d) const { return d == targetDevice(); } bool RestoreOperation::targets(const Partition& p) const { return p == restorePartition(); } void RestoreOperation::preview() { insertPreviewPartition(targetDevice(), restorePartition()); } void RestoreOperation::undo() { removePreviewPartition(targetDevice(), restorePartition()); if (overwrittenPartition()) insertPreviewPartition(targetDevice(), *overwrittenPartition()); } bool RestoreOperation::execute(Report& parent) { bool rval = false; bool warning = false; Report* report = parent.newChild(description()); if (overwrittenPartition()) restorePartition().setPartitionPath(overwrittenPartition()->devicePath()); if (overwrittenPartition() || (rval = createPartitionJob()->run(*report))) { restorePartition().setState(Partition::StateNone); if ((rval = restoreJob()->run(*report))) { if ((rval = checkTargetJob()->run(*report))) { // If the partition was written over an existing one, the partition itself may now // be larger than the filesystem, so maximize the filesystem to the partition's size // or the image length, whichever is larger. If this fails, don't return an error, just // warn the user. if ((warning = !maximizeJob()->run(*report))) report->line() << xi18nc("@info/plain", "Warning: Maximizing file system on target partition %1 to the size of the partition failed.", restorePartition().deviceNode()); } else report->line() << xi18nc("@info/plain", "Checking target file system on partition %1 after the restore failed.", restorePartition().deviceNode()); } else { if (!overwrittenPartition()) DeletePartitionJob(targetDevice(), restorePartition()).run(*report); report->line() << i18nc("@info/plain", "Restoring file system failed."); } } else report->line() << i18nc("@info/plain", "Creating the destination partition to restore to failed."); if (rval) setStatus(warning ? StatusFinishedWarning : StatusFinishedSuccess); else setStatus(StatusError); report->setStatus(i18nc("@info/plain status (success, error, warning...) of operation", "%1: %2", description(), statusText())); return rval; } QString RestoreOperation::description() const { if (overwrittenPartition()) return xi18nc("@info/plain", "Restore partition from %1 to %2", fileName(), overwrittenPartition()->deviceNode()); - return xi18nc("@info/plain", "Restore partition on %1 at %2 from %3", targetDevice().deviceNode(), Capacity::formatByteSize(restorePartition().firstSector() * targetDevice().logicalSectorSize()), fileName()); + return xi18nc("@info/plain", "Restore partition on %1 at %2 from %3", targetDevice().deviceNode(), Capacity::formatByteSize(restorePartition().firstSector() * targetDevice().logicalSize()), fileName()); } void RestoreOperation::setOverwrittenPartition(Partition* p) { // This is copied from CopyOperation. One day we might create a common base class ;-) cleanupOverwrittenPartition(); m_OverwrittenPartition = p; m_MustDeleteOverwritten = (p && p->state() == Partition::StateNone); } void RestoreOperation::cleanupOverwrittenPartition() { if (mustDeleteOverwritten()) { delete overwrittenPartition(); m_OverwrittenPartition = nullptr; } } /** Can a Partition be restored to somewhere? @param p the Partition in question, may be nullptr. @return true if a Partition can be restored to @p p. */ bool RestoreOperation::canRestore(const Partition* p) { if (p == nullptr) return false; if (p->isMounted()) return false; if (p->roles().has(PartitionRole::Extended)) return false; if (p->roles().has(PartitionRole::Luks)) return FS::luks::mapperName(p->deviceNode()).isEmpty(); return true; } /** Creates a new Partition to restore to. @param device the Device to create the Partition on @param parent the parent PartitionNode @param start start sector of the Partition @param filename name of the image file to restore from */ Partition* RestoreOperation::createRestorePartition(const Device& device, PartitionNode& parent, qint64 start, const QString& filename) { PartitionRole::Roles r = PartitionRole::Primary; if (!parent.isRoot()) r = PartitionRole::Logical; QFileInfo fileInfo(filename); if (!fileInfo.exists()) return nullptr; - const qint64 end = start + fileInfo.size() / device.logicalSectorSize() - 1; + const qint64 end = start + fileInfo.size() / device.logicalSize() - 1; Partition* p = new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Unknown, start, end), start, end, QString()); p->setState(Partition::StateRestore); return p; } diff --git a/src/plugins/dummy/dummybackend.cpp b/src/plugins/dummy/dummybackend.cpp index 43d1011..4e4b3f6 100644 --- a/src/plugins/dummy/dummybackend.cpp +++ b/src/plugins/dummy/dummybackend.cpp @@ -1,109 +1,109 @@ /************************************************************************* * 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/device.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 excludeReadOnly) { Q_UNUSED(excludeReadOnly) QList result; result.append(scanDevice(QStringLiteral("/dev/sda"))); emitScanProgress(QStringLiteral("/dev/sda"), 100); return result; } Device* DummyBackend::scanDevice(const QString& device_node) { - Device* d = new Device(QStringLiteral("Dummy Device"), QStringLiteral("/tmp") + device_node, 255, 30, 63, 512); + DiskDevice* d = new DiskDevice(QStringLiteral("Dummy Device"), QStringLiteral("/tmp") + device_node, 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; } CoreBackendDevice* DummyBackend::openDevice(const QString& device_node) { DummyDevice* device = new DummyDevice(device_node); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* DummyBackend::openDeviceExclusive(const QString& device_node) { DummyDevice* device = new DummyDevice(device_node); if (device == nullptr || !device->openExclusive()) { delete device; device = nullptr; } return device; } bool DummyBackend::closeDevice(CoreBackendDevice* core_device) { return core_device->close(); } #include "dummybackend.moc" diff --git a/src/plugins/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp index 2f4eec4..5c67371 100644 --- a/src/plugins/libparted/libpartedbackend.cpp +++ b/src/plugins/libparted/libpartedbackend.cpp @@ -1,597 +1,597 @@ /************************************************************************* * Copyright (C) 2008-2012 by Volker Lanz * * Copyright (C) 2015-2016 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 .* *************************************************************************/ /** @file */ #include "plugins/libparted/libpartedbackend.h" #include "plugins/libparted/libparteddevice.h" -#include "core/device.h" +#include "core/diskdevice.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/helpers.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(LibPartedBackendFactory, "pmlibpartedbackendplugin.json", registerPlugin();) static struct { PedPartitionFlag pedFlag; PartitionTable::Flag flag; } flagmap[] = { { PED_PARTITION_BOOT, PartitionTable::FlagBoot }, { PED_PARTITION_ROOT, PartitionTable::FlagRoot }, { PED_PARTITION_SWAP, PartitionTable::FlagSwap }, { PED_PARTITION_HIDDEN, PartitionTable::FlagHidden }, { PED_PARTITION_RAID, PartitionTable::FlagRaid }, { PED_PARTITION_LVM, PartitionTable::FlagLvm }, { PED_PARTITION_LBA, PartitionTable::FlagLba }, { PED_PARTITION_HPSERVICE, PartitionTable::FlagHpService }, { PED_PARTITION_PALO, PartitionTable::FlagPalo }, { PED_PARTITION_PREP, PartitionTable::FlagPrep }, { PED_PARTITION_MSFT_RESERVED, PartitionTable::FlagMsftReserved }, { PED_PARTITION_BIOS_GRUB, PartitionTable::FlagBiosGrub }, { PED_PARTITION_APPLE_TV_RECOVERY, PartitionTable::FlagAppleTvRecovery }, { PED_PARTITION_DIAG, PartitionTable::FlagDiag }, // generic diagnostics flag { PED_PARTITION_LEGACY_BOOT, PartitionTable::FlagLegacyBoot }, { PED_PARTITION_MSFT_DATA, PartitionTable::FlagMsftData }, { PED_PARTITION_IRST, PartitionTable::FlagIrst }, // Intel Rapid Start partition { PED_PARTITION_ESP, PartitionTable::FlagEsp } // EFI system }; 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) << i18nc("@info/plain", "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 quint64 firstUsableSector(const Device& d) +static quint64 firstUsableSector(const DiskDevice& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; quint64 rval = 0; if (pedDisk) rval = 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 = geom->start; else rval += 32; } 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 quint64 lastUsableSector(const Device& d) +static quint64 lastUsableSector(const DiskDevice& d) { PedDevice* pedDevice = ped_device_get(d.deviceNode().toLatin1().constData()); PedDisk* pedDisk = pedDevice ? ped_disk_new(pedDevice) : nullptr; quint64 rval = 0; if (pedDisk) rval = static_cast< quint64 >( pedDisk->dev->bios_geom.sectors ) * pedDisk->dev->bios_geom.heads * 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; } 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) +static void readSectorsUsed(PedDisk* pedDisk, const DiskDevice& d, Partition& p, const QString& mountPoint) { Q_ASSERT(pedDisk); const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (p.isMounted() && freeSpaceInfo.isValid() && mountPoint != QString()) p.fileSystem().setSectorsUsed(freeSpaceInfo.used() / d.logicalSectorSize()); else if (p.fileSystem().supportGetUsed() == FileSystem::cmdSupportFileSystem) p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / d.logicalSectorSize()); #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 (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (ped_partition_is_flag_available(p, flagmap[i].pedFlag) && ped_partition_get_flag(p, flagmap[i].pedFlag)) flags |= flagmap[i].flag; return flags; } static PartitionTable::Flags availableFlags(PedPartition* p) { PartitionTable::Flags flags; // see above. if (p->num <= 0) return flags; for (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (ped_partition_is_flag_available(p, flagmap[i].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 || flagmap[i].flag != PartitionTable::FlagHidden) flags |= flagmap[i].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) +void LibPartedBackend::scanDevicePartitions(DiskDevice& d, PedDisk* pedDisk) { Q_ASSERT(pedDisk); Q_ASSERT(d.partitionTable()); PedPartition* pedPartition = nullptr; KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPoints.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); QList partitions; while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition))) { if (pedPartition->num < 1) continue; PartitionRole::Roles r = PartitionRole::None; char* pedPath = ped_partition_get_path(pedPartition); FileSystem::Type type = FileSystem::Unknown; if (pedPath) type = detectFileSystem(QString::fromUtf8(pedPath)); free(pedPath); 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(); const QString node = QString::fromUtf8(ped_partition_get_path(pedPartition)); FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end); // libparted does not handle LUKS partitions QString mountPoint; bool mounted = false; if (type == FileSystem::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = dynamic_cast(fs); QString mapperNode = FS::luks::mapperName(node); bool isCryptOpen = !mapperNode.isEmpty(); luksFs->setCryptOpen(isCryptOpen); luksFs->setLogicalSectorSize(d.logicalSectorSize()); if (isCryptOpen) { luksFs->loadInnerFileSystem(node, mapperNode); if (luksFs->type() == FileSystem::Lvm2_PV) { mountPoint = FS::lvm2_pv::getVGName(mapperNode); mounted = false; } else { mountPoint = mountPoints.findByDevice(mapperNode) ? mountPoints.findByDevice(mapperNode)->mountPoint() : QString(); // We cannot use libparted to check the mounted status because // we don't have a PedPartition for the mapper device, so we use lsblk mounted = isMounted(mapperNode); } if (mounted) { const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (freeSpaceInfo.isValid() && mountPoint != QString()) luksFs->setSectorsUsed(freeSpaceInfo.used() / d.logicalSectorSize() + luksFs->getPayloadOffset(node)); } } else { mounted = false; } luksFs->setMounted(mounted); } else if (type == FileSystem::Lvm2_PV) { //TODO: adding PartitionRole mountPoint = FS::lvm2_pv::getVGName(node); mounted = false; } else { mountPoint = mountPoints.findByDevice(node) ? mountPoints.findByDevice(node)->mountPoint() : QString(); mounted = ped_partition_is_busy(pedPartition); } Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end, node, 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())); 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); foreach(const Partition * part, partitions) PartitionAlignment::isAligned(d, *part); ped_disk_destroy(pedDisk); } /** Create a Device for the given device_node and scan it for partitions. @param device_node the device node (e.g. "/dev/sda") @return the created Device object. callers need to free this. */ Device* LibPartedBackend::scanDevice(const QString& device_node) { PedDevice* pedDevice = ped_device_get(device_node.toLocal8Bit().constData()); if (pedDevice == nullptr) { Log(Log::warning) << xi18nc("@info/plain", "Could not access device %1", device_node); return nullptr; } Log(Log::information) << i18nc("@info/plain", "Device found: %1", QString::fromUtf8(pedDevice->model)); - Device* d = new Device(QString::fromUtf8(pedDevice->model), QString::fromUtf8(pedDevice->path), pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size); + DiskDevice* d = new DiskDevice(QString::fromUtf8(pedDevice->model), QString::fromUtf8(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::fromUtf8(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); } return d; } QList LibPartedBackend::scanDevices(bool excludeReadOnly) { QList result; ped_device_probe_all(); PedDevice* pedDevice = nullptr; QVector path; quint32 totalDevices = 0; while (true) { pedDevice = ped_device_get_next(pedDevice); if (!pedDevice) break; if (pedDevice->type == PED_DEVICE_DM) continue; if (excludeReadOnly && ( pedDevice->type == PED_DEVICE_LOOP || pedDevice->read_only)) continue; path.push_back(QString::fromUtf8(pedDevice->path)); ++totalDevices; } for (quint32 i = 0; i < totalDevices; ++i) { emitScanProgress(path[i], i * 100 / totalDevices); - Device* d = scanDevice(path[i]); + DiskDevice* d = dynamic_cast(scanDevice(path[i])); if (d) result.append(d); } 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) { QString s = QString::fromUtf8(blkid_get_tag_value(cache, "TYPE", partitionPath.toLocal8Bit().constData())); 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 QString st = QString::fromUtf8(blkid_get_tag_value(cache, "SEC_TYPE", partitionPath.toLocal8Bit().constData())); 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 qWarning() << "blkid: unknown file system type " << s << " on " << partitionPath; } blkid_put_cache(cache); } return rval; } CoreBackendDevice* LibPartedBackend::openDevice(const QString& device_node) { LibPartedDevice* device = new LibPartedDevice(device_node); if (device == nullptr || !device->open()) { delete device; device = nullptr; } return device; } CoreBackendDevice* LibPartedBackend::openDeviceExclusive(const QString& device_node) { LibPartedDevice* device = new LibPartedDevice(device_node); 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 (quint32 i = 0; i < sizeof(flagmap) / sizeof(flagmap[0]); i++) if (flagmap[i].flag == flag) return flagmap[i].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 a3268b4..e386c12 100644 --- a/src/plugins/libparted/libpartedbackend.h +++ b/src/plugins/libparted/libpartedbackend.h @@ -1,78 +1,78 @@ /************************************************************************* * 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(LIBPARTED__H) #define LIBPARTED__H #include "backend/corebackend.h" #include "core/partitiontable.h" #include "util/libpartitionmanagerexport.h" #include "fs/filesystem.h" #include #include #include #include class LibPartedDevice; class LibPartedPartitionTable; class LibPartedPartition; class OperationStack; -class Device; +class DiskDevice; 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 QString& device_node) override; CoreBackendDevice* openDeviceExclusive(const QString& device_node) override; bool closeDevice(CoreBackendDevice* core_device) override; Device* scanDevice(const QString& device_node) override; QList scanDevices(bool excludeReadOnly = false) override; FileSystem::Type detectFileSystem(const QString& partitionPath) override; static QString lastPartedExceptionMessage(); private: static PedPartitionFlag getPedFlag(PartitionTable::Flag flag); - void scanDevicePartitions(Device& d, PedDisk* pedDisk); + void scanDevicePartitions(DiskDevice& d, PedDisk* pedDisk); }; #endif