Merge "Only run vold command when file encryption enabled"
diff --git a/adb/Android.mk b/adb/Android.mk
index 6951904..9b6e147 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -167,6 +167,7 @@
     libbase \
     libcrypto_static \
     libcutils \
+    liblog \
     $(EXTRA_STATIC_LIBS) \
 
 # libc++ not available on windows yet
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 4751bff..0f64998 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -28,6 +28,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <string>
+
 #include "adb_io.h"
 
 static transport_type __adb_transport = kTransportAny;
@@ -108,44 +110,40 @@
 
 static int switch_socket_transport(int fd)
 {
-    char service[64];
-    char tmp[5];
-    int len;
-
-    if (__adb_serial)
-        snprintf(service, sizeof service, "host:transport:%s", __adb_serial);
-    else {
+    std::string service;
+    if (__adb_serial) {
+        service += "host:transport:";
+        service += __adb_serial;
+    } else {
         const char* transport_type = "???";
-
-         switch (__adb_transport) {
-            case kTransportUsb:
-                transport_type = "transport-usb";
-                break;
-            case kTransportLocal:
-                transport_type = "transport-local";
-                break;
-            case kTransportAny:
-                transport_type = "transport-any";
-                break;
-            case kTransportHost:
-                // no switch necessary
-                return 0;
-                break;
+        switch (__adb_transport) {
+          case kTransportUsb:
+            transport_type = "transport-usb";
+            break;
+          case kTransportLocal:
+            transport_type = "transport-local";
+            break;
+          case kTransportAny:
+            transport_type = "transport-any";
+            break;
+          case kTransportHost:
+            // no switch necessary
+            return 0;
         }
-
-        snprintf(service, sizeof service, "host:%s", transport_type);
+        service += "host:";
+        service += transport_type;
     }
-    len = strlen(service);
-    snprintf(tmp, sizeof tmp, "%04x", len);
 
-    if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) {
+    char tmp[5];
+    snprintf(tmp, sizeof(tmp), "%04zx", service.size());
+    if (!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service.c_str(), service.size())) {
         strcpy(__adb_error, "write failure during connection");
         adb_close(fd);
         return -1;
     }
     D("Switch transport in progress\n");
 
-    if(adb_status(fd)) {
+    if (adb_status(fd)) {
         adb_close(fd);
         D("Switch transport failed\n");
         return -1;
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index 9f23511..999eb11 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -31,11 +31,11 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
 #include <linux/usb/ch9.h>
-#else
-#include <linux/usb_ch9.h>
-#endif
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb.h"
 #include "transport.h"
@@ -114,10 +114,6 @@
 
 }
 
-static void register_device(const char *dev_name, const char *devpath,
-                            unsigned char ep_in, unsigned char ep_out,
-                            int ifc, int serial_index, unsigned zero_mask);
-
 static inline int badname(const char *name)
 {
     while(*name) {
@@ -571,13 +567,10 @@
     return 0;
 }
 
-static void register_device(const char *dev_name, const char *devpath,
+static void register_device(const char* dev_name, const char* dev_path,
                             unsigned char ep_in, unsigned char ep_out,
                             int interface, int serial_index, unsigned zero_mask)
 {
-    int n = 0;
-    char serial[256];
-
         /* Since Linux will not reassign the device ID (and dev_name)
         ** as long as the device is open, we can add to the list here
         ** once we open it and remove from the list when we're finally
@@ -587,8 +580,7 @@
         ** name, we have no further work to do.
         */
     adb_mutex_lock(&usb_lock);
-    for (usb_handle* usb = handle_list.next; usb != &handle_list;
-         usb = usb->next) {
+    for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
         if (!strcmp(usb->fname, dev_name)) {
             adb_mutex_unlock(&usb_lock);
             return;
@@ -596,8 +588,7 @@
     }
     adb_mutex_unlock(&usb_lock);
 
-    D("[ usb located new device %s (%d/%d/%d) ]\n",
-        dev_name, ep_in, ep_out, interface);
+    D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
     usb_handle* usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
     if (usb == nullptr) fatal("couldn't allocate usb_handle");
     strcpy(usb->fname, dev_name);
@@ -613,69 +604,40 @@
     usb->reaper_thread = 0;
 
     usb->desc = unix_open(usb->fname, O_RDWR | O_CLOEXEC);
-    if(usb->desc < 0) {
-        /* if we fail, see if have read-only access */
+    if (usb->desc == -1) {
+        // Opening RW failed, so see if we have RO access.
         usb->desc = unix_open(usb->fname, O_RDONLY | O_CLOEXEC);
-        if(usb->desc < 0) goto fail;
+        if (usb->desc == -1) {
+            D("[ usb open %s failed: %s]\n", usb->fname, strerror(errno));
+            free(usb);
+            return;
+        }
         usb->writeable = 0;
-        D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc);
-    } else {
-        D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
-        n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
-        if(n != 0) goto fail;
     }
 
-        /* read the device's serial number */
-    serial[0] = 0;
-    memset(serial, 0, sizeof(serial));
-    if (serial_index) {
-        struct usbdevfs_ctrltransfer  ctrl;
-        __u16 buffer[128];
-        __u16 languages[128];
-        int i, result;
-        int languageCount = 0;
+    D("[ usb opened %s%s, fd=%d]\n", usb->fname, (usb->writeable ? "" : " (read-only)"), usb->desc);
 
-        memset(languages, 0, sizeof(languages));
-        memset(&ctrl, 0, sizeof(ctrl));
-
-            // read list of supported languages
-        ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
-        ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
-        ctrl.wValue = (USB_DT_STRING << 8) | 0;
-        ctrl.wIndex = 0;
-        ctrl.wLength = sizeof(languages);
-        ctrl.data = languages;
-        ctrl.timeout = 1000;
-
-        result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
-        if (result > 0)
-            languageCount = (result - 2) / 2;
-
-        for (i = 1; i <= languageCount; i++) {
-            memset(buffer, 0, sizeof(buffer));
-            memset(&ctrl, 0, sizeof(ctrl));
-
-            ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
-            ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
-            ctrl.wValue = (USB_DT_STRING << 8) | serial_index;
-            ctrl.wIndex = __le16_to_cpu(languages[i]);
-            ctrl.wLength = sizeof(buffer);
-            ctrl.data = buffer;
-            ctrl.timeout = 1000;
-
-            result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
-            if (result > 0) {
-                int i;
-                // skip first word, and copy the rest to the serial string, changing shorts to bytes.
-                result /= 2;
-                for (i = 1; i < result; i++)
-                    serial[i - 1] = __le16_to_cpu(buffer[i]);
-                serial[i - 1] = 0;
-                break;
-            }
+    if (usb->writeable) {
+        if (ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
+            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", usb->desc, strerror(errno));
+            adb_close(usb->desc);
+            free(usb);
+            return;
         }
     }
 
+    // Read the device's serial number.
+    std::string serial_path =
+            android::base::StringPrintf("/sys/bus/usb/devices/%s/serial", dev_path + 4);
+    std::string serial;
+    if (!android::base::ReadFileToString(serial_path, &serial)) {
+        D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
+        adb_close(usb->desc);
+        free(usb);
+        return;
+    }
+    serial = android::base::Trim(serial);
+
         /* add to the end of the active handles */
     adb_mutex_lock(&usb_lock);
     usb->next = &handle_list;
@@ -684,19 +646,10 @@
     usb->next->prev = usb;
     adb_mutex_unlock(&usb_lock);
 
-    register_usb_transport(usb, serial, devpath, usb->writeable);
-    return;
-
-fail:
-    D("[ usb open %s error=%d, err_str = %s]\n",
-        usb->fname,  errno, strerror(errno));
-    if(usb->desc >= 0) {
-        adb_close(usb->desc);
-    }
-    free(usb);
+    register_usb_transport(usb, serial.c_str(), dev_path, usb->writeable);
 }
 
-void* device_poll_thread(void* unused)
+static void* device_poll_thread(void* unused)
 {
     D("Created device thread\n");
     for(;;) {
diff --git a/base/file.cpp b/base/file.cpp
index a51c5ff..6b19818 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -120,5 +120,29 @@
   return result || CleanUpAfterFailedWrite(path);
 }
 
+bool ReadFully(int fd, void* data, size_t byte_count) {
+  uint8_t* p = reinterpret_cast<uint8_t*>(data);
+  size_t remaining = byte_count;
+  while (remaining > 0) {
+    ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+    if (n <= 0) return false;
+    p += n;
+    remaining -= n;
+  }
+  return true;
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+  const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+  size_t remaining = byte_count;
+  while (remaining > 0) {
+    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+    if (n == -1) return false;
+    p += n;
+    remaining -= n;
+  }
+  return true;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index fc48b32..e5cf696 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -79,3 +79,28 @@
   ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << errno;
   EXPECT_EQ("abc", s);
 }
+
+TEST(file, ReadFully) {
+  int fd = open("/proc/version", O_RDONLY);
+  ASSERT_NE(-1, fd) << strerror(errno);
+
+  char buf[1024];
+  memset(buf, 0, sizeof(buf));
+  ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
+  ASSERT_STREQ("Linux", buf);
+
+  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+
+  ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
+
+  close(fd);
+}
+
+TEST(file, WriteFully) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+  std::string s;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  EXPECT_EQ("abc", s);
+}
diff --git a/base/include/base/file.h b/base/include/base/file.h
index ef97742..acd29b3 100644
--- a/base/include/base/file.h
+++ b/base/include/base/file.h
@@ -34,6 +34,9 @@
                        mode_t mode, uid_t owner, gid_t group);
 #endif
 
+bool ReadFully(int fd, void* data, size_t byte_count);
+bool WriteFully(int fd, const void* data, size_t byte_count);
+
 }  // namespace base
 }  // namespace android
 
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index e10feff..d6a6d2e 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -26,25 +26,12 @@
 #include <sys/wait.h>
 
 #include <backtrace/Backtrace.h>
+#include <base/file.h>
 #include <log/log.h>
 
 const int SLEEP_TIME_USEC = 50000;         // 0.05 seconds
 const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
 
-static int write_to_am(int fd, const char* buf, int len) {
-  int to_write = len;
-  while (to_write > 0) {
-    int written = TEMP_FAILURE_RETRY(write(fd, buf + len - to_write, to_write));
-    if (written < 0) {
-      // hard failure
-      ALOGE("AM write failure (%d / %s)\n", errno, strerror(errno));
-      return -1;
-    }
-    to_write -= written;
-  }
-  return len;
-}
-
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
   if ((ltype == ERROR)
@@ -82,9 +69,9 @@
   if (write_to_logcat) {
     __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_INFO, LOG_TAG, buf);
     if (write_to_activitymanager) {
-      int written = write_to_am(log->amfd, buf, len);
-      if (written <= 0) {
+      if (!android::base::WriteFully(log->amfd, buf, len)) {
         // timeout or other failure on write; stop informing the activity manager
+        ALOGE("AM write failed: %s", strerror(errno));
         log->amfd = -1;
       }
     }
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 1dd071c..d5e94ac 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -117,8 +117,10 @@
          * filesytsem due to an error, e2fsck is still run to do a full check
          * fix the filesystem.
          */
+        errno = 0;
         ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
-        INFO("%s(): mount(%s,%s,%s)=%d\n", __func__, blk_device, target, fs_type, ret);
+        INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
+             __func__, blk_device, target, fs_type, ret, strerror(errno));
         if (!ret) {
             int i;
             for (i = 0; i < 5; i++) {
@@ -126,6 +128,7 @@
                 // Should we try rebooting if all attempts fail?
                 int result = umount(target);
                 if (result == 0) {
+                    INFO("%s(): unmount(%s) succeeded\n", __func__, target);
                     break;
                 }
                 ERROR("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 55100bd..d772383 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -490,15 +490,6 @@
     return ret;
 }
 
-int do_setcon(int nargs, char **args) {
-    if (is_selinux_enabled() <= 0)
-        return 0;
-    if (setcon(args[1]) < 0) {
-        return -errno;
-    }
-    return 0;
-}
-
 int do_setprop(int nargs, char **args)
 {
     const char *name = args[1];
diff --git a/init/devices.cpp b/init/devices.cpp
index 96b1696..2c7f5a9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -266,7 +266,6 @@
 static void add_platform_device(const char *path)
 {
     int path_len = strlen(path);
-    struct listnode *node;
     struct platform_node *bus;
     const char *name = path;
 
@@ -276,15 +275,6 @@
             name += 9;
     }
 
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if ((bus->path_len < path_len) &&
-                (path[bus->path_len] == '/') &&
-                !strncmp(path, bus->path, bus->path_len))
-            /* subdevice of an existing platform, ignore it */
-            return;
-    }
-
     INFO("adding platform device %s (%s)\n", name, path);
 
     bus = (platform_node*) calloc(1, sizeof(struct platform_node));
diff --git a/init/init.cpp b/init/init.cpp
index b1d65db..dd74538 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -25,8 +25,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/epoll.h>
 #include <sys/mount.h>
-#include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -82,6 +82,17 @@
 
 bool waiting_for_exec = false;
 
+static int epoll_fd = -1;
+
+void register_epoll_handler(int fd, void (*fn)()) {
+    epoll_event ev;
+    ev.events = EPOLLIN;
+    ev.data.ptr = reinterpret_cast<void*>(fn);
+    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+        ERROR("epoll_ctl failed: %s\n", strerror(errno));
+    }
+}
+
 void service::NotifyStateChange(const char* new_state) {
     if (!properties_initialized()) {
         // If properties aren't available yet, we can't set them.
@@ -603,14 +614,16 @@
     }
 }
 
-static int wait_for_coldboot_done_action(int nargs, char **args)
-{
-    int ret;
-    INFO("wait for %s\n", COLDBOOT_DONE);
-    ret = wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT);
-    if (ret)
+static int wait_for_coldboot_done_action(int nargs, char **args) {
+    Timer t;
+
+    NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
+    if (wait_for_file(COLDBOOT_DONE, COMMAND_RETRY_TIMEOUT)) {
         ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
-    return ret;
+    }
+
+    NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+    return 0;
 }
 
 /*
@@ -733,7 +746,7 @@
     return 0;
 }
 
-static void import_kernel_nv(char *name, int for_emulator)
+static void import_kernel_nv(char *name, bool for_emulator)
 {
     char *value = strchr(name, '=');
     int name_len = strlen(name);
@@ -827,35 +840,9 @@
      * second pass is only necessary for qemu to export all kernel params
      * as props.
      */
-    import_kernel_cmdline(0, import_kernel_nv);
+    import_kernel_cmdline(false, import_kernel_nv);
     if (qemu[0])
-        import_kernel_cmdline(1, import_kernel_nv);
-}
-
-static int property_service_init_action(int nargs, char **args)
-{
-    /* read any property files on system or data and
-     * fire up the property service.  This must happen
-     * after the ro.foo properties are set above so
-     * that /data/local.prop cannot interfere with them.
-     */
-    start_property_service();
-    if (get_property_set_fd() < 0) {
-        ERROR("start_property_service() failed\n");
-        exit(1);
-    }
-
-    return 0;
-}
-
-static int signal_init_action(int nargs, char **args)
-{
-    signal_init();
-    if (get_signal_fd() < 0) {
-        ERROR("signal_init() failed\n");
-        exit(1);
-    }
-    return 0;
+        import_kernel_cmdline(true, import_kernel_nv);
 }
 
 static int queue_property_triggers_action(int nargs, char **args)
@@ -866,13 +853,36 @@
     return 0;
 }
 
-void selinux_init_all_handles(void)
+static void selinux_init_all_handles(void)
 {
     sehandle = selinux_android_file_context_handle();
     selinux_android_set_sehandle(sehandle);
     sehandle_prop = selinux_android_prop_context_handle();
 }
 
+enum selinux_enforcing_status { SELINUX_DISABLED, SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+static selinux_enforcing_status selinux_status_from_cmdline() {
+    selinux_enforcing_status status = SELINUX_ENFORCING;
+
+    std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
+        char *value = strchr(name, '=');
+        if (value == nullptr) { return; }
+        *value++ = '\0';
+        if (strcmp(name, "androidboot.selinux") == 0) {
+            if (strcmp(value, "disabled") == 0) {
+                status = SELINUX_DISABLED;
+            } else if (strcmp(value, "permissive") == 0) {
+                status = SELINUX_PERMISSIVE;
+            }
+        }
+    };
+    import_kernel_cmdline(false, fn);
+
+    return status;
+}
+
+
 static bool selinux_is_disabled(void)
 {
     if (ALLOW_DISABLE_SELINUX) {
@@ -881,12 +891,7 @@
             // via the kernel command line "selinux=0".
             return true;
         }
-
-        char tmp[PROP_VALUE_MAX];
-        if ((property_get("ro.boot.selinux", tmp) != 0) && (strcmp(tmp, "disabled") == 0)) {
-            // SELinux is compiled into the kernel, but we've been told to disable it.
-            return true;
-        }
+        return selinux_status_from_cmdline() == SELINUX_DISABLED;
     }
 
     return false;
@@ -895,20 +900,7 @@
 static bool selinux_is_enforcing(void)
 {
     if (ALLOW_DISABLE_SELINUX) {
-        char tmp[PROP_VALUE_MAX];
-        if (property_get("ro.boot.selinux", tmp) == 0) {
-            // Property is not set.  Assume enforcing.
-            return true;
-        }
-
-        if (strcmp(tmp, "permissive") == 0) {
-            // SELinux is in the kernel, but we've been told to go into permissive mode.
-            return false;
-        }
-
-        if (strcmp(tmp, "enforcing") != 0) {
-            ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp);
-        }
+        return selinux_status_from_cmdline() == SELINUX_ENFORCING;
     }
     return true;
 }
@@ -940,7 +932,13 @@
     return 0;
 }
 
-static void selinux_initialize() {
+static void security_failure() {
+    ERROR("Security failure; rebooting into recovery mode...\n");
+    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+    while (true) { pause(); }  // never reached
+}
+
+static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
     selinux_callback cb;
@@ -953,19 +951,25 @@
         return;
     }
 
-    INFO("Loading SELinux policy...\n");
-    if (selinux_android_load_policy() < 0) {
-        ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
-        android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-        while (1) { pause(); }  // never reached
+    if (in_kernel_domain) {
+        INFO("Loading SELinux policy...\n");
+        if (selinux_android_load_policy() < 0) {
+            ERROR("failed to load policy: %s\n", strerror(errno));
+            security_failure();
+        }
+
+        bool is_enforcing = selinux_is_enforcing();
+        security_setenforce(is_enforcing);
+
+        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+            security_failure();
+        }
+
+        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
+               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+    } else {
+        selinux_init_all_handles();
     }
-
-    selinux_init_all_handles();
-    bool is_enforcing = selinux_is_enforcing();
-    INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
-    security_setenforce(is_enforcing);
-
-    NOTICE("(Initializing SELinux took %.2fs.)\n", t.duration());
 }
 
 int main(int argc, char** argv) {
@@ -982,21 +986,18 @@
 
     add_environment("PATH", _PATH_DEFPATH);
 
+    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+
     // Get the basic filesystem setup we need put together in the initramdisk
     // on / and then we'll let the rc file figure out the rest.
-    mkdir("/dev", 0755);
-    mkdir("/proc", 0755);
-    mkdir("/sys", 0755);
-
-    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
-    mkdir("/dev/pts", 0755);
-    mkdir("/dev/socket", 0755);
-    mount("devpts", "/dev/pts", "devpts", 0, NULL);
-    mount("proc", "/proc", "proc", 0, NULL);
-    mount("sysfs", "/sys", "sysfs", 0, NULL);
-
-    // Indicate that booting is in progress to background fw loaders, etc.
-    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+    if (is_first_stage) {
+        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
+        mkdir("/dev/pts", 0755);
+        mkdir("/dev/socket", 0755);
+        mount("devpts", "/dev/pts", "devpts", 0, NULL);
+        mount("proc", "/proc", "proc", 0, NULL);
+        mount("sysfs", "/sys", "sysfs", 0, NULL);
+    }
 
     // We must have some place other than / to create the device nodes for
     // kmsg and null, otherwise we won't be able to remount / read-only
@@ -1006,20 +1007,41 @@
     klog_init();
     klog_set_level(KLOG_NOTICE_LEVEL);
 
-    NOTICE("init started!\n");
+    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
 
-    property_init();
+    if (!is_first_stage) {
+        // Indicate that booting is in progress to background fw loaders, etc.
+        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
-    // If arguments are passed both on the command line and in DT,
-    // properties set in DT always have priority over the command-line ones.
-    process_kernel_dt();
-    process_kernel_cmdline();
+        property_init();
 
-    // Propogate the kernel variables to internal variables
-    // used by init as well as the current required properties.
-    export_kernel_boot_props();
+        // If arguments are passed both on the command line and in DT,
+        // properties set in DT always have priority over the command-line ones.
+        process_kernel_dt();
+        process_kernel_cmdline();
 
-    selinux_initialize();
+        // Propogate the kernel variables to internal variables
+        // used by init as well as the current required properties.
+        export_kernel_boot_props();
+    }
+
+    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
+    selinux_initialize(is_first_stage);
+
+    // If we're in the kernel domain, re-exec init to transition to the init domain now
+    // that the SELinux policy has been loaded.
+    if (is_first_stage) {
+        if (restorecon("/init") == -1) {
+            ERROR("restorecon failed: %s\n", strerror(errno));
+            security_failure();
+        }
+        char* path = argv[0];
+        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
+        if (execv(path, args) == -1) {
+            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+            security_failure();
+        }
+    }
 
     // These directories were necessarily created before initial policy load
     // and therefore need their security context restored to the proper value.
@@ -1030,13 +1052,24 @@
     restorecon("/dev/__properties__");
     restorecon_recursive("/sys");
 
+    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+    if (epoll_fd == -1) {
+        ERROR("epoll_create1 failed: %s\n", strerror(errno));
+        exit(1);
+    }
+
+    signal_handler_init();
+
     property_load_boot_defaults();
+    start_property_service();
 
     init_parse_config_file("/init.rc");
 
     action_for_each_trigger("early-init", action_add_queue_tail);
 
+    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+    // ... so that we can start queuing up actions that require stuff from /dev.
     queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
     queue_builtin_action(keychord_init_action, "keychord_init");
     queue_builtin_action(console_init_action, "console_init");
@@ -1047,8 +1080,6 @@
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
     queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    queue_builtin_action(property_service_init_action, "property_service_init");
-    queue_builtin_action(signal_init_action, "signal_init");
 
     // Don't mount filesystems or start core system services in charger mode.
     char bootmode[PROP_VALUE_MAX];
@@ -1061,41 +1092,12 @@
     // Run all property triggers based on current state of the properties.
     queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
 
-    // TODO: why do we only initialize ufds after execute_one_command and restart_processes?
-    size_t fd_count = 0;
-    struct pollfd ufds[3];
-    bool property_set_fd_init = false;
-    bool signal_fd_init = false;
-    bool keychord_fd_init = false;
-
-    for (;;) {
+    while (true) {
         if (!waiting_for_exec) {
             execute_one_command();
             restart_processes();
         }
 
-        if (!property_set_fd_init && get_property_set_fd() > 0) {
-            ufds[fd_count].fd = get_property_set_fd();
-            ufds[fd_count].events = POLLIN;
-            ufds[fd_count].revents = 0;
-            fd_count++;
-            property_set_fd_init = true;
-        }
-        if (!signal_fd_init && get_signal_fd() > 0) {
-            ufds[fd_count].fd = get_signal_fd();
-            ufds[fd_count].events = POLLIN;
-            ufds[fd_count].revents = 0;
-            fd_count++;
-            signal_fd_init = true;
-        }
-        if (!keychord_fd_init && get_keychord_fd() > 0) {
-            ufds[fd_count].fd = get_keychord_fd();
-            ufds[fd_count].events = POLLIN;
-            ufds[fd_count].revents = 0;
-            fd_count++;
-            keychord_fd_init = true;
-        }
-
         int timeout = -1;
         if (process_needs_restart) {
             timeout = (process_needs_restart - gettime()) * 1000;
@@ -1109,21 +1111,12 @@
 
         bootchart_sample(&timeout);
 
-        int nr = poll(ufds, fd_count, timeout);
-        if (nr <= 0) {
-            continue;
-        }
-
-        for (size_t i = 0; i < fd_count; i++) {
-            if (ufds[i].revents & POLLIN) {
-                if (ufds[i].fd == get_property_set_fd()) {
-                    handle_property_set_fd();
-                } else if (ufds[i].fd == get_keychord_fd()) {
-                    handle_keychord();
-                } else if (ufds[i].fd == get_signal_fd()) {
-                    handle_signal();
-                }
-            }
+        epoll_event ev;
+        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+        if (nr == -1) {
+            ERROR("epoll_wait failed: %s\n", strerror(errno));
+        } else if (nr == 1) {
+            ((void (*)()) ev.data.ptr)();
         }
     }
 
diff --git a/init/init.h b/init/init.h
index a104af6..1cabb14 100644
--- a/init/init.h
+++ b/init/init.h
@@ -155,4 +155,6 @@
 
 void zap_stdio(void);
 
+void register_epoll_handler(int fd, void (*fn)());
+
 #endif	/* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index ff31093..b76b04e 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -184,7 +184,6 @@
     case 's':
         if (!strcmp(s, "eclabel")) return K_seclabel;
         if (!strcmp(s, "ervice")) return K_service;
-        if (!strcmp(s, "etcon")) return K_setcon;
         if (!strcmp(s, "etenv")) return K_setenv;
         if (!strcmp(s, "etprop")) return K_setprop;
         if (!strcmp(s, "etrlimit")) return K_setrlimit;
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 27894a2..10d9573 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -62,37 +62,7 @@
     }
 }
 
-void keychord_init()
-{
-    int fd, ret;
-
-    service_for_each(add_service_keycodes);
-
-    /* nothing to do if no services require keychords */
-    if (!keychords)
-        return;
-
-    fd = open("/dev/keychord", O_RDWR | O_CLOEXEC);
-    if (fd < 0) {
-        ERROR("could not open /dev/keychord\n");
-        return;
-    }
-
-    ret = write(fd, keychords, keychords_length);
-    if (ret != keychords_length) {
-        ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
-        close(fd);
-        fd = -1;
-    }
-
-    free(keychords);
-    keychords = 0;
-
-    keychord_fd = fd;
-}
-
-void handle_keychord()
-{
+static void handle_keychord() {
     struct service *svc;
     char adb_enabled[PROP_VALUE_MAX];
     int ret;
@@ -117,7 +87,28 @@
     }
 }
 
-int get_keychord_fd()
-{
-    return keychord_fd;
+void keychord_init() {
+    service_for_each(add_service_keycodes);
+
+    // Nothing to do if no services require keychords.
+    if (!keychords) {
+        return;
+    }
+
+    keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
+    if (keychord_fd == -1) {
+        ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+        return;
+    }
+
+    int ret = write(keychord_fd, keychords, keychords_length);
+    if (ret != keychords_length) {
+        ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+        close(keychord_fd);
+    }
+
+    free(keychords);
+    keychords = nullptr;
+
+    register_epoll_handler(keychord_fd, handle_keychord);
 }
diff --git a/init/keychords.h b/init/keychords.h
index 070b858..d2723b7 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -19,9 +19,7 @@
 
 struct service;
 
-void add_service_keycodes(struct service *svc);
-void keychord_init(void);
-void handle_keychord(void);
-int get_keychord_fd(void);
+void add_service_keycodes(service*);
+void keychord_init();
 
 #endif
diff --git a/init/keywords.h b/init/keywords.h
index 059dde1..37f01b8 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -20,7 +20,6 @@
 int do_restorecon_recursive(int nargs, char **args);
 int do_rm(int nargs, char **args);
 int do_rmdir(int nargs, char **args);
-int do_setcon(int nargs, char **args);
 int do_setprop(int nargs, char **args);
 int do_setrlimit(int nargs, char **args);
 int do_start(int nargs, char **args);
@@ -76,7 +75,6 @@
     KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
     KEYWORD(seclabel,    OPTION,  0, 0)
     KEYWORD(service,     SECTION, 0, 0)
-    KEYWORD(setcon,      COMMAND, 1, do_setcon)
     KEYWORD(setenv,      OPTION,  2, 0)
     KEYWORD(setprop,     COMMAND, 2, do_setprop)
     KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 8544951..930ef82 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -246,7 +246,7 @@
     return rc;
 }
 
-void handle_property_set_fd()
+static void handle_property_set_fd()
 {
     prop_msg msg;
     int s;
@@ -519,16 +519,14 @@
 }
 
 void start_property_service() {
-    int fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);
-    if (fd == -1) return;
+    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                    0666, 0, 0, NULL);
+    if (property_set_fd == -1) {
+        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+        exit(1);
+    }
 
-    fcntl(fd, F_SETFD, FD_CLOEXEC);
-    fcntl(fd, F_SETFL, O_NONBLOCK);
+    listen(property_set_fd, 8);
 
-    listen(fd, 8);
-    property_set_fd = fd;
-}
-
-int get_property_set_fd() {
-    return property_set_fd;
+    register_epoll_handler(property_set_fd, handle_property_set_fd);
 }
diff --git a/init/property_service.h b/init/property_service.h
index 825a7dd..a27053d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -20,7 +20,6 @@
 #include <stddef.h>
 #include <sys/system_properties.h>
 
-extern void handle_property_set_fd(void);
 extern void property_init(void);
 extern void property_load_boot_defaults(void);
 extern void load_persist_props(void);
@@ -30,7 +29,6 @@
 extern int __property_get(const char *name, char *value);
 extern int property_set(const char *name, const char *value);
 extern bool properties_initialized();
-int get_property_set_fd(void);
 
 #ifndef __clang__
 extern void __property_get_size_error()
diff --git a/init/readme.txt b/init/readme.txt
index 84afd11..6b9c42d 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -252,11 +252,6 @@
 rmdir <path>
    Calls rmdir(2) on the given path.
 
-setcon <seclabel>
-   Set the current process security context to the specified string.
-   This is typically only used from early-init to set the init context
-   before any other process is started.
-
 setprop <name> <value>
    Set system property <name> to <value>. Properties are expanded
    within <value>.
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 8be4af5..39a466d 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -35,14 +35,10 @@
 #define CRITICAL_CRASH_THRESHOLD    4       /* if we crash >4 times ... */
 #define CRITICAL_CRASH_WINDOW       (4*60)  /* ... in 4 minutes, goto recovery */
 
-static int signal_fd = -1;
-static int signal_recv_fd = -1;
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
 
-static void sigchld_handler(int s) {
-    write(signal_fd, &s, 1);
-}
-
-std::string DescribeStatus(int status) {
+static std::string DescribeStatus(int status) {
     if (WIFEXITED(status)) {
         return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
     } else if (WIFSIGNALED(status)) {
@@ -54,11 +50,14 @@
     }
 }
 
-static int wait_for_one_process() {
+static bool wait_for_one_process() {
     int status;
     pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
-    if (pid <= 0) {
-        return -1;
+    if (pid == 0) {
+        return false;
+    } else if (pid == -1) {
+        ERROR("waitpid failed: %s\n", strerror(errno));
+        return false;
     }
 
     service* svc = service_find_by_pid(pid);
@@ -73,7 +72,7 @@
     NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
 
     if (!svc) {
-        return 0;
+        return true;
     }
 
     // TODO: all the code from here down should be a member function on service.
@@ -96,7 +95,7 @@
         list_remove(&svc->slist);
         free(svc->name);
         free(svc);
-        return 0;
+        return true;
     }
 
     svc->pid = 0;
@@ -111,7 +110,7 @@
     // Disabled and reset processes do not get restarted automatically.
     if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
         svc->NotifyStateChange("stopped");
-        return 0;
+        return true;
     }
 
     time_t now = gettime();
@@ -122,7 +121,7 @@
                       "rebooting into recovery mode\n", svc->name,
                       CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
                 android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-                return 0;
+                return true;
             }
         } else {
             svc->time_crashed = now;
@@ -140,34 +139,47 @@
         cmd->func(cmd->nargs, cmd->args);
     }
     svc->NotifyStateChange("restarting");
-    return 0;
+    return true;
 }
 
-void handle_signal() {
-    // We got a SIGCHLD - reap and restart as needed.
-    char tmp[32];
-    read(signal_recv_fd, tmp, sizeof(tmp));
-    while (!wait_for_one_process()) {
+static void reap_any_outstanding_children() {
+    while (wait_for_one_process()) {
     }
 }
 
-void signal_init() {
+static void handle_signal() {
+    // Clear outstanding requests.
+    char buf[32];
+    read(signal_read_fd, buf, sizeof(buf));
+
+    reap_any_outstanding_children();
+}
+
+static void SIGCHLD_handler(int) {
+    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
+    }
+}
+
+void signal_handler_init() {
+    // Create a signalling mechanism for SIGCHLD.
+    int s[2];
+    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+        ERROR("socketpair failed: %s\n", strerror(errno));
+        exit(1);
+    }
+
+    signal_write_fd = s[0];
+    signal_read_fd = s[1];
+
+    // Write to signal_write_fd if we catch SIGCHLD.
     struct sigaction act;
     memset(&act, 0, sizeof(act));
-    act.sa_handler = sigchld_handler;
+    act.sa_handler = SIGCHLD_handler;
     act.sa_flags = SA_NOCLDSTOP;
     sigaction(SIGCHLD, &act, 0);
 
-    // Create a signalling mechanism for the sigchld handler.
-    int s[2];
-    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == 0) {
-        signal_fd = s[0];
-        signal_recv_fd = s[1];
-    }
+    reap_any_outstanding_children();
 
-    handle_signal();
-}
-
-int get_signal_fd() {
-    return signal_recv_fd;
+    register_epoll_handler(signal_read_fd, handle_signal);
 }
diff --git a/init/signal_handler.h b/init/signal_handler.h
index b092ccb..449b4af 100644
--- a/init/signal_handler.h
+++ b/init/signal_handler.h
@@ -17,8 +17,6 @@
 #ifndef _INIT_SIGNAL_HANDLER_H_
 #define _INIT_SIGNAL_HANDLER_H_
 
-void signal_init(void);
-void handle_signal(void);
-int get_signal_fd(void);
+void signal_handler_init(void);
 
 #endif
diff --git a/init/util.cpp b/init/util.cpp
index 3b49b30..9343145 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -179,9 +179,13 @@
 int write_file(const char* path, const char* content) {
     int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
     if (fd == -1) {
-        return -errno;
+        NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
+        return -1;
     }
-    int result = android::base::WriteStringToFd(content, fd) ? 0 : -errno;
+    int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
+    if (result == -1) {
+        NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
+    }
     TEMP_FAILURE_RETRY(close(fd));
     return result;
 }
@@ -375,27 +379,31 @@
 
 void open_devnull_stdio(void)
 {
-    int fd;
-    static const char *name = "/dev/__null__";
-    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
-        fd = open(name, O_RDWR);
-        unlink(name);
-        if (fd >= 0) {
-            dup2(fd, 0);
-            dup2(fd, 1);
-            dup2(fd, 2);
-            if (fd > 2) {
-                close(fd);
-            }
-            return;
+    // Try to avoid the mknod() call if we can. Since SELinux makes
+    // a /dev/null replacement available for free, let's use it.
+    int fd = open("/sys/fs/selinux/null", O_RDWR);
+    if (fd == -1) {
+        // OOPS, /sys/fs/selinux/null isn't available, likely because
+        // /sys/fs/selinux isn't mounted. Fall back to mknod.
+        static const char *name = "/dev/__null__";
+        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+            fd = open(name, O_RDWR);
+            unlink(name);
+        }
+        if (fd == -1) {
+            exit(1);
         }
     }
 
-    exit(1);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) {
+        close(fd);
+    }
 }
 
