diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2014-09-19 14:17:46 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2014-10-31 03:56:11 +0100 |
commit | 85da1625e47cadf0b41e24863e8988e771e50943 (patch) | |
tree | 892b2a86a0951e4521c6c3fd4a311005d0baf127 /src | |
parent | f91b81ca398c64a22fd6fb2006dbec39a3e3b2c0 (diff) |
Attempt to make QFile I/O 64-bit safe
Use qint64 wherever possible. The linear buffer is never requested to
allocate that much memory (always limited), but at least we ensure we're
not dropping bits where we shouldn't.
Windows's POSIX compatibility layer is never largefile enabled, so it is
always necessary to chunk large reads and writes. On Unix, this will
be rare, unless someone passed -no-largefile to configure, for some
weird reason.
Unfortunately, this is not testable, unless we can allocate a buffer
with 4 GB or more in size. The test for this would be to open a file we
know to be small, then try to read 4 GB + 1 byte. If everything works
correctly, we'll read the full file; if there was a truncation, we'd
read one byte.
Change-Id: If3ee511bf1de17e0123c85bbcaa463b9972746ce
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/io/qfsfileengine.cpp | 41 | ||||
-rw-r--r-- | src/corelib/io/qiodevice.cpp | 2 | ||||
-rw-r--r-- | src/corelib/io/qiodevice_p.h | 18 |
3 files changed, 39 insertions, 22 deletions
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 42250b629d..a126690240 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -73,6 +73,17 @@ QT_BEGIN_NAMESPACE # endif #endif +#ifdef Q_OS_WIN +// on Windows, read() and write() use int and unsigned int +typedef int SignedIOType; +typedef unsigned int UnsignedIOType; +#else +typedef ssize_t SignedIOType; +typedef size_t UnsignedIOType; +Q_STATIC_ASSERT_X(sizeof(SignedIOType) == sizeof(UnsignedIOType), + "Unsupported: read/write return a type with different size as the len parameter"); +#endif + /*! \class QFSFileEngine \inmodule QtCore \brief The QFSFileEngine class implements Qt's default file engine. @@ -605,13 +616,16 @@ qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len) } else if (fd != -1) { // Unbuffered stdio mode. -#ifdef Q_OS_WIN - int result; -#else - ssize_t result; -#endif + SignedIOType result; do { - result = QT_READ(fd, data + readBytes, size_t(len - readBytes)); + // calculate the chunk size + // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks + // we limit to the size of the signed type, otherwise we could get a negative number as a result + quint64 wantedBytes = quint64(len) - quint64(readBytes); + UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max(); + if (chunkSize > wantedBytes) + chunkSize = wantedBytes; + result = QT_READ(fd, data + readBytes, chunkSize); } while (result > 0 && (readBytes += result) < len); eof = !(result == -1); @@ -722,13 +736,16 @@ qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len) } else if (fd != -1) { // Unbuffered stdio mode. -#ifdef Q_OS_WIN - int result; -#else - ssize_t result; -#endif + SignedIOType result; do { - result = QT_WRITE(fd, data + writtenBytes, size_t(len - writtenBytes)); + // calculate the chunk size + // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks + // we limit to the size of the signed type, otherwise we could get a negative number as a result + quint64 wantedBytes = quint64(len) - quint64(writtenBytes); + UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max(); + if (chunkSize > wantedBytes) + chunkSize = wantedBytes; + result = QT_WRITE(fd, data + writtenBytes, chunkSize); } while (result > 0 && (writtenBytes += result) < len); } diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 89209e6118..0709a93bad 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -786,7 +786,7 @@ qint64 QIODevice::read(char *data, qint64 maxSize) bool moreToRead = true; do { // Try reading from the buffer. - int lastReadChunkSize = d->buffer.read(data, maxSize); + qint64 lastReadChunkSize = d->buffer.read(data, maxSize); if (lastReadChunkSize > 0) { *d->pPos += lastReadChunkSize; readSoFar += lastReadChunkSize; diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h index 10d92a896d..d764cb0fbb 100644 --- a/src/corelib/io/qiodevice_p.h +++ b/src/corelib/io/qiodevice_p.h @@ -98,19 +98,19 @@ public: first++; return ch; } - int read(char* target, qint64 size) { - int r = qMin(size, len); + qint64 read(char* target, qint64 size) { + qint64 r = qMin(size, len); memcpy(target, first, r); len -= r; first += r; return r; } - int peek(char* target, qint64 size) { - int r = qMin(size, len); + qint64 peek(char* target, qint64 size) { + qint64 r = qMin(size, len); memcpy(target, first, r); return r; } - char* reserve(int size) { + char* reserve(qint64 size) { makeSpace(size + len, freeSpaceAtEnd); char* writePtr = first + len; len += size; @@ -128,16 +128,16 @@ public: clear(); return retVal; } - int readLine(char* target, qint64 size) { - int r = qMin(size, len); + 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 int(r); - } + return r; + } bool canReadLine() const { return memchr(first, '\n', len); } |