diff --git a/src/core/lvmdevice.cpp b/src/core/lvmdevice.cpp index 6257fe4..5a8782a 100644 --- a/src/core/lvmdevice.cpp +++ b/src/core/lvmdevice.cpp @@ -1,343 +1,343 @@ /************************************************************************* * 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 "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "core/partition.h" #include "core/partitiontable.h" #include "util/externalcommand.h" #include "util/helpers.h" #include #include #include #include #include /** Constructs a representation of LVM device with functionning LV as Partition * * @param name Volume Group name */ LvmDevice::LvmDevice(const QString& name, const QString& iconname) : VolumeManagerDevice(name, (QStringLiteral("/dev/") + name), getPeSize(name), getTotalPE(name), iconname, Device::LVM_Device) , m_peSize(getPeSize(name)) , m_totalPE(getTotalPE(name)) , m_allocPE(getAllocatedPE(name)) , m_freePE(getFreePE(name)) , m_UUID(getUUID(name)) { initPartitions(); } void LvmDevice::initPartitions() { qint64 firstUsable = 0; qint64 lastusable = totalPE() - 1; PartitionTable* pTable = new PartitionTable(PartitionTable::vmd, firstUsable, lastusable); foreach (Partition* p, scanPartitions(*this, pTable)) { pTable->append(p); } pTable->updateUnallocated(*this); setPartitionTable(pTable); } /** * @returns sorted Partition(LV) Array */ QList LvmDevice::scanPartitions(const LvmDevice& dev, PartitionTable* pTable) const { QList pList; foreach (QString lvPath, lvPathList()) { pList.append(scanPartition(lvPath, dev, pTable)); } return pList; } /** * @returns sorted Partition(LV) Array */ Partition* LvmDevice::scanPartition(const QString& lvpath, const LvmDevice& dev, PartitionTable* pTable) const { /* * NOTE: * LVM partition have 2 different start and end sector value * 1. representing the actual LV start from 0 -> size of LV - 1 * 2. representing abstract LV's sector inside a VG partitionTable * start from size of last Partitions -> size of LV - 1 * Reason for this is for the LV Partition to worrks nicely with other parts of the codebase * without too many special cases. */ qint64 startSector; qint64 endSector; qint64 lvSize; bool mounted = isMounted(lvpath); QString mountPoint = QString(); KMountPoint::List mountPointList = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); mountPointList.append(KMountPoint::possibleMountPoints(KMountPoint::NeedRealDeviceName)); mountPoint = mountPointList.findByDevice(lvpath) ? mountPointList.findByDevice(lvpath)->mountPoint() : QString(); lvSize = getTotalLE(lvpath); startSector = mappedSector(lvpath,0); endSector = startSector + (lvSize - 1); const KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint); FileSystem* fs = FileSystemFactory::create(FileSystem::detectFileSystem(lvpath), 0, lvSize - 1); //TODO: fix used space report. currently incorrect if (mounted && freeSpaceInfo.isValid() && mountPoint != QString()) { fs->setSectorsUsed(freeSpaceInfo.used() / logicalSize()); } else if (fs->supportGetUsed() == FileSystem::cmdSupportFileSystem) { fs->setSectorsUsed(fs->readUsedCapacity(lvpath) / logicalSize()); } if (fs->supportGetLabel() != FileSystem::cmdSupportNone) { fs->setLabel(fs->readLabel(lvpath)); } Partition* part = new Partition(pTable, dev, PartitionRole(PartitionRole::Lvm_Lv), fs, startSector, endSector, lvpath, PartitionTable::Flag::FlagLvm, mountPoint, mounted); return part; } qint64 LvmDevice::mappedSector(const QString& lvpath, qint64 sector) const { qint64 mSector = 0; QList lvpathList = lvPathList(); qint32 devIndex = lvpathList.indexOf(lvpath); if (devIndex) { for (int i = 0; i < devIndex; i++) { //TODO: currently going over the same LV again and again is wasteful. Could use some more optimization mSector += getTotalLE(lvpathList[i]); } mSector += sector; } return mSector; } QList LvmDevice::deviceNodeList() const { QList devPathList; QString cmdOutput = getField(QStringLiteral("pv_name"), name()); if (cmdOutput.size()) { QList tempPathList = cmdOutput.split(QStringLiteral("\n"), QString::SkipEmptyParts); foreach(QString devPath, tempPathList) { devPathList.append(devPath.trimmed()); } } return devPathList; } QList LvmDevice::lvPathList() const { QList lvPathList; QString cmdOutput = getField(QStringLiteral("lv_path"), name()); if (cmdOutput.size()) { QList tempPathList = cmdOutput.split(QStringLiteral("\n"), QString::SkipEmptyParts); foreach(QString lvPath, tempPathList) { lvPathList.append(lvPath.trimmed()); } } return lvPathList; } qint32 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(); } qint32 LvmDevice::getAllocatedPE(const QString& vgname) { return getTotalPE(vgname) - getFreePE(vgname); } qint32 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 * @returns raw output of command output, usully with manay spaces within the returned string * */ QString LvmDevice::getField(const QString& fieldName, const QString& vgname) { ExternalCommand cmd(QStringLiteral("lvm"), { QStringLiteral("vgs"), QStringLiteral("--foreign"), QStringLiteral("--readonly"), QStringLiteral("--noheadings"), QStringLiteral("--units"), QStringLiteral("B"), QStringLiteral("--nosuffix"), QStringLiteral("--options"), fieldName, vgname }); if (cmd.run(-1) && cmd.exitCode() == 0) { return cmd.output().trimmed(); } return QString(); } qint32 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& dev, Partition& part) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvremove"), QStringLiteral("--yes"), part.partitionPath()}); if (cmd.run(-1) && cmd.exitCode() == 0) { //TODO: remove Partition from PartitionTable and delete from memory ?? dev.partitionTable()->remove(&part); return true; } return false; } bool LvmDevice::createLV(Report& report, LvmDevice& dev, Partition& part, const QString& lvname) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvcreate"), QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(part.length()), QStringLiteral("--name"), lvname, dev.name()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::resizeLV(Report& report, LvmDevice& dev, Partition& part) { Q_UNUSED(dev); //TODO: through tests and add warning that it could currupt the user data. ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("lvresize"), QStringLiteral("--force"), // this command could corrupt user data QStringLiteral("--yes"), QStringLiteral("--extents"), QString::number(part.length()), part.partitionPath()}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removePV(Report& report, LvmDevice& dev, const QString& pvPath) { //TODO: through tests ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgreduce"), //QStringLiteral("--yes"), // potentially corrupt user data dev.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::insertPV(Report& report, LvmDevice& dev, const QString& pvPath) { //TODO: through tests ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgextend"), //QStringLiteral("--yes"), // potentially corrupt user data dev.name(), pvPath}); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::createVG(Report& report, const QString vgname, const QStringList pvlist) { //TODO: check that all the pv in pvlist is lvm2_pv QStringList args = QStringList(); args << QStringLiteral("vgcreate") << vgname; foreach (QString pvnode, pvlist) { - args << pvnode; + args << pvnode.trimmed(); } ExternalCommand cmd(report, QStringLiteral("lvm"), args); return (cmd.run(-1) && cmd.exitCode() == 0); } bool LvmDevice::removeVG(Report& report, const QString vgname) { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("vgremove"), vgname }); return (cmd.run(-1) && cmd.exitCode() == 0); } diff --git a/src/fs/lvm2_pv.cpp b/src/fs/lvm2_pv.cpp index 42a131e..71a1b9e 100644 --- a/src/fs/lvm2_pv.cpp +++ b/src/fs/lvm2_pv.cpp @@ -1,269 +1,270 @@ /************************************************************************* * Copyright (C) 2012 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "fs/lvm2_pv.h" #include "util/externalcommand.h" #include "util/capacity.h" #include #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 } 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); return val.isEmpty() ? -1 : val.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) const { ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvcreate"), 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 { // TODO: check if the it is legal to resize const QString len = QString::number(length / 512) + QStringLiteral("s"); ExternalCommand cmd(report, QStringLiteral("lvm"), { QStringLiteral("pvresize"), QStringLiteral("--yes"), QStringLiteral("--setphysicalvolumesize"), len, deviceNode }); return 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(mountPoint); QString rval = getpvField(QStringLiteral("pv_in_use"), deviceNode); // this field return "used" when in use otherwise empty string if (rval.isEmpty()) { return true; } return false; } bool lvm2_pv::canUnmount(const QString& deviceNode) const { QString rval = getpvField(QStringLiteral("pv_in_use"), deviceNode); if (rval.isEmpty()) { return false; } return true; } qint64 lvm2_pv::getTotalPE(const QString& deviceNode) const { QString val = getpvField(QStringLiteral("pv_pe_count"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 lvm2_pv::getFreePE(const QString& deviceNode) const { return getTotalPE(deviceNode) - getAllocatedPE(deviceNode); } qint64 lvm2_pv::getAllocatedPE(const QString& deviceNode) const { QString val = getpvField(QStringLiteral("pv_pe_alloc_count"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 lvm2_pv::getPVSize(const QString& deviceNode) const { QString val = getpvField(QStringLiteral("pv_size"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } qint64 lvm2_pv::getPESize(const QString& deviceNode) const { QString val = getpvField(QStringLiteral("vg_extent_size"), deviceNode); return val.isEmpty() ? -1 : val.toLongLong(); } 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); } QStringList lvm2_pv::getFreePV() { QStringList rlist; QString output = getpvField(QStringLiteral("pv_name")); QStringList pvList = output.split(QStringLiteral("\n"), QString::SkipEmptyParts); foreach (QString pvnode, pvList) { if (!isUsed(pvnode.trimmed())) { - rlist.append(pvnode); + rlist.append(pvnode.trimmed()); } } + return rlist; } QStringList lvm2_pv::getUsedPV(const QString& vgname) { QStringList rlist; QString output = getpvField(QStringLiteral("pv_name"), vgname); QStringList pvList = output.split(QStringLiteral("\n"), QString::SkipEmptyParts); foreach (QString pvnode, pvList) { if (isUsed(pvnode.trimmed())) { - rlist.append(pvnode); + rlist.append(pvnode.trimmed()); } } return rlist; } bool lvm2_pv::isUsed(const QString& deviceNode) { QString output = getpvField(QStringLiteral("pv_in_use"), deviceNode.trimmed()); if (output.trimmed() == QStringLiteral("used")) { return true; } return false; } }