Merge "healthd: Don't take device-scoped battery as the main system battery."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e3a8675..44c47f3 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,6 +28,9 @@
       "name": "fs_mgr_vendor_overlay_test"
     },
     {
+      "name": "init_kill_services_test"
+    },
+    {
       "name": "libbase_test"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index 1b7a266..d8fa713 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -387,17 +387,17 @@
     generated_headers: ["platform_tools_version"],
 
     static_libs: [
-        "libadbconnection_server",
-        "libapp_processes_protos_lite",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
+        "libadbconnection_server",
         "libadb_crypto",
         "libadb_pairing_connection",
         "libadb_protos",
         "libadb_tls_connection",
         "libadbd_auth",
+        "libapp_processes_protos_lite",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -432,6 +432,7 @@
             exclude_shared_libs: [
                 "libadb_pairing_auth",
                 "libadb_pairing_connection",
+                "libapp_processes_protos_lite",
             ],
         }
     },
@@ -458,9 +459,7 @@
     static_libs: [
         "libadbconnection_server",
         "libadbd_core",
-        "libapp_processes_protos_lite",
         "libdiagnose_usb",
-        "libprotobuf-cpp-lite",
     ],
 
     shared_libs: [
@@ -470,12 +469,14 @@
         "libadb_tls_connection",
         "libadbd_auth",
         "libadbd_fs",
+        "libapp_processes_protos_lite",
         "libasyncio",
         "libbase",
         "libcrypto",
         "libcrypto_utils",
         "libcutils_sockets",
         "liblog",
+        "libprotobuf-cpp-lite",
     ],
 
     target: {
@@ -514,15 +515,14 @@
     // libminadbd wants both, as it's used to build native tests.
     compile_multilib: "both",
 
-    // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
     whole_static_libs: [
-        "libadbconnection_server",
         "libadbd_core",
-        "libapp_processes_protos_lite",
-        "libprotobuf-cpp-lite",
     ],
 
     shared_libs: [
+        "libadbconnection_server",
+        "libapp_processes_protos_lite",
+        "libprotobuf-cpp-lite",
         "libadb_crypto",
         "libadb_pairing_connection",
         "libadb_tls_connection",
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index c99aead..adae9f7 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -16,6 +16,7 @@
 
 #if !ADB_HOST
 
+#if !defined(__ANDROID_RECOVERY__)
 #define TRACE_TAG JDWP
 
 #include "sysdeps.h"
@@ -459,7 +460,7 @@
     return -1;
 }
 
-asocket* create_process_tracker_service_socket(TrackerKind kind) {
+static asocket* create_process_tracker_service_socket(TrackerKind kind) {
     auto t = std::make_unique<JdwpTracker>(kind, true);
     if (!t) {
         LOG(FATAL) << "failed to allocate JdwpTracker";
@@ -509,4 +510,28 @@
     return 0;
 }
 
+#else  // !defined(__ANDROID_RECOVERY)
+#include "adb.h"
+
+asocket* create_jdwp_service_socket(void) {
+    return nullptr;
+}
+
+unique_fd create_jdwp_connection_fd(int pid) {
+    return {};
+}
+
+asocket* create_app_tracker_service_socket() {
+    return nullptr;
+}
+
+asocket* create_jdwp_tracker_service_socket() {
+    return nullptr;
+}
+
+int init_jdwp() {
+    return 0;
+}
+
+#endif /* defined(__ANDROID_RECOVERY__) */
 #endif /* !ADB_HOST */
diff --git a/base/Android.bp b/base/Android.bp
index 12de3b2..894ad6c 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -74,10 +74,6 @@
         "test_utils.cpp",
     ],
 
-    static: {
-        cflags: ["-DNO_LIBLOG_DLSYM"],
-    },
-
     cppflags: ["-Wexit-time-destructors"],
     shared_libs: ["liblog"],
     target: {
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
index 8d59179..ba4c161 100644
--- a/base/liblog_symbols.cpp
+++ b/base/liblog_symbols.cpp
@@ -16,11 +16,9 @@
 
 #include "liblog_symbols.h"
 
-#if defined(__ANDROID__)
-#if !defined(NO_LIBLOG_DLSYM) || defined(__ANDROID_APEX__)
+#if defined(__ANDROID_SDK_VERSION__) && (__ANDROID_SDK_VERSION__ <= 29)
 #define USE_DLSYM
 #endif
-#endif
 
 #ifdef USE_DLSYM
 #include <dlfcn.h>
diff --git a/init/init.cpp b/init/init.cpp
index b29dfa3..4289dcf 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -713,8 +713,15 @@
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
-    // Will handle EPIPE at the time of write by checking the errno
-    signal(SIGPIPE, SIG_IGN);
+    // Init should not crash because of a dependence on any other process, therefore we ignore
+    // SIGPIPE and handle EPIPE at the call site directly.  Note that setting a signal to SIG_IGN
+    // is inherited across exec, but custom signal handlers are not.  Since we do not want to
+    // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
+    {
+        struct sigaction action = {.sa_flags = SA_RESTART};
+        action.sa_handler = [](int) {};
+        sigaction(SIGPIPE, &action, nullptr);
+    }
 
     // Set init and its forked children's oom_adj.
     if (auto result =
diff --git a/init/selinux.cpp b/init/selinux.cpp
index acbcbd6..808cb7f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -539,9 +539,9 @@
 
     // adb remount, snapshot-based updates, and DSUs all create files during
     // first-stage init.
-    selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE);
-
     selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
+    selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE |
+                                                        SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/test_kill_services/Android.bp b/init/test_kill_services/Android.bp
new file mode 100644
index 0000000..f6e85e2
--- /dev/null
+++ b/init/test_kill_services/Android.bp
@@ -0,0 +1,7 @@
+cc_test {
+    name: "init_kill_services_test",
+    srcs: ["init_kill_services_test.cpp"],
+    shared_libs: ["libbase"],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
new file mode 100644
index 0000000..7e543f2
--- /dev/null
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest.h>
+
+#include <android-base/properties.h>
+
+#include <iostream>
+
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+
+void ExpectKillingServiceRecovers(const std::string& service_name) {
+    const std::string status_prop = "init.svc." + service_name;
+    const std::string pid_prop = "init.svc_debug_pid." + service_name;
+
+    const std::string initial_pid = GetProperty(pid_prop, "");
+
+    EXPECT_EQ("running", GetProperty(status_prop, "")) << status_prop;
+    EXPECT_NE("", initial_pid) << pid_prop;
+
+    EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
+
+    constexpr size_t kMaxWaitMilliseconds = 10000;
+    constexpr size_t kRetryWaitMilliseconds = 100;
+
+    constexpr size_t kRetryTimes = kMaxWaitMilliseconds / kRetryWaitMilliseconds;
+
+    for (size_t retry = 0; retry < kRetryTimes; retry++) {
+        const std::string& pid = GetProperty(pid_prop, "");
+        if (pid != initial_pid && pid != "") break;
+        usleep(kRetryWaitMilliseconds * 1000);
+    }
+
+    // svc_debug_pid is set after svc property
+    EXPECT_EQ("running", GetProperty(status_prop, ""));
+}
+
+class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
+
+TEST_P(InitKillServicesTest, KillCriticalProcesses) {
+    ExpectKillingServiceRecovers(GetParam());
+
+    // sanity check init is still responding
+    EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
+    EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
+    EXPECT_TRUE(SetProperty("test.death.test", ""));
+}
+
+static inline std::string PrintName(const testing::TestParamInfo<std::string>& info) {
+    return info.param;
+}
+
+INSTANTIATE_TEST_CASE_P(DeathTest, InitKillServicesTest,
+                        ::testing::Values("lmkd", "ueventd", "hwservicemanager", "servicemanager"),
+                        PrintName);
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 1bbffaf..4081b21 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -60,6 +60,8 @@
     srcs: [
         "zip_archive.cc",
         "zip_archive_stream_entry.cc",
+        "zip_cd_entry_map.cc",
+        "zip_error.cpp",
         "zip_writer.cc",
     ],
 
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 047af90..098a9cb 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -126,6 +126,9 @@
 int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
                       bool assume_ownership = true);
 
+int32_t OpenArchiveFdRange(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+                           off64_t length, off64_t offset, bool assume_ownership = true);
+
 int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
                               ZipArchiveHandle* handle);
 /*
@@ -222,6 +225,12 @@
 
 int GetFileDescriptor(const ZipArchiveHandle archive);
 
+/**
+ * Returns the offset of the zip archive in the backing file descriptor, or 0 if the zip archive is
+ * not backed by a file descriptor.
+ */
+off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive);
+
 const char* ErrorCodeString(int32_t error_code);
 
 #if !defined(_WIN32)
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 4451507..aa8bafc 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -85,100 +85,6 @@
  * of the string length into the hash table entry.
  */
 
-/*
- * Round up to the next highest power of 2.
- *
- * Found on http://graphics.stanford.edu/~seander/bithacks.html.
- */
-static uint32_t RoundUpPower2(uint32_t val) {
-  val--;
-  val |= val >> 1;
-  val |= val >> 2;
-  val |= val >> 4;
-  val |= val >> 8;
-  val |= val >> 16;
-  val++;
-
-  return val;
-}
-
-static uint32_t ComputeHash(std::string_view name) {
-  return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
-}
-
-// Convert a ZipEntry to a hash table index, verifying that it's in a valid range.
-std::pair<int32_t, uint64_t> CdEntryMapZip32::GetCdEntryOffset(std::string_view name,
-                                                               const uint8_t* start) const {
-  const uint32_t hash = ComputeHash(name);
-
-  // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
-  uint32_t ent = hash & (hash_table_size_ - 1);
-  while (hash_table_[ent].name_offset != 0) {
-    if (hash_table_[ent].ToStringView(start) == name) {
-      return {0, hash_table_[ent].name_offset};
-    }
-    ent = (ent + 1) & (hash_table_size_ - 1);
-  }
-
-  ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
-  return {kEntryNotFound, 0};
-}
-
-int32_t CdEntryMapZip32::AddToMap(std::string_view name, const uint8_t* start) {
-  const uint64_t hash = ComputeHash(name);
-  uint32_t ent = hash & (hash_table_size_ - 1);
-
-  /*
-   * We over-allocated the table, so we're guaranteed to find an empty slot.
-   * Further, we guarantee that the hashtable size is not 0.
-   */
-  while (hash_table_[ent].name_offset != 0) {
-    if (hash_table_[ent].ToStringView(start) == name) {
-      // We've found a duplicate entry. We don't accept duplicates.
-      ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
-      return kDuplicateEntry;
-    }
-    ent = (ent + 1) & (hash_table_size_ - 1);
-  }
-
-  // `name` has already been validated before entry.
-  const char* start_char = reinterpret_cast<const char*>(start);
-  hash_table_[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
-  hash_table_[ent].name_length = static_cast<uint16_t>(name.size());
-  return 0;
-}
-
-void CdEntryMapZip32::ResetIteration() {
-  current_position_ = 0;
-}
-
-std::pair<std::string_view, uint64_t> CdEntryMapZip32::Next(const uint8_t* cd_start) {
-  while (current_position_ < hash_table_size_) {
-    const auto& entry = hash_table_[current_position_];
-    current_position_ += 1;
-
-    if (entry.name_offset != 0) {
-      return {entry.ToStringView(cd_start), entry.name_offset};
-    }
-  }
-  // We have reached the end of the hash table.
-  return {};
-}
-
-CdEntryMapZip32::CdEntryMapZip32(uint16_t num_entries) {
-  hash_table_size_ = RoundUpPower2(1 + (num_entries * 4) / 3);
-  hash_table_ = {
-      reinterpret_cast<ZipStringOffset*>(calloc(hash_table_size_, sizeof(ZipStringOffset))), free};
-}
-
-std::unique_ptr<CdEntryMapInterface> CdEntryMapZip32::Create(uint16_t num_entries) {
-  auto entry_map = new CdEntryMapZip32(num_entries);
-  CHECK(entry_map->hash_table_ != nullptr)
-      << "Zip: unable to allocate the " << entry_map->hash_table_size_
-      << " entry hash_table, entry size: " << sizeof(ZipStringOffset);
-  return std::unique_ptr<CdEntryMapInterface>(entry_map);
-}
-
 #if defined(__BIONIC__)
 uint64_t GetOwnerTag(const ZipArchive* archive) {
   return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
@@ -186,8 +92,8 @@
 }
 #endif
 
-ZipArchive::ZipArchive(const int fd, bool assume_ownership)
-    : mapped_zip(fd),
+ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
+    : mapped_zip(map),
       close_file(assume_ownership),
       directory_offset(0),
       central_directory(),
@@ -195,7 +101,8 @@
       num_entries(0) {
 #if defined(__BIONIC__)
   if (assume_ownership) {
-    android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
+    CHECK(mapped_zip.HasFd());
+    android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
   }
 #endif
 }
@@ -357,12 +264,12 @@
   const size_t cd_length = archive->central_directory.GetMapLength();
   const uint16_t num_entries = archive->num_entries;
 
-  /*
-   * Create hash table.  We have a minimum 75% load factor, possibly as
-   * low as 50% after we round off to a power of 2.  There must be at
-   * least one unused entry to avoid an infinite loop during creation.
-   */
-  archive->cd_entry_map = CdEntryMapZip32::Create(num_entries);
+  // TODO(xunchang) parse the zip64 Eocd
+  if (num_entries > UINT16_MAX) {
+    archive->cd_entry_map = CdEntryMapZip64::Create();
+  } else {
+    archive->cd_entry_map = CdEntryMapZip32::Create(num_entries);
+  }
   if (archive->cd_entry_map == nullptr) {
     return kAllocationFailed;
   }
@@ -456,14 +363,32 @@
 
 int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
                       bool assume_ownership) {
-  ZipArchive* archive = new ZipArchive(fd, assume_ownership);
+  ZipArchive* archive = new ZipArchive(MappedZipFile(fd), assume_ownership);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
 }
 
+int32_t OpenArchiveFdRange(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+                           off64_t length, off64_t offset, bool assume_ownership) {
+  ZipArchive* archive = new ZipArchive(MappedZipFile(fd, length, offset), assume_ownership);
+  *handle = archive;
+
+  if (length < 0) {
+    ALOGW("Invalid zip length %" PRId64, length);
+    return kIoError;
+  }
+
+  if (offset < 0) {
+    ALOGW("Invalid zip offset %" PRId64, offset);
+    return kIoError;
+  }
+
+  return OpenArchiveInternal(archive, debug_file_name);
+}
+
 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
   const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
-  ZipArchive* archive = new ZipArchive(fd, true);
+  ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
   *handle = archive;
 
   if (fd < 0) {
@@ -1120,24 +1045,14 @@
   return ExtractToWriter(archive, entry, &writer);
 }
 
-const char* ErrorCodeString(int32_t error_code) {
-  // Make sure that the number of entries in kErrorMessages and ErrorCodes
-  // match.
-  static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
-                "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
-
-  const uint32_t idx = -error_code;
-  if (idx < arraysize(kErrorMessages)) {
-    return kErrorMessages[idx];
-  }
-
-  return "Unknown return code";
-}
-
 int GetFileDescriptor(const ZipArchiveHandle archive) {
   return archive->mapped_zip.GetFileDescriptor();
 }
 
+off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive) {
+  return archive->mapped_zip.GetFileOffset();
+}
+
 #if !defined(_WIN32)
 class ProcessWriter : public zip_archive::Writer {
  public:
@@ -1177,31 +1092,65 @@
   return base_ptr_;
 }
 
