diff --git a/src/corelib/global/qhooks.cpp b/src/corelib/global/qhooks.cpp
index 40a7c88f13c1c895fadd754e6be4a9ee20f27757..88df9c2be9835f31fffac88e4209f1ec80fb9d92 100644
--- a/src/corelib/global/qhooks.cpp
+++ b/src/corelib/global/qhooks.cpp
@@ -61,7 +61,7 @@ quintptr Q_CORE_EXPORT qtHookData[] = {
     // The required sizes and offsets are tested in tests/auto/other/toolsupport.
     // When this fails and the change was intentional, adjust the test and
     // adjust this value here.
-    0
+    1
 };
 
 Q_STATIC_ASSERT(QHooks::LastHookIndex == sizeof(qtHookData) / sizeof(qtHookData[0]));
diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp
index 4c5ed0aef6619c94b0e7ca2e353a29c07bca5514..e94da6de64c20c1da78c88ce121f058774160260 100644
--- a/src/corelib/io/qfiledevice.cpp
+++ b/src/corelib/io/qfiledevice.cpp
@@ -355,7 +355,7 @@ bool QFileDevice::atEnd() const
     Q_D(const QFileDevice);
 
     // If there's buffered data left, we're not at the end.
-    if (!d->buffer.isEmpty())
+    if (!d->isBufferEmpty())
         return false;
 
     if (!isOpen())
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 64078b5c54044e25b0056e4ad09e445e44810603..00b50af5cabd6a05cc8d167f1d3c937519c59a19 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -143,7 +143,9 @@ static void checkWarnMessage(const QIODevice *device, const char *function, cons
  */
 QIODevicePrivate::QIODevicePrivate()
     : openMode(QIODevice::NotOpen), buffer(QIODEVICE_BUFFERSIZE),
-      pos(0), devicePos(0)
+      pos(0), devicePos(0),
+      transactionPos(0),
+      transactionStarted(false)
        , baseReadLineDataCalled(false)
        , accessMode(Unset)
 #ifdef QT_NO_QOBJECT
@@ -264,6 +266,12 @@ QIODevicePrivate::~QIODevicePrivate()
     subclassing QIODevice, remember to bypass any buffer you may use
     when the device is open in Unbuffered mode.
 
+    Usually, the incoming data stream from an asynchronous device is
+    fragmented, and chunks of data can arrive at arbitrary points in time.
+    To handle incomplete reads of data structures, use the transaction
+    mechanism implemented by QIODevice. See startTransaction() and related
+    functions for more details.
+
     \sa QBuffer, QFile, QTcpSocket
 */
 
@@ -588,6 +596,8 @@ void QIODevice::close()
     d->openMode = NotOpen;
     d->errorString.clear();
     d->pos = 0;
+    d->transactionStarted = false;
+    d->transactionPos = 0;
     d->buffer.clear();
 }
 
@@ -660,18 +670,8 @@ bool QIODevice::seek(qint64 pos)
            this, pos, d->pos, d->buffer.size());
 #endif
 
-    qint64 offset = pos - d->pos;
-    d->pos = pos;
     d->devicePos = pos;
-
-    if (offset < 0 || offset >= d->buffer.size())
-        // When seeking backwards, an operation that is only allowed for
-        // random-access devices, the buffer is cleared. The next read
-        // operation will then refill the buffer. We can optimize this, if we
-        // find that seeking backwards becomes a significant performance hit.
-        d->buffer.clear();
-    else
-        d->buffer.skip(offset);
+    d->seekBuffer(pos);
 
 #if defined QIODEVICE_DEBUG
     printf("%p \tafter: d->pos == %lld, d->buffer.size() == %lld\n", this, d->pos,
@@ -680,6 +680,24 @@ bool QIODevice::seek(qint64 pos)
     return true;
 }
 
