Merge "Don't fail if the new pss is less than stable."
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index db9b710..d29c08e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -50,6 +50,15 @@
     __adb_serial = serial;
 }
 
+void adb_get_transport(TransportType* type, const char** serial) {
+    if (type) {
+        *type = __adb_transport;
+    }
+    if (serial) {
+        *serial = __adb_serial;
+    }
+}
+
 void adb_set_tcp_specifics(int server_port)
 {
     __adb_server_port = server_port;
diff --git a/adb/adb_client.h b/adb/adb_client.h
index a9df4d7..d5cd922 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -39,6 +39,9 @@
 // Set the preferred transport to connect to.
 void adb_set_transport(TransportType type, const char* _Nullable serial);
 
+// Get the preferred transport to connect to.
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial);
+
 // Set TCP specifics of the transport to use.
 void adb_set_tcp_specifics(int server_port);
 
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 26e376c..3333fc6 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -153,7 +153,9 @@
   // - Recursive, so it uses stack space relative to number of directory
   //   components.
 
-  if (directory_exists(path)) {
+  // If path points to a symlink to a directory, that's fine.
+  struct stat sb;
+  if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
     return true;
   }
 
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f1149b3..89fcd66 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
 
 #include <string>
 
+#include <android-base/macros.h>
+
 void close_stdin();
 
 bool getcwd(std::string* cwd);
@@ -39,4 +41,45 @@
 
 bool set_file_block_mode(int fd, bool block);
 
+extern int adb_close(int fd);
+
+// Helper to automatically close an FD when it goes out of scope.
+class ScopedFd {
+  public:
+    ScopedFd() {
+    }
+
+    ~ScopedFd() {
+        Reset();
+    }
+
+    void Reset(int fd = -1) {
+        if (fd != fd_) {
+            if (valid()) {
+                adb_close(fd_);
+            }
+            fd_ = fd;
+        }
+    }
+
+    int Release() {
+        int temp = fd_;
+        fd_ = -1;
+        return temp;
+    }
+
+    bool valid() const {
+        return fd_ >= 0;
+    }
+
+    int fd() const {
+        return fd_;
+    }
+
+  private:
+    int fd_ = -1;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
 #endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 37d1146..30a38e3 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1072,6 +1072,51 @@
     return adb_command(cmd);
 }
 
+static bool adb_root(const char* command) {
+    std::string error;
+    ScopedFd fd;
+
+    fd.Reset(adb_connect(android::base::StringPrintf("%s:", command), &error));
+    if (!fd.valid()) {
+        fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
+        return false;
+    }
+
+    // Figure out whether we actually did anything.
+    char buf[256];
+    char* cur = buf;
+    ssize_t bytes_left = sizeof(buf);
+    while (bytes_left > 0) {
+        ssize_t bytes_read = adb_read(fd.fd(), cur, bytes_left);
+        if (bytes_read == 0) {
+            break;
+        } else if (bytes_read < 0) {
+            fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
+            return false;
+        }
+        cur += bytes_read;
+        bytes_left -= bytes_read;
+    }
+
+    if (bytes_left == 0) {
+        fprintf(stderr, "adb: unexpected output length for %s\n", command);
+        return false;
+    }
+
+    fflush(stdout);
+    WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
+    if (cur != buf && strstr(buf, "restarting") == nullptr) {
+        return true;
+    }
+
+    // Give adbd 500ms to kill itself, then wait-for-device for it to come back up.
+    adb_sleep_ms(500);
+    TransportType type;
+    const char* serial;
+    adb_get_transport(&type, &serial);
+    return wait_for_device("wait-for-device", type, serial);
+}
+
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
 static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1220,6 +1265,9 @@
     printf("Now unlock your device and confirm the restore operation.\n");
     copy_to_file(tarFd, fd);
 
+    // Wait until the other side finishes, or it'll get sent SIGHUP.
+    copy_to_file(fd, STDOUT_FILENO);
+
     adb_close(fd);
     adb_close(tarFd);
     return 0;
@@ -1632,8 +1680,6 @@
              !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
              !strcmp(argv[0], "usb") ||
-             !strcmp(argv[0], "root") ||
-             !strcmp(argv[0], "unroot") ||
              !strcmp(argv[0], "disable-verity") ||
              !strcmp(argv[0], "enable-verity")) {
         std::string command;
@@ -1645,8 +1691,9 @@
             command = android::base::StringPrintf("%s:", argv[0]);
         }
         return adb_connect_command(command);
-    }
-    else if (!strcmp(argv[0], "bugreport")) {
+    } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
+        return adb_root(argv[0]) ? 0 : 1;
+    } else if (!strcmp(argv[0], "bugreport")) {
         if (argc != 1) return usage();
         // No need for shell protocol with bugreport, always disable for
         // simplicity.
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index cdbff11..6a9e163 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -60,6 +60,18 @@
     }
 }
 
+static bool should_pull_file(mode_t mode) {
+    return mode & (S_IFREG | S_IFBLK | S_IFCHR);
+}
+
+static bool should_push_file(mode_t mode) {
+    mode_t mask = S_IFREG;
+#if !defined(_WIN32)
+    mask |= S_IFLNK;
+#endif
+    return mode & mask;
+}
+
 struct copyinfo {
     std::string lpath;
     std::string rpath;
@@ -483,11 +495,6 @@
 #endif
     }
 
-    if (!S_ISREG(mode)) {
-        sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
-        return false;
-    }
-
     struct stat st;
     if (stat(lpath, &st) == -1) {
         sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
@@ -619,13 +626,12 @@
         if (S_ISDIR(st.st_mode)) {
             dirlist.push_back(ci);
         } else {
-            if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                sc.Error("skipping special file '%s'", lpath.c_str());
+            if (!should_push_file(st.st_mode)) {
+                sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
                 ci.skip = true;
-            } else {
-                ci.time = st.st_mtime;
-                ci.size = st.st_size;
             }
+            ci.time = st.st_mtime;
+            ci.size = st.st_size;
             file_list->push_back(ci);
         }
     }
@@ -767,6 +773,9 @@
             success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
                                              false, false);
             continue;
+        } else if (!should_push_file(st.st_mode)) {
+            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
+            continue;
         }
 
         std::string path_holder;
@@ -804,7 +813,7 @@
     std::vector<copyinfo> linklist;
 
     // Add an entry for the current directory to ensure it gets created before pulling its contents.
