Merge "Fix spelling error in warning"
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 3de7be6..4979eef 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -782,9 +782,16 @@
     return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
 }
 
-static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
-                               bool show_progress)
-{
+static int adb_download_buffer(const char* service, const char* filename) {
+    std::string content;
+    if (!android::base::ReadFileToString(filename, &content)) {
+        fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+
+    const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
+    unsigned sz = content.size();
+
     std::string error;
     int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
     if (fd < 0) {
@@ -798,10 +805,8 @@
     unsigned total = sz;
     const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
 
-    if (show_progress) {
-        const char* x = strrchr(service, ':');
-        if (x) service = x + 1;
-    }
+    const char* x = strrchr(service, ':');
+    if (x) service = x + 1;
 
     while (sz > 0) {
         unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
@@ -814,14 +819,10 @@
         }
         sz -= xfer;
         ptr += xfer;
-        if (show_progress) {
-            printf("sending: '%s' %4d%%    \r", fn, (int)(100LL - ((100LL * sz) / (total))));
-            fflush(stdout);
-        }
+        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+        fflush(stdout);
     }
-    if (show_progress) {
-        printf("\n");
-    }
+    printf("\n");
 
     if (!adb_status(fd, &error)) {
         fprintf(stderr,"* error response '%s' *\n", error.c_str());
@@ -854,38 +855,40 @@
  * - When the other side sends "DONEDONE" instead of a block number,
  *   we hang up.
  */
-static int adb_sideload_host(const char* fn) {
-    fprintf(stderr, "loading: '%s'...\n", fn);
-
-    std::string content;
-    if (!android::base::ReadFileToString(fn, &content)) {
-        fprintf(stderr, "failed: %s\n", strerror(errno));
+static int adb_sideload_host(const char* filename) {
+    fprintf(stderr, "opening '%s'...\n", filename);
+    struct stat sb;
+    if (stat(filename, &sb) == -1) {
+        fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+    unique_fd package_fd(adb_open(filename, O_RDONLY));
+    if (package_fd == -1) {
+        fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
         return -1;
     }
 
-    const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
-    unsigned sz = content.size();
-
     fprintf(stderr, "connecting...\n");
-    std::string service =
-            android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
+    std::string service = android::base::StringPrintf(
+        "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
-    unique_fd fd(adb_connect(service, &error));
-    if (fd < 0) {
-        // Try falling back to the older sideload method.  Maybe this
+    unique_fd device_fd(adb_connect(service, &error));
+    if (device_fd < 0) {
+        // Try falling back to the older (<= K) sideload method. Maybe this
         // is an older device that doesn't support sideload-host.
         fprintf(stderr, "falling back to older sideload method...\n");
-        return adb_download_buffer("sideload", fn, data, sz, true);
+        return adb_download_buffer("sideload", filename);
     }
 
     int opt = SIDELOAD_HOST_BLOCK_SIZE;
-    adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+    adb_setsockopt(device_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+    char buf[SIDELOAD_HOST_BLOCK_SIZE];
 
     size_t xfer = 0;
     int last_percent = -1;
     while (true) {
-        char buf[9];
-        if (!ReadFdExactly(fd, buf, 8)) {
+        if (!ReadFdExactly(device_fd, buf, 8)) {
             fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
             return -1;
         }
@@ -893,26 +896,35 @@
 
         if (strcmp("DONEDONE", buf) == 0) {
             printf("\rTotal xfer: %.2fx%*s\n",
-                   (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, "");
+                   static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
+                   static_cast<int>(strlen(filename) + 10), "");
             return 0;
         }
 
         int block = strtol(buf, NULL, 10);
 
         size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
-        if (offset >= sz) {
+        if (offset >= static_cast<size_t>(sb.st_size)) {
             fprintf(stderr, "* attempt to read block %d past end\n", block);
             return -1;
         }
-        const uint8_t* start = data + offset;
-        size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE;
+
         size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
-        if (offset_end > sz) {
-            to_write = sz - offset;
+        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+            to_write = sb.st_size - offset;
         }
 
-        if (!WriteFdExactly(fd, start, to_write)) {
-            adb_status(fd, &error);
+        if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+            fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+            return -1;
+        }
+        if (!ReadFdExactly(package_fd, buf, to_write)) {
+            fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+            return -1;
+        }
+
+        if (!WriteFdExactly(device_fd, buf, to_write)) {
+            adb_status(device_fd, &error);
             fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
             return -1;
         }
@@ -924,9 +936,9 @@
         // extra access to things like the zip central directory).
         // This estimate of the completion becomes 100% when we've
         // transferred ~2.13 (=100/47) times the package size.
-        int percent = (int)(xfer * 47LL / (sz ? sz : 1));
+        int percent = static_cast<int>(xfer * 47LL / (sb.st_size ? sb.st_size : 1));
         if (percent != last_percent) {
-            printf("\rserving: '%s'  (~%d%%)    ", fn, percent);
+            printf("\rserving: '%s'  (~%d%%)    ", filename, percent);
             fflush(stdout);
             last_percent = percent;
         }
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index b89c395..e52762e 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -301,6 +301,7 @@
             allocated *= 2;
             canned_config = (struct fs_config_entry*)realloc(
                 canned_config, allocated * sizeof(struct fs_config_entry));
+            if (canned_config == NULL) die("failed to reallocate memory");
         }
 
         struct fs_config_entry* cc = canned_config + used;
@@ -320,6 +321,7 @@
         ++allocated;
         canned_config = (struct fs_config_entry*)realloc(
             canned_config, allocated * sizeof(struct fs_config_entry));
+        if (canned_config == NULL) die("failed to reallocate memory");
     }
     canned_config[used].name = NULL;
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 5ee07cd..325210d 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -77,7 +77,9 @@
     resetLogs();
     elf_set_fake_build_id("");
     siginfo_t si;
+    memset(&si, 0, sizeof(si));
     si.si_signo = SIGABRT;
+    si.si_code = SI_KERNEL;
     ptrace_set_fake_getsiginfo(si);
   }
 
@@ -292,7 +294,9 @@
   map_mock_->AddMap(map);
 
   siginfo_t si;
+  memset(&si, 0, sizeof(si));
   si.si_signo = SIGBUS;
+  si.si_code = SI_KERNEL;
   si.si_addr = reinterpret_cast<void*>(0x1000);
   ptrace_set_fake_getsiginfo(si);
   dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -348,7 +352,9 @@
   map_mock_->AddMap(map);
 
   siginfo_t si;
+  memset(&si, 0, sizeof(si));
   si.si_signo = SIGBUS;
+  si.si_code = SI_KERNEL;
   si.si_addr = reinterpret_cast<void*>(0xa533000);
   ptrace_set_fake_getsiginfo(si);
   dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -404,7 +410,9 @@
   map_mock_->AddMap(map);
 
   siginfo_t si;
+  memset(&si, 0, sizeof(si));
   si.si_signo = SIGBUS;
+  si.si_code = SI_KERNEL;
   si.si_addr = reinterpret_cast<void*>(0xa534040);
   ptrace_set_fake_getsiginfo(si);
   dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -458,7 +466,9 @@
   map_mock_->AddMap(map);
 
   siginfo_t si;
+  memset(&si, 0, sizeof(si));
   si.si_signo = SIGBUS;
+  si.si_code = SI_KERNEL;
 #if defined(__LP64__)
   si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
 #else
@@ -503,7 +513,7 @@
   map_mock_->AddMap(map);
 
   siginfo_t si;
-  si.si_signo = 0;
+  memset(&si, 0, sizeof(si));
   ptrace_set_fake_getsiginfo(si);
   dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
 
@@ -539,7 +549,9 @@
     ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
 
     siginfo_t si;
+    memset(&si, 0, sizeof(si));
     si.si_signo = i;
+    si.si_code = SI_KERNEL;
     si.si_addr = reinterpret_cast<void*>(0x1000);
     ptrace_set_fake_getsiginfo(si);
     dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -592,7 +604,7 @@
 
 TEST_F(TombstoneTest, dump_signal_info_error) {
   siginfo_t si;
-  si.si_signo = 0;
+  memset(&si, 0, sizeof(si));
   ptrace_set_fake_getsiginfo(si);
 
   dump_signal_info(&log_, 123);
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index f3bba7b..a536b22 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -35,10 +35,11 @@
                   return -1;
                 }
                 got_suffix = 1;
+                // remove below line when bootloaders fix androidboot.slot_suffix param
+                if (suffix[0] == '_') suffix.erase(suffix.begin());
             }
 
-            if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
-                         suffix.c_str()) > 0) {
+            if (asprintf(&tmp, "%s_%s", fstab->recs[n].blk_device, suffix.c_str()) > 0) {
                 free(fstab->recs[n].blk_device);
                 fstab->recs[n].blk_device = tmp;
             } else {
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 0b6ede4..41ca2e1 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -17,15 +17,16 @@
 #ifndef LIBZIPARCHIVE_ZIPWRITER_H_
 #define LIBZIPARCHIVE_ZIPWRITER_H_
 
-#include "android-base/macros.h"
-#include <utils/Compat.h>
-
 #include <cstdio>
 #include <ctime>
+#include <zlib.h>
+
 #include <memory>
 #include <string>
 #include <vector>
-#include <zlib.h>
+
+#include "android-base/macros.h"
+#include "utils/Compat.h"
 
 /**
  * Writes a Zip file via a stateful interface.
@@ -63,6 +64,20 @@
     kAlign32 = 0x02,
   };
 
+  /**
+   * A struct representing a zip file entry.
+   */
+  struct FileEntry {
+    std::string path;
+    uint16_t compression_method;
+    uint32_t crc32;
+    uint32_t compressed_size;
+    uint32_t uncompressed_size;
+    uint16_t last_mod_time;
+    uint16_t last_mod_date;
+    uint32_t local_file_header_offset;
+  };
+
   static const char* ErrorCodeString(int32_t error_code);
 
   /**
@@ -122,6 +137,19 @@
   int32_t FinishEntry();
 
   /**
+   * Discards the last-written entry. Can only be called after an entry has been written using
+   * FinishEntry().
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t DiscardLastEntry();
+
+  /**
+   * Sets `out_entry` to the last entry written after a call to FinishEntry().
+   * Returns 0 on success, and an error value < 0 if no entries have been written.
+   */
+  int32_t GetLastEntry(FileEntry* out_entry);
+
+  /**
    * Writes the Central Directory Headers and flushes the zip file stream.
    * Returns 0 on success, and an error value < 0 on failure.
    */
@@ -130,22 +158,11 @@
 private:
   DISALLOW_COPY_AND_ASSIGN(ZipWriter);
 
-  struct FileInfo {
-    std::string path;
-    uint16_t compression_method;
-    uint32_t crc32;
-    uint32_t compressed_size;
-    uint32_t uncompressed_size;
-    uint16_t last_mod_time;
-    uint16_t last_mod_date;
-    uint32_t local_file_header_offset;
-  };
-
   int32_t HandleError(int32_t error_code);
   int32_t PrepareDeflate();
-  int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
-  int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
-  int32_t FlushCompressedBytes(FileInfo* file);
+  int32_t StoreBytes(FileEntry* file, const void* data, size_t len);
+  int32_t CompressBytes(FileEntry* file, const void* data, size_t len);
+  int32_t FlushCompressedBytes(FileEntry* file);
 
   enum class State {
     kWritingZip,
@@ -157,7 +174,8 @@
   FILE* file_;
   off64_t current_offset_;
   State state_;
-  std::vector<FileInfo> files_;
+  std::vector<FileEntry> files_;
+  FileEntry current_file_entry_;
 
   std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
diff --git a/init/README.md b/init/README.md
index 99522b9..709c667 100644
--- a/init/README.md
+++ b/init/README.md
@@ -282,6 +282,11 @@
 `copy <src> <dst>`
 > Copies a file. Similar to write, but useful for binary/large
   amounts of data.
+  Regarding to the src file, copying from symbol link file and world-writable
+  or group-writable files are not allowed.
+  Regarding to the dst file, the default mode created is 0600 if it does not
+  exist. And it will be truncated if dst file is a normal regular file and
+  already exists.
 
 `domainname <name>`
 > Set the domain name.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 24875d5..32e9ef6 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -704,61 +704,11 @@
 }
 
 static int do_copy(const std::vector<std::string>& args) {
-    char *buffer = NULL;
-    int rc = 0;
-    int fd1 = -1, fd2 = -1;
-    struct stat info;
-    int brtw, brtr;
-    char *p;
-
-    if (stat(args[1].c_str(), &info) < 0)
-        return -1;
-
-    if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
-        goto out_err;
-
-    if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
-        goto out_err;
-
-    if (!(buffer = (char*) malloc(info.st_size)))
-        goto out_err;
-
-    p = buffer;
-    brtr = info.st_size;
-    while(brtr) {
-        rc = read(fd1, p, brtr);
-        if (rc < 0)
-            goto out_err;
-        if (rc == 0)
-            break;
-        p += rc;
-        brtr -= rc;
+    std::string data;
+    if (read_file(args[1].c_str(), &data)) {
+        return write_file(args[2].c_str(), data.data()) ? 0 : 1;
     }
-
-    p = buffer;
-    brtw = info.st_size;
-    while(brtw) {
-        rc = write(fd2, p, brtw);
-        if (rc < 0)
-            goto out_err;
-        if (rc == 0)
-            break;
-        p += rc;
-        brtw -= rc;
-    }
-
-    rc = 0;
-    goto out;
-out_err:
-    rc = -1;
-out:
-    if (buffer)
-        free(buffer);
-    if (fd1 >= 0)
-        close(fd1);
-    if (fd2 >= 0)
-        close(fd2);
-    return rc;
+    return 1;
 }
 
 static int do_chown(const std::vector<std::string>& args) {
diff --git a/init/init.cpp b/init/init.cpp
index 0ce1c05..23448d6 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -1322,22 +1322,24 @@
     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
     while (true) {
-        if (!(waiting_for_exec || waiting_for_prop)) {
-            am.ExecuteOneCommand();
-            restart_processes();
-        }
-
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
-        // If there's a process that needs restarting, wake up in time for that.
-        if (process_needs_restart_at != 0) {
-            epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
-            if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+        if (!(waiting_for_exec || waiting_for_prop)) {
+            am.ExecuteOneCommand();
         }
+        if (!(waiting_for_exec || waiting_for_prop)) {
+            restart_processes();
 
-        // If there's more work to do, wake up again immediately.
-        if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+            // If there's a process that needs restarting, wake up in time for that.
+            if (process_needs_restart_at != 0) {
+                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
+                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+            }
+
+            // If there's more work to do, wake up again immediately.
+            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+        }
 
         epoll_event ev;
         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 3e2d61e..1e538c5 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -182,6 +182,7 @@
 
 static void __attribute__((noreturn))
 RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+    LOG(INFO) << "Reboot ending, jumping to kernel";
     switch (cmd) {
         case ANDROID_RB_POWEROFF:
             reboot(RB_POWER_OFF);
@@ -320,6 +321,7 @@
 void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
               bool runFsck) {
     Timer t;
+    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
     std::string timeout = property_get("ro.build.shutdown_timeout");
     unsigned int delay = 0;
 
diff --git a/init/service.cpp b/init/service.cpp
index 35aaa56..c8d1cb1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -889,15 +889,10 @@
         }
     }
 
-    std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid, supp_gids,
-                                               no_capabilities, namespace_flags, seclabel,
-                                               str_args));
-    if (!svc_p) {
-        LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'";
-        return nullptr;
-    }
+    auto svc_p = std::make_unique<Service>(name, "default", flags, uid, gid, supp_gids,
+                                           no_capabilities, namespace_flags, seclabel, str_args);
     Service* svc = svc_p.get();
-    services_.push_back(std::move(svc_p));
+    services_.emplace_back(std::move(svc_p));
 
     return svc;
 }
diff --git a/init/util.cpp b/init/util.cpp
index b90e5b1..0ba9800 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -185,8 +185,8 @@
 }
 
 bool write_file(const char* path, const char* content) {
-    android::base::unique_fd fd(
-        TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+        open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
         PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
         return false;
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 24c75c4..4e82e76 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,9 +17,15 @@
 #include "util.h"
 
 #include <errno.h>
+#include <fcntl.h>
+
+#include <sys/stat.h>
 
 #include <gtest/gtest.h>
 
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+
 TEST(util, read_file_ENOENT) {
   std::string s("hello");
   errno = 0;
@@ -28,6 +34,35 @@
   EXPECT_EQ("", s); // s was cleared.
 }
 
+TEST(util, read_file_group_writeable) {
+    std::string s("hello");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
+TEST(util, read_file_world_writeable) {
+    std::string s("hello");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
+TEST(util, read_file_symbol_link) {
+    std::string s("hello");
+    errno = 0;
+    // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
+    EXPECT_FALSE(read_file("/charger", &s));
+    EXPECT_EQ(ELOOP, errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
 TEST(util, read_file_success) {
   std::string s("hello");
   EXPECT_TRUE(read_file("/proc/version", &s));
@@ -37,6 +72,42 @@
   EXPECT_STREQ("Linux", s.c_str());
 }
 
+TEST(util, write_file_not_exist) {
+    std::string s("hello");
+    std::string s2("hello");
+    TemporaryDir test_dir;
+    std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
+    EXPECT_TRUE(write_file(path.c_str(), s.c_str()));
+    EXPECT_TRUE(read_file(path.c_str(), &s2));
+    EXPECT_EQ(s, s2);
+    struct stat sb;
+    int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+    EXPECT_NE(-1, fd);
+    EXPECT_EQ(0, fstat(fd, &sb));
+    EXPECT_NE(0u, sb.st_mode & S_IRUSR);
+    EXPECT_NE(0u, sb.st_mode & S_IWUSR);
+    EXPECT_EQ(0u, sb.st_mode & S_IXUSR);
+    EXPECT_EQ(0u, sb.st_mode & S_IRGRP);
+    EXPECT_EQ(0u, sb.st_mode & S_IWGRP);
+    EXPECT_EQ(0u, sb.st_mode & S_IXGRP);
+    EXPECT_EQ(0u, sb.st_mode & S_IROTH);
+    EXPECT_EQ(0u, sb.st_mode & S_IWOTH);
+    EXPECT_EQ(0u, sb.st_mode & S_IXOTH);
+    EXPECT_EQ(0, unlink(path.c_str()));
+}
+
+TEST(util, write_file_exist) {
+    std::string s2("");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
+    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_STREQ("1hello1", s2.c_str());
+    EXPECT_TRUE(write_file(tf.path, "2hello2"));
+    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_STREQ("2hello2", s2.c_str());
+}
+
 TEST(util, decode_uid) {
   EXPECT_EQ(0U, decode_uid("root"));
   EXPECT_EQ(UINT_MAX, decode_uid("toot"));
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index f99519a..f833af6 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -116,16 +116,21 @@
  * although the developer is advised to restrict the scope to the /vendor or
  * oem/ file-system since the intent is to provide support for customized
  * portions of a separate vendor.img or oem.img.  Has to remain open so that
- * customization can also land on /system/vendor or /system/orm.  We expect
- * build-time checking or filtering when constructing the associated
- * fs_config_* files.
+ * customization can also land on /system/vendor, /system/oem or /system/odm.
+ * We expect build-time checking or filtering when constructing the associated
+ * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
  */
 static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
 static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
 static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
 static const char oem_conf_file[] = "/oem/etc/fs_config_files";
+static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs";
+static const char odm_conf_file[] = "/odm/etc/fs_config_files";
 static const char* conf[][2] = {
-    {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir}, {oem_conf_file, oem_conf_dir},
+    {sys_conf_file, sys_conf_dir},
+    {ven_conf_file, ven_conf_dir},
+    {oem_conf_file, oem_conf_dir},
+    {odm_conf_file, odm_conf_dir},
 };
 
 static const struct fs_path_config android_files[] = {
@@ -142,6 +147,8 @@
     { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" },
+    { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_dir + 1 },
+    { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_file + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 2709e3b..dee577e 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -37,6 +37,10 @@
     return pwrite(fd, buf, nbytes, offset);
 }
 
+static inline int ftruncate64(int fd, off64_t length) {
+    return ftruncate(fd, length);
+}
+
 #endif /* __APPLE__ */
 
 #if defined(_WIN32)
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index b72ed7f..7600528 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -14,21 +14,23 @@
  * limitations under the License.
  */
 
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
 #include "ziparchive/zip_writer.h"
 
-#include <utils/Log.h>
-
-#include <sys/param.h>
-
-#include <cassert>
 #include <cstdio>
-#include <memory>
-#include <vector>
+#include <sys/param.h>
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
+#include <memory>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "utils/Compat.h"
+#include "utils/Log.h"
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+
 #if !defined(powerof2)
 #define powerof2(x) ((((x)-1)&(x))==0)
 #endif
@@ -171,12 +173,12 @@
     return kInvalidAlignment;
   }
 
-  FileInfo fileInfo = {};
-  fileInfo.path = std::string(path);
-  fileInfo.local_file_header_offset = current_offset_;
+  current_file_entry_ = {};
+  current_file_entry_.path = path;
+  current_file_entry_.local_file_header_offset = current_offset_;
 
-  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(fileInfo.path.data()),
-                       fileInfo.path.size())) {
+  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
+                        current_file_entry_.path.size())) {
     return kInvalidEntryName;
   }
 
@@ -188,24 +190,24 @@
   header.gpb_flags |= kGPBDDFlagMask;
 
   if (flags & ZipWriter::kCompress) {
-    fileInfo.compression_method = kCompressDeflated;
+    current_file_entry_.compression_method = kCompressDeflated;
 
     int32_t result = PrepareDeflate();
     if (result != kNoError) {
       return result;
     }
   } else {
-    fileInfo.compression_method = kCompressStored;
+    current_file_entry_.compression_method = kCompressStored;
   }
-  header.compression_method = fileInfo.compression_method;
+  header.compression_method = current_file_entry_.compression_method;
 
-  ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
-  header.last_mod_time = fileInfo.last_mod_time;
-  header.last_mod_date = fileInfo.last_mod_date;
+  ExtractTimeAndDate(time, &current_file_entry_.last_mod_time, &current_file_entry_.last_mod_date);
+  header.last_mod_time = current_file_entry_.last_mod_time;
+  header.last_mod_date = current_file_entry_.last_mod_date;
 
-  header.file_name_length = fileInfo.path.size();
+  header.file_name_length = current_file_entry_.path.size();
 
-  off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
+  off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
   std::vector<char> zero_padding;
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
@@ -220,7 +222,8 @@
     return HandleError(kIoError);
   }
 
-  if (fwrite(path, sizeof(*path), fileInfo.path.size(), file_) != fileInfo.path.size()) {
+  if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
+      != current_file_entry_.path.size()) {
     return HandleError(kIoError);
   }
 
@@ -230,15 +233,37 @@
     return HandleError(kIoError);
   }
 
-  files_.emplace_back(std::move(fileInfo));
-
   current_offset_ = offset;
   state_ = State::kWritingEntry;
   return kNoError;
 }
 
+int32_t ZipWriter::DiscardLastEntry() {
+  if (state_ != State::kWritingZip || files_.empty()) {
+    return kInvalidState;
+  }
+
+  FileEntry& last_entry = files_.back();
+  current_offset_ = last_entry.local_file_header_offset;
+  if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+    return HandleError(kIoError);
+  }
+  files_.pop_back();
+  return kNoError;
+}
+
+int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
+  CHECK(out_entry != nullptr);
+
+  if (files_.empty()) {
+    return kInvalidState;
+  }
+  *out_entry = files_.back();
+  return kNoError;
+}
+
 int32_t ZipWriter::PrepareDeflate() {
-  assert(state_ == State::kWritingZip);
+  CHECK(state_ == State::kWritingZip);
 
   // Initialize the z_stream for compression.
   z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
@@ -269,25 +294,25 @@
     return HandleError(kInvalidState);
   }
 
-  FileInfo& currentFile = files_.back();
   int32_t result = kNoError;
-  if (currentFile.compression_method & kCompressDeflated) {
-    result = CompressBytes(&currentFile, data, len);
+  if (current_file_entry_.compression_method & kCompressDeflated) {
+    result = CompressBytes(&current_file_entry_, data, len);
   } else {
-    result = StoreBytes(&currentFile, data, len);
+    result = StoreBytes(&current_file_entry_, data, len);
   }
 
   if (result != kNoError) {
     return result;
   }
 
-  currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
-  currentFile.uncompressed_size += len;
+  current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
+                                    reinterpret_cast<const Bytef*>(data), len);
+  current_file_entry_.uncompressed_size += len;
   return kNoError;
 }
 
-int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
-  assert(state_ == State::kWritingEntry);
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
+  CHECK(state_ == State::kWritingEntry);
 
   if (fwrite(data, 1, len, file_) != len) {
     return HandleError(kIoError);
@@ -297,11 +322,11 @@
   return kNoError;
 }
 
-int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
-  assert(state_ == State::kWritingEntry);
-  assert(z_stream_);
-  assert(z_stream_->next_out != nullptr);
-  assert(z_stream_->avail_out != 0);
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
+  CHECK(state_ == State::kWritingEntry);
+  CHECK(z_stream_);
+  CHECK(z_stream_->next_out != nullptr);
+  CHECK(z_stream_->avail_out != 0);
 
   // Prepare the input.
   z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
@@ -331,17 +356,17 @@
   return kNoError;
 }
 
-int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
-  assert(state_ == State::kWritingEntry);
-  assert(z_stream_);
-  assert(z_stream_->next_out != nullptr);
-  assert(z_stream_->avail_out != 0);
+int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
+  CHECK(state_ == State::kWritingEntry);
+  CHECK(z_stream_);
+  CHECK(z_stream_->next_out != nullptr);
+  CHECK(z_stream_->avail_out != 0);
 
   // Keep deflating while there isn't enough space in the buffer to
   // to complete the compress.
   int zerr;
   while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
-    assert(z_stream_->avail_out == 0);
+    CHECK(z_stream_->avail_out == 0);
     size_t write_bytes = z_stream_->next_out - buffer_.data();
     if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
       return HandleError(kIoError);
@@ -373,9 +398,8 @@
     return kInvalidState;
   }
 
-  FileInfo& currentFile = files_.back();
-  if (currentFile.compression_method & kCompressDeflated) {
-    int32_t result = FlushCompressedBytes(&currentFile);
+  if (current_file_entry_.compression_method & kCompressDeflated) {
+    int32_t result = FlushCompressedBytes(&current_file_entry_);
     if (result != kNoError) {
       return result;
     }
@@ -388,13 +412,15 @@
   }
 
   DataDescriptor dd = {};
-  dd.crc32 = currentFile.crc32;
-  dd.compressed_size = currentFile.compressed_size;
-  dd.uncompressed_size = currentFile.uncompressed_size;
+  dd.crc32 = current_file_entry_.crc32;
+  dd.compressed_size = current_file_entry_.compressed_size;
+  dd.uncompressed_size = current_file_entry_.uncompressed_size;
   if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
     return HandleError(kIoError);
   }
 
+  files_.emplace_back(std::move(current_file_entry_));
+
   current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
   state_ = State::kWritingZip;
   return kNoError;
@@ -406,7 +432,7 @@
   }
 
   off64_t startOfCdr = current_offset_;
-  for (FileInfo& file : files_) {
+  for (FileEntry& file : files_) {
     CentralDirectoryRecord cdr = {};
     cdr.record_signature = CentralDirectoryRecord::kSignature;
     cdr.gpb_flags |= kGPBDDFlagMask;
@@ -442,11 +468,19 @@
     return HandleError(kIoError);
   }
 
+  current_offset_ += sizeof(er);
+
+  // Since we can BackUp() and potentially finish writing at an offset less than one we had
+  // already written at, we must truncate the file.
+
+  if (ftruncate64(fileno(file_), current_offset_) != 0) {
+    return HandleError(kIoError);
+  }
+
   if (fflush(file_) != 0) {
     return HandleError(kIoError);
   }
 
-  current_offset_ += sizeof(er);
   state_ = State::kDone;
   return kNoError;
 }
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 16a574d..30f4950 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -23,6 +23,10 @@
 #include <memory>
 #include <vector>
 
+static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
+                                                            ZipArchiveHandle handle,
+                                                            ZipEntry* zip_entry);
+
 struct zipwriter : public ::testing::Test {
   TemporaryFile* temp_file_;
   int fd_;
@@ -59,16 +63,10 @@
 
   ZipEntry data;
   ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
-  EXPECT_EQ(strlen(expected), data.compressed_length);
-  EXPECT_EQ(strlen(expected), data.uncompressed_length);
   EXPECT_EQ(kCompressStored, data.method);
-
-  char buffer[6];
-  EXPECT_EQ(0,
-            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
-  buffer[5] = 0;
-
-  EXPECT_STREQ(expected, buffer);
+  EXPECT_EQ(strlen(expected), data.compressed_length);
+  ASSERT_EQ(strlen(expected), data.uncompressed_length);
+  ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
 
   CloseArchive(handle);
 }
@@ -94,26 +92,19 @@
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
-  char buffer[4];
   ZipEntry data;
 
   ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(2u, data.compressed_length);
-  EXPECT_EQ(2u, data.uncompressed_length);
-  ASSERT_EQ(0,
-            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
-  buffer[2] = 0;
-  EXPECT_STREQ("he", buffer);
+  ASSERT_EQ(2u, data.uncompressed_length);
+  ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
 
   ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
   EXPECT_EQ(kCompressStored, data.method);
   EXPECT_EQ(3u, data.compressed_length);
-  EXPECT_EQ(3u, data.uncompressed_length);
-  ASSERT_EQ(0,
-            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
-  buffer[3] = 0;
-  EXPECT_STREQ("llo", buffer);
+  ASSERT_EQ(3u, data.uncompressed_length);
+  ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
 
   ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
   EXPECT_EQ(kCompressStored, data.method);
@@ -143,7 +134,7 @@
   CloseArchive(handle);
 }
 
-void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
+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;
@@ -264,14 +255,8 @@
   ZipEntry data;
   ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
   EXPECT_EQ(kCompressDeflated, data.method);
-  EXPECT_EQ(4u, data.uncompressed_length);
-
-  char buffer[5];
-  ASSERT_EQ(0,
-            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
-  buffer[4] = 0;
-
-  EXPECT_STREQ("helo", buffer);
+  ASSERT_EQ(4u, data.uncompressed_length);
+  ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
 
   CloseArchive(handle);
 }
@@ -319,3 +304,111 @@
   ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
   ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
 }
+
+TEST_F(zipwriter, BackupRemovesTheLastFile) {
+  ZipWriter writer(file_);
+
+  const char* kKeepThis = "keep this";
+  const char* kDropThis = "drop this";
+  const char* kReplaceWithThis = "replace with this";
+
+  ZipWriter::FileEntry entry;
+  EXPECT_LT(writer.GetLastEntry(&entry), 0);
+
+  ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.GetLastEntry(&entry));
+  EXPECT_EQ("keep.txt", entry.path);
+
+  ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.GetLastEntry(&entry));
+  EXPECT_EQ("drop.txt", entry.path);
+
+  ASSERT_EQ(0, writer.DiscardLastEntry());
+
+  ASSERT_EQ(0, writer.GetLastEntry(&entry));
+  EXPECT_EQ("keep.txt", entry.path);
+
+  ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.GetLastEntry(&entry));
+  EXPECT_EQ("replace.txt", entry.path);
+
+  ASSERT_EQ(0, writer.Finish());
+
+  // Verify that "drop.txt" does not exist.
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+  ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
+
+  ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+
+  ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+  ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, TruncateFileAfterBackup) {
+  ZipWriter writer(file_);
+
+  const char* kSmall = "small";
+
+  ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
+  std::vector<uint8_t> data;
+  data.resize(1024*1024, 0xef);
+  ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  off_t before_len = ftello(file_);
+
+  ZipWriter::FileEntry entry;
+  ASSERT_EQ(0, writer.GetLastEntry(&entry));
+  ASSERT_EQ(0, writer.DiscardLastEntry());
+
+  ASSERT_EQ(0, writer.Finish());
+
+  off_t after_len = ftello(file_);
+
+  ASSERT_GT(before_len, after_len);
+}
+
+static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
+                                                            ZipArchiveHandle handle,
+                                                            ZipEntry* zip_entry) {
+  if (expected.size() != zip_entry->uncompressed_length) {
+    return ::testing::AssertionFailure() << "uncompressed entry size "
+        << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
+  }
+
+  std::string actual;
+  actual.resize(expected.size());
+
+  uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
+  if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
+    return ::testing::AssertionFailure() << "failed to extract entry";
+  }
+
+  if (expected != actual) {
+    return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
+        << "' does not match expected '" << expected << "'";
+  }
+  return ::testing::AssertionSuccess();
+}
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index 7ee0464..ccb1aa6 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -32,3 +32,22 @@
         "-Werror",
     ],
 }
