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 <igorkuo@gmail.com>
Reviewed-by: David Faure <david.faure@kdab.com>
(cherry picked from commit 40dd38813cba4bc8425e846f220e358ffb0d19ac)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2023-01-31 11:25:54 -08:00 committed by Qt Cherry-pick Bot
parent 30786ef596
commit 02a03f28f5
6 changed files with 87 additions and 24 deletions

View File

@ -444,31 +444,32 @@ QMimeType QMimeDatabasePrivate::mimeTypeForData(QIODevice *device)
} }
QMimeType QMimeDatabasePrivate::mimeTypeForFile(const QString &fileName, QMimeType QMimeDatabasePrivate::mimeTypeForFile(const QString &fileName,
[[maybe_unused]] const QFileInfo *fileInfo, const QFileInfo &fileInfo,
QMimeDatabase::MatchMode mode) QMimeDatabase::MatchMode mode)
{ {
if (false) {
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
// Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again. } else if (fileInfo.isNativePath()) {
// In addition we want to follow symlinks. // If this is a local file, we'll want to do a stat() ourselves so we can
const QByteArray nativeFilePath = QFile::encodeName(fileName); // detect additional inode types. In addition we want to follow symlinks.
QT_STATBUF statBuffer; const QByteArray nativeFilePath = QFile::encodeName(fileName);
if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) { QT_STATBUF statBuffer;
if (S_ISDIR(statBuffer.st_mode)) if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) {
return mimeTypeForName(directoryMimeType()); if (S_ISDIR(statBuffer.st_mode))
if (S_ISCHR(statBuffer.st_mode)) return mimeTypeForName(directoryMimeType());
return mimeTypeForName(QStringLiteral("inode/chardevice")); if (S_ISCHR(statBuffer.st_mode))
if (S_ISBLK(statBuffer.st_mode)) return mimeTypeForName(QStringLiteral("inode/chardevice"));
return mimeTypeForName(QStringLiteral("inode/blockdevice")); if (S_ISBLK(statBuffer.st_mode))
if (S_ISFIFO(statBuffer.st_mode)) return mimeTypeForName(QStringLiteral("inode/blockdevice"));
return mimeTypeForName(QStringLiteral("inode/fifo")); if (S_ISFIFO(statBuffer.st_mode))
if (S_ISSOCK(statBuffer.st_mode)) return mimeTypeForName(QStringLiteral("inode/fifo"));
return mimeTypeForName(QStringLiteral("inode/socket")); 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());
#endif #endif
} else if (fileInfo.isDir()) {
return mimeTypeForName(directoryMimeType());
}
switch (mode) { switch (mode) {
case QMimeDatabase::MatchDefault: case QMimeDatabase::MatchDefault:
@ -615,7 +616,7 @@ QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mo
{ {
QMutexLocker locker(&d->mutex); 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) { if (mode == MatchExtension) {
return d->mimeTypeForFileExtension(fileName); return d->mimeTypeForFileExtension(fileName);
} else { } else {
return d->mimeTypeForFile(fileName, nullptr, mode); QFileInfo fileInfo(fileName);
return d->mimeTypeForFile(fileName, fileInfo, mode);
} }
} }

View File

@ -60,7 +60,7 @@ public:
QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device); QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device);
QMimeType mimeTypeForFileExtension(const QString &fileName); QMimeType mimeTypeForFileExtension(const QString &fileName);
QMimeType mimeTypeForData(QIODevice *device); 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); QMimeType findByData(const QByteArray &data, int *priorityPtr);
QStringList mimeTypeForFileName(const QString &fileName); QStringList mimeTypeForFileName(const QString &fileName);
QMimeGlobMatchResult findByFileName(const QString &fileName); QMimeGlobMatchResult findByFileName(const QString &fileName);

View File

@ -45,6 +45,14 @@ qt_internal_add_resource(tst_qmimedatabase-cache "testdata"
${testdata_resource_files} ${testdata_resource_files}
) )
qt_internal_add_resource(tst_qmimedatabase-cache "testfiles"
PREFIX
"/files"
FILES
"../test.txt"
"../test.qml"
)
# special case begin # special case begin
set(corelib_source_dir ../../../../../../src/corelib) set(corelib_source_dir ../../../../../../src/corelib)
include(${corelib_source_dir}/mimetypes/mimetypes_resources.cmake) include(${corelib_source_dir}/mimetypes/mimetypes_resources.cmake)

View File

@ -45,6 +45,14 @@ qt_internal_add_resource(tst_qmimedatabase-xml "testdata"
${testdata_resource_files} ${testdata_resource_files}
) )
qt_internal_add_resource(tst_qmimedatabase-xml "testfiles"
PREFIX
"/files"
FILES
"../test.txt"
"../test.qml"
)
# special case begin # special case begin
set(corelib_source_dir ../../../../../../src/corelib) set(corelib_source_dir ../../../../../../src/corelib)
include(${corelib_source_dir}/mimetypes/mimetypes_resources.cmake) include(${corelib_source_dir}/mimetypes/mimetypes_resources.cmake)

View File

@ -0,0 +1,6 @@
// Copyright (C) 2012 David Faure <faure@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 1.1
Item {
}

View File

@ -25,6 +25,8 @@
#include <QProcess> #include <QProcess>
#endif #endif
using namespace Qt::StringLiterals;
static const char *const additionalMimeFiles[] = { static const char *const additionalMimeFiles[] = {
"yast2-metapackage-handler-mimetypes.xml", "yast2-metapackage-handler-mimetypes.xml",
"qml-again.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). // 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("glob that ends with *, also matches *.pdf. *.pdf has higher weight") << "README.pdf" << "application/pdf";
QTest::newRow("directory") << "/" << "inode/directory"; 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, no extension") << "IDontExist" << "application/octet-stream";
QTest::newRow("doesn't exist but has known extension") << "IDontExist.txt" << "text/plain"; QTest::newRow("doesn't exist but has known extension") << "IDontExist.txt" << "text/plain";
QTest::newRow("empty") << "" << "application/octet-stream"; QTest::newRow("empty") << "" << "application/octet-stream";
@ -497,6 +500,42 @@ void tst_QMimeDatabase::mimeTypeForFileWithContent()
QCOMPARE(mime.name(), QString::fromLatin1("application/smil+xml")); 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 // Test what happens with an incorrect path
mime = db.mimeTypeForFile(QString::fromLatin1("file:///etc/passwd" /* incorrect code, use a path instead */)); mime = db.mimeTypeForFile(QString::fromLatin1("file:///etc/passwd" /* incorrect code, use a path instead */));
QVERIFY(mime.isDefault()); QVERIFY(mime.isDefault());