-    copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
+    copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
     file_list->push_back(ci);
 
     // Put the files/dirs in rpath on the lists.
@@ -819,6 +828,10 @@
         } else if (S_ISLNK(mode)) {
             linklist.push_back(ci);
         } else {
+            if (!should_pull_file(ci.mode)) {
+                sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
+                ci.skip = true;
+            }
             ci.time = time;
             ci.size = size;
             file_list->push_back(ci);
@@ -975,11 +988,6 @@
             src_isdir = remote_symlink_isdir(sc, src_path);
         }
 
-        if ((src_mode & (S_IFREG | S_IFDIR | S_IFBLK | S_IFCHR)) == 0) {
-            sc.Error("skipping remote object '%s' (mode = 0o%o)", src_path, src_mode);
-            continue;
-        }
-
         if (src_isdir) {
             std::string dst_dir = dst;
 
@@ -998,27 +1006,30 @@
 
             success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
             continue;
-        } else {
-            std::string path_holder;
-            if (dst_isdir) {
-                // If we're copying a remote file to a local directory, we
-                // really want to copy to local_dir + OS_PATH_SEPARATOR +
-                // basename(remote).
-                path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
-                                                          adb_basename(src_path).c_str());
-                dst_path = path_holder.c_str();
-            }
+        } else if (!should_pull_file(src_mode)) {
+            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_mode);
+            continue;
+        }
 
-            sc.SetExpectedTotalBytes(src_size);
-            if (!sync_recv(sc, src_path, dst_path)) {
-                success = false;
-                continue;
-            }
+        std::string path_holder;
+        if (dst_isdir) {
+            // If we're copying a remote file to a local directory, we
+            // really want to copy to local_dir + OS_PATH_SEPARATOR +
+            // basename(remote).
+            path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+                                                      adb_basename(src_path).c_str());
+            dst_path = path_holder.c_str();
+        }
 
-            if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
-                success = false;
-                continue;
-            }
+        sc.SetExpectedTotalBytes(src_size);
+        if (!sync_recv(sc, src_path, dst_path)) {
+            success = false;
+            continue;
+        }
+
+        if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+            success = false;
+            continue;
         }
     }
 
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index f84447f..ce10708 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -135,37 +135,6 @@
     return received;
 }
 
-// Helper to automatically close an FD when it goes out of scope.
-class ScopedFd {
-  public:
-    ScopedFd() {}
-    ~ScopedFd() { Reset(); }
-
-    void Reset(int fd=-1) {
-        if (fd != fd_) {
-            if (valid()) {
-                adb_close(fd_);
-            }
-            fd_ = fd;
-        }
-    }
-
-    int Release() {
-        int temp = fd_;
-        fd_ = -1;
-        return temp;
-    }
-
-    bool valid() const { return fd_ >= 0; }
-
-    int fd() const { return fd_; }
-
-  private:
-    int fd_ = -1;
-
-    DISALLOW_COPY_AND_ASSIGN(ScopedFd);
-};
-
 // Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
 bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) {
     int sockets[2];
@@ -315,7 +284,9 @@
     if (type_ == SubprocessType::kPty) {
         int fd;
         pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
-        stdinout_sfd_.Reset(fd);
+        if (pid_ > 0) {
+          stdinout_sfd_.Reset(fd);
+        }
     } else {
         if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
             *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
diff --git a/adb/socket.h b/adb/socket.h
index 4083036..9eb1b19 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -114,4 +114,13 @@
 void connect_to_remote(asocket *s, const char *destination);
 void connect_to_smartsocket(asocket *s);
 
+// Internal functions that are only made available here for testing purposes.
+namespace internal {
+
+#if ADB_HOST
+char* skip_host_serial(const char* service);
+#endif
+
+}  // namespace internal
+
 #endif  // __ADB_SOCKET_H
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 471ca09..5cbef6d 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -270,3 +270,49 @@
 }
 
 #endif  // defined(__linux__)
+
+#if ADB_HOST
+
+// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
+// |expected|, otherwise logs the failure to gtest.
+void VerifySkipHostSerial(const std::string& serial, const char* expected) {
+    const char* result = internal::skip_host_serial(serial.c_str());
+    if (expected == nullptr) {
+        EXPECT_EQ(nullptr, result);
+    } else {
+        EXPECT_STREQ(expected, result);
+    }
+}
+
+// Check [tcp:|udp:]<serial>[:<port>]:<command> format.
+TEST(socket_test, test_skip_host_serial) {
+    for (const std::string& protocol : {"", "tcp:", "udp:"}) {
+        VerifySkipHostSerial(protocol, nullptr);
+        VerifySkipHostSerial(protocol + "foo", nullptr);
+
+        VerifySkipHostSerial(protocol + "foo:bar", ":bar");
+        VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+
+        VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
+        VerifySkipHostSerial(protocol + "foo:123:456", ":456");
+        VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+
+        // Don't register a port unless it's all numbers and ends with ':'.
+        VerifySkipHostSerial(protocol + "foo:123", ":123");
+        VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+    }
+}
+
+// Check <prefix>:<serial>:<command> format.
+TEST(socket_test, test_skip_host_serial_prefix) {
+    for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
+        VerifySkipHostSerial(prefix, nullptr);
+        VerifySkipHostSerial(prefix + "foo", nullptr);
+
+        VerifySkipHostSerial(prefix + "foo:bar", ":bar");
+        VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
+        VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+    }
+}
+
+#endif  // ADB_HOST
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index d8e4e93..c083ee1 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -26,6 +26,8 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <string>
+#include <vector>
 
 #if !ADB_HOST
 #include "cutils/properties.h"
@@ -623,43 +625,43 @@
 
 #if ADB_HOST
 
