diff --git a/src/ops/createvolumegroupoperation.cpp b/src/ops/createvolumegroupoperation.cpp index 90c4826..5930905 100644 --- a/src/ops/createvolumegroupoperation.cpp +++ b/src/ops/createvolumegroupoperation.cpp @@ -1,74 +1,75 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "ops/createvolumegroupoperation.h" #include "core/lvmdevice.h" #include "fs/lvm2_pv.h" #include "jobs/createvolumegroupjob.h" #include #include /** Creates a new CreateVolumeGroupOperation. * @param vgName LVM Volume Group name * @param pvList List of LVM Physical Volumes used to create Volume Group * @param peSize LVM Physical Extent size in MiB */ CreateVolumeGroupOperation::CreateVolumeGroupOperation(const QString& vgName, const QVector& pvList, const qint32 peSize) : Operation(), m_CreateVolumeGroupJob(new CreateVolumeGroupJob(vgName, pvList, peSize)), - m_PVList(pvList) + m_PVList(pvList), + m_vgName(vgName) { addJob(createVolumeGroupJob()); } QString CreateVolumeGroupOperation::description() const { - return xi18nc("@info/plain", "Create a new LVM volume group."); + return xi18nc("@info/plain", "Create a new LVM volume group named \'%1\'.", m_vgName); } bool CreateVolumeGroupOperation::targets(const Partition& partition) const { for (const auto &p : m_PVList) { if (partition == *p) return true; } return false; } void CreateVolumeGroupOperation::preview() { LvmDevice::s_DirtyPVs << PVList(); } void CreateVolumeGroupOperation::undo() { for (const auto &pvPath : PVList()) { if (LvmDevice::s_DirtyPVs.contains(pvPath)) { LvmDevice::s_DirtyPVs.removeAll(pvPath); } } } bool CreateVolumeGroupOperation::canCreate() { return true; } diff --git a/src/ops/createvolumegroupoperation.h b/src/ops/createvolumegroupoperation.h index c75c45c..4fa49f2 100644 --- a/src/ops/createvolumegroupoperation.h +++ b/src/ops/createvolumegroupoperation.h @@ -1,74 +1,75 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_CREATEVOLUMEGROUPOPERATION_H) #define KPMCORE_CREATEVOLUMEGROUPOPERATION_H #include "util/libpartitionmanagerexport.h" #include "ops/operation.h" #include "core/lvmdevice.h" #include class CreateVolumeGroupJob; class OperationStack; class LIBKPMCORE_EXPORT CreateVolumeGroupOperation : public Operation { Q_DISABLE_COPY(CreateVolumeGroupOperation) friend class OperationStack; public: CreateVolumeGroupOperation(const QString& vgName, const QVector& pvList, const qint32 peSize = 4); public: QString iconName() const override { return QStringLiteral("document-new"); } QString description() const override; virtual bool targets(const Device&) const override { return true; } virtual bool targets(const Partition&) const override; virtual void preview() override; virtual void undo() override; static bool canCreate(); protected: CreateVolumeGroupJob* createVolumeGroupJob() { return m_CreateVolumeGroupJob; } const QVector& PVList() { return m_PVList; } private: CreateVolumeGroupJob* m_CreateVolumeGroupJob; const QVector m_PVList; + QString m_vgName; }; #endif diff --git a/src/ops/deleteoperation.cpp b/src/ops/deleteoperation.cpp index b0cea05..768051c 100644 --- a/src/ops/deleteoperation.cpp +++ b/src/ops/deleteoperation.cpp @@ -1,138 +1,159 @@ /************************************************************************* * Copyright (C) 2008, 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 "ops/createvolumegroupoperation.h" #include "ops/deleteoperation.h" #include "core/partition.h" #include "core/device.h" #include "core/partitiontable.h" #include "fs/luks.h" #include "jobs/deletepartitionjob.h" #include "jobs/deletefilesystemjob.h" #include "jobs/shredfilesystemjob.h" #include "util/capacity.h" #include #include /** Creates a new DeleteOperation @param d the Device to delete a Partition on @param p pointer to the Partition to delete. May not be nullptr */ DeleteOperation::DeleteOperation(Device& d, Partition* p, ShredAction shred) : Operation(), m_TargetDevice(d), m_DeletedPartition(p), m_ShredAction(shred), m_DeletePartitionJob(new DeletePartitionJob(targetDevice(), deletedPartition())) { switch (shredAction()) { case ShredAction::NoShred: m_DeleteFileSystemJob = static_cast(new DeleteFileSystemJob(targetDevice(), deletedPartition())); break; case ShredAction::ZeroShred: m_DeleteFileSystemJob = static_cast(new ShredFileSystemJob(targetDevice(), deletedPartition(), false)); break; case ShredAction::RandomShred: m_DeleteFileSystemJob = static_cast(new ShredFileSystemJob(targetDevice(), deletedPartition(), true)); } addJob(deleteFileSystemJob()); addJob(deletePartitionJob()); } DeleteOperation::~DeleteOperation() { if (status() != StatusPending && status() != StatusNone) // don't delete the partition if we're being merged or undone delete m_DeletedPartition; } bool DeleteOperation::targets(const Device& d) const { return d == targetDevice(); } bool DeleteOperation::targets(const Partition& p) const { return p == deletedPartition(); } void DeleteOperation::preview() { removePreviewPartition(targetDevice(), deletedPartition()); checkAdjustLogicalNumbers(deletedPartition(), false); } void DeleteOperation::undo() { checkAdjustLogicalNumbers(deletedPartition(), true); insertPreviewPartition(targetDevice(), deletedPartition()); } QString DeleteOperation::description() const { if (shredAction() != ShredAction::NoShred) return xi18nc("@info:status", "Shred partition %1 (%2, %3)", deletedPartition().deviceNode(), Capacity::formatByteSize(deletedPartition().capacity()), deletedPartition().fileSystem().name()); else return xi18nc("@info:status", "Delete partition %1 (%2, %3)", deletedPartition().deviceNode(), Capacity::formatByteSize(deletedPartition().capacity()), deletedPartition().fileSystem().name()); } void DeleteOperation::checkAdjustLogicalNumbers(Partition& p, bool undo) { // If the deleted partition is a logical one, we need to adjust the numbers of the // other logical partitions in the extended one, if there are any, because the OS // will do that, too: Logicals must be numbered without gaps, i.e., a numbering like // sda5, sda6, sda8 (after sda7 is deleted) will become sda5, sda6, sda7 Partition* parentPartition = dynamic_cast(p.parent()); if (parentPartition && parentPartition->roles().has(PartitionRole::Extended)) parentPartition->adjustLogicalNumbers(undo ? -1 : p.number(), undo ? p.number() : -1); } /** Can a Partition be deleted? @param p the Partition in question, may be nullptr. @return true if @p p can be deleted. */ -bool DeleteOperation::canDelete(const Partition* p) +bool DeleteOperation::canDelete(const Partition* p, const QList pendingOps) { if (p == nullptr) return false; if (p->isMounted()) return false; + if (p->fileSystem().type() == FileSystem::Type::Lvm2_PV) { + // See if there is a newly created VG targeting this partition + for (Operation *op : qAsConst(pendingOps)) { + if (dynamic_cast(op) && op->targets(*p)) + return false; + } + } + else if (p->fileSystem().type() == FileSystem::Type::Luks || p->fileSystem().type() == FileSystem::Type::Luks2) { + // See if innerFS is LVM + FileSystem *fs = static_cast(&p->fileSystem())->innerFS(); + + if (fs->type() == FileSystem::Type::Lvm2_PV) { + // See if there is a newly created VG targeting this partition + for (Operation *op : qAsConst(pendingOps)) { + if (dynamic_cast(op) && op->targets(*p)) + return false; + } + } + } + if (p->roles().has(PartitionRole::Unallocated)) return false; if (p->roles().has(PartitionRole::Extended)) return p->children().size() == 1 && p->children()[0]->roles().has(PartitionRole::Unallocated); if (p->roles().has(PartitionRole::Luks)) { const FS::luks* luksFs = dynamic_cast(&p->fileSystem()); if (!luksFs) return false; if (luksFs->isCryptOpen() || luksFs->isMounted()) return false; } return true; } diff --git a/src/ops/deleteoperation.h b/src/ops/deleteoperation.h index 682abd2..39faccd 100644 --- a/src/ops/deleteoperation.h +++ b/src/ops/deleteoperation.h @@ -1,109 +1,109 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(KPMCORE_DELETEOPERATION_H) #define KPMCORE_DELETEOPERATION_H #include "util/libpartitionmanagerexport.h" #include "ops/operation.h" #include class Device; class OperationStack; class Partition; class Job; class DeletePartitionJob; /** Delete a Partition. @author Volker Lanz */ class LIBKPMCORE_EXPORT DeleteOperation : public Operation { friend class OperationStack; Q_DISABLE_COPY(DeleteOperation) public: enum class ShredAction { NoShred, ZeroShred, RandomShred }; DeleteOperation(Device& d, Partition* p, ShredAction shred = ShredAction::NoShred); ~DeleteOperation(); public: QString iconName() const override { return shredAction() == ShredAction::NoShred ? QStringLiteral("edit-delete") : QStringLiteral("edit-delete-shred"); } QString description() const override; void preview() override; void undo() override; ShredAction shredAction() const { return m_ShredAction; } bool targets(const Device& d) const override; bool targets(const Partition& p) const override; - static bool canDelete(const Partition* p); + static bool canDelete(const Partition* p, const QList pendingOps = QList()); protected: Device& targetDevice() { return m_TargetDevice; } const Device& targetDevice() const { return m_TargetDevice; } Partition& deletedPartition() { return *m_DeletedPartition; } const Partition& deletedPartition() const { return *m_DeletedPartition; } void checkAdjustLogicalNumbers(Partition& p, bool undo); void setDeletedPartition(Partition* p) { m_DeletedPartition = p; } Job* deleteFileSystemJob() { return m_DeleteFileSystemJob; } DeletePartitionJob* deletePartitionJob() { return m_DeletePartitionJob; } private: Device& m_TargetDevice; Partition* m_DeletedPartition; ShredAction m_ShredAction; Job* m_DeleteFileSystemJob; DeletePartitionJob* m_DeletePartitionJob; }; #endif diff --git a/src/ops/resizeoperation.cpp b/src/ops/resizeoperation.cpp index 3bac007..57b7361 100644 --- a/src/ops/resizeoperation.cpp +++ b/src/ops/resizeoperation.cpp @@ -1,389 +1,431 @@ /************************************************************************* * 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 "ops/checkoperation.h" +#include "ops/createvolumegroupoperation.h" #include "fs/filesystem.h" +#include "fs/luks.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) { if(CheckOperation::canCheck(&partition())) 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()); if(CheckOperation::canCheck(&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 = true; Report* report = parent.newChild(description()); if (CheckOperation::canCheck(&partition())) rval = checkOriginalJob()->run(*report); if (rval) { // 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:status", "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 (CheckOperation::canCheck(&partition())) { rval = checkResizedJob()->run(*report); if (!rval) report->line() << xi18nc("@info:status", "Checking partition %1 after resize/move failed.", partition().deviceNode()); } } else report->line() << xi18nc("@info:status", "Resizing/moving partition %1 failed.", partition().deviceNode()); } } else report->line() << xi18nc("@info:status", "Checking partition %1 before resize/move failed.", partition().deviceNode()); setStatus(rval ? StatusFinishedSuccess : StatusError); report->setStatus(xi18nc("@info: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().logicalSize()); const QString origCapacity = Capacity::formatByteSize(origLength() * targetDevice().logicalSize()); const QString newCapacity = Capacity::formatByteSize(newLength() * targetDevice().logicalSize()); switch (resizeAction()) { case MoveLeft: return xi18nc("@info:status describe resize/move action", "Move partition %1 to the left by %2", partition().deviceNode(), moveDelta); case MoveRight: return xi18nc("@info:status describe resize/move action", "Move partition %1 to the right by %2", partition().deviceNode(), moveDelta); case Grow: return xi18nc("@info:status describe resize/move action", "Grow partition %1 from %2 to %3", partition().deviceNode(), origCapacity, newCapacity); case Shrink: return xi18nc("@info:status describe resize/move action", "Shrink partition %1 from %2 to %3", partition().deviceNode(), origCapacity, newCapacity); case MoveLeftGrow: return xi18nc("@info:status 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:status 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:status 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:status describe resize/move action", "Move partition %1 to the right by %2 and shrink it from %3 to %4", partition().deviceNode(), moveDelta, origCapacity, newCapacity); case None: qWarning() << "Could not determine what to do with partition " << partition().deviceNode() << "."; break; } return xi18nc("@info:status 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:status", "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:status", "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:status", "Moving partition %1 failed.", partition().deviceNode()); return false; } if (moveFileSystemJob() && !moveFileSystemJob()->run(report)) { report.line() << xi18nc("@info:status", "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:status", "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:status", "Resize/move failed: Could not grow partition %1.", partition().deviceNode()); return false; } if (growResizeJob() && !growResizeJob()->run(report)) { report.line() << xi18nc("@info:status", "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:status", "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::State::New && !p->roles().has(PartitionRole::Luks)) return true; if (p->isMounted()) return p->fileSystem().supportGrowOnline(); 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) +bool ResizeOperation::canShrink(const Partition* p, const QList pendingOps) { if (p == nullptr) return false; + if (p->fileSystem().type() == FileSystem::Type::Lvm2_PV) { + // See if there is a newly created VG targeting this partition + for (Operation *op : qAsConst(pendingOps)) { + if (dynamic_cast(op) && op->targets(*p)) + return false; + } + } + else if (p->fileSystem().type() == FileSystem::Type::Luks || p->fileSystem().type() == FileSystem::Type::Luks2) { + // See if innerFS is LVM + FileSystem *fs = static_cast(&p->fileSystem())->innerFS(); + + if (fs->type() == FileSystem::Type::Lvm2_PV) { + // See if there is a newly created VG targeting this partition + for (Operation *op : qAsConst(pendingOps)) { + if (dynamic_cast(op) && op->targets(*p)) + return false; + } + } + } + // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::State::New && !p->roles().has(PartitionRole::Luks)) return true; if (p->state() == Partition::State::Copy) return false; if (p->isMounted()) return p->fileSystem().supportShrinkOnline(); 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) +bool ResizeOperation::canMove(const Partition* p, const QList pendingOps) { if (p == nullptr) return false; + if (p->fileSystem().type() == FileSystem::Type::Lvm2_PV) { + // See if there is a newly created VG targeting this partition + for (Operation *op : qAsConst(pendingOps)) { + if (dynamic_cast(op) && op->targets(*p)) + return false; + } + } + else if (p->fileSystem().type() == FileSystem::Type::Luks || p->fileSystem().type() == FileSystem::Type::Luks2) { + // See if innerFS is LVM + FileSystem *fs = static_cast(&p->fileSystem())->innerFS(); + + if (fs->type() == FileSystem::Type::Lvm2_PV) { + // See if there is a newly created VG targeting this partition + for (Operation *op : qAsConst(pendingOps)) { + if (dynamic_cast(op) && op->targets(*p)) + return false; + } + } + } + // we can always grow, shrink or move a partition not yet written to disk if (p->state() == Partition::State::New) // 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/resizeoperation.h b/src/ops/resizeoperation.h index e620b5a..820edc7 100644 --- a/src/ops/resizeoperation.h +++ b/src/ops/resizeoperation.h @@ -1,179 +1,179 @@ /************************************************************************* * 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 .* *************************************************************************/ #if !defined(KPMCORE_RESIZEOPERATION_H) #define KPMCORE_RESIZEOPERATION_H #include "util/libpartitionmanagerexport.h" #include "ops/operation.h" #include "core/partition.h" #include class Device; class OperationStack; class Report; class CheckFileSystemJob; class SetPartGeometryJob; class ResizeFileSystemJob; class SetPartGeometryJob; class SetPartGeometryJob; class MoveFileSystemJob; class ResizeFileSystemJob; class CheckFileSystemJob; /** Resizes a Partition and FileSystem. Resize the given Partition and its FileSystem on the given Device so they start with the given new start sector and end with the given new last sector. @author Volker Lanz */ class LIBKPMCORE_EXPORT ResizeOperation : public Operation { friend class OperationStack; Q_DISABLE_COPY(ResizeOperation) protected: /** A ResizeOperation can do a combination of things; this enum is used to determine what actually is going to be done. It is used so the ResizeOperation can describe itself and when it's actually executed. */ enum ResizeAction { None = 0, /**< Nothing */ MoveLeft = 1, /**< Move to the left */ MoveRight = 2, /**< Move to the right */ Grow = 4, /**< Grow */ Shrink = 8, /**< Shrink */ MoveLeftGrow = 5, /**< Move to the left then grow */ MoveRightGrow = 6, /**< Move to the right then grow */ MoveLeftShrink = 9, /**< Shrink then move to the left */ MoveRightShrink = 10 /**< Shrink then move to the right */ }; public: ResizeOperation(Device& d, Partition& p, qint64 newfirst, qint64 newlast); public: QString iconName() const override { return QStringLiteral("arrow-right-double"); } QString description() const override; bool execute(Report& parent) override; void preview() override; void undo() override; bool targets(const Device& d) const override; bool targets(const Partition& p) const override; static bool canGrow(const Partition* p); - static bool canShrink(const Partition* p); - static bool canMove(const Partition* p); + static bool canShrink(const Partition* p, const QList pendingOps = QList()); + static bool canMove(const Partition* p, const QList pendingOps = QList()); protected: Device& targetDevice() { return m_TargetDevice; } const Device& targetDevice() const { return m_TargetDevice; } Partition& partition() { return m_Partition; } const Partition& partition() const { return m_Partition; } bool shrink(Report& report); bool move(Report& report); bool grow(Report& report); ResizeAction resizeAction() const; qint64 origFirstSector() const { return m_OrigFirstSector; } qint64 origLastSector() const { return m_OrigLastSector; } qint64 origLength() const { return origLastSector() - origFirstSector() + 1; } qint64 newFirstSector() const { return m_NewFirstSector; } qint64 newLastSector() const { return m_NewLastSector; } qint64 newLength() const { return newLastSector() - newFirstSector() + 1; } CheckFileSystemJob* checkOriginalJob() { return m_CheckOriginalJob; } SetPartGeometryJob* moveExtendedJob() { return m_MoveExtendedJob; } ResizeFileSystemJob* shrinkResizeJob() { return m_ShrinkResizeJob; } SetPartGeometryJob* shrinkSetGeomJob() { return m_ShrinkSetGeomJob; } SetPartGeometryJob* moveSetGeomJob() { return m_MoveSetGeomJob; } MoveFileSystemJob* moveFileSystemJob() { return m_MoveFileSystemJob; } ResizeFileSystemJob* growResizeJob() { return m_GrowResizeJob; } SetPartGeometryJob* growSetGeomJob() { return m_GrowSetGeomJob; } CheckFileSystemJob* checkResizedJob() { return m_CheckResizedJob; } private: Device& m_TargetDevice; Partition& m_Partition; const qint64 m_OrigFirstSector; const qint64 m_OrigLastSector; qint64 m_NewFirstSector; qint64 m_NewLastSector; CheckFileSystemJob* m_CheckOriginalJob; SetPartGeometryJob* m_MoveExtendedJob; ResizeFileSystemJob* m_ShrinkResizeJob; SetPartGeometryJob* m_ShrinkSetGeomJob; SetPartGeometryJob* m_MoveSetGeomJob; MoveFileSystemJob* m_MoveFileSystemJob; ResizeFileSystemJob* m_GrowResizeJob; SetPartGeometryJob* m_GrowSetGeomJob; CheckFileSystemJob* m_CheckResizedJob; }; #endif