diff options
author | Katja Marttila <katja.marttila@qt.io> | 2019-11-20 07:53:09 +0200 |
---|---|---|
committer | Katja Marttila <katja.marttila@qt.io> | 2019-11-20 07:53:09 +0200 |
commit | e238e764331197ec48e9b8b6c29368ee40efe82c (patch) | |
tree | d42cbdcd9b09fc7a047528ca7414bf3de5db0489 /src | |
parent | 48a43de0be59639b16962ec04f1a4e07141abaf0 (diff) | |
parent | 7bac504eb60c9783f29632c4f405eb61ca9236c5 (diff) |
Merge remote-tracking branch 'origin/3.1' into master
Change-Id: Icc077e3757b84b2367b2a7dd9c1e389f6845365b
Diffstat (limited to 'src')
20 files changed, 241 insertions, 86 deletions
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index 5a508f118..cd40d4a7b 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -1255,6 +1255,18 @@ bool Component::isAutoDependOn(const QSet<QString> &componentsToInstall) const if (autoDependOnList.isEmpty()) return false; + // If there is an essential update and autodepend on is not for essential + // update component, do not add the autodependency to an installed component as + // essential updates needs to be installed first, otherwise non-essential components + // will be installed + if (packageManagerCore()->foundEssentialUpdate()) { + const QSet<QString> autoDependOnSet = autoDependOnList.toSet(); + if (autoDependOnSet.intersects(componentsToInstall)) { + return true; + } + return false; + } + QSet<QString> components = componentsToInstall; const QStringList installedPackages = d->m_core->localInstalledPackages().keys(); foreach (const QString &name, installedPackages) diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp index 84349e4ad..917dae4c1 100644 --- a/src/libs/installer/componentselectionpage_p.cpp +++ b/src/libs/installer/componentselectionpage_p.cpp @@ -85,6 +85,7 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP m_descriptionVLayout->addWidget(m_descriptionScrollArea); m_sizeLabel = new QLabel(q); + m_sizeLabel->setMargin(5); m_sizeLabel->setWordWrap(true); m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel")); m_descriptionVLayout->addWidget(m_sizeLabel); @@ -128,8 +129,6 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All")); buttonHLayout->addWidget(m_uncheckAll); - m_treeViewVLayout->addLayout(buttonHLayout); - m_metadataProgressLabel = new QLabel(); m_metadataProgressLabel->hide(); m_treeViewVLayout->addWidget(m_metadataProgressLabel); @@ -151,9 +150,12 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP connect(q, &ComponentSelectionPage::left, m_core, &PackageManagerCore::clearComponentsToInstallCalculated); - m_mainHLayout = new QHBoxLayout(q); - m_mainHLayout->addLayout(m_treeViewVLayout, 3); - m_mainHLayout->addLayout(m_descriptionVLayout, 2); + m_mainGLayout = new QGridLayout(q); + m_mainGLayout->addLayout(buttonHLayout, 0, 1); + m_mainGLayout->addLayout(m_treeViewVLayout, 1, 1); + m_mainGLayout->addLayout(m_descriptionVLayout, 1, 2); + m_mainGLayout->setColumnStretch(1, 3); + m_mainGLayout->setColumnStretch(2, 2); #ifdef INSTALLCOMPRESSED allowCompressedRepositoryInstall(); @@ -200,6 +202,7 @@ void ComponentSelectionPagePrivate::setupCategoryLayout() return; m_categoryWidget = new QWidget(); QVBoxLayout *vLayout = new QVBoxLayout; + vLayout->setContentsMargins(0, 0, 0, 0); m_categoryWidget->setLayout(vLayout); m_categoryGroupBox = new QGroupBox(q); m_categoryGroupBox->setTitle(m_core->settings().repositoryCategoryDisplayName()); @@ -224,7 +227,7 @@ void ComponentSelectionPagePrivate::setupCategoryLayout() vLayout->addWidget(m_categoryGroupBox); vLayout->addStretch(); - m_mainHLayout->insertWidget(0, m_categoryWidget); + m_mainGLayout->addWidget(m_categoryWidget, 1, 0); } void ComponentSelectionPagePrivate::showCategoryLayout(bool show) @@ -372,19 +375,19 @@ void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show) QSpacerItem *verticalSpacer2 = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); m_treeViewVLayout->addSpacerItem(verticalSpacer2); - m_mainHLayout->removeItem(m_descriptionVLayout); + m_mainGLayout->removeItem(m_descriptionVLayout); //Hide next button during category fetch QPushButton *const b = qobject_cast<QPushButton *>(q->gui()->button(QWizard::NextButton)); b->setEnabled(!show); } else { QSpacerItem *item = m_treeViewVLayout->spacerItem(); m_treeViewVLayout->removeItem(item); - m_mainHLayout->addLayout(m_descriptionVLayout, 2); + m_mainGLayout->addLayout(m_descriptionVLayout, 1, 2); //Call completeChanged() to determine if NextButton should be shown or not after category fetch. q->completeChanged(); } if (m_categoryWidget) - m_categoryWidget->setDisabled(show); + m_categoryWidget->setVisible(!show); m_progressBar->setVisible(show); m_metadataProgressLabel->setVisible(show); @@ -392,11 +395,16 @@ void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show) m_checkDefault->setVisible(!show); m_checkAll->setVisible(!show); m_uncheckAll->setVisible(!show); - m_descriptionLabel->setVisible(!show); + m_descriptionScrollArea->setVisible(!show); m_sizeLabel->setVisible(!show); if (QAbstractButton *bspButton = q->gui()->button(QWizard::CustomButton2)) bspButton->setEnabled(!show); + + // In macOS 10.12 the widgets are not hidden if those are not updated immediately +#ifdef Q_OS_MACOS + q->repaint(); +#endif } void ComponentSelectionPagePrivate::fetchRepositoryCategories() diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h index 6169a06f5..591cd08f7 100644 --- a/src/libs/installer/componentselectionpage_p.h +++ b/src/libs/installer/componentselectionpage_p.h @@ -44,6 +44,7 @@ class QListWidgetItem; class QProgressBar; class QVBoxLayout; class QHBoxLayout; +class QGridLayout; namespace QInstaller { @@ -98,7 +99,7 @@ private: QGroupBox *m_categoryGroupBox; QLabel *m_metadataProgressLabel; QProgressBar *m_progressBar; - QHBoxLayout *m_mainHLayout; + QGridLayout *m_mainGLayout; QVBoxLayout *m_treeViewVLayout; bool m_allowCompressedRepositoryInstall; ComponentModel *m_allModel; diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp index 17e165777..e6d3b16e7 100644 --- a/src/libs/installer/createdesktopentryoperation.cpp +++ b/src/libs/installer/createdesktopentryoperation.cpp @@ -137,8 +137,7 @@ bool CreateDesktopEntryOperation::performOperation() return false; } - QFile::setPermissions(filename, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::ReadGroup - | QFile::ReadOther | QFile::ExeOwner | QFile::ExeGroup | QFile::ExeOther); + setDefaultFilePermissions(filename, DefaultFilePermissions::Executable); QTextStream stream(&file); stream.setCodec("UTF-8"); diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp index 0eabc8c35..8f9aa1ddb 100644 --- a/src/libs/installer/createlocalrepositoryoperation.cpp +++ b/src/libs/installer/createlocalrepositoryoperation.cpp @@ -82,8 +82,7 @@ static void fixPermissions(const QString &repoPath) if (!it.fileInfo().isFile()) continue; - if (!QFile::setPermissions(it.filePath(), QFile::ReadOwner | QFile::WriteOwner - | QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther)) { + if (!setDefaultFilePermissions(it.filePath(), DefaultFilePermissions::NonExecutable)) { throw Error(CreateLocalRepositoryOperation::tr("Cannot set permissions for file \"%1\".") .arg(QDir::toNativeSeparators(it.filePath()))); } diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp index 006ea3762..1f3b56d52 100644 --- a/src/libs/installer/environmentvariablesoperation.cpp +++ b/src/libs/installer/environmentvariablesoperation.cpp @@ -152,8 +152,18 @@ UpdateOperation::Error undoSetting(const QString ®Path, SettingsType registry(regPath, QSettingsWrapper::NativeFormat); actual = registry.value(name).toString(); } - if (actual != value) //key changed, don't undo - return UpdateOperation::UserDefinedError; + + if (actual != value) + { + //For unknown reason paths with @TargetDir@ variable get modified + //so that Windows file separators get replaced with unix style separators, + //fix separators before matching to actual value in register + QString tempValue = value; + QString fixedValue = tempValue.replace(QLatin1Char('/'), QLatin1Char('\\')); + + if (actual != fixedValue) //key changed, don't undo + return UpdateOperation::UserDefinedError; + } bool error = false; if (handleRegExpandSz(regPath, name, oldValue, errorString, &error)) diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 12608a0d1..e790920d1 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -28,6 +28,8 @@ #include "extractarchiveoperation_p.h" +#include "constants.h" + #include <QEventLoop> #include <QThreadPool> #include <QFileInfo> @@ -57,7 +59,6 @@ bool ExtractArchiveOperation::performOperation() Receiver receiver; Callback callback; - connect(&callback, &Callback::currentFileChanged, this, &ExtractArchiveOperation::fileFinished); connect(&callback, &Callback::progressChanged, this, &ExtractArchiveOperation::progressChanged); if (PackageManagerCore *core = packageManager()) { @@ -68,8 +69,6 @@ bool ExtractArchiveOperation::performOperation() connect(runnable, &Runnable::finished, &receiver, &Receiver::runnableFinished, Qt::QueuedConnection); - m_files.clear(); - QFileInfo fileInfo(archivePath); emit outputTextChanged(tr("Extracting \"%1\"").arg(fileInfo.fileName())); @@ -83,7 +82,46 @@ bool ExtractArchiveOperation::performOperation() receiver.runnableFinished(true, QString()); } - setValue(QLatin1String("files"), m_files); + // Write all file names which belongs to a package to a separate file and only the separate + // filename to a .dat file. There can be enormous amount of files in a package, which makes + // the dat file very slow to read and write. The .dat file is read into memory in startup, + // writing the file names to a separate file we don't need to load all the file names into + // memory as we need those only in uninstall. This will save a lot of memory. + // Parse a file and directorory structure using archivepath syntax + // installer://<component_name>/<filename>.7z Resulting structure is: + // -installerResources (dir) + // -<component_name> (dir) + // -<filename>.txt (file) + + QStringList files = callback.extractedFiles(); + + QString fileDirectory = targetDir + QLatin1String("/installerResources/") + + archivePath.section(QLatin1Char('/'), 1, 1, QString::SectionSkipEmpty) + QLatin1Char('/'); + QString archiveFileName = archivePath.section(QLatin1Char('/'), 2, 2, QString::SectionSkipEmpty); + QFileInfo fileInfo2(archiveFileName); + QString suffix = fileInfo2.suffix(); + archiveFileName.chop(suffix.length() + 1); // removes suffix (e.g. '.7z') from archive filename + QString fileName = archiveFileName + QLatin1String(".txt"); + + QFileInfo targetDirectoryInfo(fileDirectory); + + QDir dir(targetDirectoryInfo.absolutePath()); + if (!dir.exists()) { + dir.mkpath(targetDirectoryInfo.absolutePath()); + } + QFile file(targetDirectoryInfo.absolutePath() + QLatin1Char('/') + fileName); + if (file.open(QIODevice::WriteOnly)) { + setDefaultFilePermissions(file.fileName(), DefaultFilePermissions::NonExecutable); + QDataStream out (&file); + for (int i = 0; i < files.count(); ++i) { + files[i] = replacePath(files.at(i), targetDir, QLatin1String(scRelocatable)); + } + out << files; + setValue(QLatin1String("files"), file.fileName()); + file.close(); + } else { + qWarning() << "Cannot open file for writing " << file.fileName() << ":" << file.errorString(); + } // TODO: Use backups for rollback, too? Doesn't work for uninstallation though. @@ -102,8 +140,46 @@ bool ExtractArchiveOperation::performOperation() bool ExtractArchiveOperation::undoOperation() { Q_ASSERT(arguments().count() == 2); - const QStringList files = value(QLatin1String("files")).toStringList(); + // For backward compatibility, check if "files" can be converted to QStringList. + // If yes, files are listed in .dat instead of in a separate file. + bool useStringListType(value(QLatin1String("files")).type() == QVariant::StringList); + QStringList files; + if (useStringListType) { + files = value(QLatin1String("files")).toStringList(); + startUndoProcess(files); + } else { + const QString filePath = value(QLatin1String("files")).toString(); + QString targetDir = arguments().at(1); + // Does not change target on non macOS platforms. + if (QInstaller::isInBundle(targetDir, &targetDir)) + targetDir = QDir::cleanPath(targetDir + QLatin1String("/..")); + QString fileName = replacePath(filePath, QLatin1String(scRelocatable), targetDir); + QFile file(fileName); + + if (file.open(QIODevice::ReadOnly)) { + QDataStream in(&file); + in >> files; + for (int i = 0; i < files.count(); ++i) + files[i] = replacePath(files.at(i), QLatin1String(scRelocatable), targetDir); + startUndoProcess(files); + QFileInfo fileInfo(file); + file.remove(); + QDir directory(fileInfo.absoluteDir()); + if (directory.exists() && directory.isEmpty()) + directory.rmdir(directory.path()); + } else { + setError(UserDefinedError); + setErrorString(tr("Cannot open file \"%1\" for reading: %2") + .arg(QDir::toNativeSeparators(file.fileName())).arg(file.errorString())); + return false; + } + } + return true; +} + +void ExtractArchiveOperation::startUndoProcess(const QStringList &files) +{ WorkerThread *const thread = new WorkerThread(this, files); connect(thread, &WorkerThread::currentFileChanged, this, &ExtractArchiveOperation::outputTextChanged); @@ -115,7 +191,6 @@ bool ExtractArchiveOperation::undoOperation() thread->start(); loop.exec(); thread->deleteLater(); - return true; } bool ExtractArchiveOperation::testOperation() @@ -123,13 +198,4 @@ bool ExtractArchiveOperation::testOperation() return true; } -/*! - This slot is direct connected to the caller so please don't call it from another thread in the - same time. -*/ -void ExtractArchiveOperation::fileFinished(const QString &filename) -{ - m_files.prepend(filename); -} - } // namespace QInstaller diff --git a/src/libs/installer/extractarchiveoperation.h b/src/libs/installer/extractarchiveoperation.h index 3e75a9bb9..e706159ed 100644 --- a/src/libs/installer/extractarchiveoperation.h +++ b/src/libs/installer/extractarchiveoperation.h @@ -52,11 +52,10 @@ Q_SIGNALS: void outputTextChanged(const QString &progress); void progressChanged(double); -private Q_SLOTS: - void fileFinished(const QString &progress); +private: + void startUndoProcess(const QStringList &files); private: - QStringList m_files; class Callback; class Runnable; class Receiver; diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h index f333da366..9cc07246f 100644 --- a/src/libs/installer/extractarchiveoperation_p.h +++ b/src/libs/installer/extractarchiveoperation_p.h @@ -97,6 +97,10 @@ public: return m_backupFiles; } + QStringList extractedFiles() const { + return m_extractedFiles; + } + public slots: void statusChanged(QInstaller::PackageManagerCore::Status status) { @@ -113,13 +117,12 @@ public slots: } signals: - void currentFileChanged(const QString &filename); void progressChanged(double progress); private: void setCurrentFile(const QString &filename) Q_DECL_OVERRIDE { - emit currentFileChanged(QDir::toNativeSeparators(filename)); + m_extractedFiles.prepend(QDir::toNativeSeparators(filename)); } static QString generateBackupName(const QString &fn) @@ -157,6 +160,7 @@ private: private: HRESULT m_state = S_OK; BackupFiles m_backupFiles; + QStringList m_extractedFiles; }; class ExtractArchiveOperation::Runnable : public QObject, public QRunnable diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp index 18c5f90fd..b11768494 100644 --- a/src/libs/installer/fileutils.cpp +++ b/src/libs/installer/fileutils.cpp @@ -293,6 +293,39 @@ void QInstaller::removeSystemGeneratedFiles(const QString &path) #endif } +/*! + Sets permissions of file or directory specified by \a fileName to \c 644 or \c 755 + based by the value of \a permissions. +*/ +bool QInstaller::setDefaultFilePermissions(const QString &fileName, DefaultFilePermissions permissions) +{ + QFile file(fileName); + return setDefaultFilePermissions(&file, permissions); +} + +/*! + Sets permissions of file or directory specified by \a file to \c 644 or \c 755 + based by the value of \a permissions. This is effective only on Unix platforms + as \c setPermissions() does not manipulate ACLs. On Windows NTFS volumes this + only unsets the legacy read-only flag regardless of the value of \a permissions. +*/ +bool QInstaller::setDefaultFilePermissions(QFile *file, DefaultFilePermissions permissions) +{ + if (!file->exists()) { + qWarning() << "Target" << file->fileName() << "does not exists."; + return false; + } + if (file->permissions() == static_cast<QFileDevice::Permission>(permissions)) + return true; + + if (!file->setPermissions(static_cast<QFileDevice::Permission>(permissions))) { + qWarning() << "Cannot set default permissions for target" + << file->fileName() << ":" << file->errorString(); + return false; + } + return true; +} + void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir) { Q_ASSERT(QFileInfo(sourceDir).isDir()); diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h index ae08f5ff3..3607a189b 100644 --- a/src/libs/installer/fileutils.h +++ b/src/libs/installer/fileutils.h @@ -36,10 +36,17 @@ QT_BEGIN_NAMESPACE class QFileInfo; +class QFile; class QUrl; QT_END_NAMESPACE namespace QInstaller { + +enum DefaultFilePermissions { + NonExecutable = 0x6644, + Executable = 0x7755 +}; + class INSTALLER_EXPORT TempDirDeleter { public: @@ -80,6 +87,9 @@ private: void INSTALLER_EXPORT removeDirectoryThreaded(const QString &path, bool ignoreErrors = false); void INSTALLER_EXPORT removeSystemGeneratedFiles(const QString &path); + bool INSTALLER_EXPORT setDefaultFilePermissions(const QString &fileName, DefaultFilePermissions permissions); + bool INSTALLER_EXPORT setDefaultFilePermissions(QFile *file, DefaultFilePermissions permissions); + QString INSTALLER_EXPORT generateTemporaryFileName(const QString &templ=QString()); void INSTALLER_EXPORT moveDirectoryContents(const QString &sourceDir, const QString &targetDir); diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index db1f88563..a35cb99fb 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -158,6 +158,14 @@ bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &co bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version) { QSet<QString> allDependencies = component->dependencies().toSet(); + // All parents are kind of dependencies as well. If we select sub item in treeview, parent gets + // checked or partially checked as well. When adding component as dependency in script or + // config.xml we need to add the parent as dependency so the behavior is the same as in visual UI. + if (component->parentComponent() && + !allDependencies.contains(component->parentComponent()->name()) && + !m_visitedComponents.contains(component->parentComponent())) { + allDependencies.insert(component->parentComponent()->name()); + } QString requiredDependencyVersion = version; foreach (const QString &dependencyComponentName, allDependencies) { // PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp index 62ba06cb2..0c79ec4c9 100644 --- a/src/libs/installer/link.cpp +++ b/src/libs/installer/link.cpp @@ -146,9 +146,12 @@ Link createJunction(const QString &linkPath, const QString &targetPath) return Link(linkPath); } - const QString szDestDir = QString::fromLatin1("\\??\\").arg(targetPath).replace(QLatin1Char('/'), + const QString szDestDir = QString::fromLatin1("\\??\\%1").arg(targetPath).replace(QLatin1Char('/'), QLatin1Char('\\')); + // Get string length in bytes, not in characters count + const size_t destDirBytes = szDestDir.size() * sizeof(ushort); + // Allocates a block of memory for an array of num elements(1) and initializes all its bits to zero. REPARSE_DATA_BUFFER* reparseStructData = (REPARSE_DATA_BUFFER*)calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); @@ -156,11 +159,11 @@ Link createJunction(const QString &linkPath, const QString &targetPath) reparseStructData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; reparseStructData->MountPointReparseBuffer.PrintNameLength = 0; reparseStructData->MountPointReparseBuffer.SubstituteNameOffset = 0; - reparseStructData->MountPointReparseBuffer.SubstituteNameLength = szDestDir.length(); - reparseStructData->MountPointReparseBuffer.PrintNameOffset = szDestDir.length() + sizeof(WCHAR); + reparseStructData->MountPointReparseBuffer.SubstituteNameLength = destDirBytes; + reparseStructData->MountPointReparseBuffer.PrintNameOffset = destDirBytes + sizeof(WCHAR); uint spaceAfterGeneralData = sizeof(USHORT) * 5 + sizeof(WCHAR); //should be 12 - reparseStructData->ReparseDataLength = szDestDir.length() + spaceAfterGeneralData; + reparseStructData->ReparseDataLength = destDirBytes + spaceAfterGeneralData; #ifndef Q_CC_MINGW StringCchCopy(reparseStructData->MountPointReparseBuffer.PathBuffer, 1024, (wchar_t*)szDestDir.utf16()); diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 4ae994a6d..8d4e3ddc4 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -1586,16 +1586,15 @@ Component *PackageManagerCore::componentByName(const QString &name, const QList< return nullptr; } +/*! + Returns \c true if directory specified by \a path is writable by + the current user. +*/ bool PackageManagerCore::directoryWritable(const QString &path) const { return d->directoryWritable(path); } -bool PackageManagerCore::subdirectoriesWritable(const QString &path) const -{ - return d->subdirectoriesWritable(path); -} - /*! Returns a list of components that are marked for installation. The list can be empty. diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index 45bb27286..cba1592fc 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -122,7 +122,6 @@ public: static Component *componentByName(const QString &name, const QList<Component *> &components); bool directoryWritable(const QString &path) const; - bool subdirectoriesWritable(const QString &path) const; bool fetchLocalPackagesTree(); LocalPackagesHash localInstalledPackages(); diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 63b39ed76..f990a1907 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -215,6 +215,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_updaterModel(nullptr) , m_guiObject(nullptr) , m_remoteFileEngineHandler(nullptr) + , m_foundEssentialUpdate(false) { } @@ -245,6 +246,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_updaterModel(nullptr) , m_guiObject(nullptr) , m_remoteFileEngineHandler(new RemoteFileEngineHandler) + , m_foundEssentialUpdate(false) { foreach (const OperationBlob &operation, performedOperations) { QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() @@ -347,23 +349,8 @@ QString PackageManagerCorePrivate::targetDir() const bool PackageManagerCorePrivate::directoryWritable(const QString &path) const { - QTemporaryFile tempFile(path + QStringLiteral("/tempFile") + QString::number(qrand() % 1000)); - if (!tempFile.open() || !tempFile.isWritable()) - return false; - else - return true; -} - -bool PackageManagerCorePrivate::subdirectoriesWritable(const QString &path) const -{ - // Iterate over target directory subdirectories for writing access - QDirIterator iterator(path, QDir::AllDirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - while (iterator.hasNext()) { - QTemporaryFile tempFile(iterator.next() + QLatin1String("/tempFile")); - if (!tempFile.open() || !tempFile.isWritable()) - return false; - } - return true; + QTemporaryFile tempFile(path + QLatin1String("/tempFile.XXXXXX")); + return (tempFile.open() && tempFile.isWritable()); } QString PackageManagerCorePrivate::configurationFileName() const @@ -805,6 +792,7 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() : tr("Format error"); throw Error(tr("Cannot write installer configuration to %1: %2").arg(iniPath, reason)); } + setDefaultFilePermissions(iniPath, DefaultFilePermissions::NonExecutable); QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml")); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { @@ -842,6 +830,7 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() writer.writeEndElement(); writer.writeEndElement(); } + setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable); } void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir) @@ -1073,8 +1062,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q throw Error(tr("Cannot write maintenance tool data to %1: %2").arg(dataOut.fileName(), dataOut.errorString())); } - dataOut.setPermissions(dataOut.permissions() | QFile::WriteUser | QFile::ReadGroup - | QFile::ReadOther); + setDefaultFilePermissions(&dataOut, DefaultFilePermissions::NonExecutable); } { @@ -1091,12 +1079,10 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q } QFile mt(maintenanceToolRenamedName); - if (mt.setPermissions(out.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther - | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser)) { + if (setDefaultFilePermissions(&mt, DefaultFilePermissions::Executable)) qDebug() << "Wrote permissions for maintenance tool."; - } else { + else qDebug() << "Failed to write permissions for maintenance tool."; - } if (out.exists() && !out.remove()) { qWarning() << tr("Cannot remove temporary data file \"%1\": %2") @@ -1386,8 +1372,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper throw Error(tr("Cannot write maintenance tool binary data to %1: %2") .arg(file.fileName(), file.errorString())); } - file.setPermissions(file.permissions() | QFile::WriteUser | QFile::ReadGroup - | QFile::ReadOther); + setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable); } catch (const Error &/*error*/) { if (!newBinaryWritten) { newBinaryWritten = true; @@ -1494,6 +1479,8 @@ bool PackageManagerCorePrivate::runInstaller() if (!performOperationThreaded(mkdirOp)) throw Error(mkdirOp->errorString()); } + setDefaultFilePermissions(target, DefaultFilePermissions::Executable); + const QString remove = m_core->value(scRemoveTargetDir); if (QVariant(remove).toBool()) addPerformed(takeOwnedOperation(mkdirOp)); @@ -1643,16 +1630,10 @@ bool PackageManagerCorePrivate::runPackageUpdater() //to have some progress for the cleanup/write component.xml step ProgressCoordinator::instance()->addReservePercentagePoints(1); -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - // check if we need admin rights and ask before the action happens - // on Linux and macOS also check target directory subdirectories - if (!directoryWritable(targetDir()) || !subdirectoriesWritable(targetDir())) - adminRightsGained = m_core->gainAdminRights(); -#else // check if we need admin rights and ask before the action happens if (!directoryWritable(targetDir())) adminRightsGained = m_core->gainAdminRights(); -#endif + const QList<Component *> componentsToInstall = m_core->orderedComponentsToInstall(); qDebug() << "Install size:" << componentsToInstall.size() << "components"; diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index ace941ced..814ed333a 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -93,7 +93,6 @@ public: QString registerPath(); bool directoryWritable(const QString &path) const; - bool subdirectoriesWritable(const QString &path) const; QString maintenanceToolName() const; QString installerBinaryPath() const; diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp index e4e3212ac..dae5f8d34 100644 --- a/src/libs/installer/utils.cpp +++ b/src/libs/installer/utils.cpp @@ -28,6 +28,8 @@ #include "utils.h" +#include "fileutils.h" + #include <QCoreApplication> #include <QDateTime> #include <QDir> @@ -277,6 +279,7 @@ bool QInstaller::PlainVerboseWriterOutput::write(const QString &fileName, QIODev QFile output(fileName); if (output.open(openMode)) { output.write(data); + setDefaultFilePermissions(&output, DefaultFilePermissions::NonExecutable); return true; } return false; diff --git a/src/libs/kdtools/localpackagehub.cpp b/src/libs/kdtools/localpackagehub.cpp index 11ff36a88..14e6b5738 100644 --- a/src/libs/kdtools/localpackagehub.cpp +++ b/src/libs/kdtools/localpackagehub.cpp @@ -28,6 +28,7 @@ ****************************************************************************/ #include "localpackagehub.h" +#include "fileutils.h" #include "globals.h" #include "constants.h" @@ -431,6 +432,11 @@ void LocalPackageHub::writeToDisk() file.write(doc.toByteArray(4)); file.close(); + + // Write permissions for installation information file + QInstaller::setDefaultFilePermissions( + &file, DefaultFilePermissions::NonExecutable); + d->modified = false; } } diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp index 1cf5765e1..8ae6861a5 100644 --- a/src/sdk/installerbase.cpp +++ b/src/sdk/installerbase.cpp @@ -95,6 +95,22 @@ int InstallerBase::run() } } + QFile binary(binaryFile()); + +#ifdef Q_OS_WIN + // On some admin user installations it is possible that the installer.dat + // file is left without reading permissions for non-administrator users, + // we should check this and prompt the user to run the executable as admin if needed. + if (!binary.open(QIODevice::ReadOnly)) { + QFileInfo binaryInfo(binary.fileName()); + QInstaller::MessageBoxHandler::information(nullptr, QLatin1String("NoReadingPermissions"), + tr("Cannot open file \"%1\" for reading").arg(binaryInfo.fileName()), + tr("Please make sure that the current user has reading access " + "to file \"%1\" or try running %2 as an administrator.").arg(binaryInfo.fileName(), qAppName())); + return EXIT_FAILURE; + } + binary.close(); +#endif QString fileName = datFile(binaryFile()); quint64 cookie = QInstaller::BinaryContent::MagicCookieDat; if (fileName.isEmpty()) { @@ -102,7 +118,7 @@ int InstallerBase::run() cookie = QInstaller::BinaryContent::MagicCookie; } - QFile binary(fileName); + binary.setFileName(fileName); QInstaller::openForRead(&binary); qint64 magicMarker; @@ -177,11 +193,11 @@ int InstallerBase::run() // From Qt5.8 onwards a separate command line option --proxy is not needed as system // proxy is used by default. If Qt is built with QT_USE_SYSTEM_PROXIES false // then system proxies are not used by default. - if ((parser.isSet(QLatin1String(CommandLineOptions::Proxy)) -#if QT_VERSION > 0x050800 - || QNetworkProxyFactory::usesSystemConfiguration() -#endif - ) && !parser.isSet(QLatin1String(CommandLineOptions::NoProxy))) { + if (parser.isSet(QLatin1String(CommandLineOptions::NoProxy))) { + m_core->settings().setProxyType(QInstaller::Settings::NoProxy); + KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); + } else if ((parser.isSet(QLatin1String(CommandLineOptions::Proxy)) + || QNetworkProxyFactory::usesSystemConfiguration())) { m_core->settings().setProxyType(QInstaller::Settings::SystemProxy); KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); } |