summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libs/installer/abstractarchive.h4
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp6
-rw-r--r--src/libs/installer/extractarchiveoperation_p.h37
-rw-r--r--src/libs/installer/lib7z_facade.cpp3
-rw-r--r--src/libs/installer/libarchivearchive.cpp7
-rw-r--r--src/libs/installer/utils.cpp98
-rw-r--r--src/libs/installer/utils.h7
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);