-void import_kernel_cmdline(int in_qemu,
-                           void (*import_kernel_nv)(char *name, int in_qemu))
+void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
 {
     char cmdline[2048];
     char *ptr;
diff --git a/init/util.h b/init/util.h
index 8fec7a8..6864acf 100644
--- a/init/util.h
+++ b/init/util.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <string>
+#include <functional>
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
@@ -57,7 +58,7 @@
 void remove_link(const char *oldpath, const char *newpath);
 int wait_for_file(const char *filename, int timeout);
 void open_devnull_stdio(void);
-void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu));
+void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)>);
 int make_dir(const char *path, mode_t mode);
 int restorecon(const char *pathname);
 int restorecon_recursive(const char *pathname);
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index df67123..2e09192 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -28,7 +28,7 @@
         return def;
     }
     {
-        static const char log_namespace[] = "log.tag.";
+        static const char log_namespace[] = "persist.log.tag.";
         char key[sizeof(log_namespace) + strlen(tag)];
 
         strcpy(key, log_namespace);
@@ -37,6 +37,9 @@
         if (__system_property_get(key + 8, buf) <= 0) {
             buf[0] = '\0';
         }
+        if (!buf[0] && __system_property_get(key, buf) <= 0) {
+            buf[0] = '\0';
+        }
     }
     switch (toupper(buf[0])) {
         case 'V': return ANDROID_LOG_VERBOSE;
@@ -53,17 +56,6 @@
 
 int __android_log_is_loggable(int prio, const char *tag, int def)
 {
-    static char user;
-    int logLevel;
-
-    if (user == 0) {
-        char buf[PROP_VALUE_MAX];
-        if (__system_property_get("ro.build.type", buf) <= 0) {
-            buf[0] = '\0';
-        }
-        user = strcmp(buf, "user") ? -1 : 1;
-    }
-
-    logLevel = (user == 1) ? def : __android_log_level(tag, def);
+    int logLevel = __android_log_level(tag, def);
     return logLevel >= 0 && prio >= logLevel;
 }
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 979aded..b594634 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -266,3 +266,17 @@
     android_logger_list_free(logger_list);
 }
 BENCHMARK(BM_log_delay);
