File indexing completed on 2024-04-28 05:46:34
0001 /* 0002 SPDX-FileCopyrightText: 2008-2012 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas <andrius@stikonas.eu> 0004 SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org> 0005 SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com> 0006 SPDX-FileCopyrightText: 2018 Abhijeet Sharma <sharma.abhijeet2096@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 0011 #include "gui/partitionmanagerwidget.h" 0012 #include "gui/partpropsdialog.h" 0013 #include "gui/resizedialog.h" 0014 #include "gui/newdialog.h" 0015 #include "gui/applyprogressdialog.h" 0016 #include "gui/insertdialog.h" 0017 #include "gui/editmountpointdialog.h" 0018 #include "util/guihelpers.h" 0019 0020 #include <core/partition.h> 0021 #include <core/device.h> 0022 #include <core/operationstack.h> 0023 #include <core/partitiontable.h> 0024 0025 #include <fs/filesystemfactory.h> 0026 #include <fs/luks.h> 0027 0028 #include <gui/partwidget.h> 0029 0030 #include <ops/deleteoperation.h> 0031 #include <ops/resizeoperation.h> 0032 #include <ops/newoperation.h> 0033 #include <ops/copyoperation.h> 0034 #include <ops/checkoperation.h> 0035 #include <ops/backupoperation.h> 0036 #include <ops/restoreoperation.h> 0037 #include <ops/setfilesystemlabeloperation.h> 0038 #include <ops/setpartflagsoperation.h> 0039 #include <ops/createfilesystemoperation.h> 0040 0041 #include <util/globallog.h> 0042 #include <util/capacity.h> 0043 #include <util/report.h> 0044 #include <util/helpers.h> 0045 0046 #include <QFileDialog> 0047 #include <QLocale> 0048 #include <QPointer> 0049 #include <QReadLocker> 0050 0051 #include <KLocalizedString> 0052 #include <KMessageBox> 0053 0054 #include <config.h> 0055 0056 #include <typeinfo> 0057 0058 class PartitionTreeWidgetItem : public QTreeWidgetItem 0059 { 0060 Q_DISABLE_COPY(PartitionTreeWidgetItem) 0061 0062 public: 0063 PartitionTreeWidgetItem(const Partition* p) : QTreeWidgetItem(), m_Partition(p) {} 0064 const Partition* partition() const { 0065 return m_Partition; 0066 } 0067 0068 private: 0069 const Partition* m_Partition; 0070 }; 0071 0072 /** Creates a new PartitionManagerWidget instance. 0073 @param parent the parent widget 0074 */ 0075 PartitionManagerWidget::PartitionManagerWidget(QWidget* parent) : 0076 QWidget(parent), 0077 Ui::PartitionManagerWidgetBase(), 0078 m_OperationStack(nullptr), 0079 m_SelectedDevice(nullptr), 0080 m_ClipboardPartition(nullptr) 0081 { 0082 setupUi(this); 0083 0084 treePartitions().header()->setStretchLastSection(false); 0085 treePartitions().header()->setContextMenuPolicy(Qt::CustomContextMenu); 0086 treePartitions().setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::TopEdge})); 0087 } 0088 0089 PartitionManagerWidget::~PartitionManagerWidget() 0090 { 0091 saveConfig(); 0092 } 0093 0094 void PartitionManagerWidget::init(OperationStack* ostack) 0095 { 0096 m_OperationStack = ostack; 0097 0098 loadConfig(); 0099 setupConnections(); 0100 } 0101 0102 void PartitionManagerWidget::loadConfig() 0103 { 0104 QList<int> colWidths = Config::treePartitionColumnWidths(); 0105 QList<int> colPositions = Config::treePartitionColumnPositions(); 0106 QList<int> colVisible = Config::treePartitionColumnVisible(); 0107 QHeaderView* header = treePartitions().header(); 0108 0109 for (int i = 0; i < treePartitions().columnCount(); i++) { 0110 if (colPositions[0] != -1 && colPositions.size() > i) 0111 header->moveSection(header->visualIndex(i), colPositions[i]); 0112 0113 if (colVisible[0] != -1 && colVisible.size() > i) 0114 treePartitions().setColumnHidden(i, colVisible[i] == 0); 0115 0116 if (colWidths[0] != -1 && colWidths.size() > i) 0117 treePartitions().setColumnWidth(i, colWidths[i]); 0118 } 0119 } 0120 0121 void PartitionManagerWidget::saveConfig() const 0122 { 0123 QList<int> colWidths; 0124 QList<int> colPositions; 0125 QList<int> colVisible; 0126 0127 for (int i = 0; i < treePartitions().columnCount(); i++) { 0128 colPositions.append(treePartitions().header()->visualIndex(i)); 0129 colVisible.append(treePartitions().isColumnHidden(i) ? 0 : 1); 0130 colWidths.append(treePartitions().columnWidth(i)); 0131 } 0132 0133 Config::setTreePartitionColumnPositions(colPositions); 0134 Config::setTreePartitionColumnVisible(colVisible); 0135 Config::setTreePartitionColumnWidths(colWidths); 0136 0137 Config::self()->save(); 0138 } 0139 0140 void PartitionManagerWidget::setupConnections() 0141 { 0142 connect(treePartitions().header(), &QHeaderView::customContextMenuRequested, this, &PartitionManagerWidget::onHeaderContextMenu); 0143 } 0144 0145 void PartitionManagerWidget::clear() 0146 { 0147 setSelectedDevice(nullptr); 0148 setClipboardPartition(nullptr); 0149 treePartitions().clear(); 0150 partTableWidget().clear(); 0151 } 0152 0153 void PartitionManagerWidget::setSelectedPartition(const Partition* p) 0154 { 0155 if (p == nullptr) { 0156 treePartitions().setCurrentItem(nullptr); 0157 Q_EMIT selectedPartitionChanged(nullptr); 0158 updatePartitions(); 0159 } else 0160 partTableWidget().setActivePartition(p); 0161 } 0162 0163 Partition* PartitionManagerWidget::selectedPartition() 0164 { 0165 if (selectedDevice() == nullptr || selectedDevice()->partitionTable() == nullptr || partTableWidget().activeWidget() == nullptr) 0166 return nullptr; 0167 0168 return partTableWidget().activeWidget()->partition(); 0169 } 0170 0171 void PartitionManagerWidget::setSelectedDevice(const QString& deviceNode) 0172 { 0173 QReadLocker lockDevices(&operationStack().lock()); 0174 0175 const auto previewDevices = operationStack().previewDevices(); 0176 for (const auto &d : previewDevices) { 0177 if (d->deviceNode() == deviceNode) { 0178 setSelectedDevice(d); 0179 return; 0180 } 0181 } 0182 0183 setSelectedDevice(nullptr); 0184 } 0185 0186 void PartitionManagerWidget::setSelectedDevice(Device* d) 0187 { 0188 m_SelectedDevice = d; 0189 setSelectedPartition(nullptr); 0190 } 0191 0192 static QTreeWidgetItem* createTreeWidgetItem(const Partition& p) 0193 { 0194 QTreeWidgetItem* item = new PartitionTreeWidgetItem(&p); 0195 0196 int i = 0; 0197 item->setText(i++, p.deviceNode()); 0198 0199 if (p.roles().has(PartitionRole::Luks) && (p.fileSystem().name() != p.fileSystem().nameForType(FileSystem::Type::Luks) && p.fileSystem().name() != p.fileSystem().nameForType(FileSystem::Type::Luks2))) 0200 item->setText(i, xi18nc("@item:intable Encrypted file systems, e.g. btrfs[Encrypted]", "%1 [Encrypted]", p.fileSystem().name())); 0201 else 0202 item->setText(i, p.fileSystem().name()); 0203 item->setIcon(i, createFileSystemColor(p.fileSystem().type(), 14)); 0204 i++; 0205 0206 item->setText(i, p.mountPoint()); 0207 if (p.isMounted()) 0208 item->setIcon(i, QIcon::fromTheme(QStringLiteral("object-locked"))); 0209 i++; 0210 0211 item->setText(i++, p.fileSystem().label()); 0212 item->setText(i++, p.fileSystem().uuid()); 0213 item->setText(i++, p.label()); 0214 item->setText(i++, p.uuid()); 0215 0216 item->setText(i++, Capacity::formatByteSize(p.capacity())); 0217 item->setText(i++, Capacity::formatByteSize(p.used())); 0218 item->setText(i++, Capacity::formatByteSize(p.available())); 0219 0220 item->setText(i++, QLocale().toString(p.firstSector())); 0221 item->setText(i++, QLocale().toString(p.lastSector())); 0222 item->setText(i++, QLocale().toString(p.length())); 0223 0224 item->setText(i++, PartitionTable::flagNames(p.activeFlags()).join(QStringLiteral(", "))); 0225 0226 item->setSizeHint(0, QSize(0, 32)); 0227 0228 return item; 0229 } 0230 0231 void PartitionManagerWidget::updatePartitions() 0232 { 0233 if (selectedDevice() == nullptr) 0234 return; 0235 0236 treePartitions().clear(); 0237 partTableWidget().clear(); 0238 0239 partTableWidget().setPartitionTable(selectedDevice()->partitionTable()); 0240 0241 QTreeWidgetItem* deviceItem = new QTreeWidgetItem(); 0242 0243 QFont font; 0244 font.setBold(true); 0245 font.setWeight(QFont::Bold); 0246 deviceItem->setFont(0, font); 0247 0248 deviceItem->setText(0, selectedDevice()->prettyName()); 0249 deviceItem->setIcon(0, QIcon::fromTheme(selectedDevice()->iconName())); 0250 0251 deviceItem->setSizeHint(0, QSize(0, 32)); 0252 0253 treePartitions().addTopLevelItem(deviceItem); 0254 0255 if (selectedDevice()->partitionTable() != nullptr) { 0256 const auto children = selectedDevice()->partitionTable()->children(); 0257 for (const auto * p : children) { 0258 QTreeWidgetItem* item = createTreeWidgetItem(*p); 0259 0260 for (const auto &child : p->children()) { 0261 QTreeWidgetItem* childItem = createTreeWidgetItem(*child); 0262 item->addChild(childItem); 0263 } 0264 0265 deviceItem->addChild(item); 0266 item->setExpanded(true); 0267 } 0268 } 0269 0270 deviceItem->setFirstColumnSpanned(true); 0271 deviceItem->setExpanded(true); 0272 deviceItem->setFlags(Qt::ItemIsEnabled); 0273 0274 partTableWidget().update(); 0275 } 0276 0277 void PartitionManagerWidget::on_m_TreePartitions_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem*) 0278 { 0279 if (current) { 0280 const PartitionTreeWidgetItem* ptwItem = dynamic_cast<PartitionTreeWidgetItem*>(current); 0281 partTableWidget().setActivePartition(ptwItem ? ptwItem->partition() : nullptr); 0282 } else 0283 partTableWidget().setActiveWidget(nullptr); 0284 } 0285 0286 bool PartitionManagerWidget::setCurrentPartitionByName(const QString& name) 0287 { 0288 auto rootNode = treePartitions().invisibleRootItem(); 0289 for (int i = 0; i < rootNode->childCount(); i++) { 0290 auto driveNode = rootNode->child(i); 0291 for (int e = 0; e < driveNode->childCount(); e++) { 0292 auto partitionNode = driveNode->child(e); 0293 const QString text = partitionNode->data(0, Qt::DisplayRole).toString(); 0294 if (text.endsWith(name)) { 0295 partitionNode->setSelected(true); 0296 treePartitions().setCurrentItem(partitionNode); 0297 return true; 0298 } 0299 } 0300 } 0301 0302 return false; 0303 } 0304 0305 void PartitionManagerWidget::on_m_TreePartitions_itemDoubleClicked(QTreeWidgetItem* item, int) 0306 { 0307 if (item == treePartitions().topLevelItem(0)) { 0308 if (selectedDevice() != nullptr) 0309 Q_EMIT deviceDoubleClicked(selectedDevice()); 0310 } else { 0311 if (selectedPartition() != nullptr) 0312 Q_EMIT partitionDoubleClicked(selectedPartition()); 0313 } 0314 } 0315 0316 void PartitionManagerWidget::onHeaderContextMenu(const QPoint& p) 0317 { 0318 showColumnsContextMenu(p, treePartitions()); 0319 } 0320 0321 void PartitionManagerWidget::on_m_PartTableWidget_itemSelectionChanged(PartWidget* item) 0322 { 0323 if (item == nullptr) { 0324 treePartitions().setCurrentItem(nullptr); 0325 Q_EMIT selectedPartitionChanged(nullptr); 0326 return; 0327 } 0328 0329 const Partition* p = item->partition(); 0330 0331 Q_ASSERT(p); 0332 0333 if (p) { 0334 QList<QTreeWidgetItem*> findResult = treePartitions().findItems(p->deviceNode(), Qt::MatchFixedString | Qt::MatchRecursive, 0); 0335 0336 for (const auto &treeWidgetItem : findResult) { 0337 const PartitionTreeWidgetItem* ptwItem = dynamic_cast<PartitionTreeWidgetItem*>(treeWidgetItem); 0338 0339 if (ptwItem && ptwItem->partition() == p) { 0340 treePartitions().setCurrentItem(treeWidgetItem); 0341 break; 0342 } 0343 } 0344 } 0345 0346 Q_EMIT selectedPartitionChanged(p); 0347 } 0348 0349 void PartitionManagerWidget::on_m_PartTableWidget_customContextMenuRequested(const QPoint& pos) 0350 { 0351 Q_EMIT contextMenuRequested(partTableWidget().mapToGlobal(pos)); 0352 } 0353 0354 void PartitionManagerWidget::on_m_PartTableWidget_itemDoubleClicked() 0355 { 0356 if (selectedPartition()) 0357 Q_EMIT partitionDoubleClicked(selectedPartition()); 0358 } 0359 0360 void PartitionManagerWidget::on_m_TreePartitions_customContextMenuRequested(const QPoint& pos) 0361 { 0362 Q_EMIT contextMenuRequested(treePartitions().viewport()->mapToGlobal(pos)); 0363 } 0364 0365 void PartitionManagerWidget::onPropertiesPartition() 0366 { 0367 if (selectedPartition()) { 0368 Partition& p = *selectedPartition(); 0369 0370 Q_ASSERT(selectedDevice()); 0371 0372 QPointer<PartPropsDialog> dlg = new PartPropsDialog(this, *selectedDevice(), p); 0373 0374 if (dlg->exec() == QDialog::Accepted) { 0375 if (dlg->newFileSystemType() != p.fileSystem().type() || dlg->forceRecreate()) 0376 operationStack().push(new CreateFileSystemOperation(*selectedDevice(), p, dlg->newFileSystemType())); 0377 0378 if (dlg->newLabel() != p.fileSystem().label()) 0379 operationStack().push(new SetFileSystemLabelOperation(p, dlg->newLabel())); 0380 0381 if (dlg->newFlags() != p.activeFlags()) 0382 operationStack().push(new SetPartFlagsOperation(*selectedDevice(), p, dlg->newFlags())); 0383 } 0384 0385 delete dlg; 0386 } 0387 } 0388 0389 void PartitionManagerWidget::onMountPartition() 0390 { 0391 Partition* p = selectedPartition(); 0392 0393 Q_ASSERT(p); 0394 0395 if (p == nullptr) { 0396 qWarning() << "no partition selected"; 0397 return; 0398 } 0399 0400 Report report(nullptr); 0401 0402 if (p->canMount()) { 0403 if (!p->mount(report)) 0404 KMessageBox::detailedError(this, xi18nc("@info", "The file system on partition <filename>%1</filename> could not be mounted.", p->deviceNode()), QStringLiteral("<pre>%1</pre>").arg(report.toText()), xi18nc("@title:window", "Could Not Mount File System.")); 0405 } else if (p->canUnmount()) { 0406 if (!p->unmount(report)) 0407 KMessageBox::detailedError(this, xi18nc("@info", "The file system on partition <filename>%1</filename> could not be unmounted.", p->deviceNode()), QStringLiteral("<pre>%1</pre>").arg(report.toText()), xi18nc("@title:window", "Could Not Unmount File System.")); 0408 } 0409 0410 if (p->roles().has(PartitionRole::Logical)) { 0411 Partition* parent = dynamic_cast<Partition*>(p->parent()); 0412 0413 Q_ASSERT(parent); 0414 0415 if (parent != nullptr) 0416 parent->checkChildrenMounted(); 0417 else 0418 qWarning() << "parent is null"; 0419 } 0420 0421 updatePartitions(); 0422 } 0423 0424 void PartitionManagerWidget::onDecryptPartition() 0425 { 0426 Partition* p = selectedPartition(); 0427 0428 Q_ASSERT(p); 0429 0430 if (p == nullptr) { 0431 qWarning() << "no partition selected"; 0432 return; 0433 } 0434 0435 if (!p->roles().has(PartitionRole::Luks)) 0436 return; 0437 0438 const FileSystem& fsRef = p->fileSystem(); 0439 FS::luks* luksFs = const_cast<FS::luks*>(dynamic_cast<const FS::luks*>(&fsRef)); 0440 if (!luksFs) 0441 return; 0442 0443 if (luksFs->canCryptOpen(p->partitionPath())) { 0444 if (!luksFs->cryptOpen(this, p->partitionPath())) 0445 KMessageBox::detailedError(this, 0446 xi18nc("@info", 0447 "The encrypted file system on partition " 0448 "<filename>%1</filename> could not be " 0449 "unlocked.", 0450 p->deviceNode()), 0451 QString(), 0452 xi18nc("@title:window", 0453 "Could Not Unlock Encrypted File System.")); 0454 } else if (luksFs->canCryptClose(p->partitionPath())) { 0455 if (!luksFs->cryptClose(p->partitionPath())) 0456 KMessageBox::detailedError(this, 0457 xi18nc("@info", 0458 "The encrypted file system on partition " 0459 "<filename>%1</filename> could not be " 0460 "locked.", 0461 p->deviceNode()), 0462 QString(), 0463 xi18nc("@title:window", 0464 "Could Not Lock Encrypted File System.")); 0465 } 0466 0467 updatePartitions(); 0468 } 0469 0470 void PartitionManagerWidget::onEditMountPoint() 0471 { 0472 Partition* p = selectedPartition(); 0473 0474 Q_ASSERT(p); 0475 0476 if (p == nullptr) 0477 return; 0478 0479 QPointer<EditMountPointDialog> dlg = new EditMountPointDialog(this, *p); 0480 0481 if (dlg->exec() == QDialog::Accepted) 0482 updatePartitions(); 0483 0484 delete dlg; 0485 } 0486 0487 static bool checkTooManyPartitions(QWidget* parent, const Device& d, const Partition& p) 0488 { 0489 Q_ASSERT(d.partitionTable()); 0490 0491 if (p.roles().has(PartitionRole::Unallocated) && d.partitionTable()->numPrimaries() >= d.partitionTable()->maxPrimaries() && !p.roles().has(PartitionRole::Logical)) { 0492 KMessageBox::error(parent, xi18ncp("@info", 0493 "<para>There is already one primary partition on this device. This is the maximum number its partition table type can handle.</para>" 0494 "<para>You cannot create, paste or restore a primary partition on it before you delete an existing one.</para>", 0495 "<para>There are already %1 primary partitions on this device. This is the maximum number its partition table type can handle.</para>" 0496 "<para>You cannot create, paste or restore a primary partition on it before you delete an existing one.</para>", 0497 d.partitionTable()->numPrimaries()), xi18nc("@title:window", "Too Many Primary Partitions.")); 0498 return true; 0499 } 0500 0501 return false; 0502 } 0503 0504 void PartitionManagerWidget::onNewPartition() 0505 { 0506 Q_ASSERT(selectedDevice()); 0507 Q_ASSERT(selectedPartition()); 0508 0509 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0510 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0511 return; 0512 } 0513 0514 Q_ASSERT(selectedDevice()->partitionTable()); 0515 0516 if (selectedDevice()->partitionTable() == nullptr) { 0517 qWarning() << "partition table on selected device is null"; 0518 return; 0519 } 0520 0521 if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) 0522 return; 0523 0524 Partition* newPartition = NewOperation::createNew(*selectedPartition(), static_cast<FileSystem::Type>(Config::defaultFileSystem())); 0525 0526 QPointer<NewDialog> dlg = new NewDialog(this, *selectedDevice(), *newPartition, selectedDevice()->partitionTable()->childRoles(*selectedPartition())); 0527 if (dlg->exec() == QDialog::Accepted) { 0528 if (dlg->useUnsecuredPartition()) { 0529 newPartition->fileSystem().setPosixPermissions(QStringLiteral("777")); 0530 } 0531 operationStack().push(new NewOperation(*selectedDevice(), newPartition)); 0532 } else { 0533 delete newPartition; 0534 } 0535 0536 delete dlg; 0537 } 0538 0539 void PartitionManagerWidget::onDeletePartition(bool shred) 0540 { 0541 Q_ASSERT(selectedDevice()); 0542 Q_ASSERT(selectedPartition()); 0543 0544 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0545 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0546 return; 0547 } 0548 0549 if (selectedPartition()->roles().has(PartitionRole::Logical)) { 0550 Q_ASSERT(selectedPartition()->parent()); 0551 0552 if (selectedPartition()->parent() == nullptr) { 0553 qWarning() << "parent of selected partition is null."; 0554 return; 0555 } 0556 0557 if (selectedPartition()->number() > 0 && selectedPartition()->parent()->highestMountedChild() > selectedPartition()->number()) { 0558 KMessageBox::error(this, 0559 xi18nc("@info", 0560 "<para>The partition <filename>%1</filename> cannot currently be deleted because one or more partitions with higher logical numbers are still mounted.</para>" 0561 "<para>Please unmount all partitions with higher logical numbers than %2 first.</para>", 0562 selectedPartition()->deviceNode(), selectedPartition()->number()), 0563 xi18nc("@title:window", "Cannot Delete Partition.")); 0564 0565 return; 0566 } 0567 } 0568 0569 if (clipboardPartition() == selectedPartition()) { 0570 if (KMessageBox::warningContinueCancel(this, 0571 xi18nc("@info", 0572 "Do you really want to delete the partition that is currently in the clipboard? " 0573 "It will no longer be available for pasting after it has been deleted."), 0574 xi18nc("@title:window", "Really Delete Partition in the Clipboard?"), 0575 KGuiItem(xi18nc("@action:button", "Delete It"), QStringLiteral("arrow-right")), 0576 KStandardGuiItem::cancel(), QStringLiteral("reallyDeleteClipboardPartition")) == KMessageBox::Cancel) 0577 return; 0578 0579 setClipboardPartition(nullptr); 0580 } 0581 0582 if (shred && Config::shredSource() == Config::EnumShredSource::random) 0583 operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition(), DeleteOperation::ShredAction::RandomShred)); 0584 else if (shred && Config::shredSource() == Config::EnumShredSource::zeros) 0585 operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition(), DeleteOperation::ShredAction::ZeroShred)); 0586 else 0587 operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition(), DeleteOperation::ShredAction::NoShred)); 0588 } 0589 0590 void PartitionManagerWidget::onShredPartition() 0591 { 0592 onDeletePartition(true); 0593 } 0594 0595 void PartitionManagerWidget::onResizePartition() 0596 { 0597 Q_ASSERT(selectedDevice()); 0598 Q_ASSERT(selectedPartition()); 0599 0600 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0601 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0602 return; 0603 } 0604 0605 Q_ASSERT(selectedDevice()->partitionTable()); 0606 0607 if (selectedDevice()->partitionTable() == nullptr) { 0608 qWarning() << "partition table on selected device is null"; 0609 return; 0610 } 0611 0612 // we cannot work with selectedPartition() here because opening and closing the dialog will 0613 // clear the selection, so we'll lose the partition after the dialog's been exec'd 0614 Partition& p = *selectedPartition(); 0615 0616 qint64 freeBefore = selectedDevice()->partitionTable()->freeSectorsBefore(p); 0617 qint64 freeAfter = selectedDevice()->partitionTable()->freeSectorsAfter(p); 0618 0619 if (selectedDevice()->type() == Device::Type::LVM_Device) { 0620 freeBefore = 0; 0621 freeAfter = selectedDevice()->partitionTable()->freeSectors(); 0622 } 0623 0624 QPointer<ResizeDialog> dlg = new ResizeDialog(this, *selectedDevice(), p, p.firstSector() - freeBefore, p.lastSector() + freeAfter); 0625 0626 if (dlg->exec() == QDialog::Accepted) { 0627 if (dlg->resizedFirstSector() == p.firstSector() && dlg->resizedLastSector() == p.lastSector()) 0628 Log(Log::Level::information) << xi18nc("@info:status", "Partition <filename>%1</filename> has the same position and size after resize/move. Ignoring operation.", p.deviceNode()); 0629 else 0630 operationStack().push(new ResizeOperation(*selectedDevice(), p, dlg->resizedFirstSector(), dlg->resizedLastSector())); 0631 } 0632 0633 if (p.roles().has(PartitionRole::Extended)) { 0634 // Even if the user dismissed the resize dialog we must update the partitions 0635 // if it's an extended partition: 0636 // The dialog has to remove and create unallocated children if the user resizes 0637 // an extended partition. We can't know if that has happened, so to avoid 0638 // any problems (like, the user resized an extended and then canceled, which would 0639 // lead to the unallocated children having the wrong size) do this now. 0640 updatePartitions(); 0641 } 0642 0643 delete dlg; 0644 } 0645 0646 void PartitionManagerWidget::onCopyPartition() 0647 { 0648 Q_ASSERT(selectedPartition()); 0649 0650 if (selectedPartition() == nullptr) { 0651 qWarning() << "selected partition: " << selectedPartition(); 0652 return; 0653 } 0654 0655 setClipboardPartition(selectedPartition()); 0656 Log() << xi18nc("@info:status", "Partition <filename>%1</filename> has been copied to the clipboard.", selectedPartition()->deviceNode()); 0657 } 0658 0659 void PartitionManagerWidget::onPastePartition() 0660 { 0661 Q_ASSERT(selectedDevice()); 0662 Q_ASSERT(selectedPartition()); 0663 0664 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0665 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0666 return; 0667 } 0668 0669 if (clipboardPartition() == nullptr) { 0670 qWarning() << "no partition in the clipboard."; 0671 return; 0672 } 0673 0674 if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) 0675 return; 0676 0677 Device* dSource = operationStack().findDeviceForPartition(clipboardPartition()); 0678 0679 Q_ASSERT(dSource); 0680 0681 if (dSource == nullptr) { 0682 qWarning() << "source partition is null."; 0683 return; 0684 } 0685 0686 Partition* copiedPartition = CopyOperation::createCopy(*selectedPartition(), *clipboardPartition()); 0687 0688 if (showInsertDialog(*copiedPartition, clipboardPartition()->length())) 0689 operationStack().push(new CopyOperation(*selectedDevice(), copiedPartition, *dSource, clipboardPartition())); 0690 else 0691 delete copiedPartition; 0692 } 0693 0694 bool PartitionManagerWidget::showInsertDialog(Partition& insertedPartition, qint64 sourceLength) 0695 { 0696 Q_ASSERT(selectedDevice()); 0697 Q_ASSERT(selectedPartition()); 0698 0699 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0700 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0701 return false; 0702 } 0703 0704 const bool overwrite = !selectedPartition()->roles().has(PartitionRole::Unallocated); 0705 0706 // Make sure the inserted partition has the right parent and logical or primary set. Only then 0707 // can PartitionTable::alignPartition() work correctly. 0708 selectedPartition()->parent()->reparent(insertedPartition); 0709 0710 if (!overwrite) { 0711 QPointer<InsertDialog> dlg = new InsertDialog(this, *selectedDevice(), insertedPartition, *selectedPartition()); 0712 0713 int result = dlg->exec(); 0714 delete dlg; 0715 0716 if (result != QDialog::Accepted) 0717 return false; 0718 } else if (KMessageBox::warningContinueCancel(this, 0719 xi18nc("@info", "<para><warning>You are about to lose all data on partition " 0720 "<filename>%1</filename>.</warning></para>" 0721 "<para>Overwriting one partition with another (or with an image file) will " 0722 "destroy all data on this target partition.</para>" 0723 "<para>If you continue now and apply the resulting operation in the main " 0724 "window, all data currently stored on <filename>%1</filename> will " 0725 "unrecoverably be overwritten.</para>", 0726 selectedPartition()->deviceNode()), 0727 xi18nc("@title:window", "Really Overwrite Existing Partition?"), 0728 KGuiItem(xi18nc("@action:button", "Overwrite Partition"), QStringLiteral("arrow-right")), 0729 KStandardGuiItem::cancel(), 0730 QStringLiteral("reallyOverwriteExistingPartition")) == KMessageBox::Cancel) 0731 return false; 0732 0733 if (insertedPartition.length() < sourceLength) { 0734 if (overwrite) 0735 KMessageBox::error(this, xi18nc("@info", 0736 "<para>The selected partition is not large enough to hold the source partition or the backup file.</para>" 0737 "<para>Pick another target or resize this partition so it is as large as the source.</para>"), xi18nc("@title:window", "Target Not Large Enough")); 0738 else 0739 KMessageBox::error(this, xi18nc("@info", 0740 "<para>It is not possible to create the target partition large enough to hold the source.</para>" 0741 "<para>This may happen if not all partitions on a device are correctly aligned " 0742 "or when copying a primary partition into an extended partition.</para>"), 0743 xi18nc("@title:window", "Cannot Create Target Partition.")); 0744 return false; 0745 } 0746 0747 return true; 0748 } 0749 0750 void PartitionManagerWidget::onCheckPartition() 0751 { 0752 Q_ASSERT(selectedDevice()); 0753 Q_ASSERT(selectedPartition()); 0754 0755 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0756 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0757 return; 0758 } 0759 0760 operationStack().push(new CheckOperation(*selectedDevice(), *selectedPartition())); 0761 } 0762 0763 void PartitionManagerWidget::onBackupPartition() 0764 { 0765 Q_ASSERT(selectedDevice()); 0766 Q_ASSERT(selectedPartition()); 0767 0768 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0769 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0770 return; 0771 } 0772 0773 QString fileName = QFileDialog::getSaveFileName(this); 0774 0775 if (fileName.isEmpty()) 0776 return; 0777 0778 operationStack().push(new BackupOperation(*selectedDevice(), *selectedPartition(), fileName)); 0779 } 0780 0781 void PartitionManagerWidget::onRestorePartition() 0782 { 0783 Q_ASSERT(selectedDevice()); 0784 Q_ASSERT(selectedPartition()); 0785 0786 if (selectedDevice() == nullptr || selectedPartition() == nullptr) { 0787 qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); 0788 return; 0789 } 0790 0791 if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) 0792 return; 0793 0794 QString fileName = QFileDialog::getOpenFileName(this); 0795 // QString fileName = "/tmp/backuptest.img"; 0796 0797 if (!fileName.isEmpty() && QFile::exists(fileName)) { 0798 Partition* restorePartition = RestoreOperation::createRestorePartition(*selectedDevice(), *selectedPartition()->parent(), selectedPartition()->firstSector(), fileName); 0799 0800 if (restorePartition->length() > selectedPartition()->length()) { 0801 KMessageBox::error(this, xi18nc("@info", "The file system in the image file <filename>%1</filename> is too large to be restored to the selected partition.", fileName), xi18nc("@title:window", "Not Enough Space to Restore File System.")); 0802 delete restorePartition; 0803 return; 0804 } 0805 0806 if (showInsertDialog(*restorePartition, restorePartition->length())) 0807 operationStack().push(new RestoreOperation(*selectedDevice(), restorePartition, fileName)); 0808 else 0809 delete restorePartition; 0810 } 0811 } 0812 0813 #include "moc_partitionmanagerwidget.cpp"