Merge "Add a helpful error message if GetUnreachableMemory fails" into oc-dr1-dev
am: fe11ca5421  -s ours

Change-Id: I77e4ae5e39fd880dfa0914e1628447b94ceae70e
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 308ee8d..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -603,15 +603,15 @@
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
-    std::lock_guard<std::mutex> lock(transport_lock);
-    CHECK_GT(t->ref_count, 0u);
-    t->ref_count--;
-    if (t->ref_count == 0) {
+    size_t old_refcount = t->ref_count--;
+    CHECK_GT(old_refcount, 0u);
+
+    if (old_refcount == 1u) {
         D("transport: %s unref (kicking and closing)", t->serial);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
     }
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index 57fc988..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -61,7 +61,7 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
+    atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    size_t ref_count = 0;
+    std::atomic<size_t> ref_count;
     uint32_t sync_token = 0;
     bool online = false;
     TransportType type = kTransportAny;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 809ed89..9cd378c 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@
     D("transport: qemu_socket_thread() exiting");
     return;
 }
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+    // Legacy way to detect if adbd should use the goldfish pipe is to check for
+    // ro.kernel.qemu, keep that behaviour for backward compatibility.
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        return true;
+    }
+    // If service.adb.transport is present and is set to "goldfish", use the
+    // QEMUD pipe.
+    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+        return true;
+    }
+    return false;
+}
+
 #endif  // !ADB_HOST
 
 void local_init(int port)
@@ -401,13 +420,7 @@
 #else
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
-        // Running inside the emulator: use QEMUD pipe as the transport.
-        func = qemu_socket_thread;
-    } else {
-        // Running inside the device: use TCP socket as the transport.
-        func = server_socket_thread;
-    }
+    func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
     debug_name = "server";
 #endif // !ADB_HOST
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 558bc72..df7201d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -44,6 +44,9 @@
 #include <private/android_filesystem_config.h>
 #include <procinfo/process.h>
 
+#define ATRACE_TAG ATRACE_TAG_BIONIC
+#include <utils/Trace.h>
+
 #include "backtrace.h"
 #include "tombstone.h"
 #include "utility.h"
@@ -101,6 +104,7 @@
 }
 
 static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+  ATRACE_CALL();
   android::base::unique_fd amfd(socket_local_client(
       "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
   if (amfd.get() == -1) {
@@ -176,6 +180,7 @@
 }
 
 static void drop_capabilities() {
+  ATRACE_CALL();
   __user_cap_header_struct capheader;
   memset(&capheader, 0, sizeof(capheader));
   capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -194,6 +199,8 @@
 }
 
 int main(int argc, char** argv) {
+  atrace_begin(ATRACE_TAG, "before reparent");
+
   pid_t target = getppid();
   bool tombstoned_connected = false;
   unique_fd tombstoned_socket;
@@ -261,6 +268,8 @@
     PLOG(FATAL) << "parent died";
   }
 
+  atrace_end(ATRACE_TAG);
+
   // Reparent ourselves to init, so that the signal handler can waitpid on the
   // original process to avoid leaving a zombie for non-fatal dumps.
   pid_t forkpid = fork();
@@ -270,47 +279,60 @@
     exit(0);
   }
 
+  ATRACE_NAME("after reparent");
+
   // Die if we take too long.
   alarm(2);
 
   std::string attach_error;
 
-  // Seize the main thread.
-  if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
-    LOG(FATAL) << attach_error;
-  }
-
-  // Seize the siblings.
   std::map<pid_t, std::string> threads;