+
+/*
+ *	Measure the time it takes for __android_log_is_loggable.
+ */
+static void BM_is_loggable(int iters) {
+    StartBenchmarkTiming();
+
+    for (int i = 0; i < iters; ++i) {
+        __android_log_is_loggable(ANDROID_LOG_WARN, "logd", ANDROID_LOG_VERBOSE);
+    }
+
+    StopBenchmarkTiming();
+}
+BENCHMARK(BM_is_loggable);
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 8582344..34131f1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -85,7 +85,8 @@
   // Length of the central directory comment.
   uint16_t comment_length;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord);
+  EocdRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
 } __attribute__((packed));
 
 // A structure representing the fixed length fields for a single
@@ -138,7 +139,8 @@
   // beginning of this archive.
   uint32_t local_file_header_offset;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord);
+  CentralDirectoryRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
 } __attribute__((packed));
 
 // The local file header for a given entry. This duplicates information
@@ -175,7 +177,8 @@
   // will appear immediately after the entry file name.
   uint16_t extra_field_length;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader);
+  LocalFileHeader() = default;
+  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
 } __attribute__((packed));
 
 struct DataDescriptor {
@@ -189,10 +192,10 @@
   // Uncompressed size of the entry.
   uint32_t uncompressed_size;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor);