+/*!
+    \internal
+*/
+void QIODevicePrivate::seekBuffer(qint64 newPos)
+{
+    const qint64 offset = newPos - pos;
+    pos = newPos;
+
+    if (offset < 0 || offset >= buffer.size()) {
+        // When seeking backwards, an operation that is only allowed for
+        // random-access devices, the buffer is cleared. The next read
+        // operation will then refill the buffer.
+        buffer.clear();
+    } else {
+        buffer.free(offset);
+    }
+}
+
 /*!
     Returns \c true if the current read and write position is at the end
     of the device (i.e. there is no more data available for reading on
@@ -695,7 +713,7 @@ bool QIODevice::seek(qint64 pos)
 bool QIODevice::atEnd() const
 {
     Q_D(const QIODevice);
-    const bool result = (d->openMode == NotOpen || (d->buffer.isEmpty()
+    const bool result = (d->openMode == NotOpen || (d->isBufferEmpty()
                                                     && bytesAvailable() == 0));
 #if defined QIODEVICE_DEBUG
     printf("%p QIODevice::atEnd() returns %s, d->openMode == %d, d->pos == %lld\n", this,
@@ -740,7 +758,7 @@ qint64 QIODevice::bytesAvailable() const
     Q_D(const QIODevice);
     if (!d->isSequential())
         return qMax(size() - d->pos, qint64(0));
-    return d->buffer.size();
+    return d->buffer.size() - d->transactionPos;
 }
 
 /*!  For buffered devices, this function returns the number of bytes
@@ -777,9 +795,10 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
 #endif
 
     const bool sequential = d->isSequential();
+    const bool keepDataInBuffer = sequential && d->transactionStarted;
 
     // Short circuit for getChar()
-    if (maxSize == 1) {
+    if (maxSize == 1 && !keepDataInBuffer) {
         int chint;
         while ((chint = d->buffer.getChar()) != -1) {
             if (!sequential)
@@ -806,9 +825,13 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
     char *readPtr = data;
     forever {
         // Try reading from the buffer.
-        qint64 bufferReadChunkSize = d->buffer.read(data, maxSize);
+        qint64 bufferReadChunkSize = keepDataInBuffer
+                                     ? d->buffer.peek(data, maxSize, d->transactionPos)
+                                     : d->buffer.read(data, maxSize);
         if (bufferReadChunkSize > 0) {
-            if (!sequential)
+            if (keepDataInBuffer)
+                d->transactionPos += bufferReadChunkSize;
+            else if (!sequential)
                 d->pos += bufferReadChunkSize;
             readSoFar += bufferReadChunkSize;
             data += bufferReadChunkSize;
@@ -826,7 +849,8 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
             // Make sure the device is positioned correctly.
             if (sequential || d->pos == d->devicePos || seek(d->pos)) {
                 madeBufferReadsOnly = false; // fix readData attempt
-                if (maxSize >= QIODEVICE_BUFFERSIZE || (d->openMode & Unbuffered)) {
+                if ((maxSize >= QIODEVICE_BUFFERSIZE || (d->openMode & Unbuffered))
+                    && !keepDataInBuffer) {
                     // Read big chunk directly to output buffer
                     readFromDevice = readData(data, maxSize);
                     deviceAtEof = (readFromDevice != maxSize);
@@ -844,7 +868,9 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
                         }
                     }
                 } else {
-                    const qint64 bytesToBuffer = QIODEVICE_BUFFERSIZE;
+                    // Do not read more than maxSize on unbuffered devices
+                    const qint64 bytesToBuffer = (d->openMode & Unbuffered)
+                            ? qMin(maxSize, QIODEVICE_BUFFERSIZE) : QIODEVICE_BUFFERSIZE;
                     // Try to fill QIODevice buffer by single read
                     readFromDevice = readData(d->buffer.reserve(bytesToBuffer), bytesToBuffer);
                     deviceAtEof = (readFromDevice != bytesToBuffer);
@@ -907,10 +933,8 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
     debugBinaryString(data - readSoFar, readSoFar);
 #endif
 
-    if (madeBufferReadsOnly && d->buffer.isEmpty()) {
-        d->buffer.clear();
+    if (madeBufferReadsOnly && d->isBufferEmpty())
         readData(data, 0);
-    }
 
     return readSoFar;
 }
@@ -992,7 +1016,9 @@ QByteArray QIODevice::readAll()
     qint64 readBytes = (d->isSequential() ? Q_INT64_C(0) : size());
     if (readBytes == 0) {
         // Size is unknown, read incrementally.
-        qint64 readChunkSize = qMax(d->buffer.size(), QIODEVICE_BUFFERSIZE);
+        qint64 readChunkSize = qMax(QIODEVICE_BUFFERSIZE,
+                                    d->isSequential() ? (d->buffer.size() - d->transactionPos)
+                                                      : d->buffer.size());
         qint64 readResult;
         do {
             if (readBytes + readChunkSize >= MaxByteArraySize) {
@@ -1077,21 +1103,35 @@ qint64 QIODevice::readLine(char *data, qint64 maxSize)
     --maxSize;
 
     const bool sequential = d->isSequential();
+    const bool keepDataInBuffer = sequential && d->transactionStarted;
 
     qint64 readSoFar = 0;
-    if (!d->buffer.isEmpty()) {
-        readSoFar = d->buffer.readLine(data, maxSize);
+    if (keepDataInBuffer) {
+        if (d->transactionPos < d->buffer.size()) {
+            // Peek line from the specified position
+            const qint64 i = d->buffer.indexOf('\n', maxSize, d->transactionPos);
+            readSoFar = d->buffer.peek(data, i >= 0 ? (i - d->transactionPos + 1) : maxSize,
+                                       d->transactionPos);
+            d->transactionPos += readSoFar;
+            if (d->transactionPos == d->buffer.size())
+                readData(data, 0);
+        }
+    } else if (!d->buffer.isEmpty()) {
+        // QRingBuffer::readLine() terminates the line with '\0'
+        readSoFar = d->buffer.readLine(data, maxSize + 1);
         if (d->buffer.isEmpty())
             readData(data,0);
         if (!sequential)
             d->pos += readSoFar;
+    }
+
+    if (readSoFar) {
 #if defined QIODEVICE_DEBUG
         printf("%p \tread from buffer: %lld bytes, last character read: %hhx\n", this,
                readSoFar, data[readSoFar - 1]);
-        if (readSoFar)
-            debugBinaryString(data, int(readSoFar));
+        debugBinaryString(data, int(readSoFar));
 #endif
-        if (readSoFar && data[readSoFar - 1] == '\n') {
+        if (data[readSoFar - 1] == '\n') {
             if (d->openMode & Text) {
                 // QRingBuffer::readLine() isn't Text aware.
                 if (readSoFar > 1 && data[readSoFar - 2] == '\r') {
@@ -1107,7 +1147,11 @@ qint64 QIODevice::readLine(char *data, qint64 maxSize)
     if (d->pos != d->devicePos && !sequential && !seek(d->pos))
         return qint64(-1);
     d->baseReadLineDataCalled = false;
-    qint64 readBytes = readLineData(data + readSoFar, maxSize - readSoFar);
+    // Force base implementation for transaction on sequential device
+    // as it stores the data in internal buffer automatically.
+    qint64 readBytes = keepDataInBuffer
+                       ? QIODevice::readLineData(data + readSoFar, maxSize - readSoFar)
+                       : readLineData(data + readSoFar, maxSize - readSoFar);
 #if defined QIODEVICE_DEBUG
     printf("%p \tread from readLineData: %lld bytes, readSoFar = %lld bytes\n", this,
            readBytes, readSoFar);
@@ -1262,7 +1306,95 @@ qint64 QIODevice::readLineData(char *data, qint64 maxSize)
 */
 bool QIODevice::canReadLine() const
 {
-    return d_func()->buffer.canReadLine();
+    Q_D(const QIODevice);
+    return d->buffer.indexOf('\n', d->buffer.size(),
+                             d->isSequential() ? d->transactionPos : Q_INT64_C(0)) >= 0;
+}
+
+/*!
+    \since 5.7
+
+    Starts a new read transaction on the device.
+
+    Defines a restorable point within the sequence of read operations. For
+    sequential devices, read data will be duplicated internally to allow
+    recovery in case of incomplete reads. For random-access devices,
+    this function saves the current position. Call commitTransaction() or
+    rollbackTransaction() to finish the transaction.
+
+    \note Nesting transactions is not supported.
+
+    \sa commitTransaction(), rollbackTransaction()
+*/
+void QIODevice::startTransaction()
+{
+    Q_D(QIODevice);
+    if (d->transactionStarted) {
+        checkWarnMessage(this, "startTransaction", "Called while transaction already in progress");
+        return;
+    }
+    d->transactionPos = d->pos;
+    d->transactionStarted = true;
+}
+
+/*!
+    \since 5.7
+
+    Completes a read transaction.
+
+    For sequential devices, all data recorded in the internal buffer during
+    the transaction will be discarded.
+
+    \sa startTransaction(), rollbackTransaction()
+*/
+void QIODevice::commitTransaction()
+{
+    Q_D(QIODevice);
+    if (!d->transactionStarted) {
+        checkWarnMessage(this, "commitTransaction", "Called while no transaction in progress");
+        return;
+    }
+    if (d->isSequential())
+        d->buffer.free(d->transactionPos);
+    d->transactionStarted = false;
+    d->transactionPos = 0;
+}
+
+/*!
+    \since 5.7
+
+    Rolls back a read transaction.
+
+    Restores the input stream to the point of the startTransaction() call.
+    This function is commonly used to rollback the transaction when an
+    incomplete read was detected prior to committing the transaction.
+
+    \sa startTransaction(), commitTransaction()
+*/
+void QIODevice::rollbackTransaction()
+{
+    Q_D(QIODevice);
+    if (!d->transactionStarted) {
+        checkWarnMessage(this, "rollbackTransaction", "Called while no transaction in progress");
+        return;
+    }
+    if (!d->isSequential())
+        d->seekBuffer(d->transactionPos);
+    d->transactionStarted = false;
+    d->transactionPos = 0;
+}
+
+/*!
+    \since 5.7
+
+    Returns \c true if a transaction is in progress on the device, otherwise
+    \c false.
+
+    \sa startTransaction()
+*/
+bool QIODevice::isTransactionStarted() const
+{
+    return d_func()->transactionStarted;
 }
 
 /*!
@@ -1386,12 +1518,19 @@ qint64 QIODevice::write(const char *data)
 
     If \a c was not previously read from the device, the behavior is
     undefined.
+
+    \note This function is not available while a transaction is in progress.
 */
 void QIODevice::ungetChar(char c)
 {
     Q_D(QIODevice);
     CHECK_READABLE(read, Q_VOID);
 
+    if (d->transactionStarted) {
+        checkWarnMessage(this, "ungetChar", "Called while transaction is in progress");
+        return;
+    }
+
 #if defined QIODEVICE_DEBUG
     printf("%p QIODevice::ungetChar(0x%hhx '%c')\n", this, c, isprint(c) ? c : '?');
 #endif
@@ -1426,13 +1565,26 @@ bool QIODevicePrivate::putCharHelper(char c)
 */
 qint64 QIODevicePrivate::peek(char *data, qint64 maxSize)
 {
-    qint64 readBytes = q_func()->read(data, maxSize);
-    if (readBytes <= 0)
+    Q_Q(QIODevice);
+
+    if (transactionStarted) {
+        const qint64 savedTransactionPos = transactionPos;
+        const qint64 savedPos = pos;
+
+        qint64 readBytes = q->read(data, maxSize);
+
+        // Restore initial position
+        if (isSequential())
+            transactionPos = savedTransactionPos;
+        else
+            seekBuffer(savedPos);
         return readBytes;
+    }
+
+    q->startTransaction();
+    qint64 readBytes = q->read(data, maxSize);
+    q->rollbackTransaction();
 
-    buffer.ungetBlock(data, readBytes);
-    if (!isSequential())
-        pos -= readBytes;
     return readBytes;
 }
 
@@ -1441,14 +1593,26 @@ qint64 QIODevicePrivate::peek(char *data, qint64 maxSize)
 */
 QByteArray QIODevicePrivate::peek(qint64 maxSize)
 {
-    QByteArray result = q_func()->read(maxSize);
+    Q_Q(QIODevice);
+
+    if (transactionStarted) {
+        const qint64 savedTransactionPos = transactionPos;
+        const qint64 savedPos = pos;
+
+        QByteArray result = q->read(maxSize);
 
-    if (result.isEmpty())
+        // Restore initial position
+        if (isSequential())
+            transactionPos = savedTransactionPos;
+        else
+            seekBuffer(savedPos);
         return result;
+    }
+
+    q->startTransaction();
+    QByteArray result = q->read(maxSize);
+    q->rollbackTransaction();
 
-    buffer.ungetBlock(result.constData(), result.size());
-    if (!isSequential())
-        pos -= result.size();
     return result;
 }
 
diff --git a/src/corelib/io/qiodevice.h b/src/corelib/io/qiodevice.h
index b62c8d266c20649a157402b70426caea6e0121c3..49f6848540c0e73e3f5027ee253b31c6f15fd411 100644
--- a/src/corelib/io/qiodevice.h
+++ b/src/corelib/io/qiodevice.h
@@ -111,6 +111,11 @@ public:
     QByteArray readLine(qint64 maxlen = 0);
     virtual bool canReadLine() const;
 
+    void startTransaction();
+    void commitTransaction();
+    void rollbackTransaction();
+    bool isTransactionStarted() const;
+
     qint64 write(const char *data, qint64 len);
     qint64 write(const char *data);
     inline qint64 write(const QByteArray &data)
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h
index 56a89ab680ac52da218cf0207b2115c1e6dab162..45f5219240cb4951f31943cfbef3ad7d236563a2 100644
--- a/src/corelib/io/qiodevice_p.h
+++ b/src/corelib/io/qiodevice_p.h
@@ -62,138 +62,6 @@ QT_BEGIN_NAMESPACE
 
 Q_CORE_EXPORT int qt_subtract_from_timeout(int timeout, int elapsed);
 
-// This is QIODevice's read buffer, optimized for read(), isEmpty() and getChar()
-class QIODevicePrivateLinearBuffer
-{
-public:
-    QIODevicePrivateLinearBuffer(int) : len(0), first(0), buf(0), capacity(0) {
-    }
-    ~QIODevicePrivateLinearBuffer() {
-        delete [] buf;
-    }
-    void clear() {
-        len = 0;
-        delete [] buf;
-        buf = 0;
-        first = buf;
-        capacity = 0;
-    }
-    qint64 size() const {
-        return len;
-    }
-    bool isEmpty() const {
-        return len == 0;
-    }
-    void skip(qint64 n) {
-        if (n >= len) {
-            clear();
-        } else {
-            len -= n;
-            first += n;
-        }
-    }
-    int getChar() {
-        if (len == 0)
-            return -1;
-        int ch = uchar(*first);
-        len--;
-        first++;
-        return ch;
-    }
-    qint64 read(char* target, qint64 size) {
-        qint64 r = qMin(size, len);
-        memcpy(target, first, r);
-        len -= r;
-        first += r;
-        return r;
-    }
-    qint64 peek(char* target, qint64 size) {
-        qint64 r = qMin(size, len);
-        memcpy(target, first, r);
-        return r;
-    }
-    char* reserve(qint64 size) {
-        makeSpace(size + len, freeSpaceAtEnd);
-        char* writePtr = first + len;
-        len += size;
-        return writePtr;
-    }
-    void chop(qint64 size) {
-        if (size >= len) {
-            clear();
-        } else {
-            len -= size;
-        }
-    }
-    QByteArray readAll() {
-        QByteArray retVal(first, len);
-        clear();
-        return retVal;
-    }
-    qint64 readLine(char* target, qint64 size) {
-        qint64 r = qMin(size, len);
-        char* eol = static_cast<char*>(memchr(first, '\n', r));
-        if (eol)
-            r = 1+(eol-first);
-        memcpy(target, first, r);
-        len -= r;
-        first += r;
-        return r;
-    }
-    bool canReadLine() const {
-        return memchr(first, '\n', len);
-    }
-    void ungetChar(char c) {
-        if (first == buf) {
-            // underflow, the existing valid data needs to move to the end of the (potentially bigger) buffer
-            makeSpace(len+1, freeSpaceAtStart);
-        }
-        first--;
-        len++;
-        *first = c;
-    }
-    void ungetBlock(const char* block, qint64 size) {
-        if ((first - buf) < size) {
-            // underflow, the existing valid data needs to move to the end of the (potentially bigger) buffer
-            makeSpace(len + size, freeSpaceAtStart);
-        }
-        first -= size;
-        len += size;
-        memcpy(first, block, size);
-    }
-
-private:
-    enum FreeSpacePos {freeSpaceAtStart, freeSpaceAtEnd};
-    void makeSpace(size_t required, FreeSpacePos where) {
-        size_t newCapacity = qMax(capacity, size_t(QIODEVICE_BUFFERSIZE));
-        while (newCapacity < required)
-            newCapacity *= 2;
-        const size_t moveOffset = (where == freeSpaceAtEnd) ? 0 : newCapacity - size_t(len);
-        if (newCapacity > capacity) {
-            // allocate more space
-            char* newBuf = new char[newCapacity];
-            memmove(newBuf + moveOffset, first, len);
-            delete [] buf;
-            buf = newBuf;
-            capacity = newCapacity;
-        } else {
-            // shift any existing data to make space
-            memmove(buf + moveOffset, first, len);
-        }
-        first = buf + moveOffset;
-    }
-
-private:
-    // length of the unread data
-    qint64 len;
-    // start of the unread data
-    char* first;
-    // the allocated buffer
-    char* buf;
-    // allocated buffer size
-    size_t capacity;
-};
-
 class Q_CORE_EXPORT QIODevicePrivate
 #ifndef QT_NO_QOBJECT
     : public QObjectPrivate
@@ -208,9 +76,11 @@ public:
     QIODevice::OpenMode openMode;
     QString errorString;
 
-    QIODevicePrivateLinearBuffer buffer;
+    QRingBuffer buffer;
     qint64 pos;
     qint64 devicePos;
+    qint64 transactionPos;
+    bool transactionStarted;
     bool baseReadLineDataCalled;
 
     virtual bool putCharHelper(char c);
@@ -228,6 +98,13 @@ public:
         return accessMode == Sequential;
     }
 