+
   {
-    std::set<pid_t> siblings;
-    if (!android::procinfo::GetProcessTids(target, &siblings)) {
-      PLOG(FATAL) << "failed to get process siblings";
+    ATRACE_NAME("ptrace");
+    // Seize the main thread.
+    if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+      LOG(FATAL) << attach_error;
     }
 
-    // but not the already attached main thread.
-    siblings.erase(main_tid);
-    // or the handler pseudothread.
-    siblings.erase(pseudothread_tid);
+    // Seize the siblings.
+    {
+      std::set<pid_t> siblings;
+      if (!android::procinfo::GetProcessTids(target, &siblings)) {
+        PLOG(FATAL) << "failed to get process siblings";
+      }
 
-    for (pid_t sibling_tid : siblings) {
-      if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
-        LOG(WARNING) << attach_error;
-      } else {
-        threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+      // but not the already attached main thread.
+      siblings.erase(main_tid);
+      // or the handler pseudothread.
+      siblings.erase(pseudothread_tid);
+
+      for (pid_t sibling_tid : siblings) {
+        if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
+          LOG(WARNING) << attach_error;
+        } else {
+          threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+        }
       }
     }
   }
 
   // Collect the backtrace map, open files, and process/thread names, while we still have caps.
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
-  if (!backtrace_map) {
-    LOG(FATAL) << "failed to create backtrace map";
+  std::unique_ptr<BacktraceMap> backtrace_map;
+  {
+    ATRACE_NAME("backtrace map");
+    backtrace_map.reset(BacktraceMap::Create(main_tid));
+    if (!backtrace_map) {
+      LOG(FATAL) << "failed to create backtrace map";
+    }
   }
 
   // Collect the list of open files.
   OpenFilesList open_files;
-  populate_open_files_list(target, &open_files);
+  {
+    ATRACE_NAME("open files");
+    populate_open_files_list(target, &open_files);
+  }
 
   std::string process_name = get_process_name(main_tid);
   threads.emplace(main_tid, get_thread_name(main_tid));
@@ -318,9 +340,12 @@
   // Drop our capabilities now that we've attached to the threads we care about.
   drop_capabilities();
 
-  const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
-  LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
-  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+  {
+    ATRACE_NAME("tombstoned_connect");
+    const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+    tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+  }
 
   // Write a '\1' to stdout to tell the crashing process to resume.
   // It also restores the value of PR_SET_DUMPABLE at this point.
@@ -349,9 +374,12 @@
   }
 
   siginfo_t siginfo = {};
-  if (!wait_for_signal(main_tid, &siginfo)) {
-    printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
-    exit(1);
+  {
+    ATRACE_NAME("wait_for_signal");
+    if (!wait_for_signal(main_tid, &siginfo)) {
+      printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+      exit(1);
+    }
   }
 
   int signo = siginfo.si_signo;
@@ -373,8 +401,10 @@
 
   std::string amfd_data;
   if (backtrace) {
+    ATRACE_NAME("dump_backtrace");
     dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
   } else {
+    ATRACE_NAME("engrave_tombstone");
     engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
                       process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
   }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9008b95..da8ad37 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -89,7 +89,7 @@
   } while (0)
 
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
-                                 DebuggerdDumpType intercept_type) {
+                                 InterceptStatus* status, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
@@ -136,7 +136,7 @@
            << ", received " << rc;
   }
 
-  ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+  *status = response.status;
 }
 
 class CrasherTest : public ::testing::Test {
@@ -180,7 +180,9 @@
     FAIL() << "crasher hasn't been started";
   }
 
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type);
+  InterceptStatus status;
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -598,7 +600,9 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+    InterceptStatus status;
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+    ASSERT_EQ(InterceptStatus::kRegistered, status);
 
     {
       unique_fd tombstoned_socket, input_fd;
@@ -630,7 +634,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -661,7 +667,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       {
         unique_fd tombstoned_socket, input_fd;
@@ -685,3 +693,65 @@
     thread.join();
   }
 }