+
+// ========================================================
+// Benchmark
+// ========================================================
+cc_benchmark {
+    name: "android_fork_execvp_ext_benchmark",
+    srcs: [
+        "android_fork_execvp_ext_benchmark.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "liblogwrap",
+    ],
+    cflags: [
+        "-Werror",
+    ]
+}
diff --git a/logwrapper/android_fork_execvp_ext_benchmark.cpp b/logwrapper/android_fork_execvp_ext_benchmark.cpp
new file mode 100644
index 0000000..1abd932
--- /dev/null
+++ b/logwrapper/android_fork_execvp_ext_benchmark.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "logwrap/logwrap.h"
+
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
+
+static void BM_android_fork_execvp_ext(benchmark::State& state) {
+    const char* argv[] = {"/system/bin/echo", "hello", "world"};
+    const int argc = 3;
+    while (state.KeepRunning()) {
+        int rc = android_fork_execvp_ext(
+            argc, (char**)argv, NULL /* status */, false /* ignore_int_quit */, LOG_NONE,
+            false /* abbreviated */, NULL /* file_path */, NULL /* opts */, 0 /* opts_len */);
+        CHECK_EQ(0, rc);
+    }
+}
+BENCHMARK(BM_android_fork_execvp_ext);
+
+BENCHMARK_MAIN();
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index 89a8fdd..d3538b3 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -54,9 +54,8 @@
  *           the specified log until the child has exited.
  *   file_path: if log_target has the LOG_FILE bit set, then this parameter
  *           must be set to the pathname of the file to log to.
