diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp index 9a46095c..3e7dd5a2 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 a2f7806a..0eabc8c3 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 65386989..308bfa09 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 e208620c..ad1e5ecf 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 + 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 c2090bf9..419acccf 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 05f4ec21..3c54d1e2 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(QString::fromLatin1(Protocol::QAbstractFileEngineOpen), - static_cast(mode)); + static_cast(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 ddf4d6ec..66cfefeb 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 e32bcf14..aa69baa5 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 5a47bc47..b188e694 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 475c4d8b..67fa7e2c 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 7118d067..b7c602e2 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, ":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("");