+
+TEST(tombstoned, java_trace_intercept_smoke) {
+  // Using a "real" PID is a little dangerous here - if the test fails
+  // or crashes, we might end up getting a bogus / unreliable stack
+  // trace.
+  const pid_t self = getpid();
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  // First connect to tombstoned requesting a native backtrace. This
+  // should result in a "regular" FD and not the installed intercept.
+  const char native[] = "native";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  // Then, connect to tombstoned asking for a java backtrace. This *should*
+  // trigger the intercept.
+  const char java[] = "java";
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(java)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("java", outbuf);
+}
+
+TEST(tombstoned, multiple_intercepts) {
+  const pid_t fake_pid = 1'234'567;
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  unique_fd intercept_fd_2, output_fd_2;
+  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+}
+
+TEST(tombstoned, intercept_any) {
+  const pid_t fake_pid = 1'234'567;
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  const char any[] = "any";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(any)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("any", outbuf);
+}
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index b4a2bde..b920ebc 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -165,7 +165,7 @@
 };
 
 // Whether java trace dumps are produced via tombstoned.
-static constexpr bool kJavaTraceDumpsEnabled = false;
+static constexpr bool kJavaTraceDumpsEnabled = true;
 
 /* static */ CrashQueue* const CrashQueue::tombstone =
     new CrashQueue("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..0177fdf 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -689,27 +689,55 @@
     return read_verity_state(fstab->verity_loc, offset, mode);
 }
 
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
-    std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+//   verity table token:       /dev/block/bootdevice/by-name/vendor
+//   blk_device-1 (non-A/B):   /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device-2 (A/B):       /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+//   verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device:         /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+                                           bool slot_select) {
+    bool updated = false;
+    std::string result, ab_suffix;
     auto tokens = android::base::Split(*table, " ");
 