+    inline bool isBufferEmpty() const
+    {
+        return buffer.isEmpty() || (transactionStarted && isSequential()
+                                    && transactionPos == buffer.size());
+    }
+    void seekBuffer(qint64 newPos);
+
     virtual qint64 peek(char *data, qint64 maxSize);
     virtual QByteArray peek(qint64 maxSize);
 
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 823dc4c144a36b17b7b383ca847da2809b5b885f..2392f7cbfd90f94c60a874637efde35eb02c38db 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -1395,15 +1395,17 @@ QProcess::ProcessChannel QProcess::readChannel() const
 void QProcess::setReadChannel(ProcessChannel channel)
 {
     Q_D(QProcess);
+
+    if (d->transactionStarted) {
+        qWarning("QProcess::setReadChannel: Failed due to the active read transaction");
+        return;
+    }
+
     if (d->processChannel != channel) {
-        QByteArray buf = d->buffer.readAll();
-        if (d->processChannel == QProcess::StandardOutput) {
-            for (int i = buf.size() - 1; i >= 0; --i)
-                d->stdoutChannel.buffer.ungetChar(buf.at(i));
-        } else {
-            for (int i = buf.size() - 1; i >= 0; --i)
-                d->stderrChannel.buffer.ungetChar(buf.at(i));
-        }
+        QRingBuffer *buffer = (d->processChannel == QProcess::StandardOutput)
+                              ? &d->stdoutChannel.buffer
+                              : &d->stderrChannel.buffer;
+        d->buffer.read(buffer->reserveFront(d->buffer.size()), d->buffer.size());
     }
     d->processChannel = channel;
 }
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 1dfd87a0f85721f66b1780c244da7e33c7ac98da..6f3ed58e189f294ef42394fc35a930ed6677d19e 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -2531,7 +2531,7 @@ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
     if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) {
         //unencrypted mode - do not use QIODevice::peek, as it reads ahead data from the plain socket
         //peek at data already in the QIODevice buffer (from a previous read)
-        qint64 r = buffer.peek(data, maxSize);
+        qint64 r = buffer.peek(data, maxSize, transactionPos);
         if (r == maxSize)
             return r;
         data += r;
@@ -2560,7 +2560,7 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
         //peek at data already in the QIODevice buffer (from a previous read)
         QByteArray ret;
         ret.reserve(maxSize);
-        ret.resize(buffer.peek(ret.data(), maxSize));
+        ret.resize(buffer.peek(ret.data(), maxSize, transactionPos));
         if (ret.length() == maxSize)
             return ret;
         //peek at data in the plain socket
diff --git a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp
index 334f5aba05ad631bf679610aa870b94b7a7715a7..9271630b326d00a3a1506987c46da3c51a346ccc 100644
--- a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp
+++ b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp
@@ -57,9 +57,11 @@ private slots:
     void readLine2_data();
     void readLine2();
 
-    void peekBug();
     void readAllKeepPosition();
     void writeInTextMode();
+
+    void transaction_data();
+    void transaction();
 };
 
 void tst_QIODevice::initTestCase()