+off64_t MappedZipFile::GetFileOffset() const {
+  return fd_offset_;
+}
+
 off64_t MappedZipFile::GetFileLength() const {
   if (has_fd_) {
-    off64_t result = lseek64(fd_, 0, SEEK_END);
-    if (result == -1) {
+    if (data_length_ != -1) {
+      return data_length_;
+    }
+    data_length_ = lseek64(fd_, 0, SEEK_END);
+    if (data_length_ == -1) {
       ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
     }
-    return result;
+    return data_length_;
   } else {
     if (base_ptr_ == nullptr) {
       ALOGE("Zip: invalid file map");
       return -1;
     }
-    return static_cast<off64_t>(data_length_);
+    return data_length_;
   }
 }
 
 // Attempts to read |len| bytes into |buf| at offset |off|.
 bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
   if (has_fd_) {
-    if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
+    if (off < 0) {
+      ALOGE("Zip: invalid offset %" PRId64, off);
+      return false;
+    }
+
+    off64_t read_offset;
+    if (__builtin_add_overflow(fd_offset_, off, &read_offset)) {
+      ALOGE("Zip: invalid read offset %" PRId64 " overflows, fd offset %" PRId64, off, fd_offset_);
+      return false;
+    }
+
+    if (data_length_ != -1) {
+      off64_t read_end;
+      if (len > std::numeric_limits<off64_t>::max() ||
+          __builtin_add_overflow(off, static_cast<off64_t>(len), &read_end)) {
+        ALOGE("Zip: invalid read length %" PRId64 " overflows, offset %" PRId64,
+              static_cast<off64_t>(len), off);
+        return false;
+      }
+
+      if (read_end > data_length_) {
+        ALOGE("Zip: invalid read length %" PRId64 " exceeds data length %" PRId64 ", offset %"
+              PRId64, static_cast<off64_t>(len), data_length_, off);
+        return false;
+      }
+    }
+
+    if (!android::base::ReadFullyAtOffset(fd_, buf, len, read_offset)) {
       ALOGE("Zip: failed to read at offset %" PRId64, off);
       return false;
     }
   } else {
-    if (off < 0 || off > static_cast<off64_t>(data_length_)) {
+    if (off < 0 || off > data_length_) {
       ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
       return false;
     }
@@ -1219,7 +1168,8 @@
 bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
   if (mapped_zip.HasFd()) {
     directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
-                                                      cd_start_offset, cd_size, PROT_READ);
+                                                      mapped_zip.GetFileOffset() + cd_start_offset,
+                                                      cd_size, PROT_READ);
     if (!directory_map) {
       ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
             cd_start_offset, cd_size, strerror(errno));
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 68977f6..3509b89 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -28,80 +28,20 @@
 
 #include "android-base/macros.h"
 #include "android-base/mapped_file.h"
-
-static const char* kErrorMessages[] = {
-    "Success",
-    "Iteration ended",
-    "Zlib error",
-    "Invalid file",
-    "Invalid handle",
-    "Duplicate entries in archive",
-    "Empty archive",
-    "Entry not found",
-    "Invalid offset",
-    "Inconsistent information",
-    "Invalid entry name",
-    "I/O error",
-    "File mapping failed",
-    "Allocation failed",
-};
-
-enum ErrorCodes : int32_t {
-  kIterationEnd = -1,
-
-  // We encountered a Zlib error when inflating a stream from this file.
-  // Usually indicates file corruption.
-  kZlibError = -2,
-
-  // The input file cannot be processed as a zip archive. Usually because
-  // it's too small, too large or does not have a valid signature.
-  kInvalidFile = -3,
-
-  // An invalid iteration / ziparchive handle was passed in as an input
-  // argument.
-  kInvalidHandle = -4,
-
-  // The zip archive contained two (or possibly more) entries with the same
-  // name.
-  kDuplicateEntry = -5,
-
-  // The zip archive contains no entries.
-  kEmptyArchive = -6,
-
-  // The specified entry was not found in the archive.
-  kEntryNotFound = -7,
-
-  // The zip archive contained an invalid local file header pointer.
-  kInvalidOffset = -8,
-
-  // The zip archive contained inconsistent entry information. This could
-  // be because the central directory & local file header did not agree, or
-  // if the actual uncompressed length or crc32 do not match their declared
-  // values.
-  kInconsistentInformation = -9,
-
-  // An invalid entry name was encountered.
-  kInvalidEntryName = -10,
-
-  // An I/O related system call (read, lseek, ftruncate, map) failed.
-  kIoError = -11,
-
-  // We were not able to mmap the central directory or entry contents.
-  kMmapFailed = -12,
-
-  // An allocation failed.
-  kAllocationFailed = -13,
-
-  kLastErrorCode = kAllocationFailed,
-};
+#include "zip_cd_entry_map.h"
+#include "zip_error.h"
 
 class MappedZipFile {
  public:
   explicit MappedZipFile(const int fd)
-      : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
+      : has_fd_(true), fd_(fd), fd_offset_(0), base_ptr_(nullptr), data_length_(-1) {}
+
+  explicit MappedZipFile(const int fd, off64_t length, off64_t offset)
+      : has_fd_(true), fd_(fd), fd_offset_(offset), base_ptr_(nullptr), data_length_(length) {}
 
   explicit MappedZipFile(const void* address, size_t length)
-      : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
+      : has_fd_(false), fd_(-1), fd_offset_(0), base_ptr_(address),
+        data_length_(static_cast<off64_t>(length)) {}
 
   bool HasFd() const { return has_fd_; }
 
@@ -109,6 +49,8 @@
 
   const void* GetBasePtr() const;
 
+  off64_t GetFileOffset() const;
+
   off64_t GetFileLength() const;
 
   bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
@@ -121,9 +63,10 @@
   const bool has_fd_;
 
   const int fd_;
+  const off64_t fd_offset_;
 
   const void* const base_ptr_;
-  const off64_t data_length_;
+  mutable off64_t data_length_;
 };
 
 class CentralDirectory {
@@ -141,75 +84,6 @@
   size_t length_;
 };
 
-// This class is the interface of the central directory entries map. The map
-// helps to locate a particular cd entry based on the filename.
-class CdEntryMapInterface {
- public:
-  virtual ~CdEntryMapInterface() = default;
-  // Adds an entry to the map. The |name| should internally points to the
-  // filename field of a cd entry. And |start| points to the beginning of the
-  // central directory. Returns 0 on success.
-  virtual int32_t AddToMap(std::string_view name, const uint8_t* start) = 0;
-  // For the zip entry |entryName|, finds the offset of its filename field in
-  // the central directory. Returns a pair of [status, offset]. The value of
-  // the status is 0 on success.
-  virtual std::pair<int32_t, uint64_t> GetCdEntryOffset(std::string_view name,
-                                                        const uint8_t* cd_start) const = 0;
-  // Resets the iterator to the beginning of the map.
-  virtual void ResetIteration() = 0;
-  // Returns the [name, cd offset] of the current element. Also increments the
-  // iterator to points to the next element. Returns an empty pair we have read
-  // past boundary.
-  virtual std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) = 0;
-};
-
-/**
- * More space efficient string representation of strings in an mmaped zipped
- * file than std::string_view. Using std::string_view as an entry in the
- * ZipArchive hash table wastes space. std::string_view stores a pointer to a
- * string (on 64 bit, 8 bytes) and the length to read from that pointer,
- * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
- * 6 bytes.
- *
- * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
- * mapped file instead of the entire address, consuming 8 bytes with alignment.
- */
-struct ZipStringOffset {
-  uint32_t name_offset;
-  uint16_t name_length;
-
-  const std::string_view ToStringView(const uint8_t* start) const {
-    return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
-  }
-};
-
-// This implementation of CdEntryMap uses an array hash table. It uses less
-// memory than std::map; and it's used as the default implementation for zip
-// archives without zip64 extension.
-class CdEntryMapZip32 : public CdEntryMapInterface {
- public:
-  static std::unique_ptr<CdEntryMapInterface> Create(uint16_t num_entries);
-
-  int32_t AddToMap(std::string_view name, const uint8_t* start) override;
-  std::pair<int32_t, uint64_t> GetCdEntryOffset(std::string_view name,
-                                                const uint8_t* cd_start) const override;
-  void ResetIteration() override;
-  std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
-
- private:
-  explicit CdEntryMapZip32(uint16_t num_entries);
-
-  // We know how many entries are in the Zip archive, so we can have a
-  // fixed-size hash table. We define a load factor of 0.75 and over
-  // allocate so the maximum number entries can never be higher than
-  // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
-  uint32_t hash_table_size_{0};
-  std::unique_ptr<ZipStringOffset[], decltype(&free)> hash_table_{nullptr, free};
-
-  // The position of element for the current iteration.
-  uint32_t current_position_{0};
-};
-
 struct ZipArchive {
   // open Zip archive
   mutable MappedZipFile mapped_zip;
@@ -224,7 +98,7 @@
   uint16_t num_entries;
   std::unique_ptr<CdEntryMapInterface> cd_entry_map;
 
-  ZipArchive(const int fd, bool assume_ownership);
+  ZipArchive(MappedZipFile&& map, bool assume_ownership);
   ZipArchive(const void* address, size_t length);
   ~ZipArchive();
 
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 0916304..5caca8a 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -24,11 +24,14 @@
 #include <unistd.h>
 
 #include <memory>
+#include <set>
+#include <string_view>
 #include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/mapped_file.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <ziparchive/zip_archive.h>
@@ -53,6 +56,76 @@
   return OpenArchive(abs_path.c_str(), handle);
 }
 
+class CdEntryMapTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    names_ = {
+        "a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt",
+    };
+    separator_ = "separator";
+    header_ = "metadata";
+    joined_names_ = header_ + android::base::Join(names_, separator_);
+    base_ptr_ = reinterpret_cast<uint8_t*>(&joined_names_[0]);
+
+    entry_maps_.emplace_back(CdEntryMapZip32::Create(static_cast<uint16_t>(names_.size())));
+    entry_maps_.emplace_back(CdEntryMapZip64::Create());
+    for (auto& cd_map : entry_maps_) {
+      ASSERT_NE(nullptr, cd_map);
+      size_t offset = header_.size();
+      for (const auto& name : names_) {
+        auto status = cd_map->AddToMap(
+            std::string_view{joined_names_.c_str() + offset, name.size()}, base_ptr_);
+        ASSERT_EQ(0, status);
+        offset += name.size() + separator_.size();
+      }
+    }
+  }
+
+  std::vector<std::string> names_;
+  // A continuous region of memory serves as a mock of the central directory.
+  std::string joined_names_;
+  // We expect some metadata at the beginning of the central directory and between filenames.
+  std::string header_;
+  std::string separator_;
+
+  std::vector<std::unique_ptr<CdEntryMapInterface>> entry_maps_;
+  uint8_t* base_ptr_{nullptr};  // Points to the start of the central directory.
+};
+
+TEST_F(CdEntryMapTest, AddDuplicatedEntry) {
+  for (auto& cd_map : entry_maps_) {
+    std::string_view name = "b.txt";
+    ASSERT_NE(0, cd_map->AddToMap(name, base_ptr_));
+  }
+}
+
+TEST_F(CdEntryMapTest, FindEntry) {
+  for (auto& cd_map : entry_maps_) {
+    uint64_t expected_offset = header_.size();
+    for (const auto& name : names_) {
+      auto [status, offset] = cd_map->GetCdEntryOffset(name, base_ptr_);
+      ASSERT_EQ(status, kSuccess);
+      ASSERT_EQ(offset, expected_offset);
+      expected_offset += name.size() + separator_.size();
+    }
+  }
+}
+
+TEST_F(CdEntryMapTest, Iteration) {
+  std::set<std::string_view> expected(names_.begin(), names_.end());
+  for (auto& cd_map : entry_maps_) {
+    cd_map->ResetIteration();
+    std::set<std::string_view> entry_set;
+    auto ret = cd_map->Next(base_ptr_);
+    while (ret != std::pair<std::string_view, uint64_t>{}) {
+      auto [it, insert_status] = entry_set.insert(ret.first);
+      ASSERT_TRUE(insert_status);
+      ret = cd_map->Next(base_ptr_);
+    }
+    ASSERT_EQ(expected, entry_set);
+  }
+}
+
 TEST(ziparchive, Open) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -108,6 +181,32 @@
   close(fd);
 }
 
