File indexing completed on 2024-04-28 05:46:34

0001 /*
0002     SPDX-FileCopyrightText: 2008-2012 Volker Lanz <vl@fidra.de>
0003     SPDX-FileCopyrightText: 2013-2020 Andrius Štikonas <andrius@stikonas.eu>
0004     SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
0005     SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com>
0006     SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
0007 
0008     SPDX-License-Identifier: GPL-3.0-or-later
0009 */
0010 
0011 #include "gui/newdialog.h"
0012 #include "gui/sizedialogwidget.h"
0013 #include "gui/sizedetailswidget.h"
0014 
0015 #include <core/partition.h>
0016 #include <core/device.h>
0017 
0018 #include <fs/filesystemfactory.h>
0019 #include <fs/luks.h>
0020 
0021 #include <util/capacity.h>
0022 #include <util/helpers.h>
0023 #include "util/guihelpers.h"
0024 
0025 #include <utility>
0026 
0027 #include <QtGlobal>
0028 #include <QFontDatabase>
0029 #include <QPalette>
0030 
0031 #include <KColorScheme>
0032 #include <KConfigGroup>
0033 #include <KLocalizedString>
0034 #include <KSharedConfig>
0035 
0036 /** Creates a NewDialog
0037     @param parent the parent widget
0038     @param device the Device on which a new Partition is to be created
0039     @param unallocatedPartition the unallocated space on the Device to create a Partition in
0040     @param r the permitted Roles for the new Partition
0041 */
0042 NewDialog::NewDialog(QWidget* parent, Device& device, Partition& unallocatedPartition, PartitionRole::Roles r) :
0043     SizeDialogBase(parent, device, unallocatedPartition, unallocatedPartition.firstSector(), unallocatedPartition.lastSector()),
0044     m_PartitionRoles(r),
0045     m_IsValidPassword(true)
0046 {
0047     setWindowTitle(xi18nc("@title:window", "Create a new partition"));
0048 
0049     setupDialog();
0050     setupConstraints();
0051     setupConnections();
0052 
0053     updateOkButtonStatus();
0054 
0055     KConfigGroup kcg(KSharedConfig::openConfig(), QStringLiteral("newDialog"));
0056     restoreGeometry(kcg.readEntry<QByteArray>("Geometry", QByteArray()));
0057 }
0058 
0059 NewDialog::~NewDialog()
0060 {
0061     KConfigGroup kcg(KSharedConfig::openConfig(), QStringLiteral("newDialog"));
0062     kcg.writeEntry("Geometry", saveGeometry());
0063 }
0064 
0065 void NewDialog::setupDialog()
0066 {
0067     QStringList fsNames;
0068     for (const auto &fs : FileSystemFactory::map()) {
0069         if (fs->supportCreate() != FileSystem::cmdSupportNone &&
0070             fs->type() != FileSystem::Type::Extended &&
0071             fs->type() != FileSystem::Type::Luks &&
0072             fs->type() != FileSystem::Type::Luks2)
0073             fsNames.append(fs->name());
0074     }
0075 
0076     std::sort(fsNames.begin(), fsNames.end(), caseInsensitiveLessThan);
0077 
0078     for (const auto &fsName : std::as_const(fsNames))
0079         dialogWidget().comboFileSystem().addItem(createFileSystemColor(FileSystem::typeForName(fsName), 8), fsName);
0080 
0081     QString selected = FileSystem::nameForType(GuiHelpers::defaultFileSystem());
0082     const int idx = dialogWidget().comboFileSystem().findText(selected);
0083     dialogWidget().comboFileSystem().setCurrentIndex(idx != -1 ? idx : 0);
0084 
0085     dialogWidget().radioPrimary().setVisible(partitionRoles() & PartitionRole::Primary);
0086     dialogWidget().radioExtended().setVisible(partitionRoles() & PartitionRole::Extended);
0087     dialogWidget().radioLogical().setVisible(partitionRoles() & PartitionRole::Logical);
0088 
0089     if (partitionRoles() & PartitionRole::Primary)
0090         dialogWidget().radioPrimary().setChecked(true);
0091     else
0092         dialogWidget().radioLogical().setChecked(true);
0093 
0094     SizeDialogBase::setupDialog();
0095 
0096     dialogWidget().checkBoxEncrypt().hide();
0097     dialogWidget().editPassphrase().hide();
0098 
0099     if (device().type() == Device::Type::LVM_Device) {
0100         dialogWidget().comboFileSystem().removeItem(dialogWidget().comboFileSystem().findText(QStringLiteral("lvm2 pv")));
0101     }
0102 
0103 
0104     dialogWidget().editPassphrase().setMinimumPasswordLength(1);
0105     dialogWidget().editPassphrase().setMaximumPasswordLength(512); // cryptsetup does not support longer passwords
0106 
0107     // set a background warning color (taken from the current color scheme)
0108     KColorScheme colorScheme(QPalette::Active, KColorScheme::View);
0109     dialogWidget().editPassphrase().setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color());
0110 
0111 
0112     // don't move these above the call to parent's setupDialog, because only after that has
0113     // run there is a valid partition set in the part resizer widget and they will need that.
0114     onRoleChanged(false);
0115     onFilesystemChanged(dialogWidget().comboFileSystem().currentIndex());
0116 
0117     auto showPermissionsGroup = [this] {
0118         const QString currText = dialogWidget().comboFileSystem().currentText();
0119         const bool enablePosixPermission = QList<QString>({
0120                 QStringLiteral("btrfs"),
0121                 QStringLiteral("ext2"),
0122                 QStringLiteral("ext3"),
0123                 QStringLiteral("ext4"),
0124                 QStringLiteral("f2fs"),
0125                 QStringLiteral("hfsplus"),
0126                 QStringLiteral("jfs"),
0127                 QStringLiteral("minix"),
0128                 QStringLiteral("ocfs2"),
0129                 QStringLiteral("reiserfs"),
0130                 QStringLiteral("reiser4"),
0131                 QStringLiteral("udf"),
0132                 QStringLiteral("xfs"),
0133                 QStringLiteral("zfs"),}
0134             ).contains(currText);
0135         if (enablePosixPermission) {
0136             dialogWidget().showPosixPermissions();
0137         } else {
0138             dialogWidget().hidePosixPermissions();
0139         }
0140     };
0141     connect(&dialogWidget().comboFileSystem(), QOverload<int>::of(&QComboBox::currentIndexChanged), this, showPermissionsGroup);
0142     showPermissionsGroup();
0143     dialogWidget().radioRootPermissions().setChecked(true);
0144 }
0145 
0146 void NewDialog::setupConnections()
0147 {
0148     connect(&dialogWidget().radioPrimary(), &QRadioButton::toggled, this, &NewDialog::onRoleChanged);
0149     connect(&dialogWidget().radioExtended(), &QRadioButton::toggled, this, &NewDialog::onRoleChanged);
0150     connect(&dialogWidget().radioLogical(), &QRadioButton::toggled, this, &NewDialog::onRoleChanged);
0151     connect(&dialogWidget().checkBoxEncrypt(), &QCheckBox::toggled, this, &NewDialog::onRoleChanged);
0152     connect(&dialogWidget().comboFileSystem(), &QComboBox::currentIndexChanged, this, &NewDialog::onFilesystemChanged);
0153     connect(&dialogWidget().label(), &QLineEdit::textChanged, this, &NewDialog::onLabelChanged);
0154     // listen to password status updates
0155     connect(&dialogWidget().editPassphrase(), &KNewPasswordWidget::passwordStatusChanged, this, &NewDialog::slotPasswordStatusChanged);
0156 
0157     SizeDialogBase::setupConnections();
0158 }
0159 
0160 bool NewDialog::canMove() const
0161 {
0162     return (device().type() == Device::Type::LVM_Device) ? false : true;
0163 }
0164 
0165 void NewDialog::accept()
0166 {
0167     if (partition().roles().has(PartitionRole::Extended)) {
0168         partition().deleteFileSystem();
0169         partition().setFileSystem(FileSystemFactory::create(FileSystem::Type::Extended,
0170                                                             partition().firstSector(),
0171                                                             partition().lastSector(),
0172                                                             partition().sectorSize()));
0173     }
0174     else if (partition().roles().has(PartitionRole::Luks)) {
0175         FileSystem::Type innerFsType = partition().fileSystem().type();
0176         partition().deleteFileSystem();
0177         FS::luks* luksFs = dynamic_cast< FS::luks* >(
0178                                FileSystemFactory::create(FileSystem::Type::Luks,
0179                                                          partition().firstSector(),
0180                                                          partition().lastSector(),
0181                                                          partition().sectorSize()));
0182         luksFs->createInnerFileSystem(innerFsType);
0183         luksFs->setPassphrase(dialogWidget().editPassphrase().password());
0184         partition().setFileSystem(luksFs);
0185         partition().fileSystem().setLabel(dialogWidget().label().text());
0186     }
0187 
0188     QDialog::accept();
0189 }
0190 
0191 void NewDialog::onRoleChanged(bool)
0192 {
0193     PartitionRole::Roles r = PartitionRole::None;
0194 
0195     if (dialogWidget().radioPrimary().isChecked())
0196         r = PartitionRole::Primary;
0197     else if (dialogWidget().radioExtended().isChecked())
0198         r = PartitionRole::Extended;
0199     else if (dialogWidget().radioLogical().isChecked())
0200         r = PartitionRole::Logical;
0201 
0202     bool doEncrypt = dialogWidget().checkBoxEncrypt().isVisible() &&
0203                        dialogWidget().checkBoxEncrypt().isChecked();
0204     if (doEncrypt)
0205         r |= PartitionRole::Luks;
0206 
0207     dialogWidget().editPassphrase().setVisible(doEncrypt);
0208 
0209     // Make sure an extended partition gets correctly displayed: Set its file system to extended.
0210     // Also make sure to set a primary's or logical's file system once the user goes back from
0211     // extended to any of those.
0212     if (r == PartitionRole::Extended)
0213         updateFileSystem(FileSystem::Type::Extended);
0214     else
0215         updateFileSystem(FileSystem::typeForName(dialogWidget().comboFileSystem().currentText()));
0216 
0217     dialogWidget().comboFileSystem().setEnabled(r != PartitionRole::Extended);
0218     partition().setRoles(PartitionRole(r));
0219 
0220     setupConstraints();
0221 
0222     dialogWidget().partResizerWidget().resizeLogicals(0, 0, true);
0223     dialogWidget().partResizerWidget().update();
0224 
0225     updateHideAndShow();
0226 }
0227 
0228 void NewDialog::updateFileSystem(FileSystem::Type t)
0229 {
0230     partition().deleteFileSystem();
0231     partition().setFileSystem(FileSystemFactory::create(t, partition().firstSector(), partition().lastSector(), partition().sectorSize()));
0232 }
0233 
0234 void NewDialog::onFilesystemChanged(int idx)
0235 {
0236     updateFileSystem(FileSystem::typeForName(dialogWidget().comboFileSystem().itemText(idx)));
0237 
0238     setupConstraints();
0239     updateOkButtonStatus();
0240 
0241     const FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(dialogWidget().comboFileSystem().currentText()), -1, -1, -1, -1, QString());
0242     dialogWidget().m_EditLabel->setMaxLength(fs->maxLabelLength());
0243     dialogWidget().m_EditLabel->setValidator(fs->labelValidator(dialogWidget().m_EditLabel));
0244 
0245     updateSpinCapacity(partition().length());
0246     dialogWidget().partResizerWidget().update();
0247 
0248     updateHideAndShow();
0249 }
0250 
0251 void NewDialog::onLabelChanged(const QString& newLabel)
0252 {
0253     partition().fileSystem().setLabel(newLabel);
0254 }
0255 
0256 void NewDialog::slotPasswordStatusChanged()
0257 {
0258     // You may want to extend this switch with more cases,
0259     // in order to warn the user about all the possible password issues.
0260     switch (dialogWidget().editPassphrase().passwordStatus()) {
0261     case KNewPasswordWidget::WeakPassword:
0262     case KNewPasswordWidget::StrongPassword:
0263         m_IsValidPassword = true;
0264         break;
0265     default:
0266         m_IsValidPassword = false;
0267         break;
0268     }
0269     updateOkButtonStatus();
0270 }
0271 
0272 void NewDialog::updateHideAndShow()
0273 {
0274     // this is mostly copy'n'pasted from PartPropsDialog::updateHideAndShow()
0275     if (partition().roles().has(PartitionRole::Extended) ||
0276        (partition().fileSystem().supportSetLabel() == FileSystem::cmdSupportNone &&
0277         partition().fileSystem().supportCreateWithLabel() == FileSystem::cmdSupportNone) )
0278     {
0279         dialogWidget().label().setReadOnly(true);
0280         dialogWidget().noSetLabel().setVisible(true);
0281         dialogWidget().noSetLabel().setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
0282 
0283         QPalette palette = dialogWidget().noSetLabel().palette();
0284         QColor f = palette.color(QPalette::WindowText);
0285         f.setAlpha(128);
0286         palette.setColor(QPalette::WindowText, f);
0287         dialogWidget().noSetLabel().setPalette(palette);
0288         dialogWidget().checkBoxEncrypt().hide();
0289         dialogWidget().editPassphrase().hide();
0290     } else {
0291         dialogWidget().label().setReadOnly(false);
0292         dialogWidget().noSetLabel().setVisible(false);
0293     }
0294     if (FileSystemFactory::map()[FileSystem::Type::Luks]->supportCreate() && FS::luks::canEncryptType(FileSystem::typeForName(dialogWidget().comboFileSystem().currentText())) && !partition().roles().has(PartitionRole::Extended))
0295     {
0296         dialogWidget().checkBoxEncrypt().show();
0297         if (dialogWidget().checkBoxEncrypt().isChecked())
0298         {
0299             dialogWidget().editPassphrase().show();
0300             slotPasswordStatusChanged();
0301         }
0302     }
0303     else
0304     {
0305         dialogWidget().checkBoxEncrypt().hide();
0306         dialogWidget().editPassphrase().hide();
0307     }
0308 
0309 }
0310 
0311 void NewDialog::updateOkButtonStatus()
0312 {
0313     okButton->setEnabled(isValidPassword() && isValidLVName());
0314 }
0315 
0316 bool NewDialog::useUnsecuredPartition() const
0317 {
0318     return !dialogWidget().isPermissionOnlyRoot();
0319 }