diff --git a/src/core/devicescanner.cpp b/src/core/devicescanner.cpp index aa5be4e..632cb9b 100644 --- a/src/core/devicescanner.cpp +++ b/src/core/devicescanner.cpp @@ -1,77 +1,82 @@ /************************************************************************* * Copyright (C) 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/devicescanner.h" #include "backend/corebackend.h" #include "backend/corebackendmanager.h" #include "core/operationstack.h" #include "core/device.h" #include "core/lvmdevice.h" #include "core/diskdevice.h" +#include "fs/lvm2_pv.h" + #include "util/externalcommand.h" #include #include /** Constructs a DeviceScanner @param ostack the OperationStack where the devices will be created */ DeviceScanner::DeviceScanner(QObject* parent, OperationStack& ostack) : QThread(parent), m_OperationStack(ostack) { setupConnections(); } void DeviceScanner::setupConnections() { connect(CoreBackendManager::self()->backend(), &CoreBackend::scanProgress, this, &DeviceScanner::progress); } void DeviceScanner::clear() { operationStack().clearOperations(); operationStack().clearDevices(); } void DeviceScanner::run() { scan(); } void DeviceScanner::scan() { emit progress(QString(), 0); clear(); const QList deviceList = CoreBackendManager::self()->backend()->scanDevices(); - const QList lvmList = LvmDevice::scanSystemLVM(deviceList); // NOTE: PVs inside LVM won't be scanned + const QList lvmList = LvmDevice::scanSystemLVM(); // NOTE: PVs inside LVM won't be scanned + operationStack().physicalVolumes() = FS::lvm2_pv::getPVs(deviceList); for (const auto &d : deviceList) operationStack().addDevice(d); operationStack().sortDevices(); - for (const auto &d : lvmList) + for (const auto &d : lvmList) { operationStack().addDevice(d); + operationStack().physicalVolumes().append(FS::lvm2_pv::getPVinNode(d->partitionTable())); + } } diff --git a/src/core/lvmdevice.cpp b/src/core/lvmdevice.cpp index f99bc6e..4bff050 100644 --- a/src/core/lvmdevice.cpp +++ b/src/core/lvmdevice.cpp @@ -1,498 +1,508 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "core/lvmdevice.h" #include "core/partition.h" #include "fs/filesystem.h" #include "fs/lvm2_pv.h" #include "fs/luks.h" #include "fs/filesystemfactory.h" #include "core/partitiontable.h" #include "util/externalcommand.h" #include "util/helpers.h" #include "util/report.h" #include #include #include #include #include /** Constructs a representation of LVM device with initialized LV as Partitions * * @param vgName Volume Group name - * @param devices list of devices where we look for LVM PVs belonging to this VG. * @param iconName Icon representing LVM Volume group */ -LvmDevice::LvmDevice(const QString& vgName, const QList& devices, const QString& iconName) +LvmDevice::LvmDevice(const QString& vgName, const QString& iconName) : VolumeManagerDevice(vgName, (QStringLiteral("/dev/") + vgName), getPeSize(vgName), getTotalPE(vgName), iconName, Device::LVM_Device) { m_peSize = logicalSize(); m_totalPE = totalLogical(); m_freePE = getFreePE(vgName); m_allocPE = m_totalPE - m_freePE; m_UUID = getUUID(vgName); - m_PVs = FS::lvm2_pv::getPVs(devices, vgName); m_LVPathList = new QStringList(getLVs(vgName)); m_LVSizeMap = new QMap(); initPartitions(); } /** * shared list of PV's paths that will be added to any VGs. * (have been added to an operation, but not yet applied) */ QStringList LvmDevice::s_DirtyPVs; LvmDevice::~LvmDevice() { delete m_LVPathList; delete m_LVSizeMap; } void LvmDevice::initPartitions() { qint64 firstUsable = 0; qint64 lastusable = totalPE() - 1; PartitionTable* pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastusable); for (const auto &p : scanPartitions(pTable)) { LVSizeMap()->insert(p->partitionPath(), p->length()); pTable->append(p); } pTable->updateUnallocated(*this); setPartitionTable(pTable); } /** * @return a initialized Partition(LV) list */ const QList LvmDevice::scanPartitions(PartitionTable* pTable) const { QList pList; for (const auto &lvPath : partitionNodes()) { pList.append(scanPartition(lvPath, pTable)); } return pList; } /** scan and construct a partition(LV) at a given path * * NOTE: * LVM partition has 2 different start and end sector values * 1. representing the actual LV start from 0 -> size of LV - 1 * 2. representing abstract LV's sector inside a VG partitionTable * start from last sector + 1 of last Partitions -> size of LV - 1 * Reason for this is for the LV Partition to work nicely with other parts of the codebase * without too many special cases. * * @param lvPath LVM Logical Volume path * @param pTable Abstract partition table representing partitions of LVM Volume Group * @return initialized Partition(LV) */ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTable) const { activateLV(lvPath); qint64 lvSize = getTotalLE(lvPath); qint64 startSector = mappedSector(lvPath, 0); qint64 endSector = startSector + lvSize - 1; FileSystem::Type type = FileSystem::detectFileSystem(lvPath); FileSystem* fs = FileSystemFactory::create(type, 0, lvSize - 1); fs->scan(lvPath); bool mounted = isMounted(lvPath); QString mountPoint = QString(); KMountPoint::List mountPointList = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPointList.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); PartitionRole::Roles r = PartitionRole::Lvm_Lv; if (type == FileSystem::Luks) { r |= PartitionRole::Luks; FS::luks* luksFs = static_cast(fs); QString mapperNode = luksFs->mapperName(); bool isCryptOpen = !mapperNode.isEmpty(); luksFs->setCryptOpen(isCryptOpen); luksFs->setLogicalSectorSize(logicalSize()); if (isCryptOpen) { luksFs->loadInnerFileSystem(mapperNode); - mountPoint = mountPointList.findByDevice(mapperNode) ? - mountPointList.findByDevice(mapperNode)->mountPoint() : - QString(); - if (mountPoint == QStringLiteral("none")) - mountPoint = QString(); - mounted = isMounted(mapperNode); - if (mounted) { - const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); - if (freeSpaceInfo.isValid() && mountPoint != QString()) - luksFs->setSectorsUsed((freeSpaceInfo.used() + luksFs->payloadOffset()) / logicalSize()); + + if (luksFs->type() == FileSystem::Lvm2_PV) { + mountPoint = FS::lvm2_pv::getVGName(mapperNode); + mounted = false; + } + else { + mountPoint = mountPointList.findByDevice(mapperNode) ? + mountPointList.findByDevice(mapperNode)->mountPoint() : + QString(); + if (mountPoint == QStringLiteral("none")) + mountPoint = QString(); + mounted = isMounted(mapperNode); + if (mounted) { + const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); + if (freeSpaceInfo.isValid() && mountPoint != QString()) + luksFs->setSectorsUsed((freeSpaceInfo.used() + luksFs->payloadOffset()) / logicalSize()); + } } } else { mounted = false; } luksFs->setMounted(mounted); - } else { + } + else if (type == FileSystem::Lvm2_PV) { + r |= PartitionRole::Lvm_Lv; + mountPoint = FS::lvm2_pv::getVGName(lvPath); + mounted = false; + } + else { mountPoint = mountPointList.findByDevice(lvPath) ? mountPointList.findByDevice(lvPath)->mountPoint() : QString(); const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); if (mountPoint == QStringLiteral("none")) mountPoint = QString(); if (logicalSize() > 0) { if (mounted && freeSpaceInfo.isValid() && mountPoint != QString()) { fs->setSectorsUsed(freeSpaceInfo.used() / logicalSize()); } else if (fs->supportGetUsed() == FileSystem::cmdSupportFileSystem) { fs->setSectorsUsed(qCeil(fs->readUsedCapacity(lvPath) / static_cast(logicalSize()))); } } } if (fs->supportGetLabel() != FileSystem::cmdSupportNone) { fs->setLabel(fs->readLabel(lvPath)); } if (fs->supportGetUUID() != FileSystem::cmdSupportNone) fs->setUUID(fs->readUUID(lvPath)); Partition* part = new Partition(pTable, *this, PartitionRole(r), fs, startSector, endSector, lvPath, PartitionTable::Flag::FlagNone, mountPoint, mounted); return part; } /** scan and contruct list of initialized LvmDevice objects. * - * @param devices list of Devices which we scan for LVM PVs * @return list of initialized LvmDevices */ -QList LvmDevice::scanSystemLVM(const QList& devices) +QList LvmDevice::scanSystemLVM() { QList lvmList; for (const auto &vgName : getVGs()) { - lvmList.append(new LvmDevice(vgName, devices)); + lvmList.append(new LvmDevice(vgName)); } return lvmList; } qint64 LvmDevice::mappedSector(const QString& lvPath, qint64 sector) const { qint64 mSector = 0; QList lvpathList = partitionNodes(); qint32 devIndex = lvpathList.indexOf(lvPath); if (devIndex) { for (int i = 0; i < devIndex; i++) { mSector += LVSizeMap()->value(lvpathList[i]); } mSector += sector; } return mSector; } const QStringList LvmDevice::deviceNodes() const { QStringList pvList; for (const auto &p : physicalVolumes()) pvList << p->partitionPath(); return pvList; } const QStringList LvmDevice::partitionNodes() const { return *LVPathList(); } qint64 LvmDevice::partitionSize(QString& partitionPath) const { return LVSizeMap()->value(partitionPath); } const QStringList LvmDevice::getVGs() { QStringList vgList; QString output = getField(QStringLiteral("vg_name")); if (!output.isEmpty()) { const QStringList vgNameList = output.split(QStringLiteral("\n"), QString::SkipEmptyParts); for (const auto &vgName : vgNameList) { vgList.append(vgName.trimmed()); } } return vgList; } const QStringList LvmDevice::getLVs(const QString& vgName) { QStringList lvPathList; QString cmdOutput = getField(QStringLiteral("lv_path"), vgName); if (cmdOutput.size()) { const QStringList tempPathList = cmdOutput.split(QStringLiteral("\n"), QString::SkipEmptyParts); for (const auto &lvPath : tempPathList) { lvPathList.append(lvPath.trimmed()); } } return lvPathList; } qint64 LvmDevice::getPeSize(const QString& vgName) { QString val = getField(QStringLiteral("vg_extent_size"), vgName); return val.isEmpty() ? -1 : val.toInt(); } qint64 LvmDevice::getTotalPE(const QString& vgName) { QString val = getField(QStringLiteral("vg_extent_count"), vgName); return val.isEmpty() ? -1 : val.toInt(); } qint64 LvmDevice::getAllocatedPE(const QString& vgName) { return getTotalPE(vgName) - getFreePE(vgName); } qint64 LvmDevice::getFreePE(const QString& vgName) { QString val = getField(QStringLiteral("vg_free_count"), vgName); return val.isEmpty() ? -1 : val.toInt(); } QString LvmDevice::getUUID(const QString& vgName) { QString val = getField(QStringLiteral("vg_uuid"), vgName); return val.isEmpty() ? QStringLiteral("---") : val; } /** Get LVM vgs command output with field name * * @param fieldName LVM field name * @param vgName the name of LVM Volume Group * @return raw output of command output, usully with many spaces within the returned string * */ QString LvmDevice::getField(const QString& fieldName, const QString& vgName) { QStringList args = { QStringLiteral("vgs"), QStringLiteral("--foreign"), QStringLiteral("--readonly"), QStringLiteral("--noheadings"), QStringLiteral("--units"), QStringLiteral("B"), QStringLiteral("--nosuffix"), QStringLiteral("--options"), fieldName }; if (!vgName.isEmpty()) { args << vgName; } ExternalCommand cmd(QStringLiteral("lvm"), args); if (cmd.run(-1) && cmd.exitCode() == 0) { return cmd.output().trimmed(); } return QString(); } qint64 LvmDevice::getTotalLE(const QString& lvPath) { ExternalCommand cmd(QStringLiteral("lvm"), { QStringLiteral("lvdisplay"), lvPath}); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression re(QStringLiteral("Current LE\\h+(\\d+)")); QRegularExpressionMatch match = re.match(cmd.output()); if (match.hasMatch()) { return match.captured(1).toInt(); } } return -1; } bool LvmDevice::removeLV(Report& report, LvmDevice& d, Partition& p) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvremove"), QStringLiteral("--yes"), p.partitionPath()}); if (cmd.run(-1) && cmd.exitCode() == 0) { d.partitionTable()->remove(&p); return true; } return false; } bool LvmDevice::createLV(Report& report, LvmDevice& d, Partition& p, const QString& lvName) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(p.length()), QStringLiteral("--name"), lvName, d.name()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createLVSnapshot(Report& report, Partition& p, const QString& name, const qint64 extents) { QString numExtents = (extents > 0) ? QString::number(extents) : QString::number(p.length()); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), numExtents, QStringLiteral("--snapshot"), QStringLiteral("--name"), name, p.partitionPath() }); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::resizeLV(Report& report, Partition& p) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvresize"), QStringLiteral("--force"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(p.length()), p.partitionPath()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removePV(Report& report, LvmDevice& d, const QString& pvPath) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgreduce"), d.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::insertPV(Report& report, LvmDevice& d, const QString& pvPath) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgextend"), QStringLiteral("--yes"), d.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::movePV(Report& report, const QString& pvPath, const QStringList& destinations) { if (FS::lvm2_pv::getAllocatedPE(pvPath) <= 0) return true; QStringList args = QStringList(); args << QStringLiteral("pvmove"); args << pvPath; if (!destinations.isEmpty()) for (const auto &destPath : destinations) args << destPath.trimmed(); ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createVG(Report& report, const QString vgName, const QStringList pvList, const qint32 peSize) { QStringList args = QStringList(); args << QStringLiteral("vgcreate") << QStringLiteral("--physicalextentsize") << QString::number(peSize); args << vgName; for (const auto &pvNode : pvList) args << pvNode.trimmed(); ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removeVG(Report& report, LvmDevice& d) { bool deactivated = deactivateVG(report, d); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgremove"), d.name() }); return (deactivated && cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::deactivateVG(Report& report, const LvmDevice& d) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("vgchange"), QStringLiteral("--activate"), QStringLiteral("n"), d.name() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::deactivateLV(Report& report, const Partition& p) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("lvchange"), QStringLiteral("--activate"), QStringLiteral("n"), p.partitionPath() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::activateVG(Report& report, const LvmDevice& d) { ExternalCommand deactivate(report, QStringLiteral("lvm"), { QStringLiteral("vgchange"), QStringLiteral("--activate"), QStringLiteral("y"), d.name() }); return deactivate.run(-1) && deactivate.exitCode() == 0; } bool LvmDevice::activateLV(const QString& lvPath) { ExternalCommand deactivate(QStringLiteral("lvm"), { QStringLiteral("lvchange"), QStringLiteral("--activate"), QStringLiteral("y"), lvPath }); return deactivate.run(-1) && deactivate.exitCode() == 0; } diff --git a/src/core/lvmdevice.h b/src/core/lvmdevice.h index 535d805..fff4077 100644 --- a/src/core/lvmdevice.h +++ b/src/core/lvmdevice.h @@ -1,137 +1,137 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(LVMDEVICE__H) #define LVMDEVICE__H #include "core/device.h" #include "core/volumemanagerdevice.h" #include "util/libpartitionmanagerexport.h" #include #include #include #include class PartitionTable; class Report; class Partition; class SmartStatus; /** Representation of LVM Volume Group(VG). Devices are the outermost entity; they contain a PartitionTable that itself contains Partitions. @see Device, VolumeManagerDevice, PartitionTable, Partition */ class LIBKPMCORE_EXPORT LvmDevice : public VolumeManagerDevice { Q_DISABLE_COPY(LvmDevice) public: - LvmDevice(const QString& name, const QList& devices, const QString& iconName = QString()); + LvmDevice(const QString& name, const QString& iconName = QString()); ~LvmDevice(); public: const QStringList deviceNodes() const override; const QStringList partitionNodes() const override; qint64 partitionSize(QString& partitionPath) const override; static QStringList s_DirtyPVs; public: - static QList scanSystemLVM(const QList& devices); + static QList scanSystemLVM(); static const QStringList getVGs(); static const QStringList getLVs(const QString& vgName); static qint64 getPeSize(const QString& vgName); static qint64 getTotalPE(const QString& vgName); static qint64 getAllocatedPE(const QString& vgName); static qint64 getFreePE(const QString& vgName); static QString getUUID(const QString& vgName); static QString getField(const QString& fieldName, const QString& vgName = QString()); static qint64 getTotalLE(const QString& lvPath); static bool removeLV(Report& report, LvmDevice& d, Partition& p); static bool createLV(Report& report, LvmDevice& d, Partition& p, const QString& lvName); static bool createLVSnapshot(Report& report, Partition& p, const QString& name, const qint64 extents = 0); static bool resizeLV(Report& report, Partition& p); static bool deactivateLV(Report& report, const Partition& p); static bool activateLV(const QString& deviceNode); static bool removePV(Report& report, LvmDevice& d, const QString& pvPath); static bool insertPV(Report& report, LvmDevice& d, const QString& pvPath); static bool movePV(Report& report, const QString& pvPath, const QStringList& destinations = QStringList()); static bool removeVG(Report& report, LvmDevice& d); static bool createVG(Report& report, const QString vgName, const QStringList pvList, const qint32 peSize = 4); // peSize in megabytes static bool deactivateVG(Report& report, const LvmDevice& d); static bool activateVG(Report& report, const LvmDevice& d); protected: void initPartitions() override; const QList scanPartitions(PartitionTable* pTable) const; Partition* scanPartition(const QString& lvPath, PartitionTable* pTable) const; qint64 mappedSector(const QString& lvPath, qint64 sector) const override; public: qint64 peSize() const { return m_peSize; } qint64 totalPE() const { return m_totalPE; } qint64 allocatedPE() const { return m_allocPE; } qint64 freePE() const { return m_freePE; } QString UUID() const { return m_UUID; } QStringList* LVPathList() const { return m_LVPathList; } const QList physicalVolumes() const { return m_PVs; } protected: QMap* LVSizeMap() const { return m_LVSizeMap; } private: qint64 m_peSize; qint64 m_totalPE; qint64 m_allocPE; qint64 m_freePE; QString m_UUID; mutable QStringList* m_LVPathList; mutable QList m_PVs; mutable QMap* m_LVSizeMap; }; #endif diff --git a/src/core/operationstack.h b/src/core/operationstack.h index b6fbfac..fd9ce1f 100644 --- a/src/core/operationstack.h +++ b/src/core/operationstack.h @@ -1,108 +1,117 @@ /************************************************************************* * 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(OPERATIONSTACK__H) #define OPERATIONSTACK__H #include "util/libpartitionmanagerexport.h" #include #include #include #include class Device; class Partition; class Operation; class DeviceScanner; /** The list of Operations the user wants to have performed. OperationStack also handles the Devices that were found on this computer and the merging of Operations, e.g., when the user first creates a Partition, then deletes it. @author Volker Lanz */ class LIBKPMCORE_EXPORT OperationStack : public QObject { Q_OBJECT Q_DISABLE_COPY(OperationStack) friend class DeviceScanner; public: typedef QList Devices; + typedef QList> PhysicalVolumes; typedef QList Operations; public: OperationStack(QObject* parent = nullptr); ~OperationStack(); Q_SIGNALS: void operationsChanged(); void devicesChanged(); public: void push(Operation* o); void pop(); bool contains(const Partition* p) const; void clearOperations(); int size() const { return operations().size(); /**< @return number of operations */ } Devices& previewDevices() { return m_PreviewDevices; /**< @return the list of Devices */ } const Devices& previewDevices() const { return m_PreviewDevices; /**< @return the list of Devices */ } + PhysicalVolumes& physicalVolumes() { + return m_LVMPhysicalVolumes; /**< @return the list of LVM PVs */ + } + const PhysicalVolumes& physicalVolumes() const { + return m_LVMPhysicalVolumes; /**< @return the list of LVM PVs */ + } + Operations& operations() { return m_Operations; /**< @return the list of operations */ } const Operations& operations() const { return m_Operations; /**< @return the list of operations */ } Device* findDeviceForPartition(const Partition* p); QReadWriteLock& lock() { return m_Lock; } protected: void clearDevices(); void addDevice(Device* d); void sortDevices(); bool mergeNewOperation(Operation*& currentOp, Operation*& pushedOp); bool mergeCopyOperation(Operation*& currentOp, Operation*& pushedOp); bool mergeRestoreOperation(Operation*& currentOp, Operation*& pushedOp); bool mergePartFlagsOperation(Operation*& currentOp, Operation*& pushedOp); bool mergePartLabelOperation(Operation*& currentOp, Operation*& pushedOp); bool mergeCreatePartitionTableOperation(Operation*& currentOp, Operation*& pushedOp); private: Operations m_Operations; mutable Devices m_PreviewDevices; + mutable PhysicalVolumes m_LVMPhysicalVolumes; QReadWriteLock m_Lock; }; #endif diff --git a/src/fs/lvm2_pv.cpp b/src/fs/lvm2_pv.cpp index 36ba4d2..aa0f73c 100644 --- a/src/fs/lvm2_pv.cpp +++ b/src/fs/lvm2_pv.cpp @@ -1,357 +1,356 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ -#include "core/device.h" #include "fs/lvm2_pv.h" +#include "core/device.h" #include "util/externalcommand.h" #include "util/capacity.h" #include #include namespace FS { FileSystem::CommandSupportType lvm2_pv::m_GetUsed = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_GetLabel = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Create = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Grow = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Shrink = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Move = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Check = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Copy = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_Backup = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_SetLabel = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_UpdateUUID = FileSystem::cmdSupportNone; FileSystem::CommandSupportType lvm2_pv::m_GetUUID = FileSystem::cmdSupportNone; lvm2_pv::lvm2_pv(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Lvm2_PV) { } void lvm2_pv::init() { CommandSupportType lvmFound = findExternal(QStringLiteral("lvm")) ? cmdSupportFileSystem : cmdSupportNone; m_Create = lvmFound; m_Check = lvmFound; m_Grow = lvmFound; m_Shrink = lvmFound; m_UpdateUUID = lvmFound; m_GetUsed = lvmFound; m_Move = (m_Check != cmdSupportNone) ? cmdSupportCore : cmdSupportNone; m_GetLabel = cmdSupportCore; m_Backup = cmdSupportCore; m_GetUUID = cmdSupportCore; m_GetLabel = cmdSupportNone; m_Copy = cmdSupportNone; // Copying PV can confuse LVM } void lvm2_pv::scan(const QString& deviceNode) { getPESize(deviceNode); m_AllocatedPE = getAllocatedPE(deviceNode); m_TotalPE = getTotalPE(deviceNode); } bool lvm2_pv::supportToolFound() const { return m_GetUsed != cmdSupportNone && // m_GetLabel != cmdSupportNone && // m_SetLabel != cmdSupportNone && m_Create != cmdSupportNone && m_Check != cmdSupportNone && m_UpdateUUID != cmdSupportNone && m_Grow != cmdSupportNone && m_Shrink != cmdSupportNone && // m_Copy != cmdSupportNone && m_Move != cmdSupportNone && m_Backup != cmdSupportNone && m_GetUUID != cmdSupportNone; } FileSystem::SupportTool lvm2_pv::supportToolName() const { return SupportTool(QStringLiteral("lvm2"), QUrl(QStringLiteral("http://sourceware.org/lvm2/"))); } qint64 lvm2_pv::maxCapacity() const { return Capacity::unitFactor(Capacity::Byte, Capacity::EiB); } qint64 lvm2_pv::readUsedCapacity(const QString& deviceNode) const { QString val = getpvField(QStringLiteral("pv_used"), deviceNode); QString metadataOffset = getpvField(QStringLiteral("pe_start"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong() + metadataOffset.toLongLong(); } bool lvm2_pv::check(Report& report, const QString& deviceNode) const { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvck"), QStringLiteral("--verbose"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool lvm2_pv::create(Report& report, const QString& deviceNode) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvcreate"), QStringLiteral("--force"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool lvm2_pv::remove(Report& report, const QString& deviceNode) const { // TODO: check if PV is a member of an exported VG ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvremove"), QStringLiteral("--force"), QStringLiteral("--force"), QStringLiteral("--yes"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool lvm2_pv::resize(Report& report, const QString& deviceNode, qint64 length) const { bool rval = true; qint64 metadataOffset = getpvField(QStringLiteral("pe_start"), deviceNode).toLongLong(); qint64 lastPE = getTotalPE(deviceNode) - 1; // starts from 0 if (lastPE > 0) { // make sure that the PV is already in a VG qint64 targetPE = (length - metadataOffset) / peSize() - 1; // starts from 0 if (targetPE < lastPE) { //shrinking FS qint64 firstMovedPE = qMax(targetPE + 1, getAllocatedPE(deviceNode)); // starts from 1 ExternalCommand moveCmd(report, QStringLiteral("lvm"), { QStringLiteral("pvmove"), QStringLiteral("--alloc"), QStringLiteral("anywhere"), deviceNode + QStringLiteral(":") + QString::number(firstMovedPE) + QStringLiteral("-") + QString::number(lastPE), deviceNode + QStringLiteral(":") + QStringLiteral("0-") + QString::number(firstMovedPE - 1) }); rval = moveCmd.run(-1) && (moveCmd.exitCode() == 0 || moveCmd.exitCode() == 5); // FIXME: exit code 5: NO data to move } } ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvresize"), QStringLiteral("--yes"), QStringLiteral("--setphysicalvolumesize"), QString::number(length) + QStringLiteral("B"), deviceNode }); return rval && cmd.run(-1) && cmd.exitCode() == 0; } bool lvm2_pv::updateUUID(Report& report, const QString& deviceNode) const { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvchange"), QStringLiteral("--uuid"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } QString lvm2_pv::readUUID(const QString& deviceNode) const { return getpvField(QStringLiteral("pv_uuid"), deviceNode); } bool lvm2_pv::mount(Report& report, const QString& deviceNode, const QString& mountPoint) { Q_UNUSED(report); Q_UNUSED(deviceNode); Q_UNUSED(mountPoint); return false; } bool lvm2_pv::unmount(Report& report, const QString& deviceNode) { Q_UNUSED(deviceNode); Q_UNUSED(report); return false; } bool lvm2_pv::canMount(const QString & deviceNode, const QString & mountPoint) const { Q_UNUSED(deviceNode); Q_UNUSED(mountPoint); return false; } bool lvm2_pv::canUnmount(const QString& deviceNode) const { Q_UNUSED(deviceNode); return false; } qint64 lvm2_pv::getTotalPE(const QString& deviceNode) { QString val = getpvField(QStringLiteral("pv_pe_count"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 lvm2_pv::getTotalPE(const QStringList& deviceNodeList) { qint64 sum = 0; for (const auto &deviceNode : deviceNodeList) { qint64 totalPE = getTotalPE(deviceNode); if (totalPE < 0) { sum = -1; break; } sum += totalPE; } return sum; } qint64 lvm2_pv::getFreePE(const QString& deviceNode) { return getTotalPE(deviceNode) - getAllocatedPE(deviceNode); } qint64 lvm2_pv::getFreePE(const QStringList& deviceNodeList) { qint64 sum = 0; for (QString deviceNode :deviceNodeList) { qint64 freePE = getFreePE(deviceNode); if (freePE < 0) { sum = -1; break; } sum += freePE; } return sum; } qint64 lvm2_pv::getAllocatedPE(const QString& deviceNode) { QString val = getpvField(QStringLiteral("pv_pe_alloc_count"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 lvm2_pv::getAllocatedPE(const QStringList& deviceNodeList) { qint64 sum = 0; for (QString deviceNode : deviceNodeList) { qint64 allocatedPE = getAllocatedPE(deviceNode); if (allocatedPE < 0) { sum = -1; break; } sum += allocatedPE; } return sum; } qint64 lvm2_pv::getPVSize(const QString& deviceNode) { QString val = getpvField(QStringLiteral("pv_size"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 lvm2_pv::getPVSize(const QStringList& deviceNodeList) { qint64 sum = 0; for (QString deviceNode : deviceNodeList) { qint64 pvsize = getPVSize(deviceNode); if (pvsize < 0) { sum = -1; break; } sum += pvsize; } return sum; } void lvm2_pv::getPESize(const QString& deviceNode) { QString val = getpvField(QStringLiteral("vg_extent_size"), deviceNode); m_PESize = val.isEmpty() ? -1 : val.toLongLong(); } /** Get pvs command output with field name * * @param fieldName LVM field name * @param deviceNode path to PV * @return raw output of pvs command, usually with many spaces */ QString lvm2_pv::getpvField(const QString& fieldName, const QString& deviceNode) { QStringList args = { QStringLiteral("pvs"), QStringLiteral("--foreign"), QStringLiteral("--readonly"), QStringLiteral("--noheadings"), QStringLiteral("--units"), QStringLiteral("B"), QStringLiteral("--nosuffix"), QStringLiteral("--options"), fieldName }; if (!deviceNode.isEmpty()) { args << deviceNode; } ExternalCommand cmd(QStringLiteral("lvm"), args); if (cmd.run(-1) && cmd.exitCode() == 0) { return cmd.output().trimmed(); } return QString(); } QString lvm2_pv::getVGName(const QString& deviceNode) { return getpvField(QStringLiteral("vg_name"), deviceNode); } -QList lvm2_pv::getPVinNode(const PartitionNode* parent, const QString& vgName) +lvm2_pv::PhysicalVolumes lvm2_pv::getPVinNode(const PartitionNode* parent) { - QList partitions; + PhysicalVolumes partitions; if (parent == nullptr) return partitions; for (const auto &node : parent->children()) { const Partition* p = dynamic_cast(node); if (p == nullptr) continue; if (node->children().size() > 0) - partitions.append(getPVinNode(node, vgName)); + partitions.append(getPVinNode(node)); // FIXME: reenable newly created PVs (before applying) once everything works - if(p->fileSystem().type() == FileSystem::Lvm2_PV && p->mountPoint() == QString() && p->deviceNode() == p->partitionPath()) - partitions.append(p); + if(p->fileSystem().type() == FileSystem::Lvm2_PV && p->deviceNode() == p->partitionPath()) + partitions.append(QPair(p->mountPoint(), p)); } return partitions; } /** construct a list of Partition objects for LVM PVs that are either unused or belong to some VG. * * @param devices list of Devices which we scan for LVM PVs - * @param vgName name of the volume group * @return list of LVM PVs */ -QList lvm2_pv::getPVs(const QList& devices, const QString& vgName) +lvm2_pv::PhysicalVolumes lvm2_pv::getPVs(const QList& devices) { - QList partitions; + PhysicalVolumes partitions; for (auto const &d : devices) - partitions.append(getPVinNode(d->partitionTable(), vgName)); + partitions.append(getPVinNode(d->partitionTable())); return partitions; } } diff --git a/src/fs/lvm2_pv.h b/src/fs/lvm2_pv.h index b175411..a28ae48 100644 --- a/src/fs/lvm2_pv.h +++ b/src/fs/lvm2_pv.h @@ -1,147 +1,149 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(LVM2_PV__H) #define LVM2_PV__H #include "util/libpartitionmanagerexport.h" #include "core/partition.h" #include "fs/filesystem.h" #include class Report; class QString; namespace FS { /** LVM2 physical volume. @author Andrius Štikonas */ class LIBKPMCORE_EXPORT lvm2_pv : public FileSystem { +public: + typedef QList> PhysicalVolumes; + public: lvm2_pv(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); public: void init() override; void scan(const QString& deviceNode) override; qint64 readUsedCapacity(const QString& deviceNode) const override; bool check(Report& report, const QString& deviceNode) const override; bool create(Report& report, const QString& deviceNode) override; bool remove(Report& report, const QString& deviceNode) const override; bool resize(Report& report, const QString& deviceNode, qint64 length) const override; // bool writeLabel(Report& report, const QString& deviceNode, const QString& newLabel) override; bool updateUUID(Report& report, const QString& deviceNode) const override; QString readUUID(const QString& deviceNode) const override; bool canMount(const QString & deviceNode, const QString & mountPoint) const override; bool canUnmount(const QString& deviceNode) const override; bool mount(Report& report, const QString& deviceNode, const QString& mountPoint) override; // mountPoint == VG name bool unmount(Report& report, const QString& deviceNode) override; CommandSupportType supportGetUsed() const override { return m_GetUsed; } CommandSupportType supportGetLabel() const override { return m_GetLabel; } CommandSupportType supportCreate() const override { return m_Create; } CommandSupportType supportGrow() const override { return m_Grow; } CommandSupportType supportShrink() const override { return m_Shrink; } CommandSupportType supportMove() const override { return m_Move; } CommandSupportType supportCheck() const override { return m_Check; } CommandSupportType supportCopy() const override { return m_Copy; } CommandSupportType supportBackup() const override { return m_Backup; } CommandSupportType supportSetLabel() const override { return m_SetLabel; } CommandSupportType supportUpdateUUID() const override { return m_UpdateUUID; } CommandSupportType supportGetUUID() const override { return m_GetUUID; } qint64 maxCapacity() const override; SupportTool supportToolName() const override; bool supportToolFound() const override; static QString getpvField(const QString& fieldName, const QString& deviceNode = QString()); static qint64 getTotalPE(const QString& deviceNode); static qint64 getTotalPE(const QStringList& deviceNodeList); static qint64 getFreePE(const QString& deviceNode); static qint64 getFreePE(const QStringList& deviceNodeList); static qint64 getAllocatedPE(const QString& deviceNode); static qint64 getAllocatedPE(const QStringList& deviceNodeList); void getPESize(const QString& deviceNode); // return PE size in bytes static qint64 getPVSize(const QString& deviceNode); // return PV size in bytes static qint64 getPVSize(const QStringList& deviceNodeList); static QString getVGName(const QString& deviceNode); - static QList getPVinNode(const PartitionNode* parent, const QString& vgName = QString()); - static QList getPVs(const QList& devices, const QString& vgName = QString()); + static PhysicalVolumes getPVinNode(const PartitionNode* parent); + static PhysicalVolumes getPVs(const QList& devices); qint64 allocatedPE() const { return m_AllocatedPE; }; qint64 freePE() const { return m_TotalPE - m_AllocatedPE; }; qint64 totalPE() const { return m_TotalPE; }; qint64 peSize() const { return m_PESize; }; public: static CommandSupportType m_GetUsed; static CommandSupportType m_GetLabel; static CommandSupportType m_Create; static CommandSupportType m_Grow; static CommandSupportType m_Shrink; static CommandSupportType m_Move; static CommandSupportType m_Check; static CommandSupportType m_Copy; static CommandSupportType m_Backup; static CommandSupportType m_SetLabel; static CommandSupportType m_UpdateUUID; static CommandSupportType m_GetUUID; private: qint64 m_PESize; qint64 m_TotalPE; qint64 m_AllocatedPE; - }; } #endif