diff options
-rw-r--r-- | src/libs/installer/abstractarchive.h | 4 | ||||
-rw-r--r-- | src/libs/installer/extractarchiveoperation.cpp | 6 | ||||
-rw-r--r-- | src/libs/installer/extractarchiveoperation_p.h | 37 | ||||
-rw-r--r-- | src/libs/installer/lib7z_facade.cpp | 3 | ||||
-rw-r--r-- | src/libs/installer/libarchivearchive.cpp | 7 | ||||
-rw-r--r-- | src/libs/installer/utils.cpp | 98 | ||||
-rw-r--r-- | src/libs/installer/utils.h | 7 |
7 files changed, 155 insertions, 7 deletions
diff --git a/src/libs/installer/abstractarchive.h b/src/libs/installer/abstractarchive.h index 55489eb14..a41d7e795 100644 --- a/src/libs/installer/abstractarchive.h +++ b/src/libs/installer/abstractarchive.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -46,6 +46,7 @@ struct INSTALLER_EXPORT ArchiveEntry { ArchiveEntry() : isDirectory(false) + , isSymbolicLink(false) , compressedSize(0) , uncompressedSize(0) , permissions_mode(0) @@ -55,6 +56,7 @@ struct INSTALLER_EXPORT ArchiveEntry QDateTime utcTime; QPoint archiveIndex; bool isDirectory; + bool isSymbolicLink; quint64 compressedSize; quint64 uncompressedSize; mode_t permissions_mode; diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 7660dadb0..19db53472 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -92,8 +92,10 @@ bool ExtractArchiveOperation::performOperation() connect(worker, &Worker::finished, &receiver, &Receiver::workerFinished, Qt::QueuedConnection); - if (PackageManagerCore *core = packageManager()) + if (PackageManagerCore *core = packageManager()) { connect(core, &PackageManagerCore::statusChanged, worker, &Worker::onStatusChanged); + worker->setPackageManagerCore(core); + } QFileInfo fileInfo(archivePath); emit outputTextChanged(tr("Extracting \"%1\"").arg(fileInfo.fileName())); diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h index c674da78e..87c126a81 100644 --- a/src/libs/installer/extractarchiveoperation_p.h +++ b/src/libs/installer/extractarchiveoperation_p.h @@ -33,6 +33,10 @@ #include "fileutils.h" #include "archivefactory.h" #include "packagemanagercore.h" +#include "remoteclient.h" +#include "adminauthorization.h" +#include "utils.h" +#include "errors.h" #include <QRunnable> #include <QThread> @@ -158,8 +162,14 @@ public: , m_targetDir(targetDir) , m_canceled(false) , m_callback(callback) + , m_core(nullptr) {} + void setPackageManagerCore(PackageManagerCore *core) + { + m_core = core; + } + Q_SIGNALS: void finished(bool success, const QString &errorString); @@ -188,23 +198,49 @@ public Q_SLOTS: m_archive->errorString())); return; } + const bool hasAdminRights = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive()); + const bool canCreateSymLinks = QInstaller::canCreateSymbolicLinks(); + bool needsAdminRights = false; + for (auto &entry : entries) { QString completeFilePath = m_targetDir + QDir::separator() + entry.path; if (!entry.isDirectory && !m_callback->prepareForFile(completeFilePath)) { emit finished(false, tr("Cannot prepare for file \"%1\"").arg(completeFilePath)); return; } + if (!hasAdminRights && !canCreateSymLinks && entry.isSymbolicLink) + needsAdminRights = true; } if (m_canceled) { // For large archives the reading takes some time, and the user might have // canceled before we start the actual extracting. emit finished(false, tr("Extract for archive \"%1\" canceled.").arg(m_archivePath)); + return; + } + + bool gainedAdminRights = false; + if (needsAdminRights && m_core) { + // This must be invoked in the main thread. + QMetaObject::invokeMethod(m_core, [&] { + try { + return m_core->gainAdminRights(); + } catch (const QInstaller::Error &) { + return false; + } + }, Qt::BlockingQueuedConnection, &gainedAdminRights); + } + if (needsAdminRights && !gainedAdminRights) { + emit finished(false, tr("Could not request administrator privileges required to extract " + "archive \"%1\".").arg(m_archivePath)); } else if (!m_archive->extract(m_targetDir, entries.size())) { emit finished(false, tr("Error while extracting archive \"%1\": %2").arg(m_archivePath, m_archive->errorString())); } else { emit finished(true, QString()); } + + if (gainedAdminRights) + m_core->dropAdminRights(); } void onStatusChanged(PackageManagerCore::Status status) @@ -232,6 +268,7 @@ private: QScopedPointer<AbstractArchive> m_archive; bool m_canceled; Callback *m_callback; + PackageManagerCore *m_core; }; class ExtractArchiveOperation::Receiver : public QObject diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp index 6d1b6a57d..fc8c6c334 100644 --- a/src/libs/installer/lib7z_facade.cpp +++ b/src/libs/installer/lib7z_facade.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -538,6 +538,7 @@ QVector<File> listArchive(QFileDevice *archive) f.archiveIndex.setY(item); f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); Archive_IsItem_Folder(arch, item, f.isDirectory); + Archive_GetItemBoolProp(arch, item, kpidSymLink, f.isSymbolicLink); f.permissions_enum = getPermissions(arch, item, nullptr); getDateTimeProperty(arch, item, kpidMTime, &(f.utcTime)); f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0); diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp index 3ada73dd2..4493e7a8f 100644 --- a/src/libs/installer/libarchivearchive.cpp +++ b/src/libs/installer/libarchivearchive.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -39,6 +39,10 @@ #include <QDir> #include <QTimer> +#if defined(Q_OS_WIN) && !defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) +#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +#endif + namespace QInstaller { /*! @@ -588,6 +592,7 @@ QVector<ArchiveEntry> LibArchiveArchive::list() archiveEntry.path = QLatin1String(archive_entry_pathname(entry)); archiveEntry.utcTime = QDateTime::fromTime_t(archive_entry_mtime(entry)); archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR); + archiveEntry.isSymbolicLink = (archive_entry_filetype(entry) == AE_IFLNK); archiveEntry.uncompressedSize = archive_entry_size(entry); archiveEntry.permissions_mode = archive_entry_perm(entry); diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp index 7506a13fe..5294d00f6 100644 --- a/src/libs/installer/utils.cpp +++ b/src/libs/installer/utils.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -29,6 +29,7 @@ #include "utils.h" #include "fileutils.h" +#include "qsettingswrapper.h" #include <QCoreApplication> #include <QDateTime> @@ -323,6 +324,21 @@ QStringList QInstaller::parseCommandLineArgs(int argc, char **argv) } #endif +/*! + On Windows checks if the user account has the privilege required to create a symbolic links. + Returns \c true if the privilege is held, \c false otherwise. + + On Unix platforms always returns \c true. +*/ +bool QInstaller::canCreateSymbolicLinks() +{ +#ifdef Q_OS_WIN + return ((setPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME, true) + && checkPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME)) || developerModeEnabled()); +#endif + return true; +} + #ifdef Q_OS_WIN // taken from qprocess_win.cpp static QString qt_create_commandline(const QString &program, const QStringList &arguments) @@ -401,4 +417,84 @@ QString QInstaller::windowsErrorString(int errorCode) return ret; } +/*! + \internal + + Sets the enabled state of \a privilege to \a enable for this process. + The privilege must be held by the current login user. Returns \c true + on success, \c false on failure. +*/ +bool QInstaller::setPrivilege(const wchar_t *privilege, bool enable) +{ + LUID luid; + TOKEN_PRIVILEGES privileges; + HANDLE token; + HANDLE process = GetCurrentProcess(); + + if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &token)) + return false; + + if (!LookupPrivilegeValue(nullptr, privilege, &luid)) + return false; + + privileges.PrivilegeCount = 1; + privileges.Privileges[0].Luid = luid; + if (enable) + privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + else + privileges.Privileges[0].Attributes = 0; + + if (!AdjustTokenPrivileges(token, FALSE, &privileges, + sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) { + return false; + } + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) + return false; + + return true; +} + +/*! + \internal + + Returns \c true if the specified \a privilege is enabled for the client + process, \c false otherwise. +*/ +bool QInstaller::checkPrivilege(const wchar_t *privilege) +{ + LUID luid; + PRIVILEGE_SET privileges; + HANDLE token; + HANDLE process = GetCurrentProcess(); + + if (!OpenProcessToken(process, TOKEN_QUERY, &token)) + return false; + + if (!LookupPrivilegeValue(nullptr, privilege, &luid)) + return false; + + privileges.PrivilegeCount = 1; + privileges.Control = PRIVILEGE_SET_ALL_NECESSARY; + privileges.Privilege[0].Luid = luid; + privileges.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; + + BOOL result; + PrivilegeCheck(token, &privileges, &result); + + return result; +} + +/*! + \internal + + Returns \c true if the 'Developer mode' is enabled on system. +*/ +bool QInstaller::developerModeEnabled() +{ + QSettingsWrapper appModelUnlock(QLatin1String("HKLM\\SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\AppModelUnlock"), QSettingsWrapper::NativeFormat); + + return appModelUnlock.value(QLatin1String("AllowDevelopmentWithoutDevLicense"), false).toBool(); +} + #endif diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h index a22acd879..2bf997835 100644 --- a/src/libs/installer/utils.h +++ b/src/libs/installer/utils.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -57,9 +57,14 @@ namespace QInstaller { QString INSTALLER_EXPORT replaceWindowsEnvironmentVariables(const QString &str); QStringList INSTALLER_EXPORT parseCommandLineArgs(int argc, char **argv); + bool INSTALLER_EXPORT canCreateSymbolicLinks(); + #ifdef Q_OS_WIN QString windowsErrorString(int errorCode); QString createCommandline(const QString &program, const QStringList &arguments); + bool setPrivilege(const wchar_t *privilege, bool enable); + bool checkPrivilege(const wchar_t *privilege); + bool developerModeEnabled(); #endif QStringList INSTALLER_EXPORT localeCandidates(const QString &locale); |