+TEST(ziparchive, OpenAssumeFdRangeOwnership) {
+  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+  ASSERT_NE(-1, fd);
+  const off64_t length = lseek64(fd, 0, SEEK_END);
+  ASSERT_NE(-1, length);
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
+                                  static_cast<size_t>(length), 0));
+  CloseArchive(handle);
+  ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
+  ASSERT_EQ(EBADF, errno);
+}
+
+TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
+  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+  ASSERT_NE(-1, fd);
+  const off64_t length = lseek(fd, 0, SEEK_END);
+  ASSERT_NE(-1, length);
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
+                                  static_cast<size_t>(length), 0, false));
+  CloseArchive(handle);
+  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
+  close(fd);
+}
+
 TEST(ziparchive, Iteration_std_string_view) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -254,6 +353,48 @@
   CloseArchive(handle);
 }
 
+TEST(ziparchive, OpenArchiveFdRange) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+
+  const std::string leading_garbage(21, 'x');
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
+                                        leading_garbage.size()));
+
+  std::string valid_content;
+  ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
+
+  const std::string ending_garbage(42, 'x');
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
+                                        ending_garbage.size()));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
+                                  valid_content.size(),
+                                  static_cast<off64_t>(leading_garbage.size())));
+
+  // An entry that's deflated.
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+  const uint32_t a_size = data.uncompressed_length;
+  ASSERT_EQ(a_size, kATxtContents.size());
+  auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
+  ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
+
+  // An entry that's stored.
+  ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
+  const uint32_t b_size = data.uncompressed_length;
+  ASSERT_EQ(b_size, kBTxtContents.size());
+  buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
+  ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
+
+  CloseArchive(handle);
+}
+
 TEST(ziparchive, ExtractToMemory) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
