QResource: obey the MapPrivateOption option to provide RW memory

The documentation says:

 The mapping will have the same open mode as the file (read and/or
 write), except when using MapPrivateOption, in which case it is always
 possible to write to the mapped memory.

So obey it.

This may cause high memory use by copying data we already have. This may
be important because applications may want to memory-map resources which
they intentionally didn't compress because those resources are
large. Later commits will implement some workarounds.

Fixes: QTBUG-124608
Pick-to: 6.6 6.5
Change-Id: I6979d02a7395405cbf23fffd17c8f03baf0ec00d
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit 39e156e639ac4eadd7a0d4dac73d05db077e817b)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2024-04-23 07:49:57 -07:00 committed by Qt Cherry-pick Bot
parent 404ac7d7f3
commit 1163efe559
3 changed files with 41 additions and 5 deletions

View File

@ -1354,6 +1354,7 @@ private:
uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
bool unmap(uchar *ptr);
void uncompress() const;
void mapUncompressed();
qint64 offset;
QResource resource;
mutable QByteArray uncompressed;
@ -1577,7 +1578,6 @@ bool QResourceFileEngine::supportsExtension(Extension extension) const
uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
{
Q_Q(QResourceFileEngine);
Q_UNUSED(flags);
Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
|| !uncompressed.isNull(), "QFile::map()",
"open() should have uncompressed compressed resources");
@ -1596,6 +1596,12 @@ uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::Memory
// resource was not compressed
address = resource.data();
if (flags & QFile::MapPrivateOption) {
// We need to provide read-write memory
mapUncompressed();
address = reinterpret_cast<const uchar *>(uncompressed.constData());
}
return const_cast<uchar *>(address) + offset;
}
@ -1613,6 +1619,15 @@ void QResourceFileEnginePrivate::uncompress() const
uncompressed = resource.uncompressedData();
}
void QResourceFileEnginePrivate::mapUncompressed()
{
Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
if (!uncompressed.isNull())
return; // nothing to do
uncompressed = resource.uncompressedData();
uncompressed.detach();
}
#endif // !defined(QT_BOOTSTRAPPED)
QT_END_NAMESPACE

View File

@ -9,10 +9,10 @@
<file>searchpath1/search_file.txt</file>
<file>searchpath2/search_file.txt</file>
<file>search_file.txt</file>
</qresource>
<qresource><file>test/testdir.txt</file>
<file>test/testdir.txt</file>
<file>otherdir/otherdir.txt</file>
<file alias="aliasdir/aliasdir.txt">test/testdir2.txt</file>
<file alias="uncompresseddir/uncompressed.txt" compression-algorithm="none">aliasdir/compressme.txt</file>
<file>test/test</file>
</qresource>
<qresource lang="ko">
@ -21,7 +21,7 @@
<qresource lang="de_CH">
<file alias="aliasdir/aliasdir.txt" compress="9" threshold="30">aliasdir/compressme.txt</file>
</qresource>
<qresource lang="de">
<qresource lang="de" compression-algorithm="none">
<file alias="aliasdir/aliasdir.txt">test/german.txt</file>
</qresource>
<qresource prefix="withoutslashes">

View File

@ -180,6 +180,7 @@ void tst_QResourceEngine::checkStructure_data()
#if defined(BUILTIN_TESTDATA)
<< QLatin1String("testqrc")
#endif
<< QLatin1String("uncompresseddir")
<< QLatin1String("withoutslashes");
QTest::newRow("root dir") << QString(":/")
@ -384,8 +385,16 @@ void tst_QResourceEngine::checkStructure_data()
QFile file(QFINDTESTDATA("testqrc/aliasdir/compressme.txt"));
file.open(QFile::ReadOnly);
info = QFileInfo(QFINDTESTDATA("testqrc/aliasdir/compressme.txt"));
QByteArray compressmeContents = file.readAll();
QTest::addRow("%s compressed text", qPrintable(root)) << QString(root + "aliasdir/aliasdir.txt")
<< file.readAll()
<< compressmeContents
<< QStringList()
<< QStringList()
<< QLocale("de_CH")
<< qlonglong(info.size());
QTest::addRow("%s non-compressed text", qPrintable(root)) << QString(root + "uncompresseddir/uncompressed.txt")
<< compressmeContents
<< QStringList()
<< QStringList()
<< QLocale("de_CH")
@ -463,6 +472,18 @@ void tst_QResourceEngine::checkStructure()
// check that it is still valid after closing the file
file.close();
QCOMPARE(ba, contents);
// memory should be writable because we used MapPrivateOption
*ptr = '\0';
// but shouldn't affect the actual file or a new mapping
QFile file2(pathName);
QVERIFY(file2.open(QFile::ReadOnly));
QCOMPARE(file2.readAll(), contents);
ptr = file2.map(0, file.size(), QFile::MapPrivateOption);
QVERIFY2(ptr, qPrintable(file2.errorString()));
QByteArrayView bav(reinterpret_cast<const char *>(ptr), file.size());
QCOMPARE(bav, contents);
}
QLocale::setDefault(QLocale::system());
}