QDataStream: adjust containers' deserialization in transaction mode

If an error occurs during the transaction, we should prevent the
containers from being successfully read. So, check the status of the
stream before reading the container, because the deserialization
procedure temporarily resets it on entry.

Task-number: QTBUG-54022
Change-Id: Ie955c2fa3e449374f0f8403f00e487efa2bfdaf3
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Alex Trotsenko 2016-06-17 15:07:26 +03:00
parent 9467bdc909
commit 902a5e7aaa
2 changed files with 97 additions and 54 deletions

View File

@ -63,6 +63,9 @@ template <class Key, class T> class QMap;
#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED)
class QDataStreamPrivate;
namespace QtPrivate {
class StreamStateSaver;
}
class Q_CORE_EXPORT QDataStream
{
public:
@ -192,6 +195,7 @@ private:
Status q_status;
int readBlock(char *data, int len);
friend class QtPrivate::StreamStateSaver;
};
namespace QtPrivate {
@ -201,7 +205,8 @@ class StreamStateSaver
public:
inline StreamStateSaver(QDataStream *s) : stream(s), oldStatus(s->status())
{
stream->resetStatus();
if (!stream->dev || !stream->dev->isTransactionStarted())
stream->resetStatus();
}
inline ~StreamStateSaver()
{

View File

@ -2750,27 +2750,43 @@ void tst_QDataStream::status_QBitArray()
}
#define MAP_TEST(byteArray, initialStatus, expectedStatus, expectedHash) \
{ \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
stream.setStatus(initialStatus); \
stream >> hash; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
QCOMPARE(hash.size(), expectedHash.size()); \
QCOMPARE(hash, expectedHash); \
} \
{ \
QByteArray ba = byteArray; \
StringMap expectedMap; \
StringHash::const_iterator it = expectedHash.constBegin(); \
for (; it != expectedHash.constEnd(); ++it) \
expectedMap.insert(it.key(), it.value()); \
QDataStream stream(&ba, QIODevice::ReadOnly); \
stream.setStatus(initialStatus); \
stream >> map; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
QCOMPARE(map.size(), expectedMap.size()); \
QCOMPARE(map, expectedMap); \
for (bool inTransaction = false;; inTransaction = true) { \
{ \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
if (inTransaction) \
stream.startTransaction(); \
stream.setStatus(initialStatus); \
stream >> hash; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
if (!inTransaction || stream.commitTransaction()) { \
QCOMPARE(hash.size(), expectedHash.size()); \
QCOMPARE(hash, expectedHash); \
} else { \
QVERIFY(hash.isEmpty()); \
} \
} \
{ \
QByteArray ba = byteArray; \
StringMap expectedMap; \
StringHash::const_iterator it = expectedHash.constBegin(); \
for (; it != expectedHash.constEnd(); ++it) \
expectedMap.insert(it.key(), it.value()); \
QDataStream stream(&ba, QIODevice::ReadOnly); \
if (inTransaction) \
stream.startTransaction(); \
stream.setStatus(initialStatus); \
stream >> map; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
if (!inTransaction || stream.commitTransaction()) { \
QCOMPARE(map.size(), expectedMap.size()); \
QCOMPARE(map, expectedMap); \
} else { \
QVERIFY(map.isEmpty()); \
} \
} \
if (inTransaction) \
break; \
}
void tst_QDataStream::status_QHash_QMap()
@ -2815,38 +2831,60 @@ void tst_QDataStream::status_QHash_QMap()
}
#define LIST_TEST(byteArray, initialStatus, expectedStatus, expectedList) \
{ \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
stream.setStatus(initialStatus); \
stream >> list; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
QCOMPARE(list.size(), expectedList.size()); \
QCOMPARE(list, expectedList); \
} \
{ \
LinkedList expectedLinkedList; \
for (int i = 0; i < expectedList.count(); ++i) \
expectedLinkedList << expectedList.at(i); \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
stream.setStatus(initialStatus); \
stream >> linkedList; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
QCOMPARE(linkedList.size(), expectedLinkedList.size()); \
QCOMPARE(linkedList, expectedLinkedList); \
} \
{ \
Vector expectedVector; \
for (int i = 0; i < expectedList.count(); ++i) \
expectedVector << expectedList.at(i); \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
stream.setStatus(initialStatus); \
stream >> vector; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
QCOMPARE(vector.size(), expectedVector.size()); \
QCOMPARE(vector, expectedVector); \
for (bool inTransaction = false;; inTransaction = true) { \
{ \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
if (inTransaction) \
stream.startTransaction(); \
stream.setStatus(initialStatus); \
stream >> list; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
if (!inTransaction || stream.commitTransaction()) { \
QCOMPARE(list.size(), expectedList.size()); \
QCOMPARE(list, expectedList); \
} else { \
QVERIFY(list.isEmpty()); \
} \
} \
{ \
LinkedList expectedLinkedList; \
for (int i = 0; i < expectedList.count(); ++i) \
expectedLinkedList << expectedList.at(i); \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
if (inTransaction) \
stream.startTransaction(); \
stream.setStatus(initialStatus); \
stream >> linkedList; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
if (!inTransaction || stream.commitTransaction()) { \
QCOMPARE(linkedList.size(), expectedLinkedList.size()); \
QCOMPARE(linkedList, expectedLinkedList); \
} else { \
QVERIFY(linkedList.isEmpty()); \
} \
} \
{ \
Vector expectedVector; \
for (int i = 0; i < expectedList.count(); ++i) \
expectedVector << expectedList.at(i); \
QByteArray ba = byteArray; \
QDataStream stream(&ba, QIODevice::ReadOnly); \
if (inTransaction) \
stream.startTransaction(); \
stream.setStatus(initialStatus); \
stream >> vector; \
QCOMPARE((int)stream.status(), (int)expectedStatus); \
if (!inTransaction || stream.commitTransaction()) { \
QCOMPARE(vector.size(), expectedVector.size()); \
QCOMPARE(vector, expectedVector); \
} else { \
QVERIFY(vector.isEmpty()); \
} \
} \
if (inTransaction) \
break; \
}
void tst_QDataStream::status_QLinkedList_QList_QVector()