+    // If slot_select is set, it means blk_device is already updated with ab_suffix.
+    if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
     for (const auto& token : tokens) {
-        if (android::base::StartsWith(token, "/dev/block/") &&
-            android::base::StartsWith(blk_device, token.c_str())) {
-            word = blk_device;
+        std::string new_token;
+        if (android::base::StartsWith(token, "/dev/block/")) {
+            if (token == blk_device) return;  // no need to update if they're already the same.
+            std::size_t found1 = blk_device.find("by-name");
+            std::size_t found2 = token.find("by-name");
+            if (found1 != std::string::npos && found2 != std::string::npos &&
+                blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+                new_token = blk_device;
+            }
+        }
+
+        if (!new_token.empty()) {
+            updated = true;
+            LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+                  << "'";
         } else {
-            word = token;
+            new_token = token;
         }
 
         if (result.empty()) {
-            result = word;
+            result = new_token;
         } else {
-            result += " " + word;
+            result += " " + new_token;
         }
     }
 
-    if (result.empty()) {
+    if (!updated) {
         return;
     }
 
@@ -825,10 +853,9 @@
     LINFO << "Enabling dm-verity for " << mount_point.c_str()
           << " (mode " << params.mode << ")";
 
-    if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
-        // Update the verity params using the actual block device path
-        update_verity_table_blk_device(fstab->blk_device, &params.table);
-    }
+    // Update the verity params using the actual block device path
+    update_verity_table_blk_device(fstab->blk_device, &params.table,
+                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
     if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 31fc2df..ece8693 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -71,8 +71,17 @@
   // Modification time. The zipfile format specifies
   // that the first two little endian bytes contain the time
   // and the last two little endian bytes contain the date.
+  // See `GetModificationTime`.
+  // TODO: should be overridden by extra time field, if present.
   uint32_t mod_time;
 
+  // Returns `mod_time` as a broken-down struct tm.
+  struct tm GetModificationTime() const;
+
+  // Suggested Unix mode for this entry, from the zip archive if created on
+  // Unix, or a default otherwise.
+  mode_t unix_mode;
+
   // 1 if this entry contains a data descriptor segment, 0
   // otherwise.
   uint8_t has_data_descriptor;
diff --git a/init/Android.bp b/init/Android.bp
index af1e9d3..fce424e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -20,7 +20,6 @@
     sanitize: {
         misc_undefined: ["integer"],
     },
-    tidy_checks: ["-misc-forwarding-reference-overload"],
     cppflags: [
         "-DLOG_UEVENTS=0",
         "-Wall",
diff --git a/init/Android.mk b/init/Android.mk
index 489d076..6cd47f4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -40,8 +40,6 @@
 # --
 
 include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
diff --git a/init/service.cpp b/init/service.cpp
index 7c931da..1a6474b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -209,17 +209,6 @@
 }
 
 void Service::KillProcessGroup(int signal) {
-    // We ignore reporting errors of ESRCH as this commonly happens in the below case,
-    // 1) Terminate() is called, which sends SIGTERM to the process
-    // 2) The process successfully exits
-    // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
-    // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
-    // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
-    // we should do this kill with that pid first before calling waitpid().
-    if (kill(-pid_, signal) == -1 && errno != ESRCH) {
-        PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
-    }
-
     // If we've already seen a successful result from killProcessGroup*(), then we have removed
     // the cgroup already and calling these functions a second time will simply result in an error.
     // This is true regardless of which signal was sent.
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 7170b48..3472a67 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -343,7 +343,7 @@
     return 0;
 }
 
-static void set_timerslack_ns(int tid, unsigned long long slack) {
+static void set_timerslack_ns(int tid, unsigned long slack) {
     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
     // TODO: once we've backported this, log if the open(2) fails.
     if (__sys_supports_timerslack) {
@@ -351,7 +351,7 @@
         snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
         int fd = open(buf, O_WRONLY | O_CLOEXEC);
         if (fd != -1) {
-            int len = snprintf(buf, sizeof(buf), "%llu", slack);
+            int len = snprintf(buf, sizeof(buf), "%lu", slack);
             if (write(fd, buf, len) != len) {
                 SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
             }
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 /*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 27b4065..f5d4e1c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,10 +27,12 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <memory>
 #include <mutex>
+#include <set>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -258,6 +260,12 @@
         return -errno;
     }
 
+    // We separate all of the pids in the cgroup into those pids that are also the leaders of
+    // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+    std::set<pid_t> pgids;
+    pgids.emplace(initialPid);
+    std::set<pid_t> pids;
+
     int ret;
     pid_t pid;
     int processes = 0;
@@ -269,8 +277,40 @@
             LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
             continue;
         }
+        pid_t pgid = getpgid(pid);
+        if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+        if (pgid == pid) {
+            pgids.emplace(pid);
+        } else {
+            pids.emplace(pid);
+        }
+    }
+
+    // Erase all pids that will be killed when we kill the process groups.
+    for (auto it = pids.begin(); it != pids.end();) {
+        pid_t pgid = getpgid(pid);
+        if (pgids.count(pgid) == 1) {
+            it = pids.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Kill all process groups.
+    for (const auto pgid : pgids) {
+        LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+                     << " as part of process cgroup " << initialPid;
+
+        if (kill(-pgid, signal) == -1) {
+            PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+        }
+    }
+
+    // Kill remaining pids.
+    for (const auto pid : pids) {
         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
                      << initialPid;
+
         if (kill(pid, signal) == -1) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 12f77bb..cbf042e 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -45,9 +45,11 @@
 #ifndef _WIN32
     if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
         ALOGE("%s: Failed to format string", __FUNCTION__);
+        va_end(arglist);
         return;
     }
 #else
+    va_end(arglist);
     return;
 #endif
 
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 287a99c..1084d59 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -52,17 +52,25 @@
     ],
 }
 
-
 cc_library {
     name: "libziparchive",
     host_supported: true,
-    vendor_available:true,
+    vendor_available: true,
 
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    shared_libs: ["liblog", "libbase"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
     target: {
         android: {
-            shared_libs: ["libz", "libutils"],
+            shared_libs: [
+                "libz",
+                "libutils",
+            ],
         },
         host: {
             static_libs: ["libutils"],
@@ -88,7 +96,10 @@
     name: "libziparchive-host",
     host_supported: true,
     device_supported: false,
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
     shared_libs: ["libz-host"],
     static_libs: ["libutils"],
 }
@@ -150,3 +161,13 @@
         },
     },
 }
+
+cc_binary {
+    name: "unzip",
+    defaults: ["libziparchive_flags"],
+    srcs: ["unzip.cpp"],
+    shared_libs: [
+        "libbase",
+        "libziparchive",
+    ],
+}
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
new file mode 100644
index 0000000..6756007
--- /dev/null
+++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+
+enum OverwriteMode {
+  kAlways,
+  kNever,
+  kPrompt,
+};
+
+static OverwriteMode overwrite_mode = kPrompt;
+static const char* flag_d = nullptr;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = false;
+static const char* archive_name = nullptr;
+static std::set<std::string> includes;
+static std::set<std::string> excludes;
+static uint64_t total_uncompressed_length = 0;
+static uint64_t total_compressed_length = 0;
+static size_t file_count = 0;
+
+static bool Filter(const std::string& name) {
+  if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
+  if (!includes.empty() && includes.find(name) == includes.end()) return true;
+  return false;
+}
+
+static bool MakeDirectoryHierarchy(const std::string& path) {
+  // stat rather than lstat because a symbolic link to a directory is fine too.
+  struct stat sb;
+  if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
+
+  // Ensure the parent directories exist first.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
+
+  // Then try to create this directory.
+  return (mkdir(path.c_str(), 0777) != -1);
+}
+
+static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+  if (uncompressed == 0) return 0;
+  return (100LL * (uncompressed - compressed)) / uncompressed;
+}
+
+static void MaybeShowHeader() {
+  if (!flag_q) printf("Archive:  %s\n", archive_name);
+  if (flag_v) {
+    printf(
+        " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
+        "--------  ------  ------- ---- ---------- ----- --------  ----\n");
+  } else if (flag_l) {
+    printf(
+        "  Length      Date    Time    Name\n"
+        "---------  ---------- -----   ----\n");
+  }
+}
+
+static void MaybeShowFooter() {
+  if (flag_v) {
+    printf(
+        "--------          -------  ---                            -------\n"
+        "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+        total_uncompressed_length, total_compressed_length,
+        CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+        (file_count == 1) ? "" : "s");
+  } else if (flag_l) {
+    printf(
+        "---------                     -------\n"
+        "%9" PRId64 "                     %zu file%s\n",
+        total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+  }
+}
+
+static bool PromptOverwrite(const std::string& dst) {
+  // TODO: [r]ename not implemented because it doesn't seem useful.
+  printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
+  fflush(stdout);
+  while (true) {
+    char* line = nullptr;
+    size_t n;
+    if (getline(&line, &n, stdin) == -1) {
+      error(1, 0, "(EOF/read error; assuming [N]one...)");
+      overwrite_mode = kNever;
+      return false;
+    }
+    if (n == 0) continue;
+    char cmd = line[0];
+    free(line);
+    switch (cmd) {
+      case 'y':
+        return true;
+      case 'n':
+        return false;
+      case 'A':
+        overwrite_mode = kAlways;
+        return true;
+      case 'N':
+        overwrite_mode = kNever;
+        return false;
+    }
+  }
+}
+
+static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // We need to extract to memory because ExtractEntryToFile insists on
+  // being able to seek and truncate, and you can't do that with stdout.
+  uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+  int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+  if (err < 0) {
+    error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+  }
+  if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+    error(1, errno, "failed to write %s to stdout", name.c_str());
+  }
+  delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // Bad filename?
+  if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+      name.find("/../") != std::string::npos) {
+    error(1, 0, "bad filename %s", name.c_str());
+  }
+
+  // Where are we actually extracting to (for human-readable output)?
+  std::string dst;
+  if (flag_d) {
+    dst = flag_d;
+    if (!android::base::EndsWith(dst, "/")) dst += '/';
+  }
+  dst += name;
+
+  // Ensure the directory hierarchy exists.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+    error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+  }
+
+  // An entry in a zip file can just be a directory itself.
+  if (android::base::EndsWith(name, "/")) {
+    if (mkdir(name.c_str(), entry.unix_mode) == -1) {
+      // If the directory already exists, that's fine.
+      if (errno == EEXIST) {
+        struct stat sb;
+        if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
+      }
+      error(1, errno, "couldn't extract directory %s", dst.c_str());
+    }
+    return;
+  }
+
+  // Create the file.
+  int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
+  if (fd == -1 && errno == EEXIST) {
+    if (overwrite_mode == kNever) return;
+    if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
+    // Either overwrite_mode is kAlways or the user consented to this specific case.
+    fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
+  }
+  if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+
+  // Actually extract into the file.
+  if (!flag_q) printf("  inflating: %s\n", dst.c_str());
+  int err = ExtractEntryToFile(zah, &entry, fd);
+  if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+  close(fd);
+}
+
+static void ListOne(const ZipEntry& entry, const std::string& name) {
+  tm t = entry.GetModificationTime();
+  char time[32];
+  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+           t.tm_mday, t.tm_hour, t.tm_min);
+  if (flag_v) {
+    printf("%8d  %s  %7d %3d%% %s %08x  %s\n", entry.uncompressed_length,
+           (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
+           CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
+           name.c_str());
+  } else {
+    printf("%9d  %s   %s\n", entry.uncompressed_length, time, name.c_str());
+  }
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  if (flag_l || flag_v) {
+    // -l or -lv or -lq or -v.
+    ListOne(entry, name);
+  } else {
+    // Actually extract.
+    if (flag_p) {
+      ExtractToPipe(zah, entry, name);
+    } else {
+      ExtractOne(zah, entry, name);
+    }
+  }
+  total_uncompressed_length += entry.uncompressed_length;
+  total_compressed_length += entry.compressed_length;
+  ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+  MaybeShowHeader();
+
+  // libziparchive iteration order doesn't match the central directory.
+  // We could sort, but that would cost extra and wouldn't match either.
+  void* cookie;
+  int err = StartIteration(zah, &cookie, nullptr, nullptr);
+  if (err != 0) {
+    error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  ZipEntry entry;
+  ZipString string;
+  while ((err = Next(cookie, &entry, &string)) >= 0) {
+    std::string name(string.name, string.name + string.name_length);
+    if (!Filter(name)) ProcessOne(zah, entry, name);
+  }
+
+  if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+  EndIteration(cookie);
+
+  MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+  fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+  if (!full) exit(EXIT_FAILURE);
+
+  printf(
+      "\n"
+      "Extract FILEs from ZIP archive. Default is all files.\n"
+      "\n"
+      "-d DIR	Extract into DIR\n"
+      "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
+      "-n	Never overwrite files (default: prompt)\n"
+      "-o	Always overwrite files\n"
+      "-p	Pipe to stdout\n"
+      "-q	Quiet\n"
+      "-v	List contents verbosely\n"
+      "-x FILE	Exclude files\n");
+  exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+  static struct option opts[] = {
+      {"help", no_argument, 0, 'h'},
+  };
+  bool saw_x = false;
+  int opt;
+  while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+    switch (opt) {
+      case 'd':
+        flag_d = optarg;
+        break;
+      case 'h':
+        ShowHelp(true);
+        break;
+      case 'l':
+        flag_l = true;
+        break;
+      case 'n':
+        overwrite_mode = kNever;
+        break;
+      case 'o':
+        overwrite_mode = kAlways;
+        break;
+      case 'p':
+        flag_p = flag_q = true;
+        break;
+      case 'q':
+        flag_q = true;
+        break;
+      case 'v':
+        flag_v = true;
+        break;
+      case 'x':
+        saw_x = true;
+        break;
+      case 1:
+        // -x swallows all following arguments, so we use '-' in the getopt
+        // string and collect files here.
+        if (!archive_name) {
+          archive_name = optarg;
+        } else if (saw_x) {
+          excludes.insert(optarg);
+        } else {
+          includes.insert(optarg);
+        }
+        break;
+      default:
+        ShowHelp(false);
+    }
+  }
+
+  if (!archive_name) error(1, 0, "missing archive filename");
+
+  // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
+  ZipArchiveHandle zah;
+  int32_t err;
+  if ((err = OpenArchive(archive_name, &zah)) != 0) {
+    error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  // Implement -d by changing into that directory.
+  // We'll create implicit directories based on paths in the zip file, but we
+  // require that the -d directory already exists.
+  if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+
+  ProcessAll(zah);
+
+  CloseArchive(zah);
+  return 0;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 78de40a..c82eaca 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <memory>
@@ -48,6 +49,10 @@
 
 using android::base::get_unaligned;
 
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static const bool kCrcChecksEnabled = false;
+
 // This is for windows. If we don't open a file in binary mode, weird
 // things will happen.
 #ifndef O_BINARY
@@ -496,8 +501,7 @@
   delete archive;
 }
 
-static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
-                                             ZipEntry *entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
   uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
   if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
     return kIoError;
@@ -507,9 +511,17 @@
   const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
   const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
 
-  entry->crc32 = descriptor->crc32;
-  entry->compressed_length = descriptor->compressed_size;
-  entry->uncompressed_length = descriptor->uncompressed_size;
+  // Validate that the values in the data descriptor match those in the central
+  // directory.
+  if (entry->compressed_length != descriptor->compressed_size ||
+      entry->uncompressed_length != descriptor->uncompressed_size ||
+      entry->crc32 != descriptor->crc32) {
+    ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+          "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+          entry->compressed_length, entry->uncompressed_length, entry->crc32,
+          descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+    return kInconsistentInformation;
+  }
 
   return 0;
 }
@@ -577,12 +589,22 @@
   // Paranoia: Match the values specified in the local file header
   // to those specified in the central directory.
 
-  // Verify that the central directory and local file header have the same general purpose bit
-  // flags set.
-  if (lfh->gpb_flags != cdr->gpb_flags) {
-    ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+  // Warn if central directory and local file header don't agree on the use
+  // of a trailing Data Descriptor. The reference implementation is inconsistent
+  // and appears to use the LFH value during extraction (unzip) but the CD value
+  // while displayng information about archives (zipinfo). The spec remains
+  // silent on this inconsistency as well.
+  //
+  // For now, always use the version from the LFH but make sure that the values
+  // specified in the central directory match those in the data descriptor.
+  //
+  // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+  // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+  // encoded using UTF-8). This implementation does not check for the presence of
+  // that flag and always enforces that entry names are valid UTF-8.
+  if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
+    ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
           cdr->gpb_flags, lfh->gpb_flags);
-    return kInconsistentInformation;
   }
 
   // If there is no trailing data descriptor, verify that the central directory and local file
@@ -602,6 +624,13 @@
     data->has_data_descriptor = 1;
   }
 
+  // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+  if ((cdr->version_made_by >> 8) == 3) {
+    data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+  } else {
+    data->unix_mode = 0777;
+  }
+
   // Check that the local file header name matches the declared
   // name in the central directory.
   if (lfh->file_name_length == nameLen) {
@@ -945,6 +974,7 @@
 
   const uint32_t uncompressed_length = entry->uncompressed_length;
 
+  uint64_t crc = 0;
   uint32_t compressed_length = entry->compressed_length;
   do {
     /* read as much as we can */
@@ -977,6 +1007,8 @@
       if (!writer->Append(&write_buf[0], write_size)) {
         // The file might have declared a bogus length.
         return kInconsistentInformation;
+      } else {
+        crc = crc32(crc, &write_buf[0], write_size);
       }
 
       zstream.next_out = &write_buf[0];
@@ -986,8 +1018,13 @@
 
   assert(zerr == Z_STREAM_END);     /* other errors should've been caught */
 
-  // stream.adler holds the crc32 value for such streams.
-  *crc_out = zstream.adler;
+  // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+  // "feature" of zlib to tell it there won't be a zlib file header. zlib
+  // doesn't bother calculating the checksum in that scenario. We just do
+  // it ourselves above because there are no additional gains to be made by
+  // having zlib calculate it for us, since they do it by calling crc32 in
+  // the same manner that we have above.
+  *crc_out = crc;
 
   if (zstream.total_out != uncompressed_length || compressed_length != 0) {
     ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
@@ -1050,15 +1087,14 @@
   }
 
   if (!return_value && entry->has_data_descriptor) {
-    return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+    return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
     if (return_value) {
       return return_value;
     }
   }
 
-  // TODO: Fix this check by passing the right flags to inflate2 so that
-  // it calculates the CRC for us.
-  if (entry->crc32 != crc && false) {
+  // Validate that the CRC matches the calculated value.
+  if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
     ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
     return kInconsistentInformation;
   }
@@ -1240,3 +1276,17 @@
   }
   return true;
 }
+
+tm ZipEntry::GetModificationTime() const {
+  tm t = {};
+
+  t.tm_hour = (mod_time >> 11) & 0x1f;
+  t.tm_min = (mod_time >> 5) & 0x3f;
+  t.tm_sec = (mod_time & 0x1f) << 1;
+
+  t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
+  t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
+  t.tm_mday = (mod_time >> 16) & 0x1f;
+
+  return t;
+}
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index ca42509..bc1ebb4 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -73,7 +73,7 @@
 
   // The start of record signature. Must be |kSignature|.
   uint32_t record_signature;
-  // Tool version. Ignored by this implementation.
+  // Source tool version. Top byte gives source OS.
   uint16_t version_made_by;
   // Tool version. Ignored by this implementation.
   uint16_t version_needed;
@@ -106,7 +106,7 @@
   uint16_t file_start_disk;
   // File attributes. Ignored by this implementation.
   uint16_t internal_file_attributes;
-  // File attributes. Ignored by this implementation.
+  // File attributes. For archives created on Unix, the top bits are the mode.
   uint32_t external_file_attributes;
   // The offset to the local file header for this entry, from the
   // beginning of this archive.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 493a0ce..7376725 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -642,6 +642,96 @@
   CloseArchive(handle);
 }
 
+// Generated using the following Java program:
+//     public static void main(String[] foo) throws Exception {
+//       FileOutputStream fos = new
+//       FileOutputStream("/tmp/data_descriptor.zip");
+//       ZipOutputStream zos = new ZipOutputStream(fos);
+//       ZipEntry ze = new ZipEntry("name");
+//       ze.setMethod(ZipEntry.DEFLATED);
+//       zos.putNextEntry(ze);
+//       zos.write("abdcdefghijk".getBytes());
+//       zos.closeEntry();
+//       zos.close();
+//     }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+    0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+    //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+    0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+    0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+                                 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+
+  // This function expects a variant of kDataDescriptorZipFile, for look for
+  // an entry whose name is "name" and whose size is 12 (contents =
+  // "abdcdefghijk").
+  ZipEntry entry;
+  ZipString empty_name;
+  SetZipString(&empty_name, "name");
+
+  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+  entry_out->resize(12);
+  (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+  ASSERT_EQ(0, error_code);
+  ASSERT_EQ(12u, entry.size());
+  ASSERT_EQ('a', entry[0]);
+  ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors) {
+  std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+  invalid_csize[kCSizeOffset] = 0xfe;
+
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_GT(0, error_code);
+  ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+
+  std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+  invalid_csize[kSizeOffset] = 0xfe;
+
+  error_code = 0;
+  entry.clear();
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_GT(0, error_code);
+  ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+}
+
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..9ad0252 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -135,17 +135,6 @@
   CloseArchive(handle);
 }
 
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
-  memset(tm, 0, sizeof(struct tm));
-  tm->tm_hour = (zip_time >> 11) & 0x1f;
-  tm->tm_min = (zip_time >> 5) & 0x3f;
-  tm->tm_sec = (zip_time & 0x1f) << 1;
-
-  tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
-  tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
-  tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
 static struct tm MakeTm() {
   struct tm tm;
   memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -228,8 +216,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
deleted file mode 100644
index 8fb5655..0000000
--- a/toolbox/upstream-netbsd/include/sys/mtio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/mtio.h>