Merge "MappedFile and FileMap should support zero-length mappings."
am: 5f5ffc8e19

Change-Id: I98b1465f0bf851a611358fb12dff85bc3965cd5e
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index f7901af..faa845d 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -16,6 +16,8 @@
 
 #include "android-base/mapped_file.h"
 
+#include <errno.h>
+
 namespace android {
 namespace base {
 
@@ -50,7 +52,14 @@
       new MappedFile{static_cast<char*>(base), length, slop, handle});
 #else
   void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd, file_offset);
-  if (base == MAP_FAILED) return nullptr;
+  if (base == MAP_FAILED) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // mmap fails with EINVAL for a zero length region.
+    if (errno == EINVAL && length == 0) {
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
+    }
+    return nullptr;
+  }
   return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
 #endif
 }
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index 7e89723..cfde73c 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -25,7 +25,6 @@
 #include <string>
 
 #include "android-base/file.h"
-#include "android-base/unique_fd.h"
 
 TEST(mapped_file, smoke) {
   TemporaryFile tf;
@@ -37,3 +36,13 @@
   ASSERT_EQ('l', m->data()[0]);
   ASSERT_EQ('o', m->data()[1]);
 }
+
+TEST(mapped_file, zero_length_mapping) {
+  // http://b/119818070 "app crashes when reading asset of zero length".
+  // mmap fails with EINVAL for a zero length region.
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
+  ASSERT_EQ(0u, m->size());
+}
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 5feb2aa..1202c15 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -174,12 +174,6 @@
         return false;
     }
 #else // !defined(__MINGW32__)
-    int     prot, flags, adjust;
-    off64_t adjOffset;
-    size_t  adjLength;
-
-    void* ptr;
-
     assert(fd >= 0);
     assert(offset >= 0);
     assert(length > 0);
@@ -193,20 +187,23 @@
         }
     }
 
-    adjust = offset % mPageSize;
-    adjOffset = offset - adjust;
-    adjLength = length + adjust;
+    int adjust = offset % mPageSize;
+    off64_t adjOffset = offset - adjust;
+    size_t adjLength = length + adjust;
 
-    flags = MAP_SHARED;
-    prot = PROT_READ;
-    if (!readOnly)
-        prot |= PROT_WRITE;
+    int flags = MAP_SHARED;
+    int prot = PROT_READ;
+    if (!readOnly) prot |= PROT_WRITE;
 
-    ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+    void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
-        ALOGE("mmap(%lld,%zu) failed: %s\n",
-            (long long)adjOffset, adjLength, strerror(errno));
-        return false;
+        if (errno == EINVAL && length == 0) {
+            ptr = nullptr;
+            adjust = 0;
+        } else {
+            ALOGE("mmap(%lld,%zu) failed: %s\n", (long long)adjOffset, adjLength, strerror(errno));
+            return false;
+        }
     }
     mBasePtr = ptr;
 #endif // !defined(__MINGW32__)
@@ -217,8 +214,6 @@
     mDataPtr = (char*) mBasePtr + adjust;
     mDataLength = length;
 
-    assert(mBasePtr != NULL);
-
     ALOGV("MAP: base %p/%zu data %p/%zu\n",
         mBasePtr, mBaseLength, mDataPtr, mDataLength);
 
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 1390552..62f5acb 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -22,6 +22,7 @@
 
     srcs: [
         "BitSet_test.cpp",
+        "FileMap_test.cpp",
         "LruCache_test.cpp",
         "Mutex_test.cpp",
         "Singleton_test.cpp",
diff --git a/libutils/tests/FileMap_test.cpp b/libutils/tests/FileMap_test.cpp
new file mode 100644
index 0000000..576d89b
--- /dev/null
+++ b/libutils/tests/FileMap_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/FileMap.h"
+
+#include <gtest/gtest.h>
+
+#include "android-base/file.h"
+
+TEST(FileMap, zero_length_mapping) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // mmap fails with EINVAL for a zero length region.
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+
+    android::FileMap m;
+    ASSERT_TRUE(m.create("test", tf.fd, 4096, 0, true));
+    ASSERT_STREQ("test", m.getFileName());
+    ASSERT_EQ(0u, m.getDataLength());
+    ASSERT_EQ(4096, m.getDataOffset());
+}