-#define PREFIX(str) { str, sizeof(str) - 1 }
-static const struct prefix_struct {
-    const char *str;
-    const size_t len;
-} prefixes[] = {
-    PREFIX("usb:"),
-    PREFIX("product:"),
-    PREFIX("model:"),
-    PREFIX("device:"),
-};
-static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0]));
+namespace internal {
 
-/* skip_host_serial return the position in a string
-   skipping over the 'serial' parameter in the ADB protocol,
-   where parameter string may be a host:port string containing
-   the protocol delimiter (colon). */
-static char *skip_host_serial(char *service) {
-    char *first_colon, *serial_end;
-    int i;
+// Returns the position in |service| following the target serial parameter. Serial format can be
+// any of:
+//   * [tcp:|udp:]<serial>[:<port>]:<command>
+//   * <prefix>:<serial>:<command>
+// Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
+//
+// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
+char* skip_host_serial(const char* service) {
+    static const std::vector<std::string>& prefixes =
+        *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
 
-    for (i = 0; i < num_prefixes; i++) {
-        if (!strncmp(service, prefixes[i].str, prefixes[i].len))
-            return strchr(service + prefixes[i].len, ':');
+    for (const std::string& prefix : prefixes) {
+        if (!strncmp(service, prefix.c_str(), prefix.length())) {
+            return strchr(service + prefix.length(), ':');
+        }
     }
 
-    first_colon = strchr(service, ':');
+    // For fastboot compatibility, ignore protocol prefixes.
+    if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
+        service += 4;
+    }
+
+    char* first_colon = strchr(service, ':');
     if (!first_colon) {
-        /* No colon in service string. */
-        return NULL;
+        // No colon in service string.
+        return nullptr;
     }
-    serial_end = first_colon;
+
+    char* serial_end = first_colon;
     if (isdigit(serial_end[1])) {
         serial_end++;
-        while ((*serial_end) && isdigit(*serial_end)) {
+        while (*serial_end && isdigit(*serial_end)) {
             serial_end++;
         }
-        if ((*serial_end) != ':') {
+        if (*serial_end != ':') {
             // Something other than numbers was found, reset the end.
             serial_end = first_colon;
         }
@@ -667,6 +669,8 @@
     return serial_end;
 }
 
+}  // namespace internal
+
 #endif // ADB_HOST
 
 static int smart_socket_enqueue(asocket *s, apacket *p)
@@ -725,7 +729,7 @@
         service += strlen("host-serial:");
 
         // serial number should follow "host:" and could be a host:port string.
-        serial_end = skip_host_serial(service);
+        serial_end = internal::skip_host_serial(service);
         if (serial_end) {
             *serial_end = 0; // terminate string
             serial = service;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index ce0f289..81d201e 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -576,8 +576,7 @@
 // Closes a file descriptor that came from adb_open() or adb_open_mode(), but
 // not designed to take a file descriptor from unix_open(). See the comments
 // for adb_open() for more info.
-static __inline__ int  adb_close(int fd)
-{
+__inline__ int adb_close(int fd) {
     return close(fd);
 }
 #undef   close
diff --git a/adb/test_device.py b/adb/test_device.py
index 18174a2..9dab3ae 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -216,8 +216,8 @@
         """Send data through adb forward and read it back via adb reverse"""
         forward_port = 12345
         reverse_port = forward_port + 1
-        forward_spec = "tcp:" + str(forward_port)
-        reverse_spec = "tcp:" + str(reverse_port)
+        forward_spec = 'tcp:' + str(forward_port)
+        reverse_spec = 'tcp:' + str(reverse_port)
         forward_setup = False
         reverse_setup = False
 
@@ -739,7 +739,7 @@
             # Create some random files and a subdirectory containing more files.
             temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
 
-            subdir = os.path.join(host_dir, "subdir")
+            subdir = os.path.join(host_dir, 'subdir')
             os.mkdir(subdir)
             subdir_temp_files = make_random_host_files(in_dir=subdir,
                                                        num_files=4)
@@ -756,7 +756,7 @@
             for subdir_temp_file in subdir_temp_files:
                 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
                                              # BROKEN: http://b/25394682
-                                             # "subdir",
+                                             # 'subdir';
                                              temp_file.base_name)
                 self._verify_remote(temp_file.checksum, remote_path)
 
@@ -777,11 +777,11 @@
             tmp_file.flush()
             try:
                 self.device.push(local=tmp_file.name, remote='/system/')
-                self.fail("push should not have succeeded")
+                self.fail('push should not have succeeded')
             except subprocess.CalledProcessError as e:
                 output = e.output
 
-            self.assertIn("Permission denied", output)
+            self.assertIn('Permission denied', output)
 
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
@@ -845,6 +845,96 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
+    def test_pull_dir_symlink(self):
+        """Pull a directory into a symlink to a directory.
+
+        Bug: http://b/27362811
+        """
+        if os.name != 'posix':
+            raise unittest.SkipTest('requires POSIX')
+
+        try:
+            host_dir = tempfile.mkdtemp()
+            real_dir = os.path.join(host_dir, 'dir')
+            symlink = os.path.join(host_dir, 'symlink')
+            os.mkdir(real_dir)
+            os.symlink(real_dir, symlink)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+            self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+            # Populate device directory with random files.
+            temp_files = make_random_device_files(
+                self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+            self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
+
+            for temp_file in temp_files:
+                host_path = os.path.join(
+                    real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+                    temp_file.base_name)
+                self._verify_local(temp_file.checksum, host_path)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        finally:
+            if host_dir is not None:
+                shutil.rmtree(host_dir)
+
+    def test_pull_dir_symlink_collision(self):
+        """Pull a directory into a colliding symlink to directory."""
+        if os.name != 'posix':
+            raise unittest.SkipTest('requires POSIX')
+
+        try:
+            host_dir = tempfile.mkdtemp()
+            real_dir = os.path.join(host_dir, 'real')
+            tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
+            symlink = os.path.join(host_dir, tmp_dirname)
+            os.mkdir(real_dir)
+            os.symlink(real_dir, symlink)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+            self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+            # Populate device directory with random files.
+            temp_files = make_random_device_files(
+                self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+            self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+            for temp_file in temp_files:
+                host_path = os.path.join(real_dir, temp_file.base_name)
+                self._verify_local(temp_file.checksum, host_path)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        finally:
+            if host_dir is not None:
+                shutil.rmtree(host_dir)
+
+    def test_pull_dir_nonexistent(self):
+        """Pull a directory of files from the device to a nonexistent path."""
+        try:
+            host_dir = tempfile.mkdtemp()
+            dest_dir = os.path.join(host_dir, 'dest')
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+            self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+            # Populate device directory with random files.
+            temp_files = make_random_device_files(
+                self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+            self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+            for temp_file in temp_files:
+                host_path = os.path.join(dest_dir, temp_file.base_name)
+                self._verify_local(temp_file.checksum, host_path)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        finally:
+            if host_dir is not None:
+                shutil.rmtree(host_dir)
+
     def test_pull_symlink_dir(self):
         """Pull a symlink to a directory of symlinks to files."""
         try:
@@ -900,7 +990,7 @@
         try:
             host_dir = tempfile.mkdtemp()
 
-            subdir = posixpath.join(self.DEVICE_TEMP_DIR, "subdir")
+            subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
             self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
             self.device.shell(['mkdir', '-p', subdir])
 
@@ -921,7 +1011,7 @@
 
             for subdir_temp_file in subdir_temp_files:
                 local_path = os.path.join(host_dir,
-                                          "subdir",
+                                          'subdir',
                                           subdir_temp_file.base_name)
                 self._verify_local(subdir_temp_file.checksum, local_path)
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index d9180bc..e3340af 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -30,6 +30,7 @@
 #include <list>
 
 #include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
@@ -679,11 +680,7 @@
 
         // Check for matching serial number.
         if (serial) {
-            if ((t->serial && !strcmp(serial, t->serial)) ||
-                (t->devpath && !strcmp(serial, t->devpath)) ||
-                qual_match(serial, "product:", t->product, false) ||
-                qual_match(serial, "model:", t->model, true) ||
-                qual_match(serial, "device:", t->device, false)) {
+            if (t->MatchesTarget(serial)) {
                 if (result) {
                     *error_out = "more than one device";
                     if (is_ambiguous) *is_ambiguous = true;
@@ -837,6 +834,43 @@
     disconnects_.clear();
 }
 
+bool atransport::MatchesTarget(const std::string& target) const {
+    if (serial) {
+        if (target == serial) {
+            return true;
+        } else if (type == kTransportLocal) {
+            // Local transports can match [tcp:|udp:]<hostname>[:port].
+            const char* local_target_ptr = target.c_str();
+
+            // For fastboot compatibility, ignore protocol prefixes.
+            if (android::base::StartsWith(target, "tcp:") ||
+                    android::base::StartsWith(target, "udp:")) {
+                local_target_ptr += 4;
+            }
+
+            // Parse our |serial| and the given |target| to check if the hostnames and ports match.
+            std::string serial_host, error;
+            int serial_port = -1;
+            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr,
+                                               &error)) {
+                // |target| may omit the port to default to ours.
+                std::string target_host;
+                int target_port = serial_port;
+                if (android::base::ParseNetAddress(local_target_ptr, &target_host, &target_port,
+                                                   nullptr, &error) &&
+                        serial_host == target_host && serial_port == target_port) {
+                    return true;
+                }
+            }
+        }
+    }
+
+    return (devpath && target == devpath) ||
+           qual_match(target.c_str(), "product:", product, false) ||
+           qual_match(target.c_str(), "model:", model, true) ||
+           qual_match(target.c_str(), "device:", device, false);
+}
+
 #if ADB_HOST
 
 static void append_transport_info(std::string* result, const char* key,
diff --git a/adb/transport.h b/adb/transport.h
index 4c0c008..5857249 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -107,6 +107,21 @@
     void RemoveDisconnect(adisconnect* disconnect);
     void RunDisconnects();
 
+    // Returns true if |target| matches this transport. A matching |target| can be any of:
+    //   * <serial>
+    //   * <devpath>
+    //   * product:<product>
+    //   * model:<model>
+    //   * device:<device>
+    //
+    // If this is a local transport, serial will also match [tcp:|udp:]<hostname>[:port] targets.
+    // For example, serial "100.100.100.100:5555" would match any of:
+    //   * 100.100.100.100
+    //   * tcp:100.100.100.100
+    //   * udp:100.100.100.100:5555
+    // This is to make it easier to use the same network target for both fastboot and adb.
+    bool MatchesTarget(const std::string& target) const;
+
 private:
     // A set of features transmitted in the banner with the initial connection.
     // This is stored in the banner as 'features=feature0,feature1,etc'.
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 1bdea2a..2028ecc 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -218,3 +218,60 @@
     ASSERT_EQ(std::string("bar"), t.model);
     ASSERT_EQ(std::string("baz"), t.device);
 }
+
+TEST(transport, test_matches_target) {
+    std::string serial = "foo";
+    std::string devpath = "/path/to/bar";
+    std::string product = "test_product";
+    std::string model = "test_model";
+    std::string device = "test_device";
+
+    atransport t;
+    t.serial = &serial[0];
+    t.devpath = &devpath[0];
+    t.product = &product[0];
+    t.model = &model[0];
+    t.device = &device[0];
+
+    // These tests should not be affected by the transport type.
+    for (TransportType type : {kTransportAny, kTransportLocal}) {
+        t.type = type;
+
+        EXPECT_TRUE(t.MatchesTarget(serial));
+        EXPECT_TRUE(t.MatchesTarget(devpath));
+        EXPECT_TRUE(t.MatchesTarget("product:" + product));
+        EXPECT_TRUE(t.MatchesTarget("model:" + model));
+        EXPECT_TRUE(t.MatchesTarget("device:" + device));
+
+        // Product, model, and device don't match without the prefix.
+        EXPECT_FALSE(t.MatchesTarget(product));
+        EXPECT_FALSE(t.MatchesTarget(model));
+        EXPECT_FALSE(t.MatchesTarget(device));
+    }
+}
+
+TEST(transport, test_matches_target_local) {
+    std::string serial = "100.100.100.100:5555";
+
+    atransport t;
+    t.serial = &serial[0];
+
+    // Network address matching should only be used for local transports.
+    for (TransportType type : {kTransportAny, kTransportLocal}) {
+        t.type = type;
+        bool should_match = (type == kTransportLocal);
+
+        EXPECT_EQ(should_match, t.MatchesTarget("100.100.100.100"));
+        EXPECT_EQ(should_match, t.MatchesTarget("tcp:100.100.100.100"));
+        EXPECT_EQ(should_match, t.MatchesTarget("tcp:100.100.100.100:5555"));
+        EXPECT_EQ(should_match, t.MatchesTarget("udp:100.100.100.100"));
+        EXPECT_EQ(should_match, t.MatchesTarget("udp:100.100.100.100:5555"));
+
+        // Wrong protocol, hostname, or port should never match.
+        EXPECT_FALSE(t.MatchesTarget("100.100.100"));
+        EXPECT_FALSE(t.MatchesTarget("100.100.100.100:"));
+        EXPECT_FALSE(t.MatchesTarget("100.100.100.100:-1"));
+        EXPECT_FALSE(t.MatchesTarget("100.100.100.100:5554"));
+        EXPECT_FALSE(t.MatchesTarget("abc:100.100.100.100"));
+    }
+}
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cd526d0..e5babed 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -150,6 +150,14 @@
 #define UNIMPLEMENTED(level) \
   LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
 
+#ifdef __clang_analyzer__
+// ClangL static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;;abort())
+#else
+#define ABORT_AFTER_LOG_FATAL
+#endif
+
 // Check whether condition x holds and LOG(FATAL) if not. The value of the
 // expression x is only evaluated once. Extra logging can be appended using <<
 // after. For example:
@@ -160,6 +168,7 @@
   if (LIKELY((x)))                                                            \
     ;                                                                         \
   else                                                                        \
+    ABORT_AFTER_LOG_FATAL                                                     \
     ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                                 ::android::base::FATAL, -1).stream()          \
         << "Check failed: " #x << " "
@@ -169,6 +178,7 @@
   for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);        \
        UNLIKELY(!(_values.lhs OP _values.rhs));                             \
        /* empty */)                                                         \
+  ABORT_AFTER_LOG_FATAL                                                     \
   ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                               ::android::base::FATAL, -1).stream()          \
       << "Check failed: " << #LHS << " " << #OP << " " << #RHS              \
@@ -192,6 +202,7 @@
   if (LIKELY((strcmp(s1, s2) == 0) == sense))                              \
     ;                                                                      \
   else                                                                     \
+    ABORT_AFTER_LOG_FATAL                                                  \
     LOG(FATAL) << "Check failed: "                                         \
                << "\"" << s1 << "\""                                       \
                << (sense ? " == " : " != ") << "\"" << s2 << "\""
@@ -206,6 +217,7 @@
     int rc = call args;                                                \
     if (rc != 0) {                                                     \
       errno = rc;                                                      \
+      ABORT_AFTER_LOG_FATAL                                            \
       PLOG(FATAL) << #call << " failed for " << what; \
     }                                                                  \
   } while (false)
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c199190..bcc8abd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -170,7 +170,9 @@
   // use this signal to mark the time of the factory reset.
   if (!boot_event_store.GetBootEvent("factory_reset", &record)) {
     boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc);
-    boot_event_store.AddBootEventWithValue("time_since_factory_reset", 0);
+
+    // Don't log the time_since_factory_reset until some time has elapsed.
+    // The data is not meaningful yet and skews the histogram buckets.
     return;
   }
 
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 13ef27e..3c20fc8 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -3,10 +3,13 @@
 on post-fs-data
     mkdir /data/misc/bootstat 0700 root root
 
-# This marker, boot animation stopped, is considered the point at which the
+# The first marker, boot animation stopped, is considered the point at which
 # the user may interact with the device, so it is a good proxy for the boot
 # complete signal.
-on property:init.svc.bootanim=stopped
+#
+# The second marker ensures an encrypted device is decrypted before logging
+# boot time data.
+on property:init.svc.bootanim=stopped && property:vold.decrypt=trigger_restart_framework
     # Record boot_complete timing event.
     exec - root root -- /system/bin/bootstat -r boot_complete
 
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index c47a585..02aff55 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -600,7 +600,7 @@
                   fstab->recs[top_idx].fs_type);
             if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
                 strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
-                int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644);
+                int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
                 if (fd >= 0) {
                     INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
                     wipe_block_device(fd, get_file_size(fd));
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index 853bf0b..c63ff67 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -36,7 +36,7 @@
     uint64_t dev_sz;
     int fd, rc = 0;
 
-    if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
+    if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
         ERROR("Cannot open block device.  %s\n", strerror(errno));
         return -1;
     }
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index b4fdab0..7254cf2 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -76,7 +76,7 @@
 
     void store_sid(uint32_t uid, uint64_t sid) {
         char filename[21];
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
         if (fd < 0) {
             ALOGE("could not open file: %s: %s", filename, strerror(errno));
@@ -102,7 +102,7 @@
 
     void maybe_store_sid(uint32_t uid, uint64_t sid) {
         char filename[21];
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         if (access(filename, F_OK) == -1) {
             store_sid(uid, sid);
         }
@@ -111,7 +111,7 @@
     uint64_t read_sid(uint32_t uid) {
         char filename[21];
         uint64_t sid;
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         int fd = open(filename, O_RDONLY);
         if (fd < 0) return 0;
         read(fd, &sid, sizeof(sid));
@@ -121,7 +121,7 @@
 
     void clear_sid(uint32_t uid) {
         char filename[21];
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         if (remove(filename) < 0) {
             ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
             store_sid(uid, 0);
diff --git a/init/init.cpp b/init/init.cpp
index 4aef823..9e6143b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -561,6 +561,7 @@
         #define MAKE_STR(x) __STRING(x)
         mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
         mount("sysfs", "/sys", "sysfs", 0, NULL);
+        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
     }
 
     // We must have some place other than / to create the device nodes for
diff --git a/init/parser.cpp b/init/parser.cpp
index 8193729..ae103ec 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -12,7 +12,7 @@
     char buf[128];
     int off;
 
-    snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+    snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
     buf[127] = 0;
     off = strlen(buf);
 
diff --git a/init/util.cpp b/init/util.cpp
index 84b4155..bddc3b2 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -401,20 +401,18 @@
 
 void open_devnull_stdio(void)
 {
-    // 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);
-        }
+        /* Fail silently.
+         * stdout/stderr isn't available, and because
+         * klog_init() is called after open_devnull_stdio(), we can't
+         * log to dmesg. Reordering klog_init() to be called before
+         * open_devnull_stdio() isn't an option either, as then klog_fd
+         * will be assigned 0 or 1, which will end up getting clobbered
+         * by the code below. There's nowhere good to log.
+         */
+
+        exit(1);
     }
 
     dup2(fd, 0);
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index f985f52..67e583f 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -23,7 +23,6 @@
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
 
-#include <android-base/strings.h>
 #include <backtrace/Backtrace.h>
 
 #include "BacktraceLog.h"
@@ -125,16 +124,6 @@
         num_ignore_frames--;
       }
     }
-
-    // For now, do not attempt to do local unwinds through .dex, or .oat
-    // maps. We can only unwind through these if there is a compressed
-    // section available, almost all local unwinds are done by ART
-    // which will dump the Java frames separately.
-    // TODO: Come up with a flag to control this.
-    if (android::base::EndsWith(frame->map.name, ".dex")
-        || android::base::EndsWith(frame->map.name, ".oat")) {
-      break;
-    }
     ret = unw_step (cursor.get());
   } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
 
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
index 894f90e..d4d07c2 100644
--- a/libion/tests/Android.mk
+++ b/libion/tests/Android.mk
@@ -17,8 +17,9 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
+LOCAL_CLANG := true
 LOCAL_MODULE := ion-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
 LOCAL_SHARED_LIBRARIES += libion
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
 LOCAL_SRC_FILES := \
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index e26b302..3c4524e 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <memory>
 #include <sys/mman.h>
 
 #include <gtest/gtest.h>
@@ -90,7 +91,7 @@
 
 TEST_F(Allocate, Zeroed)
 {
-    void *zeroes = calloc(4096, 1);
+    auto zeroes_ptr = std::make_unique<char[]>(4096);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -125,14 +126,11 @@
         ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
-        ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+        ASSERT_EQ(0, memcmp(ptr, zeroes_ptr.get(), 4096));
 
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(zeroes);
-
 }
 
 TEST_F(Allocate, Large)
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
index 6f6e1bd..0be52bf 100644
--- a/libion/tests/device_test.cpp
+++ b/libion/tests/device_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <fcntl.h>
+#include <memory>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -133,8 +134,8 @@
 
 TEST_F(Device, KernelReadCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -161,14 +162,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelWriteCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -195,14 +194,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAReadCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -227,14 +224,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAWriteCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -261,14 +256,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelReadCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -295,14 +288,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelWriteCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -329,14 +320,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAReadCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -363,14 +352,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAWriteCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -399,13 +386,11 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 TEST_F(Device, KernelRead)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -432,14 +417,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelWrite)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -466,14 +449,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMARead)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -498,14 +479,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAWrite)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -532,13 +511,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, IsCached)
 {
-    void *buf = malloc(4096);
+    auto buf_ptr = std::make_unique<char[]>(4096);
+    void *buf = buf_ptr.get();
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 5644aa6..b16c0e6 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -29,9 +29,19 @@
 void PreloadPublicNativeLibraries();
 
 __attribute__((visibility("default")))
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, bool is_shared, jstring library_path,
-                        jstring permitted_path);
+jstring CreateClassLoaderNamespace(JNIEnv* env,
+                                   int32_t target_sdk_version,
+                                   jobject class_loader,
+                                   bool is_shared,
+                                   jstring library_path,
+                                   jstring permitted_path);
+
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(JNIEnv* env,
+                        int32_t target_sdk_version,
+                        const char* path,
+                        jobject class_loader,
+                        jstring library_path);
 
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index f8bb5fd..86d9d77 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -21,6 +21,7 @@
 #ifdef __ANDROID__
 #include <android/dlext.h>
 #include "cutils/properties.h"