- *   opts: set to non-NULL if you want to use one or more of the
- *           FORK_EXECVP_OPTION_* features.
- *   opts_len: the length of the opts array. When opts is NULL, pass 0.
+ *   unused_opts: currently unused.
+ *   unused_opts_len: currently unused.
  *
  * Return value:
  *   0 when logwrap successfully run the child process and captured its status
@@ -72,30 +71,10 @@
 #define LOG_KLOG        2
 #define LOG_FILE        4
 
-/* Write data to child's stdin. */
-#define FORK_EXECVP_OPTION_INPUT             0
-/* Capture data from child's stdout and stderr. */
-#define FORK_EXECVP_OPTION_CAPTURE_OUTPUT    1
-
-struct AndroidForkExecvpOption {
-    int opt_type;
-    union {
-        struct {
-            const uint8_t* input;
-            size_t input_len;
-        } opt_input;
-        struct {
-            void (*on_output)(const uint8_t* /*output*/,
-                              size_t /*output_len*/,
-                              void* /* user_pointer */);
-            void* user_pointer;
-        } opt_capture_output;
-    };
-};
-
+// TODO: Remove unused_opts / unused_opts_len in a followup change.
 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path,
-        const struct AndroidForkExecvpOption* opts, size_t opts_len);
+        int log_target, bool abbreviated, char *file_path, void* unused_opts,
+        int unused_opts_len);
 
 /* Similar to above, except abbreviated logging is not available, and if logwrap
  * is true, logging is to the Android system log, and if false, there is no
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 3ad0983..7076078 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -291,8 +291,7 @@
 }
 
 static int parent(const char *tag, int parent_read, pid_t pid,
-        int *chld_sts, int log_target, bool abbreviated, char *file_path,
-        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
+        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
     int status = 0;
     char buffer[4096];
     struct pollfd poll_fds[] = {
@@ -359,13 +358,6 @@
             sz = TEMP_FAILURE_RETRY(
                 read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
 
-            for (size_t i = 0; sz > 0 && i < opts_len; ++i) {
-                if (opts[i].opt_type == FORK_EXECVP_OPTION_CAPTURE_OUTPUT) {
-                  opts[i].opt_capture_output.on_output(
-                      (uint8_t*)&buffer[b], sz, opts[i].opt_capture_output.user_pointer);
-                }
-            }
-
             sz += b;
             // Log one line at a time
             for (b = 0; b < sz; b++) {
@@ -483,7 +475,7 @@
 
 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
         int log_target, bool abbreviated, char *file_path,
-        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
+        void *unused_opts, int unused_opts_len) {
     pid_t pid;
     int parent_ptty;
     int child_ptty;
@@ -493,6 +485,9 @@
     sigset_t oldset;
     int rc = 0;
 
+    LOG_ALWAYS_FATAL_IF(unused_opts != NULL);
+    LOG_ALWAYS_FATAL_IF(unused_opts_len != 0);
+
     rc = pthread_mutex_lock(&fd_mutex);
     if (rc) {
         ERROR("failed to lock signal_fd mutex\n");
@@ -538,13 +533,6 @@
         pthread_sigmask(SIG_SETMASK, &oldset, NULL);
         close(parent_ptty);
 
-        // redirect stdin, stdout and stderr
-        for (size_t i = 0; i < opts_len; ++i) {
-            if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
-                dup2(child_ptty, 0);
-                break;
-            }
-        }
         dup2(child_ptty, 1);
         dup2(child_ptty, 2);
         close(child_ptty);
@@ -561,24 +549,8 @@
             sigaction(SIGQUIT, &ignact, &quitact);
         }
 
-        for (size_t i = 0; i < opts_len; ++i) {
-            if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
-                size_t left = opts[i].opt_input.input_len;
-                const uint8_t* input = opts[i].opt_input.input;
-                while (left > 0) {
-                    ssize_t res =
-                        TEMP_FAILURE_RETRY(write(parent_ptty, input, left));
-                    if (res < 0) {
-                        break;
-                    }
-                    left -= res;
-                    input += res;
-                }
-            }
-        }
-
         rc = parent(argv[0], parent_ptty, pid, status, log_target,
-                    abbreviated, file_path, opts, opts_len);
+                    abbreviated, file_path);
     }
 
     if (ignore_int_quit) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index dcb3b25..ad5647b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -346,6 +346,9 @@
     # create the lost+found directories, so as to enforce our permissions
     mkdir /cache/lost+found 0770 root root
 
+on late-fs
+    start hwservicemanager
+
 on post-fs-data
     # We chown/chmod /data again so because mount is run as root + defaults
     chown system system /data
@@ -408,6 +411,9 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
 
+    mkdir /data/vendor 0771 root root
+    mkdir /data/vendor/hardware 0771 root root
+
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local/tmp 0771 shell shell
@@ -586,8 +592,9 @@
     # Define default initial receive window size in segments.
     setprop net.tcp.default_init_rwnd 60
 
-    # Start all binderized HAL daemons
-    start hwservicemanager
+    # Start standard binderized HAL daemons
+    class_start hal
+
     class_start core
 
 on nonencrypted