diff --git a/libziparchive/zip_cd_entry_map.cc b/libziparchive/zip_cd_entry_map.cc
new file mode 100644
index 0000000..f187c06
--- /dev/null
+++ b/libziparchive/zip_cd_entry_map.cc
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 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 "zip_cd_entry_map.h"
+
+#include <android-base/logging.h>
+#include <log/log.h>
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+static uint32_t RoundUpPower2(uint32_t val) {
+  val--;
+  val |= val >> 1;
+  val |= val >> 2;
+  val |= val >> 4;
+  val |= val >> 8;
+  val |= val >> 16;
+  val++;
+
+  return val;
+}
+
+static uint32_t ComputeHash(std::string_view name) {
+  return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
+}
+
+// Convert a ZipEntry to a hash table index, verifying that it's in a valid range.
+std::pair<ZipError, uint64_t> CdEntryMapZip32::GetCdEntryOffset(std::string_view name,
+                                                                const uint8_t* start) const {
+  const uint32_t hash = ComputeHash(name);
+
+  // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
+  uint32_t ent = hash & (hash_table_size_ - 1);
+  while (hash_table_[ent].name_offset != 0) {
+    if (hash_table_[ent].ToStringView(start) == name) {
+      return {kSuccess, hash_table_[ent].name_offset};
+    }
+    ent = (ent + 1) & (hash_table_size_ - 1);
+  }
+
+  ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
+  return {kEntryNotFound, 0};
+}
+
+ZipError CdEntryMapZip32::AddToMap(std::string_view name, const uint8_t* start) {
+  const uint64_t hash = ComputeHash(name);
+  uint32_t ent = hash & (hash_table_size_ - 1);
+
+  /*
+   * We over-allocated the table, so we're guaranteed to find an empty slot.
+   * Further, we guarantee that the hashtable size is not 0.
+   */
+  while (hash_table_[ent].name_offset != 0) {
+    if (hash_table_[ent].ToStringView(start) == name) {
+      // We've found a duplicate entry. We don't accept duplicates.
+      ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
+      return kDuplicateEntry;
+    }
+    ent = (ent + 1) & (hash_table_size_ - 1);
+  }
+
+  // `name` has already been validated before entry.
+  const char* start_char = reinterpret_cast<const char*>(start);
+  hash_table_[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
+  hash_table_[ent].name_length = static_cast<uint16_t>(name.size());
+  return kSuccess;
+}
+
+void CdEntryMapZip32::ResetIteration() {
+  current_position_ = 0;
+}
+
+std::pair<std::string_view, uint64_t> CdEntryMapZip32::Next(const uint8_t* cd_start) {
+  while (current_position_ < hash_table_size_) {
+    const auto& entry = hash_table_[current_position_];
+    current_position_ += 1;
+
+    if (entry.name_offset != 0) {
+      return {entry.ToStringView(cd_start), entry.name_offset};
+    }
+  }
+  // We have reached the end of the hash table.
+  return {};
+}
+
+CdEntryMapZip32::CdEntryMapZip32(uint16_t num_entries) {
+  /*
+   * Create hash table.  We have a minimum 75% load factor, possibly as
+   * low as 50% after we round off to a power of 2.  There must be at
+   * least one unused entry to avoid an infinite loop during creation.
+   */
+  hash_table_size_ = RoundUpPower2(1 + (num_entries * 4) / 3);
+  hash_table_ = {
+      reinterpret_cast<ZipStringOffset*>(calloc(hash_table_size_, sizeof(ZipStringOffset))), free};
+}
+
+std::unique_ptr<CdEntryMapInterface> CdEntryMapZip32::Create(uint16_t num_entries) {
+  auto entry_map = new CdEntryMapZip32(num_entries);
+  CHECK(entry_map->hash_table_ != nullptr)
+      << "Zip: unable to allocate the " << entry_map->hash_table_size_
+      << " entry hash_table, entry size: " << sizeof(ZipStringOffset);
+  return std::unique_ptr<CdEntryMapInterface>(entry_map);
+}
+
+std::unique_ptr<CdEntryMapInterface> CdEntryMapZip64::Create() {
+  return std::unique_ptr<CdEntryMapInterface>(new CdEntryMapZip64());
+}
+
+ZipError CdEntryMapZip64::AddToMap(std::string_view name, const uint8_t* start) {
+  const auto [it, added] =
+      entry_table_.insert({name, name.data() - reinterpret_cast<const char*>(start)});
+  if (!added) {
+    ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
+    return kDuplicateEntry;
+  }
+  return kSuccess;
+}
+
+std::pair<ZipError, uint64_t> CdEntryMapZip64::GetCdEntryOffset(std::string_view name,
+                                                                const uint8_t* /*cd_start*/) const {
+  const auto it = entry_table_.find(name);
+  if (it == entry_table_.end()) {
+    ALOGV("Zip: Could not find entry %.*s", static_cast<int>(name.size()), name.data());
+    return {kEntryNotFound, 0};
+  }
+
+  return {kSuccess, it->second};
+}
+
+void CdEntryMapZip64::ResetIteration() {
+  iterator_ = entry_table_.begin();
+}
+
+std::pair<std::string_view, uint64_t> CdEntryMapZip64::Next(const uint8_t* /*cd_start*/) {
+  if (iterator_ == entry_table_.end()) {
+    return {};
+  }
+
+  return *iterator_++;
+}
diff --git a/libziparchive/zip_cd_entry_map.h b/libziparchive/zip_cd_entry_map.h
new file mode 100644
index 0000000..4957f75
--- /dev/null
+++ b/libziparchive/zip_cd_entry_map.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include "zip_error.h"
+
+// This class is the interface of the central directory entries map. The map
+// helps to locate a particular cd entry based on the filename.
+class CdEntryMapInterface {
+ public:
+  virtual ~CdEntryMapInterface() = default;
+  // Adds an entry to the map. The |name| should internally points to the
+  // filename field of a cd entry. And |start| points to the beginning of the
+  // central directory. Returns 0 on success.
+  virtual ZipError AddToMap(std::string_view name, const uint8_t* start) = 0;
+  // For the zip entry |entryName|, finds the offset of its filename field in
+  // the central directory. Returns a pair of [status, offset]. The value of
+  // the status is 0 on success.
+  virtual std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
+                                                         const uint8_t* cd_start) const = 0;
+  // Resets the iterator to the beginning of the map.
+  virtual void ResetIteration() = 0;
+  // Returns the [name, cd offset] of the current element. Also increments the
+  // iterator to points to the next element. Returns an empty pair we have read
+  // past boundary.
+  virtual std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) = 0;
+};
+
+/**
+ * More space efficient string representation of strings in an mmaped zipped
+ * file than std::string_view. Using std::string_view as an entry in the
+ * ZipArchive hash table wastes space. std::string_view stores a pointer to a
+ * string (on 64 bit, 8 bytes) and the length to read from that pointer,
+ * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
+ * 6 bytes.
+ *
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
+ * mapped file instead of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+  uint32_t name_offset;
+  uint16_t name_length;
+
+  const std::string_view ToStringView(const uint8_t* start) const {
+    return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
+  }
+};
+
+// This implementation of CdEntryMap uses an array hash table. It uses less
+// memory than std::map; and it's used as the default implementation for zip
+// archives without zip64 extension.
+class CdEntryMapZip32 : public CdEntryMapInterface {
+ public:
+  static std::unique_ptr<CdEntryMapInterface> Create(uint16_t num_entries);
+
+  ZipError AddToMap(std::string_view name, const uint8_t* start) override;
+  std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
+                                                 const uint8_t* cd_start) const override;
+  void ResetIteration() override;
+  std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
+
+ private:
+  explicit CdEntryMapZip32(uint16_t num_entries);
+
+  // We know how many entries are in the Zip archive, so we can have a
+  // fixed-size hash table. We define a load factor of 0.75 and over
+  // allocate so the maximum number entries can never be higher than
+  // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
+  uint32_t hash_table_size_{0};
+  std::unique_ptr<ZipStringOffset[], decltype(&free)> hash_table_{nullptr, free};
+
+  // The position of element for the current iteration.
+  uint32_t current_position_{0};
+};
+
+// This implementation of CdEntryMap uses a std::map
+class CdEntryMapZip64 : public CdEntryMapInterface {
+ public:
+  static std::unique_ptr<CdEntryMapInterface> Create();
+
+  ZipError AddToMap(std::string_view name, const uint8_t* start) override;
+  std::pair<ZipError, uint64_t> GetCdEntryOffset(std::string_view name,
+                                                 const uint8_t* cd_start) const override;
+  void ResetIteration() override;
+  std::pair<std::string_view, uint64_t> Next(const uint8_t* cd_start) override;
+
+ private:
+  CdEntryMapZip64() = default;
+
+  std::map<std::string_view, uint64_t> entry_table_;
+
+  std::map<std::string_view, uint64_t>::iterator iterator_;
+};
diff --git a/libziparchive/zip_error.cpp b/libziparchive/zip_error.cpp
new file mode 100644
index 0000000..107ec47
--- /dev/null
+++ b/libziparchive/zip_error.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 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 "zip_error.h"
+
+#include <android-base/macros.h>
+
+static const char* kErrorMessages[] = {
+    "Success",
+    "Iteration ended",
+    "Zlib error",
+    "Invalid file",
+    "Invalid handle",
+    "Duplicate entries in archive",
+    "Empty archive",
+    "Entry not found",
+    "Invalid offset",
+    "Inconsistent information",
+    "Invalid entry name",
+    "I/O error",
+    "File mapping failed",
+    "Allocation failed",
+};
+
+const char* ErrorCodeString(int32_t error_code) {
+  // Make sure that the number of entries in kErrorMessages and the ZipError
+  // enum match.
+  static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
+                "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
+
+  const uint32_t idx = -error_code;
+  if (idx < arraysize(kErrorMessages)) {
+    return kErrorMessages[idx];
+  }
+
+  return "Unknown return code";
+}
diff --git a/libziparchive/zip_error.h b/libziparchive/zip_error.h
new file mode 100644
index 0000000..37fd55f
--- /dev/null
+++ b/libziparchive/zip_error.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+enum ZipError : int32_t {
+  kSuccess = 0,
+
+  kIterationEnd = -1,
+
+  // We encountered a Zlib error when inflating a stream from this file.
+  // Usually indicates file corruption.
+  kZlibError = -2,
+
+  // The input file cannot be processed as a zip archive. Usually because
+  // it's too small, too large or does not have a valid signature.
+  kInvalidFile = -3,
+
+  // An invalid iteration / ziparchive handle was passed in as an input
+  // argument.
+  kInvalidHandle = -4,
+
+  // The zip archive contained two (or possibly more) entries with the same
+  // name.
+  kDuplicateEntry = -5,
+
+  // The zip archive contains no entries.
+  kEmptyArchive = -6,
+
+  // The specified entry was not found in the archive.
+  kEntryNotFound = -7,
+
+  // The zip archive contained an invalid local file header pointer.
+  kInvalidOffset = -8,
+
+  // The zip archive contained inconsistent entry information. This could
+  // be because the central directory & local file header did not agree, or
+  // if the actual uncompressed length or crc32 do not match their declared
+  // values.
+  kInconsistentInformation = -9,
+
+  // An invalid entry name was encountered.
+  kInvalidEntryName = -10,
+
+  // An I/O related system call (read, lseek, ftruncate, map) failed.
+  kIoError = -11,
+
+  // We were not able to mmap the central directory or entry contents.
+  kMmapFailed = -12,
+
+  // An allocation failed.
+  kAllocationFailed = -13,
+
+  kLastErrorCode = kAllocationFailed,
+};
diff --git a/storaged/main.cpp b/storaged/main.cpp
index a7bda14..bbed210 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -71,6 +71,7 @@
     bool flag_dump_perf = false;
     int opt;
 
+    signal(SIGPIPE, SIG_IGN);
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
     for (;;) {