+#include "log/log.h"
 #endif
 
 #include <algorithm>
@@ -59,10 +60,11 @@
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
-                                   bool is_shared,
-                                   jstring java_library_path,
-                                   jstring java_permitted_path) {
+  android_namespace_t* Create(JNIEnv* env,
+                              jobject class_loader,
+                              bool is_shared,
+                              jstring java_library_path,
+                              jstring java_permitted_path) {
     ScopedUtfChars library_path(env, java_library_path);
 
     std::string permitted_path;
@@ -75,13 +77,9 @@
       return nullptr;
     }
 
-    std::lock_guard<std::mutex> guard(mutex_);
-
     android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
 
-    if (ns != nullptr) {
-      return ns;
-    }
+    LOG_FATAL_IF(ns != nullptr, "There is already a namespace associated with this classloader");
 
     uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
     if (is_shared) {
@@ -96,7 +94,9 @@
                                       permitted_path.c_str() :
                                       nullptr);
 
-    namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
+    if (ns != nullptr) {
+      namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
+    }
 
     return ns;
   }
@@ -128,36 +128,74 @@
   }
 
   bool initialized_;
-  std::mutex mutex_;
   std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
 
+static std::mutex g_namespaces_mutex;
 static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
+
+static bool namespaces_enabled(uint32_t target_sdk_version) {
+  return target_sdk_version > 0;
+}
 #endif
 
 void PreloadPublicNativeLibraries() {
 #if defined(__ANDROID__)
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
   g_namespaces->PreloadPublicLibraries();
 #endif
 }
 
 
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, bool is_shared, jstring java_library_path,
-                        jstring java_permitted_path) {
+jstring CreateClassLoaderNamespace(JNIEnv* env,
+                                   int32_t target_sdk_version,
+                                   jobject class_loader,
+                                   bool is_shared,
+                                   jstring library_path,
+                                   jstring permitted_path) {
 #if defined(__ANDROID__)
-  if (target_sdk_version == 0 || class_loader == nullptr) {
+  if (!namespaces_enabled(target_sdk_version)) {
+    return nullptr;
+  }
+
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  android_namespace_t* ns = g_namespaces->Create(env,
+                                                 class_loader,
+                                                 is_shared,
+                                                 library_path,
+                                                 permitted_path);
+  if (ns == nullptr) {
+    return env->NewStringUTF(dlerror());
+  }
+#else
+  UNUSED(env, target_sdk_version, class_loader, is_shared,
+         library_path, permitted_path);
+#endif
+  return nullptr;
+}
+
+void* OpenNativeLibrary(JNIEnv* env,
+                        int32_t target_sdk_version,
+                        const char* path,
+                        jobject class_loader,
+                        jstring library_path) {
+#if defined(__ANDROID__)
+  if (!namespaces_enabled(target_sdk_version) || class_loader == nullptr) {
     return dlopen(path, RTLD_NOW);
   }
 
-  android_namespace_t* ns =
-      g_namespaces->GetOrCreate(env, class_loader, is_shared,
-                                java_library_path, java_permitted_path);
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
 
   if (ns == nullptr) {
-    return nullptr;
+    // This is the case where the classloader was not created by ApplicationLoaders
+    // In this case we create an isolated not-shared namespace for it.
+    ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr);
+    if (ns == nullptr) {
+      return nullptr;
+    }
   }
 
   android_dlextinfo extinfo;
@@ -166,14 +204,14 @@
 
   return android_dlopen_ext(path, RTLD_NOW, &extinfo);
 #else
-  UNUSED(env, target_sdk_version, class_loader, is_shared,
-         java_library_path, java_permitted_path);
+  UNUSED(env, target_sdk_version, class_loader, library_path);
   return dlopen(path, RTLD_NOW);
 #endif
 }
 
 #if defined(__ANDROID__)
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
   return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
 }
 #endif
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index ee9ea3c..9a5f7d8 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -92,7 +92,7 @@
 
         for (j = 0; j < 2; j++) {
             unsigned val = i + j * 3 + 1;
-            sprintf(str, "test_fence%d-%d", i, j);
+            snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
             int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
             if (fd < 0) {
                 printf("can't create sync pt %d: %s", val, strerror(errno));
@@ -106,7 +106,7 @@
 
     sync_data[3].thread_no = 3;
     for (j = 0; j < 2; j++) {
-        sprintf(str, "merged_fence%d", j);
+        snprintf(str, sizeof(str), "merged_fence%d", j);
         sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
         if (sync_data[3].fd[j] < 0) {
             printf("can't merge sync pts %d and %d: %s\n",
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 02907ad..ea1e4db 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -190,17 +190,22 @@
         {
             Mutex::Autolock _l(mMutex);
             char buf[128];
-            sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+            snprintf(buf, sizeof(buf),
+                     "Strong references on RefBase %p (weakref_type %p):\n",
+                     mBase, this);
             text.append(buf);
             printRefsLocked(&text, mStrongRefs);
-            sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+            snprintf(buf, sizeof(buf),
+                     "Weak references on RefBase %p (weakref_type %p):\n",
+                     mBase, this);
             text.append(buf);
             printRefsLocked(&text, mWeakRefs);
         }
 
         {
             char name[100];
-            snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+            snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
+                     this);
             int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
             if (rc >= 0) {
                 write(rc, text.string(), text.length());
@@ -293,8 +298,8 @@
         char buf[128];
         while (refs) {
             char inc = refs->ref >= 0 ? '+' : '-';
-            sprintf(buf, "\t%c ID %p (ref %d):\n", 
-                    inc, refs->id, refs->ref);
+            snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
+                     inc, refs->id, refs->ref);
             out->append(buf);
 #if DEBUG_REFS_CALLSTACK_ENABLED
             out->append(refs->stack.toString("\t\t"));
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index a4b96d3..2a04880 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -90,7 +90,7 @@
         while(it != times.end()) {
             if (*it == me) {
                 times.erase(it);
-                me->release_Locked();
+                me->release_nodelete_Locked();
                 break;
             }
             it++;
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index f5969df..b66ff9e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -75,7 +75,13 @@
     void triggerSkip_Locked(log_id_t id, unsigned int skip) { skipAhead[id] = skip; }
     void cleanSkip_Locked(void);
 
-    // Called after LogTimeEntry removed from list, lock implicitly held
+    // These called after LogTimeEntry removed from list, lock implicitly held
+    void release_nodelete_Locked(void) {
+        mRelease = true;
+        pthread_cond_signal(&threadTriggeredCondition);
+        // assumes caller code path will call decRef_Locked()
+    }
+
     void release_Locked(void) {
         mRelease = true;
         pthread_cond_signal(&threadTriggeredCondition);
diff --git a/logd/main.cpp b/logd/main.cpp
index f4d7464..3095f7f 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -165,7 +165,7 @@
         char newkey[PROPERTY_KEY_MAX];
         snprintf(newkey, sizeof(newkey), "ro.%s", key);
         property_get(newkey, property, "");
-        // persist properties set by /data require innoculation with
+        // persist properties set by /data require inoculation with
         // logd-reinit. They may be set in init.rc early and function, but
         // otherwise are defunct unless reset. Do not rely on persist
         // properties for startup-only keys unless you are willing to restart
@@ -265,8 +265,11 @@
     set_sched_policy(0, SP_BACKGROUND);
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
 
-    setgid(AID_SYSTEM);
-    setuid(AID_SYSTEM);
+    // If we are AID_ROOT, we should drop to AID_SYSTEM, if we are anything
+    // else, we have even lesser privileges and accept our fate. Not worth
+    // checking for error returns setting this thread's privileges.
+    (void)setgid(AID_SYSTEM);
+    (void)setuid(AID_SYSTEM);
 
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
 
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 5ab6195..2bd8230 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -43,7 +43,11 @@
 
     uint32_t tags_addr;    /* physical addr for kernel tags */
     uint32_t page_size;    /* flash page size we assume */
-    uint32_t unused[2];    /* future expansion: should be 0 */
+
+    /* operating system version; "1.2.34" -> 010234 */
+    uint32_t os_version;
+    /* operating system patch level; "2016-01-01" -> 20160101 */
+    uint32_t os_patch_level;
 
     uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
 
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index f95d703..be342c9 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -20,6 +20,7 @@
 from struct import pack
 from hashlib import sha1
 import sys
+import re
 
 def filesize(f):
     if f is None:
@@ -47,7 +48,7 @@
 def write_header(args):
     BOOT_MAGIC = 'ANDROID!'.encode()
     args.output.write(pack('8s', BOOT_MAGIC))
-    args.output.write(pack('8I',
+    args.output.write(pack('10I',
         filesize(args.kernel),                          # size in bytes
         args.base + args.kernel_offset,                 # physical load addr
         filesize(args.ramdisk),                         # size in bytes
@@ -55,8 +56,9 @@
         filesize(args.second),                          # size in bytes
         args.base + args.second_offset,                 # physical load addr
         args.base + args.tags_offset,                   # physical addr for kernel tags
-        args.pagesize))                                 # flash page size we assume
-    args.output.write(pack('8x'))                       # future expansion: should be 0
+        args.pagesize,                                  # flash page size we assume
+        args.os_version,                                # operating system version
+        args.os_patch_level))                           # security patch level
     args.output.write(pack('16s', args.board.encode())) # asciiz product name
     args.output.write(pack('512s', args.cmdline[:512].encode()))
 
@@ -97,6 +99,18 @@
 def parse_int(x):
     return int(x, 0)
 
+def match_to_int(x):
+    if (x and x.lastindex == 3):
+        return (parse_int(x.group(3)) +
+                parse_int(x.group(2)) * 100 +
+                parse_int(x.group(1)) * 10000)
+    return 0
+
+def parse_os_version(x):
+    return match_to_int(re.search(r'^(\d+)\.(\d{1,2})\.(\d{1,2})', x))
+
+def parse_os_patch_level(x):
+    return match_to_int(re.search(r'^(\d{4,})-(\d{2})-(\d{2})', x))
 
 def parse_cmdline():
     parser = ArgumentParser()
@@ -111,6 +125,10 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
+    parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
+                        default=0)
+    parser.add_argument('--os_patch_level', help='operating system patch level',
+                        type=parse_os_patch_level, default=0)
     parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
     parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
                         maxlen=16)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d90f988..d53af2f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -70,6 +70,11 @@
     ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
     ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
 endif
+# The A/B updater uses a top-level /postinstall directory to mount the new
+# system before reboot.
+ifeq ($(AB_OTA_UPDATER),true)
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
+endif
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1f63fcf..2b12099 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -23,6 +23,9 @@
     # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
     mkdir /mnt 0775 root system
 
+    # Set the security context of /postinstall if present.
+    restorecon /postinstall
+
     start ueventd
 
 on init
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index fc2898e..b1cdb60 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -38,7 +38,6 @@
     nandread \
     newfs_msdos \
     ps \
-    prlimit \
     sendevent \
     start \
     stop \
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 5b98a01..27ea9e8 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -695,7 +695,7 @@
                                                     (u_int)tm->tm_min));
                 mk4(bsx->volid, x);
                 mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
-                sprintf(buf, "FAT%u", fat);
+                snprintf(buf, sizeof(buf), "FAT%u", fat);
                 setstr(bsx->type, buf, sizeof(bsx->type));
                 if (!opt_B) {
                     x1 += sizeof(struct bsx);
diff --git a/toolbox/prlimit.c b/toolbox/prlimit.c
deleted file mode 100644
index 8cf202a..0000000
--- a/toolbox/prlimit.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-static void
-usage(const char *s)
-{
-    fprintf(stderr, "usage: %s pid resource cur max\n", s);
-    exit(EXIT_FAILURE);
-}
-
-int prlimit_main(int argc, char *argv[])
-{
-    pid_t pid;
-    struct rlimit64 rl;
-    int resource;
-    int rc;
-
-    if (argc != 5)
-        usage(*argv);
-
-    if (sscanf(argv[1], "%d", &pid) != 1)
-        usage(*argv);
-
-    if (sscanf(argv[2], "%d", &resource) != 1)
-        usage(*argv);
-
-    if (sscanf(argv[3], "%llu", &rl.rlim_cur) != 1)
-        usage(*argv);
-
-    if (sscanf(argv[4], "%llu", &rl.rlim_max) != 1)
-        usage(*argv);
-
-    printf("setting resource %d of pid %d to [%llu,%llu]\n", resource, pid,
-            rl.rlim_cur, rl.rlim_max);
-    rc = prlimit64(pid, resource, &rl, NULL);
-    if (rc < 0) {
-        perror("prlimit");
-        exit(EXIT_FAILURE);
-    }
-
-    return 0;
-}
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 7e70c71..d366f3e 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -57,16 +57,16 @@
     int prio, nice, rtprio, sched, psr;
     struct passwd *pw;
 
-    sprintf(statline, "/proc/%d", tid ? tid : pid);
+    snprintf(statline, sizeof(statline), "/proc/%d", tid ? tid : pid);
     stat(statline, &stats);
 
     if(tid) {
-        sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+        snprintf(statline, sizeof(statline), "/proc/%d/task/%d/stat", pid, tid);
         cmdline[0] = 0;
         snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
     } else {
-        sprintf(statline, "/proc/%d/stat", pid);
-        sprintf(cmdline, "/proc/%d/cmdline", pid);
+        snprintf(statline, sizeof(statline), "/proc/%d/stat", pid);
+        snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid);
         snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
         int fd = open(cmdline, O_RDONLY);
         if(fd == 0) {
@@ -149,7 +149,7 @@
 
     pw = getpwuid(stats.st_uid);
     if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
-        sprintf(user,"%d",(int)stats.st_uid);
+        snprintf(user,sizeof(user),"%d",(int)stats.st_uid);
     } else {
         strcpy(user,pw->pw_name);
     }
@@ -208,7 +208,7 @@
     int fd, r;
     char exeline[1024];
 
-    sprintf(exeline, "/proc/%d/exe", pid);
+    snprintf(exeline, sizeof(exeline), "/proc/%d/exe", pid);
     fd = open(exeline, O_RDONLY);
     if(fd == 0) {
         printf("    ");
@@ -243,7 +243,7 @@
     DIR *d;
     struct dirent *de;
 
-    sprintf(tmp,"/proc/%d/task",pid);
+    snprintf(tmp,sizeof(tmp),"/proc/%d/task",pid);
     d = opendir(tmp);
     if(d == 0) return;
 
diff --git a/toolbox/top.c b/toolbox/top.c
index 6fda132..003f4c9 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -258,29 +258,29 @@
 
             proc->pid = proc->tid = pid;
 
-            sprintf(filename, "/proc/%d/stat", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
             read_stat(filename, proc);
 
-            sprintf(filename, "/proc/%d/cmdline", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
             read_cmdline(filename, proc);
 
-            sprintf(filename, "/proc/%d/status", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
             read_status(filename, proc);
 
             read_policy(pid, proc);
 
             proc->num_threads = 0;
         } else {
-            sprintf(filename, "/proc/%d/cmdline", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
             read_cmdline(filename, &cur_proc);
 
-            sprintf(filename, "/proc/%d/status", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
             read_status(filename, &cur_proc);
 
             proc = NULL;
         }
 
-        sprintf(filename, "/proc/%d/task", pid);
+        snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
         task_dir = opendir(filename);
         if (!task_dir) continue;
 
@@ -295,7 +295,7 @@
 
                 proc->pid = pid; proc->tid = tid;
 
-                sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+                snprintf(filename, sizeof(filename), "/proc/%d/task/%d/stat", pid, tid);
                 read_stat(filename, proc);
 
                 read_policy(tid, proc);
@@ -484,7 +484,7 @@
         if (user && user->pw_name) {
             user_str = user->pw_name;
         } else {
-            snprintf(user_buf, 20, "%d", proc->uid);
+            snprintf(user_buf, sizeof(user_buf), "%d", proc->uid);
             user_str = user_buf;
         }
         if (!threads) {