summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArttu Tarkiainen <arttu.tarkiainen@qt.io>2019-07-10 17:06:00 +0300
committerArttu Tarkiainen <arttu.tarkiainen@qt.io>2019-08-22 11:49:19 +0000
commitb3eaeb178299c4c847d38969f095ac1ce3c0545f (patch)
tree934b062604a45ddd90ed436f9b3cae41713fb113
parent8efb76dc0a9121732b924c10de70b97185cf140d (diff)
Enable support for Qt 5.12 in installer framework
Workaround issues of IFW not being able to work on elevated mode caused by changes in qtbase, namely when initializing socket connection between remote installer client and server, and writing maintenance tool binary. Switch to using unbuffered mode for QFSFileEngine instances as buffered mode support has been dropped. Fix calls to QFile::copy() when running elevated installer process. Make minor modifications for unit tests to pass. Explicitly fail and return when performing CreateLocalRepositoryOperation on non-owned directory. Task-number: QTIFW-1312 Change-Id: I3db72547ee95c87d8c02d27e5b31c7b30e793431 Reviewed-by: Katja Marttila <katja.marttila@qt.io>
-rw-r--r--src/libs/installer/binaryformat.cpp2
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp18
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp17
-rw-r--r--src/libs/installer/remoteclient.cpp24
-rw-r--r--src/libs/installer/remoteclient.h1
-rw-r--r--src/libs/installer/remotefileengine.cpp4
-rw-r--r--src/libs/installer/remoteserver.cpp22
-rw-r--r--src/libs/installer/remoteserver.h1
-rw-r--r--src/libs/installer/remoteserverconnection.cpp7
-rw-r--r--tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp9
-rw-r--r--tests/auto/installer/scriptengine/tst_scriptengine.cpp4
11 files changed, 96 insertions, 13 deletions
diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp
index 9a46095ce..3e7dd5a2a 100644
--- a/src/libs/installer/binaryformat.cpp
+++ b/src/libs/installer/binaryformat.cpp
@@ -158,7 +158,7 @@ bool Resource::open()
if (isOpen())
return false;
- if (!m_file.open(QIODevice::ReadOnly)) {
+ if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
setErrorString(m_file.errorString());
return false;
}
diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp
index a2f7806a3..0eabc8c35 100644
--- a/src/libs/installer/createlocalrepositoryoperation.cpp
+++ b/src/libs/installer/createlocalrepositoryoperation.cpp
@@ -38,6 +38,7 @@
#include "lib7z_facade.h"
#include "packagemanagercore.h"
#include "productkeycheck.h"
+#include "constants.h"
#include "updateoperations.h"
@@ -183,6 +184,23 @@ bool CreateLocalRepositoryOperation::performOperation()
}
setValue(QLatin1String("createddir"), mkDirOp.value(QLatin1String("createddir")));
+#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
+ // Internal changes to QTemporaryFile break copying Qt resources through
+ // QInstaller::RemoteFileEngine. We do not register resources to be handled by
+ // RemoteFileEngine, instead copying using 5.9 succeeded because QFile::copy()
+ // creates a QTemporaryFile object internally that is handled by the remote engine.
+ //
+ // This will not work with Qt 5.10 and above as QTemporaryFile introduced a new
+ // rename() implementation that explicitly uses its own QTemporaryFileEngine.
+ //
+ // Fail and return early if we are working on an elevated permission directory.
+ if (core && !core->directoryWritable(repoPath)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Creating local repository into elevated permissions "
+ "directory: %1 is not supported.").arg(repoPath));
+ return false;
+ }
+#endif
// copy the whole meta data into local repository
CopyDirectoryOperation copyDirOp(core);
copyDirOp.setArguments(QStringList() << QLatin1String(":/metadata/") << repoPath);
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 653869898..308bfa097 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -1034,7 +1034,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
qDebug() << "Writing maintenance tool:" << maintenanceToolRenamedName;
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Writing maintenance tool."));
- QTemporaryFile out;
+ QFile out(generateTemporaryFileName());
QInstaller::openForWrite(&out); // throws an exception in case of error
if (!input->seek(0))
@@ -1052,7 +1052,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
#endif
// It's a bit odd to have only the magic in the data file, but this simplifies
// other code a lot (since installers don't have any appended data either)
- QTemporaryFile dataOut;
+ QFile dataOut(generateTemporaryFileName());
QInstaller::openForWrite(&dataOut);
QInstaller::appendInt64(&dataOut, 0); // operations start
QInstaller::appendInt64(&dataOut, 0); // operations end
@@ -1070,10 +1070,9 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
}
if (!dataOut.rename(resourcePath.filePath(QLatin1String("installer.dat")))) {
- throw Error(tr("Cannot write maintenance tool data to %1: %2").arg(out.fileName(),
- out.errorString()));
+ throw Error(tr("Cannot write maintenance tool data to %1: %2").arg(dataOut.fileName(),
+ dataOut.errorString()));
}
- dataOut.setAutoRemove(false);
dataOut.setPermissions(dataOut.permissions() | QFile::WriteUser | QFile::ReadGroup
| QFile::ReadOther);
}
@@ -1098,6 +1097,11 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
} else {
qDebug() << "Failed to write permissions for maintenance tool.";
}
+
+ if (out.exists() && !out.remove()) {
+ qWarning() << tr("Cannot remove temporary data file \"%1\": %2")
+ .arg(out.fileName(), out.errorString());
+ }
}
void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *output, QFile *const input,
@@ -1367,7 +1371,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
m_core->setValue(QLatin1String("installedOperationAreSorted"), QLatin1String("true"));
try {
- QTemporaryFile file;
+ QFile file(generateTemporaryFileName());
QInstaller::openForWrite(&file);
writeMaintenanceToolBinaryData(&file, &input, performedOperations, layout);
QInstaller::appendInt64(&file, BinaryContent::MagicCookieDat);
@@ -1382,7 +1386,6 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
throw Error(tr("Cannot write maintenance tool binary data to %1: %2")
.arg(file.fileName(), file.errorString()));
}
- file.setAutoRemove(false);
file.setPermissions(file.permissions() | QFile::WriteUser | QFile::ReadGroup
| QFile::ReadOther);
} catch (const Error &/*error*/) {
diff --git a/src/libs/installer/remoteclient.cpp b/src/libs/installer/remoteclient.cpp
index e208620cb..ad1e5ecf6 100644
--- a/src/libs/installer/remoteclient.cpp
+++ b/src/libs/installer/remoteclient.cpp
@@ -29,6 +29,8 @@
#include "remoteclient.h"
#include "remoteclient_p.h"
+#include <QDir>
+
namespace QInstaller {
RemoteClient *RemoteClient::s_instance = nullptr;
@@ -61,6 +63,18 @@ QString RemoteClient::authorizationKey() const
return d->m_key;
}
+QString RemoteClient::socketPathName(const QString &socketName) const
+{
+ QString socketPathName;
+ if (socketName.startsWith(QLatin1Char('/'))) {
+ socketPathName = socketName;
+ } else {
+ socketPathName = QDir::tempPath();
+ socketPathName += QLatin1Char('/') + socketName;
+ }
+ return socketPathName;
+}
+
/*!
Initializes the client with \a socketName, with the \a key the client
sends to authenticate with the server, \a mode and \a startAs.
@@ -69,7 +83,17 @@ void RemoteClient::init(const QString &socketName, const QString &key, Protocol:
Protocol::StartAs startAs)
{
Q_D(RemoteClient);
+
+ // Since Qt 5.12.0, we should determince the full socket path on Unix
+ // platforms before calling QLocalSocketPrivate::_q_connectToSocket().
+ // Otherwise the new canonical implementation of QDir::tempPath()
+ // presents unintended usage of RemoteFileEngine.
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,0) && defined(Q_OS_UNIX)
+ d->init(socketPathName(socketName), key, mode, startAs);
+#else
d->init(socketName, key, mode, startAs);
+#endif
}
void RemoteClient::setAuthorizationFallbackDisabled(bool disabled)
diff --git a/src/libs/installer/remoteclient.h b/src/libs/installer/remoteclient.h
index c2090bf98..419acccfa 100644
--- a/src/libs/installer/remoteclient.h
+++ b/src/libs/installer/remoteclient.h
@@ -54,6 +54,7 @@ public:
QString socketName() const;
QString authorizationKey() const;
+ QString socketPathName(const QString &socketName) const;
bool isActive() const;
void setActive(bool active);
diff --git a/src/libs/installer/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp
index 05f4ec212..3c54d1e29 100644
--- a/src/libs/installer/remotefileengine.cpp
+++ b/src/libs/installer/remotefileengine.cpp
@@ -324,9 +324,9 @@ bool RemoteFileEngine::open(QIODevice::OpenMode mode)
{
if (connectToServer()) {
return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineOpen),
- static_cast<qint32>(mode));
+ static_cast<qint32>(mode | QIODevice::Unbuffered));
}
- return m_fileEngine.open(mode);
+ return m_fileEngine.open(mode | QIODevice::Unbuffered);
}
/*!
diff --git a/src/libs/installer/remoteserver.cpp b/src/libs/installer/remoteserver.cpp
index ddf4d6ecf..66cfefebb 100644
--- a/src/libs/installer/remoteserver.cpp
+++ b/src/libs/installer/remoteserver.cpp
@@ -93,7 +93,17 @@ void RemoteServer::start()
void RemoteServer::init(const QString &socketName, const QString &key, Protocol::Mode mode)
{
Q_D(RemoteServer);
+
+ // Since Qt 5.12.0, we should determince the full socket path on Unix
+ // platforms before calling QLocalSocketPrivate::_q_connectToSocket().
+ // Otherwise the new canonical implementation of QDir::tempPath()
+ // presents unintended usage of RemoteFileEngine.
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,0) && defined(Q_OS_UNIX)
+ d->m_socketName = socketPathName(socketName);
+#else
d->m_socketName = socketName;
+#endif
d->m_key = key;
d->m_mode = mode;
}
@@ -116,6 +126,18 @@ QString RemoteServer::authorizationKey() const
return d->m_key;
}
+QString RemoteServer::socketPathName(const QString &socketName) const
+{
+ QString socketPathName;
+ if (socketName.startsWith(QLatin1Char('/'))) {
+ socketPathName = socketName;
+ } else {
+ socketPathName = QDir::tempPath();
+ socketPathName += QLatin1Char('/') + socketName;
+ }
+ return socketPathName;
+}
+
/*!
Restarts the watchdog that tries to kill the server.
*/
diff --git a/src/libs/installer/remoteserver.h b/src/libs/installer/remoteserver.h
index e32bcf143..aa69baa55 100644
--- a/src/libs/installer/remoteserver.h
+++ b/src/libs/installer/remoteserver.h
@@ -53,6 +53,7 @@ public:
QString socketName() const;
QString authorizationKey() const;
+ QString socketPathName(const QString &socketName) const;
private slots:
void restartWatchdog();
diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp
index 5a47bc472..b188e694e 100644
--- a/src/libs/installer/remoteserverconnection.cpp
+++ b/src/libs/installer/remoteserverconnection.cpp
@@ -382,7 +382,14 @@ void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QStrin
} else if (command == QLatin1String(Protocol::QAbstractFileEngineCopy)) {
QString newName;
data >>newName;
+#ifdef Q_OS_LINUX
+ // QFileSystemEngine::copyFile() is currently unimplemented on Linux,
+ // copy using QFile instead of directly with QFSFileEngine.
+ QFile file(m_engine->fileName(QAbstractFileEngine::AbsoluteName));
+ sendData(socket, file.copy(newName));
+#else
sendData(socket, m_engine->copy(newName));
+#endif
} else if (command == QLatin1String(Protocol::QAbstractFileEngineEntryList)) {
qint32 filters;
QStringList filterNames;
diff --git a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
index 475c4d8b4..67fa7e2c5 100644
--- a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
+++ b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp
@@ -119,15 +119,18 @@ private slots:
QVERIFY(core.calculateComponentsToInstall());
{
- QTemporaryFile dummy(testDirectory + QLatin1String("/dummy"));
- dummy.open();
+ QFile dummy(testDirectory + QLatin1String("/dummy"));
+ QVERIFY(dummy.open(QIODevice::ReadWrite));
core.runInstaller();
QVERIFY(QDir(testDirectory).exists());
QVERIFY(QFileInfo(dummy.fileName()).exists());
+
+ dummy.close();
+ QVERIFY(dummy.remove());
}
- QDir().rmdir(testDirectory);
+ QVERIFY(QDir().rmdir(testDirectory));
ProgressCoordinator::instance()->reset();
}
diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
index 7118d067d..b7c602e26 100644
--- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp
+++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp
@@ -280,7 +280,11 @@ private slots:
// ignore Output from script
setExpectedScriptOutput("function receive()");
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,0)
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:38: ReferenceError: foo is not defined");
+#else
QTest::ignoreMessage(QtWarningMsg, ":38: ReferenceError: foo is not defined");
+#endif
emiter.produceSignal();
const QJSValue value = m_scriptEngine->evaluate("");