+  DataDescriptor() = default;
+  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
 } __attribute__((packed));
 
-#undef DISALLOW_IMPLICIT_CONSTRUCTORS
 
 static const uint32_t kGPBDDFlagMask = 0x0008;         // mask value that signifies that the entry has a DD
 
@@ -265,8 +268,6 @@
 
 static const int32_t kErrorMessageLowerBound = -13;
 
-static const char kTempMappingFileName[] = "zip: ExtractFileToFile";
-
 /*
  * A Read-only Zip archive.
  *
@@ -324,35 +325,6 @@
   }
 };
 
-static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) {
-  static const uint32_t kBufSize = 32768;
-  uint8_t buf[kBufSize];
-
-  uint32_t count = 0;
-  uint64_t crc = 0;
-  while (count < length) {
-    uint32_t remaining = length - count;
-
-    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
-    // value.
-    ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining;
-    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
-
-    if (actual != get_size) {
-      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size);
-      return kIoError;
-    }
-
-    memcpy(begin + count, buf, get_size);
-    crc = crc32(crc, buf, get_size);
-    count += get_size;
-  }
-
-  *crc_out = crc;
-
-  return 0;
-}
-
 /*
  * Round up to the next highest power of 2.
  *
@@ -972,6 +944,127 @@
   return kIterationEnd;
 }
 
+class Writer {
+ public:
+  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+  virtual ~Writer() {}
+ protected:
+  Writer() = default;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+// A Writer that writes data to a fixed size memory region.
+// The size of the memory region must be equal to the total size of
+// the data appended to it.
+class MemoryWriter : public Writer {
+ public:
+  MemoryWriter(uint8_t* buf, size_t size) : Writer(),
+      buf_(buf), size_(size), bytes_written_(0) {
+  }
+
+  virtual bool Append(uint8_t* buf, size_t buf_size) override {
+    if (bytes_written_ + buf_size > size_) {
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+            size_, bytes_written_ + buf_size);
+      return false;
+    }
+
+    memcpy(buf_ + bytes_written_, buf, buf_size);
+    bytes_written_ += buf_size;
+    return true;
+  }
+
+ private:
+  uint8_t* const buf_;
+  const size_t size_;
+  size_t bytes_written_;
+};
+
+// A Writer that appends data to a file |fd| at its current position.
+// The file will be truncated to the end of the written data.
+class FileWriter : public Writer {
+ public:
+
+  // Creates a FileWriter for |fd| and prepare to write |entry| to it,
+  // guaranteeing that the file descriptor is valid and that there's enough
+  // space on the volume to write out the entry completely and that the file
+  // is truncated to the correct length.
+  //
+  // Returns a valid FileWriter on success, |nullptr| if an error occurred.
+  static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
+    const uint32_t declared_length = entry->uncompressed_length;
+    const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
+    if (current_offset == -1) {
+      ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
+      return nullptr;
+    }
+
+    int result = 0;
+#if defined(__linux__)
+    if (declared_length > 0) {
+      // Make sure we have enough space on the volume to extract the compressed
+      // entry. Note that the call to ftruncate below will change the file size but
+      // will not allocate space on disk and this call to fallocate will not
+      // change the file size.
+      result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+      if (result == -1) {
+        ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
+              static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+        return std::unique_ptr<FileWriter>(nullptr);
+      }
+    }
+#endif  // __linux__
+
+    result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+    if (result == -1) {
+      ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+      return std::unique_ptr<FileWriter>(nullptr);
+    }
+
+    return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
+  }
+
+  virtual bool Append(uint8_t* buf, size_t buf_size) override {
+    if (total_bytes_written_ + buf_size > declared_length_) {
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+            declared_length_, total_bytes_written_ + buf_size);
+      return false;
+    }
+
+    // Keep track of the start position so we can calculate the
+    // total number of bytes written.
+    const uint8_t* const start = buf;
+    while (buf_size > 0) {
+      ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, buf, buf_size));
+      if (bytes_written == -1) {
+        ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+        return false;
+      }
+
+      buf_size -= bytes_written;
+      buf += bytes_written;
+    }
+
+    total_bytes_written_ += static_cast<size_t>(
+        reinterpret_cast<uintptr_t>(buf) - reinterpret_cast<uintptr_t>(start));
+
+    return true;
+  }
+ private:
+  FileWriter(const int fd, const size_t declared_length) :
+      Writer(),
+      fd_(fd),
+      declared_length_(declared_length),
+      total_bytes_written_(0) {
+  }
+
+  const int fd_;
+  const size_t declared_length_;
+  size_t total_bytes_written_;
+};
+
 // This method is using libz macros with old-style-casts
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -980,9 +1073,8 @@
 }
 #pragma GCC diagnostic pop
 
-static int32_t InflateToFile(int fd, const ZipEntry* entry,
-                             uint8_t* begin, uint32_t length,
-                             uint64_t* crc_out) {
+static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
+                                    Writer* writer, uint64_t* crc_out) {
   const size_t kBufSize = 32768;
   std::vector<uint8_t> read_buf(kBufSize);
   std::vector<uint8_t> write_buf(kBufSize);
@@ -1027,7 +1119,6 @@
   const uint32_t uncompressed_length = entry->uncompressed_length;
 
   uint32_t compressed_length = entry->compressed_length;
-  uint32_t write_count = 0;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
@@ -1057,12 +1148,10 @@
     if (zstream.avail_out == 0 ||
       (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
       const size_t write_size = zstream.next_out - &write_buf[0];
-      // The file might have declared a bogus length.
-      if (write_size + write_count > length) {
-        return -1;
+      if (!writer->Append(&write_buf[0], write_size)) {
+        // The file might have declared a bogus length.
+        return kInconsistentInformation;
       }
-      memcpy(begin + write_count, &write_buf[0], write_size);
-      write_count += write_size;
 
       zstream.next_out = &write_buf[0];
       zstream.avail_out = kBufSize;
@@ -1083,8 +1172,41 @@
   return 0;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle,
-                        ZipEntry* entry, uint8_t* begin, uint32_t size) {
+static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
+                                 uint64_t *crc_out) {
+  static const uint32_t kBufSize = 32768;
+  std::vector<uint8_t> buf(kBufSize);
+
+  const uint32_t length = entry->uncompressed_length;
+  uint32_t count = 0;
+  uint64_t crc = 0;
+  while (count < length) {
+    uint32_t remaining = length - count;
+
+    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
+    // value.
+    const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+    const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
+
+    if (actual != block_size) {
+      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+      return kIoError;
+    }
+
+    if (!writer->Append(&buf[0], block_size)) {
+      return kIoError;
+    }
+    crc = crc32(crc, &buf[0], block_size);
+    count += block_size;
+  }
+
+  *crc_out = crc;
+
+  return 0;
+}
+
+int32_t ExtractToWriter(ZipArchiveHandle handle,
+                        ZipEntry* entry, Writer* writer) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   const uint16_t method = entry->method;
   off64_t data_offset = entry->offset;
@@ -1098,9 +1220,9 @@
   int32_t return_value = -1;
   uint64_t crc = 0;
   if (method == kCompressStored) {
-    return_value = CopyFileToFile(archive->fd, begin, size, &crc);
+    return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
   } else if (method == kCompressDeflated) {
-    return_value = InflateToFile(archive->fd, entry, begin, size, &crc);
+    return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
   }
 
   if (!return_value && entry->has_data_descriptor) {
@@ -1120,55 +1242,20 @@
   return return_value;
 }
 
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
+                        uint8_t* begin, uint32_t size) {
+  std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
+  return ExtractToWriter(handle, entry, writer.get());
+}
+
 int32_t ExtractEntryToFile(ZipArchiveHandle handle,
                            ZipEntry* entry, int fd) {
-  const uint32_t declared_length = entry->uncompressed_length;
-
-  const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
-  if (current_offset == -1) {
-    ALOGW("Zip: unable to seek to current location on fd %d: %s", fd,
-          strerror(errno));
+  std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
+  if (writer.get() == nullptr) {
     return kIoError;
   }
 
-  int result = 0;
-#if defined(__linux__)
-  // Make sure we have enough space on the volume to extract the compressed
-  // entry. Note that the call to ftruncate below will change the file size but
-  // will not allocate space on disk.
-  if (declared_length > 0) {
-    result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
-    if (result == -1) {
-      ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
-            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
-      return kIoError;
-    }
-  }
-#endif  // defined(__linux__)
-
-  result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
-  if (result == -1) {
-    ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
-          static_cast<int64_t>(declared_length + current_offset), strerror(errno));
-    return kIoError;
-  }
-
-  // Don't attempt to map a region of length 0. We still need the
-  // ftruncate() though, since the API guarantees that we will truncate
-  // the file to the end of the uncompressed output.
-  if (declared_length == 0) {
-      return 0;
-  }
-
-  android::FileMap map;
-  if (!map.create(kTempMappingFileName, fd, current_offset, declared_length, false)) {
-    return kMmapFailed;
-  }
-
-  const int32_t error = ExtractToMemory(handle, entry,
-                                        reinterpret_cast<uint8_t*>(map.getDataPtr()),
-                                        map.getDataLength());
-  return error;
+  return ExtractToWriter(handle, entry, writer.get());
 }
 
 const char* ErrorCodeString(int32_t error_code) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 64faa6d..f8952ce 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <vector>
 
+#include <base/file.h>
 #include <gtest/gtest.h>
 
 static std::string test_data_dir;
@@ -228,6 +229,44 @@
       0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
       0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
 
+// This is a zip file containing a single entry (ab.txt) that contains
+// 90072 repetitions of the string "ab\n" and has an uncompressed length
+// of 270216 bytes.
+static const uint16_t kAbZip[] = {
+  0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
+  0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
+  0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+  0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
+  0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
+  0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
+  0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
+  0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+  0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
+  0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
+  0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+  0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
+};
+
+static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
+static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
+static const size_t kAbUncompressedSize = 270216;
+
 static int make_temporary_file(const char* file_name_pattern) {
   char full_path[1024];
   // Account for differences between the host and the target.
@@ -275,6 +314,55 @@
   close(output_fd);
 }
 
+TEST(ziparchive, EntryLargerThan32K) {
+  char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
+  int fd = make_temporary_file(temp_file_pattern);
+  ASSERT_NE(-1, fd);
+  ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+                         sizeof(kAbZip) - 1));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+
+  ZipEntry entry;
+  ZipEntryName ab_name;
+  ab_name.name = kAbTxtName;
+  ab_name.name_length = kAbTxtNameLength;
+  ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+  ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
+
+  // Extract the entry to memory.
+  std::vector<uint8_t> buffer(kAbUncompressedSize);
+  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+
+  // Extract the entry to a file.
+  char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
+  int output_fd = make_temporary_file(output_file_pattern);
+  ASSERT_NE(-1, output_fd);
+  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+  // Make sure the extracted file size is as expected.
+  struct stat stat_buf;
+  ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+  ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
+
+  // Read the file back to a buffer and make sure the contents are
+  // the same as the memory buffer we extracted directly to.
+  std::vector<uint8_t> file_contents(kAbUncompressedSize);
+  ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+  ASSERT_EQ(file_contents, buffer);
+
+  for (int i = 0; i < 90072; ++i) {
+    const uint8_t* line = &file_contents[0] + (3 * i);
+    ASSERT_EQ('a', line[0]);
+    ASSERT_EQ('b', line[1]);
+    ASSERT_EQ('\n', line[2]);
+  }
+
+  close(fd);
+  close(output_fd);
+}
+
 TEST(ziparchive, TrailerAfterEOCD) {
   char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
   int fd = make_temporary_file(temp_file_pattern);
diff --git a/rootdir/etc/mountd.conf b/rootdir/etc/mountd.conf
deleted file mode 100644
index 094a2c7..0000000
--- a/rootdir/etc/mountd.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-## mountd configuration file
-
-## add a mount entry for each mount point to be managed by mountd
-mount {
-    ## root block device with partition map or raw FAT file system
-    block_device    /dev/block/mmcblk0
-        
-    ## mount point for block device
-    mount_point     /sdcard
-    
-    ## true if this mount point can be shared via USB mass storage
-    enable_ums      true
-    
-    ## path to the UMS driver file for specifying the block device path  
-    ## use this for the mass_storage function driver
-    driver_store_path   /sys/devices/platform/usb_mass_storage/lun0/file
-    ## use this for android_usb composite gadget driver
-    ##driver_store_path   /sys/devices/platform/msm_hsusb/gadget/lun0/file
-}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b353d9d..3709b82 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -14,13 +14,6 @@
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
-    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
-    write /sys/fs/selinux/checkreqprot 0
-
-    # Set the security context for the init process.
-    # This should occur before anything else (e.g. ueventd) is started.
-    setcon u:r:init:s0
-
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys
 
@@ -162,6 +155,7 @@
 # Load properties from /system/ + /factory after fs mount.
 on load_all_props_action
     load_all_props
+    start logd
     start logd-reinit
 
 # Indicate to fw loaders that the relevant mounts are up.
@@ -448,6 +442,7 @@
 
 on property:vold.decrypt=trigger_load_persist_props
     load_persist_props
+    start logd
     start logd-reinit
 
 on property:vold.decrypt=trigger_post_fs_data
@@ -492,7 +487,6 @@
     socket logdw dgram 0222 logd logd
 
 service logd-reinit /system/bin/logd --reinit
-    start logd
     oneshot
     disabled
 
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 041c37a..893c0dc 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -1875,7 +1875,7 @@
     struct fuse fuse;
 
     /* cleanup from previous instance, if necessary */