@@ -528,77 +530,23 @@ void tst_QIODevice::readLine2()
     }
 }
 
-
-class PeekBug : public QIODevice {
-    Q_OBJECT
-public:
-    char alphabet[27];
-    qint64 counter;
-    PeekBug() : QIODevice(), counter(0) {
-        memcpy(alphabet,"abcdefghijklmnopqrstuvqxyz",27);
-    };
-    qint64 readData(char *data, qint64 maxlen) {
-        qint64 pos = 0;
-        while (pos < maxlen) {
-            *(data + pos) = alphabet[counter];
-            pos++;
-            counter++;
-            if (counter == 26)
-                counter = 0;
-        }
-        return maxlen;
-    }
-    qint64 writeData(const char * /* data */, qint64 /* maxlen */) {
-        return -1;
-    }
-
-};
-
-// This is a regression test for an old bug where peeking at
-// more than one character failed to put them back.
-void tst_QIODevice::peekBug()
-{
-    PeekBug peekBug;
-    peekBug.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
-    char onetwo[2];
-    peekBug.peek(onetwo, 2);
-    QCOMPARE(onetwo[0], 'a');
-    QCOMPARE(onetwo[1], 'b');
-
-    peekBug.read(onetwo, 1);
-    QCOMPARE(onetwo[0], 'a');
-
-    peekBug.peek(onetwo, 2);
-    QCOMPARE(onetwo[0], 'b');
-    QCOMPARE(onetwo[1], 'c');
-
-    peekBug.read(onetwo, 1);
-    QCOMPARE(onetwo[0], 'b');
-    peekBug.read(onetwo, 1);
-    QCOMPARE(onetwo[0], 'c');
-    peekBug.read(onetwo, 1);
-    QCOMPARE(onetwo[0], 'd');
-
-    peekBug.peek(onetwo, 2);
-    QCOMPARE(onetwo[0], 'e');
-    QCOMPARE(onetwo[1], 'f');
-
-}
-
 class SequentialReadBuffer : public QIODevice
 {
 public:
-    SequentialReadBuffer(const char *data) : QIODevice(), buf(data), offset(0) { }
+    SequentialReadBuffer(const char *data)
+        : QIODevice(), buf(new QByteArray(data)), offset(0), ownbuf(true) { }
+    SequentialReadBuffer(QByteArray *byteArray)
+        : QIODevice(), buf(byteArray), offset(0), ownbuf(false) { }
+    virtual ~SequentialReadBuffer() { if (ownbuf) delete buf; }
 
     bool isSequential() const Q_DECL_OVERRIDE { return true; }
-    const QByteArray &buffer() const { return buf; }
+    const QByteArray &buffer() const { return *buf; }
 
 protected:
     qint64 readData(char *data, qint64 maxSize) Q_DECL_OVERRIDE
     {
-        maxSize = qMin(maxSize, qint64(buf.size() - offset));
-        memcpy(data, buf.constData() + offset, maxSize);
+        maxSize = qMin(maxSize, qint64(buf->size() - offset));
+        memcpy(data, buf->constData() + offset, maxSize);
         offset += maxSize;
         return maxSize;
     }
@@ -608,8 +556,9 @@ protected:
     }
 
 private:
-    QByteArray buf;
+    QByteArray *buf;
     int offset;
+    bool ownbuf;
 };
 
 // Test readAll() on position change for sequential device
@@ -669,5 +618,117 @@ void tst_QIODevice::writeInTextMode()
 #endif
 }
 
+void tst_QIODevice::transaction_data()
+{
+    QTest::addColumn<bool>("sequential");
+    QTest::addColumn<qint8>("i8Data");
+    QTest::addColumn<qint16>("i16Data");
+    QTest::addColumn<qint32>("i32Data");
+    QTest::addColumn<qint64>("i64Data");
+    QTest::addColumn<bool>("bData");
+    QTest::addColumn<float>("fData");
+    QTest::addColumn<double>("dData");
+    QTest::addColumn<QByteArray>("strData");
+
+    bool sequential = true;
+    do {
+        QByteArray devName(sequential ? "sequential" : "random-access");
+
+        QTest::newRow(qPrintable(devName + '1')) << sequential << qint8(1) << qint16(2)
+                                                 << qint32(3) << qint64(4) << true
+                                                 << 5.0f << double(6.0)
+                                                 << QByteArray("Hello world!");
+        QTest::newRow(qPrintable(devName + '2')) << sequential << qint8(1 << 6) << qint16(1 << 14)
+                                                 << qint32(1 << 30) << (qint64(1) << 62) << false
+                                                 << 123.0f << double(234.0)
+                                                 << QByteArray("abcdefghijklmnopqrstuvwxyz");
+        QTest::newRow(qPrintable(devName + '3')) << sequential << qint8(-1) << qint16(-2)
+                                                 << qint32(-3) << qint64(-4) << true
+                                                 << -123.0f << double(-234.0)
+                                                 << QByteArray("Qt rocks!");
+        sequential = !sequential;
+    } while (!sequential);
+}
+
+// Test transaction integrity
+void tst_QIODevice::transaction()
+{
+    QByteArray testBuffer;
+
+    QFETCH(bool, sequential);
+    QFETCH(qint8, i8Data);
+    QFETCH(qint16, i16Data);
+    QFETCH(qint32, i32Data);
+    QFETCH(qint64, i64Data);
+    QFETCH(bool, bData);
+    QFETCH(float, fData);
+    QFETCH(double, dData);
+    QFETCH(QByteArray, strData);
+
+    {
+        QDataStream stream(&testBuffer, QIODevice::WriteOnly);
+
+        stream << i8Data << i16Data << i32Data << i64Data
+               << bData << fData << dData << strData.constData();
+    }
+
+    for (int splitPos = 0; splitPos <= testBuffer.size(); ++splitPos) {
+        QByteArray readBuffer(testBuffer.left(splitPos));
+        QIODevice *dev = sequential ? (QIODevice *) new SequentialReadBuffer(&readBuffer)
+                                    : (QIODevice *) new QBuffer(&readBuffer);
+        dev->open(QIODevice::ReadOnly);
+        QDataStream stream(dev);
+
+        qint8 i8;
+        qint16 i16;
+        qint32 i32;
+        qint64 i64;
+        bool b;
+        float f;
+        double d;
+        char *str;
+
+        forever {
+            QVERIFY(!dev->isTransactionStarted());
+            dev->startTransaction();
+            QVERIFY(dev->isTransactionStarted());
+
+            // Try to read all data in one go. If the status of the data stream
+            // indicates an unsuccessful operation, restart a read transaction
+            // on the completed buffer.
+            stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> str;
+
+            QVERIFY(stream.atEnd());
+            if (stream.status() == QDataStream::Ok) {
+                dev->commitTransaction();
+                break;
+            }
+
+            dev->rollbackTransaction();
+            QVERIFY(splitPos == 0 || !stream.atEnd());
+            QCOMPARE(dev->pos(), Q_INT64_C(0));
+            QCOMPARE(dev->bytesAvailable(), qint64(readBuffer.size()));
+            QVERIFY(readBuffer.size() < testBuffer.size());
+            delete [] str;
+            readBuffer.append(testBuffer.right(testBuffer.size() - splitPos));
+            stream.resetStatus();
+        }
+
+        QVERIFY(!dev->isTransactionStarted());
+        QVERIFY(stream.atEnd());
+        QCOMPARE(i8, i8Data);
+        QCOMPARE(i16, i16Data);
+        QCOMPARE(i32, i32Data);
+        QCOMPARE(i64, i64Data);
+        QCOMPARE(b, bData);
+        QCOMPARE(f, fData);
+        QCOMPARE(d, dData);
+        QVERIFY(strData == str);
+        delete [] str;
+        stream.setDevice(0);
+        delete dev;
+    }
+}
+
 QTEST_MAIN(tst_QIODevice)
 #include "tst_qiodevice.moc"
diff --git a/tests/auto/other/toolsupport/tst_toolsupport.cpp b/tests/auto/other/toolsupport/tst_toolsupport.cpp
index 7942a8461524dc54ff2127b732baa294b0c5f1bd..93cbb2809d797d7405835d430bf8a98323da706e 100644
--- a/tests/auto/other/toolsupport/tst_toolsupport.cpp
+++ b/tests/auto/other/toolsupport/tst_toolsupport.cpp
@@ -124,7 +124,7 @@ void tst_toolsupport::offsets_data()
     {
         QTestData &data = QTest::newRow("QFilePrivate::fileName")
                 << pmm_to_offsetof(&QFilePrivate::fileName);
-        data << 168 << 248;
+        data << 184 << 256;
     }
 #endif