OSDN Git Service

StreamingZipInflater: fix mmap'd end of read
authorKenny Root <kroot@google.com>
Fri, 15 Feb 2013 17:37:11 +0000 (09:37 -0800)
committerKenny Root <kroot@google.com>
Fri, 15 Feb 2013 19:05:48 +0000 (11:05 -0800)
When reaching the end of a mmap'd segment, it was erroneously trying to
read more data. This is because we were reading data one character at a
time, so we never reached the Z_STREAM_END result without going through
the loop once more.

Bug: https://code.google.com/p/android/issues/detail?id=39041
Change-Id: I6c53b8187384a42217c32112d6a0c2857f471109

libs/androidfw/StreamingZipInflater.cpp

index d3fb98d..1dfec23 100644 (file)
 #include <string.h>
 #include <stddef.h>
 #include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    typeof (exp) _rc;                      \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+#endif
 
 static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; }
 
@@ -135,7 +152,7 @@ ssize_t StreamingZipInflater::read(void* outBuf, size_t count) {
             // if we don't have any data to decode, read some in.  If we're working
             // from mmapped data this won't happen, because the clipping to total size
             // will prevent reading off the end of the mapped input chunk.
-            if (mInflateState.avail_in == 0) {
+            if ((mInflateState.avail_in == 0) && (mDataMap == NULL)) {
                 int err = readNextChunk();
                 if (err < 0) {
                     ALOGE("Unable to access asset data: %d", err);
@@ -191,11 +208,10 @@ int StreamingZipInflater::readNextChunk() {
     if (mInNextChunkOffset < mInTotalSize) {
         size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset);
         if (toRead > 0) {
-            ssize_t didRead = ::read(mFd, mInBuf, toRead);
+            ssize_t didRead = TEMP_FAILURE_RETRY(::read(mFd, mInBuf, toRead));
             //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead);
             if (didRead < 0) {
-                // TODO: error
-                ALOGE("Error reading asset data");
+                ALOGE("Error reading asset data: %s", strerror(errno));
                 return didRead;
             } else {
                 mInNextChunkOffset += didRead;