-    umount2(dest_path, 2);
+    umount2(dest_path, MNT_DETACH);
 
     fd = open("/dev/fuse", O_RDWR);
     if (fd < 0){
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
index 0eac390..915da44 100644
--- a/toolbox/toolbox.c
+++ b/toolbox/toolbox.c
@@ -1,6 +1,8 @@
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 int main(int, char **);
 
@@ -31,11 +33,24 @@
     { 0, 0 },
 };
 
+static void SIGPIPE_handler(int signal) {
+    // Those desktop Linux tools that catch SIGPIPE seem to agree that it's
+    // a successful way to exit, not a failure. (Which makes sense --- we were
+    // told to stop by a reader, rather than failing to continue ourselves.)
+    _exit(0);
+}
+
 int main(int argc, char **argv)
 {
     int i;
     char *name = argv[0];
 
+    // Let's assume that none of this code handles broken pipes. At least ls,
+    // ps, and top were broken (though I'd previously added this fix locally
+    // to top). We exit rather than use SIG_IGN because tools like top will
+    // just keep on writing to nowhere forever if we don't stop them.
+    signal(SIGPIPE, SIGPIPE_handler);
+
     if((argc > 1) && (argv[1][0] == '@')) {
         name = argv[1] + 1;
         argc--;
diff --git a/toolbox/top.c b/toolbox/top.c
index b1a275c..1e99d4c 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -109,15 +109,9 @@
 static int numcmp(long long a, long long b);
 static void usage(char *cmd);
 
-static void exit_top(int signal) {
-  exit(EXIT_FAILURE);
-}
-
 int top_main(int argc, char *argv[]) {
     num_used_procs = num_free_procs = 0;
 
-    signal(SIGPIPE, exit_top);
-
     max_procs = 0;
     delay = 3;
     iterations = -1;