Merge "storaged: ignore SIGPIPE"
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/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/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/zip_archive.cc b/libziparchive/zip_archive.cc
index 4451507..b5a855a 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,
@@ -357,12 +263,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;
   }
@@ -1120,20 +1026,6 @@
   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();
 }
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 68977f6..536894c 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -28,72 +28,8 @@
 
 #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:
@@ -141,75 +77,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;
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 0916304..523d4c5 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));
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,
+};