From 88689368d95e6a68890b6d6121a7edebad2686c7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 31 Jan 2023 11:25:54 -0800 Subject: [PATCH] QMimeDatabase: don't stat() something that isn't a local file We must check that the path is an actual file on the filesystem before using native APIs. This regression was introduced by commit 047d8f36de45ebb318726167f941b0dbc64754ba. [ChangeLog][QtCore][QMimeDatabase] Fixed a regression from 6.4.0 that made certain QMimeDatabase functions that inspected file contents to fail on Unix systems, if the file was not a native file (e.g., a Qt resource). Fixes: QTBUG-110707 Change-Id: I570832c9ac8b4e03bde8fffd173f7b4c6b164192 Reviewed-by: Igor Kushnir Reviewed-by: David Faure (cherry picked from commit 40dd38813cba4bc8425e846f220e358ffb0d19ac) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/mimetypes/qmimedatabase.cpp | 48 ++++++++++--------- src/corelib/mimetypes/qmimedatabase_p.h | 2 +- .../qmimedatabase-cache/CMakeLists.txt | 8 ++++ .../qmimedatabase-xml/CMakeLists.txt | 8 ++++ .../corelib/mimetypes/qmimedatabase/test.txt | 6 +++ .../qmimedatabase/tst_qmimedatabase.cpp | 39 +++++++++++++++ 6 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 tests/auto/corelib/mimetypes/qmimedatabase/test.txt diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp index 3aad45792e4..c55904c1d07 100644 --- a/src/corelib/mimetypes/qmimedatabase.cpp +++ b/src/corelib/mimetypes/qmimedatabase.cpp @@ -444,31 +444,32 @@ QMimeType QMimeDatabasePrivate::mimeTypeForData(QIODevice *device) } QMimeType QMimeDatabasePrivate::mimeTypeForFile(const QString &fileName, - [[maybe_unused]] const QFileInfo *fileInfo, + const QFileInfo &fileInfo, QMimeDatabase::MatchMode mode) { + if (false) { #ifdef Q_OS_UNIX - // Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again. - // In addition we want to follow symlinks. - const QByteArray nativeFilePath = QFile::encodeName(fileName); - QT_STATBUF statBuffer; - if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) { - if (S_ISDIR(statBuffer.st_mode)) - return mimeTypeForName(directoryMimeType()); - if (S_ISCHR(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/chardevice")); - if (S_ISBLK(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/blockdevice")); - if (S_ISFIFO(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/fifo")); - if (S_ISSOCK(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/socket")); - } -#else - const bool isDirectory = fileInfo ? fileInfo->isDir() : QFileInfo(fileName).isDir(); - if (isDirectory) - return mimeTypeForName(directoryMimeType()); + } else if (fileInfo.isNativePath()) { + // If this is a local file, we'll want to do a stat() ourselves so we can + // detect additional inode types. In addition we want to follow symlinks. + const QByteArray nativeFilePath = QFile::encodeName(fileName); + QT_STATBUF statBuffer; + if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) { + if (S_ISDIR(statBuffer.st_mode)) + return mimeTypeForName(directoryMimeType()); + if (S_ISCHR(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/chardevice")); + if (S_ISBLK(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/blockdevice")); + if (S_ISFIFO(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/fifo")); + if (S_ISSOCK(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/socket")); + } #endif + } else if (fileInfo.isDir()) { + return mimeTypeForName(directoryMimeType()); + } switch (mode) { case QMimeDatabase::MatchDefault: @@ -615,7 +616,7 @@ QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mo { QMutexLocker locker(&d->mutex); - return d->mimeTypeForFile(fileInfo.filePath(), &fileInfo, mode); + return d->mimeTypeForFile(fileInfo.filePath(), fileInfo, mode); } /*! @@ -630,7 +631,8 @@ QMimeType QMimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode if (mode == MatchExtension) { return d->mimeTypeForFileExtension(fileName); } else { - return d->mimeTypeForFile(fileName, nullptr, mode); + QFileInfo fileInfo(fileName); + return d->mimeTypeForFile(fileName, fileInfo, mode); } } diff --git a/src/corelib/mimetypes/qmimedatabase_p.h b/src/corelib/mimetypes/qmimedatabase_p.h index 2dd8ecf9845..96981ba3fe1 100644 --- a/src/corelib/mimetypes/qmimedatabase_p.h +++ b/src/corelib/mimetypes/qmimedatabase_p.h @@ -60,7 +60,7 @@ public: QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device); QMimeType mimeTypeForFileExtension(const QString &fileName); QMimeType mimeTypeForData(QIODevice *device); - QMimeType mimeTypeForFile(const QString &fileName, const QFileInfo *fileInfo, QMimeDatabase::MatchMode mode); + QMimeType mimeTypeForFile(const QString &fileName, const QFileInfo &fileInfo, QMimeDatabase::MatchMode mode); QMimeType findByData(const QByteArray &data, int *priorityPtr); QStringList mimeTypeForFileName(const QString &fileName); QMimeGlobMatchResult findByFileName(const QString &fileName); diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-cache/CMakeLists.txt b/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-cache/CMakeLists.txt index c32ad90437b..589b129fad7 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-cache/CMakeLists.txt +++ b/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-cache/CMakeLists.txt @@ -48,6 +48,14 @@ qt_internal_add_resource(tst_qmimedatabase-cache "testdata" ${testdata_resource_files} ) +qt_internal_add_resource(tst_qmimedatabase-cache "testfiles" + PREFIX + "/files" + FILES + "../test.txt" + "../test.qml" +) + # special case begin set(corelib_source_dir ../../../../../../src/corelib) include(${corelib_source_dir}/mimetypes/mimetypes_resources.cmake) diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-xml/CMakeLists.txt b/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-xml/CMakeLists.txt index 831e8797645..a2633b5feca 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-xml/CMakeLists.txt +++ b/tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-xml/CMakeLists.txt @@ -48,6 +48,14 @@ qt_internal_add_resource(tst_qmimedatabase-xml "testdata" ${testdata_resource_files} ) +qt_internal_add_resource(tst_qmimedatabase-xml "testfiles" + PREFIX + "/files" + FILES + "../test.txt" + "../test.qml" +) + # special case begin set(corelib_source_dir ../../../../../../src/corelib) include(${corelib_source_dir}/mimetypes/mimetypes_resources.cmake) diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/test.txt b/tests/auto/corelib/mimetypes/qmimedatabase/test.txt new file mode 100644 index 00000000000..752cf553e9b --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/test.txt @@ -0,0 +1,6 @@ +// Copyright (C) 2012 David Faure +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick 1.1 +Item { +} diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 293f31e4647..c3aece66edd 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -25,6 +25,8 @@ #include #endif +using namespace Qt::StringLiterals; + static const char *const additionalMimeFiles[] = { "yast2-metapackage-handler-mimetypes.xml", "qml-again.xml", @@ -245,6 +247,7 @@ void tst_QMimeDatabase::mimeTypeForFileName_data() // fdo bug 15436, needs shared-mime-info >= 0.40 (and this tests the globs2-parsing code). QTest::newRow("glob that ends with *, also matches *.pdf. *.pdf has higher weight") << "README.pdf" << "application/pdf"; QTest::newRow("directory") << "/" << "inode/directory"; + QTest::newRow("resource-directory") << ":/files/" << "inode/directory"; QTest::newRow("doesn't exist, no extension") << "IDontExist" << "application/octet-stream"; QTest::newRow("doesn't exist but has known extension") << "IDontExist.txt" << "text/plain"; QTest::newRow("empty") << "" << "application/octet-stream"; @@ -497,6 +500,42 @@ void tst_QMimeDatabase::mimeTypeForFileWithContent() QCOMPARE(mime.name(), QString::fromLatin1("application/smil+xml")); } + // Test what happens with Qt resources (file engines in general) + { + QFile rccFile(":/files/test.txt"); + + mime = db.mimeTypeForFile(rccFile.fileName()); + QCOMPARE(mime.name(), "text/plain"_L1); + + QVERIFY(rccFile.open(QIODevice::ReadOnly)); + mime = db.mimeTypeForData(&rccFile); + QCOMPARE(mime.name(), "text/x-qml"_L1); + QVERIFY(rccFile.isOpen()); + + mime = db.mimeTypeForFile(rccFile.fileName(), QMimeDatabase::MatchContent); + QCOMPARE(mime.name(), "text/x-qml"_L1); + } + + // Directories + { + mime = db.mimeTypeForFile("/"); + QCOMPARE(mime.name(), "inode/directory"_L1); + + QString dirName = QDir::tempPath(); + if (!dirName.endsWith(u'/')) + dirName += u'/'; + mime = db.mimeTypeForFile(dirName); + QCOMPARE(mime.name(), "inode/directory"_L1); + + while (dirName.endsWith(u'/')) + dirName.chop(1); + mime = db.mimeTypeForFile(dirName); + QCOMPARE(mime.name(), "inode/directory"_L1); + + mime = db.mimeTypeForFile(":/files"); + QCOMPARE(mime.name(), "inode/directory"_L1); + } + // Test what happens with an incorrect path mime = db.mimeTypeForFile(QString::fromLatin1("file:///etc/passwd" /* incorrect code, use a path instead */)); QVERIFY(mime.isDefault());