Merge "Clean up the partition name mapping in fastboot."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 39e71e5..0181daa 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,6 +31,8 @@
 #include <time.h>
 
 #include <chrono>
+#include <condition_variable>
+#include <mutex>
 #include <string>
 #include <thread>
 #include <vector>
@@ -48,6 +50,7 @@
 #include "adb_io.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
 
 #if !ADB_HOST
@@ -313,19 +316,15 @@
     if (type == "bootloader") {
         D("setting connection_state to kCsBootloader");
         t->SetConnectionState(kCsBootloader);
-        update_transports();
     } else if (type == "device") {
         D("setting connection_state to kCsDevice");
         t->SetConnectionState(kCsDevice);
-        update_transports();
     } else if (type == "recovery") {
         D("setting connection_state to kCsRecovery");
         t->SetConnectionState(kCsRecovery);
-        update_transports();
     } else if (type == "sideload") {
         D("setting connection_state to kCsSideload");
         t->SetConnectionState(kCsSideload);
-        update_transports();
     } else {
         D("setting connection_state to kCsHost");
         t->SetConnectionState(kCsHost);
@@ -353,6 +352,8 @@
         send_auth_request(t);
     }
 #endif
+
+    update_transports();
 }
 
 void handle_packet(apacket *p, atransport *t)
@@ -1229,4 +1230,50 @@
       return ret - 1;
     return -1;
 }
+
+static auto& init_mutex = *new std::mutex();
+static auto& init_cv = *new std::condition_variable();
+static bool device_scan_complete = false;
+static bool transports_ready = false;
+
+void update_transport_status() {
+    bool result = iterate_transports([](const atransport* t) {
+        if (t->type == kTransportUsb && t->online != 1) {
+            return false;
+        }
+        return true;
+    });
+
+    D("update_transport_status: transports_ready = %s", result ? "true" : "false");
+
+    bool ready;
+
+    {
+        std::lock_guard<std::mutex> lock(init_mutex);
+        transports_ready = result;
+        ready = transports_ready && device_scan_complete;
+    }
+
+    if (ready) {
+        D("update_transport_status: notifying");
+        init_cv.notify_all();
+    }
+}
+
+void adb_notify_device_scan_complete() {
+    D("device scan complete");
+
+    {
+        std::lock_guard<std::mutex> lock(init_mutex);
+        device_scan_complete = true;
+    }
+
+    update_transport_status();
+}
+
+void adb_wait_for_device_initialization() {
+    std::unique_lock<std::mutex> lock(init_mutex);
+    init_cv.wait_for(lock, 3s, []() { return device_scan_complete && transports_ready; });
+}
+
 #endif  // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index e3675d8..d6b2b81 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -228,4 +228,18 @@
 
 void parse_banner(const std::string&, atransport* t);
 
+// On startup, the adb server needs to wait until all of the connected devices are ready.
+// To do this, we need to know when the scan has identified all of the potential new transports, and
+// when each transport becomes ready.
+// TODO: Do this for mDNS as well, instead of just USB?
+
+// We've found all of the transports we potentially care about.
+void adb_notify_device_scan_complete();
+
+// One or more transports have changed status, check to see if we're ready.
+void update_transport_status();
+
+// Wait until device scan has completed and every transport is ready, or a timeout elapses.
+void adb_wait_for_device_initialization();
+
 #endif
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index b656887..4f3ff25 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -28,12 +28,15 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <condition_variable>
+#include <mutex>
 #include <string>
 #include <thread>
 #include <vector>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/sockets.h>
 
 #include "adb_io.h"
@@ -177,9 +180,8 @@
         } else {
             fprintf(stderr, "* daemon started successfully\n");
         }
-        // Give the server some time to start properly and detect devices.
-        std::this_thread::sleep_for(3s);
-        // fall through to _adb_connect
+        // The server will wait until it detects all of its connected devices before acking.
+        // Fall through to _adb_connect.
     } else {
         // If a server is already running, check its version matches.
         int version = ADB_SERVER_VERSION - 1;
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 606203c..fe5099c 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -156,33 +156,38 @@
         }
 #endif
 
-        // Inform our parent that we are up and running.
+        // Wait for the USB scan to complete before notifying the parent that we're up.
+        // We need to perform this in a thread, because we would otherwise block the event loop.
+        std::thread notify_thread([ack_reply_fd]() {
+            adb_wait_for_device_initialization();
 
-        // Any error output written to stderr now goes to adb.log. We could
-        // keep around a copy of the stderr fd and use that to write any errors
-        // encountered by the following code, but that is probably overkill.
+            // Any error output written to stderr now goes to adb.log. We could
+            // keep around a copy of the stderr fd and use that to write any errors
+            // encountered by the following code, but that is probably overkill.
 #if defined(_WIN32)
-        const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
-        const CHAR ack[] = "OK\n";
-        const DWORD bytes_to_write = arraysize(ack) - 1;
-        DWORD written = 0;
-        if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
-            fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
-                  android::base::SystemErrorCodeToString(GetLastError()).c_str());
-        }
-        if (written != bytes_to_write) {
-            fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
-                  bytes_to_write, written);
-        }
-        CloseHandle(ack_reply_handle);
+            const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+            const CHAR ack[] = "OK\n";
+            const DWORD bytes_to_write = arraysize(ack) - 1;
+            DWORD written = 0;
+            if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+                fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+                      android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            }
+            if (written != bytes_to_write) {
+                fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
+                      written);
+            }
+            CloseHandle(ack_reply_handle);
 #else
-        // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
-        // "OKAY".
-        if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
-            fatal_errno("error writing ACK to fd %d", ack_reply_fd);
-        }
-        unix_close(ack_reply_fd);
+            // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+            // "OKAY".
+            if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+                fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+            }
+            unix_close(ack_reply_fd);
 #endif
+        });
+        notify_thread.detach();
     }
 
     D("Event loop starting");
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index d39884a..20610ee 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -352,6 +352,8 @@
         }
         libusb_free_device_list(list, 1);
 
+        adb_notify_device_scan_complete();
+
         std::this_thread::sleep_for(500ms);
     }
 }
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index e4a543b..e366754 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -38,6 +38,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb.h"
 #include "transport.h"
@@ -429,7 +430,7 @@
     VLOG(USB) << "RunLoopThread done";
 }
 
-static void usb_cleanup() {
+static void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
     VLOG(USB) << "usb_cleanup";
     // Wait until usb operations in RunLoopThread finish, and prevent further operations.
     operate_device_lock.lock();
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 72c9eef..b28de4b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,15 +26,19 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <functional>
 #include <list>
+#include <mutex>
 #include <unordered_map>
 #include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb_io.h"
 #include "adb_trace.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 
 #if !ADB_HOST
@@ -75,6 +79,10 @@
 static bool main_thread_valid;
 static unsigned long main_thread_id;
 
+static auto& run_queue_notify_fd = *new unique_fd();
+static auto& run_queue_mutex = *new std::mutex();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+
 void check_main_thread() {
     if (main_thread_valid) {
         CHECK_EQ(main_thread_id, adb_thread_id());
@@ -112,8 +120,7 @@
     return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
 }
 
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
     check_main_thread();
     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
     if(fde == 0) return 0;
@@ -122,8 +129,7 @@
     return fde;
 }
 
-void fdevent_destroy(fdevent *fde)
-{
+void fdevent_destroy(fdevent* fde) {
     check_main_thread();
     if(fde == 0) return;
     if(!(fde->state & FDE_CREATED)) {
@@ -278,8 +284,7 @@
     }
 }
 
-static void fdevent_call_fdfunc(fdevent* fde)
-{
+static void fdevent_call_fdfunc(fdevent* fde) {
     unsigned events = fde->events;
     fde->events = 0;
     CHECK(fde->state & FDE_PENDING);
@@ -292,10 +297,7 @@
 
 #include <sys/ioctl.h>
 
-static void fdevent_subproc_event_func(int fd, unsigned ev,
-                                       void* /* userdata */)
-{
-
+static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
     D("subproc handling on fd = %d, ev = %x", fd, ev);
 
     CHECK_GE(fd, 0);
@@ -342,8 +344,7 @@
     }
 }
 
-void fdevent_subproc_setup()
-{
+static void fdevent_subproc_setup() {
     int s[2];
 
     if(adb_socketpair(s)) {
@@ -358,12 +359,63 @@
 }
 #endif // !ADB_HOST
 
-void fdevent_loop()
-{
+static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
+    for (auto& f : run_queue) {
+        f();
+    }
+    run_queue.clear();
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+    CHECK_GE(fd, 0);
+    CHECK(ev & FDE_READ);
+
+    char buf[1024];
+
+    // Empty the fd.
+    if (adb_read(fd, buf, sizeof(buf)) == -1) {
+        PLOG(FATAL) << "failed to empty run queue notify fd";
+    }
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    fdevent_run_flush();
+}
+
+static void fdevent_run_setup() {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    CHECK(run_queue_notify_fd.get() == -1);
+    int s[2];
+    if (adb_socketpair(s) != 0) {
+        PLOG(FATAL) << "failed to create run queue notify socketpair";
+    }
+
+    run_queue_notify_fd.reset(s[0]);
+    fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+    CHECK(fde != nullptr);
+    fdevent_add(fde, FDE_READ);
+
+    fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue.push_back(std::move(fn));
+
+    // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+    // In that case, rely on the setup code to flush the queue without a notification being needed.
+    if (run_queue_notify_fd != -1) {
+        if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+            PLOG(FATAL) << "failed to write to run queue notify fd";
+        }
+    }
+}
+
+void fdevent_loop() {
     set_main_thread();
 #if !ADB_HOST
     fdevent_subproc_setup();
 #endif // !ADB_HOST
+    fdevent_run_setup();
 
     while (true) {
         if (terminate_loop) {
@@ -393,6 +445,11 @@
 void fdevent_reset() {
     g_poll_node_map.clear();
     g_pending_list.clear();
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue_notify_fd.reset();
+    run_queue.clear();
+
     main_thread_valid = false;
     terminate_loop = false;
 }
diff --git a/adb/fdevent.h b/adb/fdevent.h
index e32845a..896400a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -20,6 +20,8 @@
 #include <stddef.h>
 #include <stdint.h>  /* for int64_t */
 
+#include <functional>
+
 /* events that may be observed */
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
@@ -78,6 +80,9 @@
 
 void check_main_thread();
 
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
 // The following functions are used only for tests.
 void fdevent_terminate_loop();
 size_t fdevent_installed_count();
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index bdb973a..86e0209 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -173,3 +173,24 @@
     std::thread thread(InvalidFdThreadFunc);
     thread.join();
 }
+
+TEST_F(FdeventTest, run_on_main_thread) {
+    std::vector<int> vec;
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    for (int i = 0; i < 100; ++i) {
+        fdevent_run_on_main_thread([i, &vec]() {
+            check_main_thread();
+            vec.push_back(i);
+        });
+    }
+
+    TerminateThread(thread);
+
+    ASSERT_EQ(100u, vec.size());
+    for (int i = 0; i < 100; ++i) {
+        ASSERT_EQ(i, vec[i]);
+    }
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index f4215ae..5ca49ac 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -53,11 +53,11 @@
 
     size_t GetAdditionalLocalSocketCount() {
 #if ADB_HOST
-        // dummy socket installed in PrepareThread()
-        return 1;
-#else
-        // dummy socket and one more socket installed in fdevent_subproc_setup()
+        // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
         return 2;
+#else
+        // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
+        return 3;
 #endif
     }
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index cc8c162..20de642 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -400,8 +400,27 @@
     return &tracker->socket;
 }
 
+// Check if all of the USB transports are connected.
+bool iterate_transports(std::function<bool(const atransport*)> fn) {
+    std::lock_guard<std::mutex> lock(transport_lock);
+    for (const auto& t : transport_list) {
+        if (!fn(t)) {
+            return false;
+        }
+    }
+    for (const auto& t : pending_list) {
+        if (!fn(t)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 // Call this function each time the transport list has changed.
 void update_transports() {
+    update_transport_status();
+
+    // Notify `adb track-devices` clients.
     std::string transports = list_transports(false);
 
     device_tracker* tracker = device_tracker_list;
diff --git a/adb/transport.h b/adb/transport.h
index 8c15d66..e129355 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,6 +198,10 @@
 void kick_transport(atransport* t);
 void update_transports(void);
 
+// Iterates across all of the current and pending transports.
+// Stops iteration and returns false if fn returns false, otherwise returns true.
+bool iterate_transports(std::function<bool(const atransport*)> fn);
+
 void init_transport_registration(void);
 void init_mdns_transport_discovery(void);
 std::string list_transports(bool long_listing);
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
index e49b1c6..3603f09 100644
--- a/adb/transport_mdns.cpp
+++ b/adb/transport_mdns.cpp
@@ -24,6 +24,8 @@
 #include <arpa/inet.h>
 #endif
 
+#include <thread>
+
 #include <android-base/stringprintf.h>
 #include <dns_sd.h>
 
@@ -262,19 +264,22 @@
     }
 }
 
-void init_mdns_transport_discovery(void) {
-    DNSServiceErrorType errorCode =
-        DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
-                         register_mdns_transport, nullptr);
+void init_mdns_transport_discovery_thread(void) {
+    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+                                                     register_mdns_transport, nullptr);
 
     if (errorCode != kDNSServiceErr_NoError) {
         D("Got %d initiating mDNS browse.", errorCode);
         return;
     }
 
-    fdevent_install(&service_ref_fde,
-                    adb_DNSServiceRefSockFD(service_ref),
-                    pump_service_ref,
-                    &service_ref);
-    fdevent_set(&service_ref_fde, FDE_READ);
+    fdevent_run_on_main_thread([]() {
+        fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
+                        &service_ref);
+        fdevent_set(&service_ref_fde, FDE_READ);
+    });
+}
+
+void init_mdns_transport_discovery(void) {
+    std::thread(init_mdns_transport_discovery_thread).detach();
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 9efe985..5d5ac9b 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#define _LARGEFILE64_SOURCE
-
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
diff --git a/init/README.md b/init/README.md
index 9fc8d47..72b6c6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -356,9 +356,9 @@
   Init halts executing commands until the forked process exits.
 
 `exec_start <service>`
-> Start service a given service and halt processing of additional init commands
-  until it returns.  It functions similarly to the `exec` command, but uses an
-  existing service definition instead of providing them as arguments.
+> Start a given service and halt the processing of additional init commands
+  until it returns.  The command functions similarly to the `exec` command,
+  but uses an existing service definition in place of the exec argument vector.
 
 `export <name> <value>`
 > Set the environment variable _name_ equal to _value_ in the
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a2dd677..2b3443f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -92,6 +92,9 @@
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
     // clang-format on
 };
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_dirs = android_dirs;
+#endif
 
 // Rules for files.
 // These rules are applied based on "first match", so they
@@ -238,6 +241,9 @@
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
     // clang-format on
 };
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_files = android_files;
+#endif
 
 static size_t strip(const char* path, size_t len, const char suffix[]) {
     if (len < strlen(suffix)) return len;
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index a0b1d7b..7884190 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -27,7 +27,8 @@
                 "test_str_parms.cpp",
                 "android_get_control_socket_test.cpp",
                 "android_get_control_file_test.cpp",
-                "multiuser_test.cpp"
+                "multiuser_test.cpp",
+                "fs_config.cpp",
             ],
         },
 
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
new file mode 100644
index 0000000..3917a0b
--- /dev/null
+++ b/libcutils/tests/fs_config.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+
+extern const struct fs_path_config* __for_testing_only__android_dirs;
+extern const struct fs_path_config* __for_testing_only__android_files;
+
+static void check_one(const struct fs_path_config* paths, const std::string& prefix,
+                      const std::string& alternate) {
+    for (size_t idx = 0; paths[idx].prefix; ++idx) {
+        std::string path(paths[idx].prefix);
+        if (android::base::StartsWith(path, prefix.c_str())) {
+            path = alternate + path.substr(prefix.length());
+            size_t second;
+            for (second = 0; paths[second].prefix; ++second) {
+                if (path == paths[second].prefix) break;
+            }
+            if (!paths[second].prefix) {
+                // guaranteed to fail expectations, trigger test failure with
+                // a message that reports the violation as an inequality.
+                EXPECT_STREQ((prefix + path.substr(alternate.length())).c_str(), path.c_str());
+            }
+        }
+    }
+}
+
+static void check_two(const struct fs_path_config* paths, const std::string& prefix) {
+    ASSERT_FALSE(paths == nullptr);
+    std::string alternate = "system/" + prefix;
+    check_one(paths, prefix, alternate);
+    check_one(paths, alternate, prefix);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+    check_two(__for_testing_only__android_files, "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+    check_two(__for_testing_only__android_files, "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+    check_two(__for_testing_only__android_files, "odm/");
+}
diff --git a/libmemunreachable/.clang-format b/libmemunreachable/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libmemunreachable/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 2867f64..cdac76b 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -9,8 +9,16 @@
     clang: true,
     shared_libs: [
         "libbase",
-        "liblog",
     ],
+
+    target: {
+        android: {
+            static_libs: ["libasync_safe"],
+        },
+        host: {
+            shared_libs: ["liblog"],
+        }
+    },
 }
 
 cc_library_shared {
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 62366f2..c365ae5 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -42,11 +42,9 @@
   } else {
     Range overlap = inserted.first->first;
     if (overlap != range) {
-      ALOGE("range %p-%p overlaps with existing range %p-%p",
-          reinterpret_cast<void*>(begin),
-          reinterpret_cast<void*>(end),
-          reinterpret_cast<void*>(overlap.begin),
-          reinterpret_cast<void*>(overlap.end));
+      MEM_ALOGE("range %p-%p overlaps with existing range %p-%p", reinterpret_cast<void*>(begin),
+                reinterpret_cast<void*>(end), reinterpret_cast<void*>(overlap.begin),
+                reinterpret_cast<void*>(overlap.end));
     }
     return false;
   }
@@ -154,7 +152,7 @@
 
   void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
   if (ret == MAP_FAILED) {
-    ALOGE("failed to map page at %p: %s", page, strerror(errno));
+    MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
     return false;
   }
 
@@ -167,7 +165,7 @@
     handler.reset();
     return;
   }
-  ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+  MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
   if (!MapOverPage(si->si_addr)) {
     handler.reset();
   }
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 080f8a7..78117e2 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -44,11 +44,11 @@
 
   int ret = sendmsg(sock, &hdr, 0);
   if (ret < 0) {
-    ALOGE("failed to send fd: %s", strerror(errno));
+    MEM_ALOGE("failed to send fd: %s", strerror(errno));
     return false;
   }
   if (ret == 0) {
-    ALOGE("eof when sending fd");
+    MEM_ALOGE("eof when sending fd");
     return false;
   }
 
@@ -71,17 +71,17 @@
 
   int ret = recvmsg(sock, &hdr, 0);
   if (ret < 0) {
-    ALOGE("failed to receive fd: %s", strerror(errno));
+    MEM_ALOGE("failed to receive fd: %s", strerror(errno));
     return -1;
   }
   if (ret == 0) {
-    ALOGE("eof when receiving fd");
+    MEM_ALOGE("eof when receiving fd");
     return -1;
   }
 
   struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
   if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
-    ALOGE("missing fd while receiving fd");
+    MEM_ALOGE("missing fd while receiving fd");
     return -1;
   }
 
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3f4e0b7..3ea2d8f 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -36,7 +36,7 @@
   LeakPipe() {
     int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+      MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
     }
   }
 
@@ -105,10 +105,10 @@
     bool Send(const T& value) {
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
       if (ret < 0) {
-        ALOGE("failed to send value: %s", strerror(errno));
+        MEM_ALOGE("failed to send value: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != sizeof(T)) {
-        ALOGE("eof while writing value");
+        MEM_ALOGE("eof while writing value");
         return false;
       }
 
@@ -124,10 +124,10 @@
 
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
       if (ret < 0) {
-        ALOGE("failed to send vector: %s", strerror(errno));
+        MEM_ALOGE("failed to send vector: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != size) {
-        ALOGE("eof while writing vector");
+        MEM_ALOGE("eof while writing vector");
         return false;
       }
 
@@ -143,10 +143,10 @@
     bool Receive(T* value) {
       ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
       if (ret < 0) {
-        ALOGE("failed to receive value: %s", strerror(errno));
+        MEM_ALOGE("failed to receive value: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != sizeof(T)) {
-        ALOGE("eof while receiving value");
+        MEM_ALOGE("eof while receiving value");
         return false;
       }
 
@@ -166,10 +166,10 @@
       while (size > 0) {
         ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
         if (ret < 0) {
-          ALOGE("failed to send vector: %s", strerror(errno));
+          MEM_ALOGE("failed to send vector: %s", strerror(errno));
           return false;
         } else if (ret == 0) {
-          ALOGE("eof while reading vector");
+          MEM_ALOGE("eof while reading vector");
           return false;
         }
         size -= ret;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ac19a66..e7c0beb 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -78,51 +78,49 @@
 
 bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
     const allocator::vector<Mapping>& mappings) {
-  ALOGI("searching process %d for allocations", pid_);
+  MEM_ALOGI("searching process %d for allocations", pid_);
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
   allocator::vector<Mapping> globals_mappings{mappings};
   allocator::vector<Mapping> stack_mappings{mappings};
-  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
-      globals_mappings, stack_mappings)) {
+  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) {
     return false;
   }
 
   for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
-    ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
-    HeapIterate(*it, [&](uintptr_t base, size_t size) {
-      heap_walker_.Allocation(base, base + size);
-    });
+    MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    HeapIterate(*it,
+                [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); });
   }
 
   for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
-    ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
     heap_walker_.Allocation(it->begin, it->end);
   }
 
   for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
-    ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
     heap_walker_.Root(it->begin, it->end);
   }
 
   for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
     for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
       if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
-        ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+        MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
         heap_walker_.Root(thread_it->stack.first, it->end);
       }
     }
     heap_walker_.Root(thread_it->regs);
   }
 
-  ALOGI("searching done");
+  MEM_ALOGI("searching done");
 
   return true;
 }
 
 bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
     size_t limit, size_t* num_leaks, size_t* leak_bytes) {
-  ALOGI("sweeping process %d for unreachable memory", pid_);
+  MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
   leaks.clear();
 
   if (!heap_walker_.DetectLeaks()) {
@@ -133,9 +131,9 @@
   allocator::vector<Range> leaked1{allocator_};
   heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
 
-  ALOGI("sweeping done");
+  MEM_ALOGI("sweeping done");
 
-  ALOGI("folding related leaks");
+  MEM_ALOGI("folding related leaks");
 
   LeakFolding folding(allocator_, heap_walker_);
   if (!folding.FoldLeaks()) {
@@ -188,7 +186,7 @@
         std::min(leak->size, Leak::contents_length));
   }
 
-  ALOGI("folding done");
+  MEM_ALOGI("folding done");
 
   std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
     return a.total_size > b.total_size;
@@ -276,7 +274,7 @@
     /////////////////////////////////////////////
     // Collection thread
     /////////////////////////////////////////////
-    ALOGI("collecting thread info for process %d...", parent_pid);
+    MEM_ALOGI("collecting thread info for process %d...", parent_pid);
 
     ThreadCapture thread_capture(parent_pid, heap);
     allocator::vector<ThreadInfo> thread_info(heap);
@@ -351,7 +349,7 @@
     } else {
       // Nothing left to do in the collection thread, return immediately,
       // releasing all the captured threads.
-      ALOGI("collection thread done");
+      MEM_ALOGI("collection thread done");
       return 0;
     }
   }};
@@ -397,10 +395,10 @@
     return false;
   }
 
-  ALOGI("unreachable memory detection done");
-  ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
-      info.leak_bytes, info.num_leaks, plural(info.num_leaks),
-      info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+  MEM_ALOGI("unreachable memory detection done");
+  MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+            info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes,
+            info.num_allocations, plural(info.num_allocations));
   return true;
 }
 
@@ -517,7 +515,7 @@
   }
 
   for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
-    ALOGE("%s", it->ToString(log_contents).c_str());
+    MEM_ALOGE("%s", it->ToString(log_contents).c_str());
   }
   return true;
 }
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 4e3c41e..73b0493 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -70,7 +70,7 @@
     child_pid_(0) {
   stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
   if (stack_->top() == nullptr) {
-    LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+    MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
   }
 
   func_ = std::function<int()>{[&, func]() -> int {
@@ -102,7 +102,7 @@
        CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
        reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
-    ALOGE("failed to clone child: %s", strerror(errno));
+    MEM_ALOGE("failed to clone child: %s", strerror(errno));
     return false;
   }
 
@@ -120,7 +120,7 @@
   int status;
   int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
   if (ret < 0) {
-    ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+    MEM_ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
     return -1;
   }
 
@@ -131,7 +131,7 @@
   } else if (WIFSIGNALED(status)) {
     return -WTERMSIG(status);
   } else {
-    ALOGE("unexpected status %x", status);
+    MEM_ALOGE("unexpected status %x", status);
     return -1;
   }
 }
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 9beef9a..7f44953 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -26,7 +26,7 @@
   ScopedPipe() : pipefd_{-1, -1} {
     int ret = pipe2(pipefd_, O_CLOEXEC);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to open pipe");
+      MEM_LOG_ALWAYS_FATAL("failed to open pipe");
     }
   }
   ~ScopedPipe() {
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 1fd9d4d..ada2ae4 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -37,22 +37,18 @@
 
   template <class F>
   void install(int signal, F&& f) {
-    LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+    MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
 
     handler_ = SignalFn(std::allocator_arg, allocator_,
-        [=](int signal, siginfo_t* si, void* uctx) {
-          f(*this, signal, si, uctx);
-        });
+                        [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
 
-    struct sigaction act{};
-    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
-      handler_(signal, si, uctx);
-    };
+    struct sigaction act {};
+    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
     act.sa_flags = SA_SIGINFO;
 
     int ret = sigaction(signal, &act, &old_act_);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+      MEM_LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
     }
 
     signal_ = signal;
@@ -62,7 +58,7 @@
     if (signal_ != -1) {
       int ret = sigaction(signal_, &old_act_, NULL);
       if (ret < 0) {
-        ALOGE("failed to uninstall segfault handler");
+        MEM_ALOGE("failed to uninstall segfault handler");
       }
       handler_ = SignalFn{};
       signal_ = -1;
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 9155c29..3891f2d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -110,7 +110,7 @@
 
   android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
   if (fd == -1) {
-    ALOGE("failed to open %s: %s", path, strerror(errno));
+    MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
     return false;
   }
 
@@ -126,7 +126,7 @@
   do {
     nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
     if (nread < 0) {
-      ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+      MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
       return false;
     } else if (nread > 0) {
       ssize_t off = 0;
@@ -177,8 +177,7 @@
 void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
   void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
   if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
-    ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
   }
 }
 
@@ -187,8 +186,7 @@
 int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
   int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
   if (ret < 0) {
-    ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
     return -1;
   }
 
@@ -200,8 +198,7 @@
     if (errno == ESRCH) {
       return 0;
     } else {
-      ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
-          strerror(errno));
+      MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
       PtraceDetach(tid, 0);
       return -1;
     }
@@ -219,8 +216,7 @@
   iovec.iov_len = sizeof(regs);
 
   if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
-    ALOGE("ptrace getregset for thread %d of process %d failed: %s",
-        tid, pid_, strerror(errno));
+    MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
     return false;
   }
 
@@ -258,15 +254,13 @@
 
   int status = 0;
   if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
-    ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
     PtraceDetach(tid, 0);
     return -1;
   }
 
   if (!WIFSTOPPED(status)) {
-    ALOGE("thread %d of process %d was not paused after waitpid, killed?",
-        tid, pid_);
+    MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
     return 0;
   }
 
@@ -285,8 +279,8 @@
         // normal ptrace interrupt stop
         break;
       default:
-        ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
-            signal, tid, pid_);
+        MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
+                  tid, pid_);
         return -1;
     }
   } else {
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index cdfbfd9..1725c53 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,32 @@
 
 #define LOG_TAG "libmemunreachable"
 
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
+  ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
+
+#else
+
 #include <log/log.h>
 
+#define MEM_ALOGW ALOGW
+#define MEM_ALOGE ALOGE
+#define MEM_ALOGV ALOGV
+#define MEM_ALOGI ALOGI
+
+#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
+#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
+
+#endif
+
 #endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index aa755ed..d6ead1a 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -95,13 +95,3 @@
 LOCAL_MODULE := grep
 LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
 include $(BUILD_EXECUTABLE)
-
-
-# We build gzip separately, so it can provide gunzip and zcat too.
-include $(CLEAR_VARS)
-LOCAL_MODULE := gzip
-LOCAL_SRC_FILES := gzip.c
-LOCAL_CFLAGS += -Wall -Werror
-LOCAL_SHARED_LIBRARIES += libz
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,gunzip zcat,ln -sf gzip $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)
diff --git a/toolbox/gzip.c b/toolbox/gzip.c
deleted file mode 100644
index 62c4518..0000000
--- a/toolbox/gzip.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/* gzip.c - gzip/gunzip/zcat tools for gzip data
- *
- * Copyright 2017 The Android Open Source Project
- *
- * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
-
-TODO: port to toybox.
-
-*/
-
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include <zlib.h>
-
-// toybox-style flags/globals.
-#define FLAG_c 1
-#define FLAG_d 2
-#define FLAG_f 4
-#define FLAG_k 8
-static struct {
-  int optflags;
-} toys;
-static struct {
-  int level;
-} TT;
-
-static void xstat(const char *path, struct stat *sb)
-{
-  if (stat(path, sb)) error(1, errno, "stat %s", path);
-}
-
-static void fix_time(const char *path, struct stat *sb)
-{
-  struct timespec times[] = { sb->st_atim, sb->st_mtim };
-
-  if (utimensat(AT_FDCWD, path, times, 0)) error(1, errno, "utimes");
-}
-
-static FILE *xfdopen(const char *name, int flags, mode_t open_mode,
-    const char *mode)
-{
-  FILE *fp;
-  int fd;
-
-  if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
-  else fd = open(name, flags, open_mode);
-
-  if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
-  fp = fdopen(fd, mode);
-  if (fp == NULL) error(1, errno, "fopen %s (%s)", name, mode);
-  return fp;
-}
-
-static gzFile xgzopen(const char *name, int flags, mode_t open_mode,
-    const char *mode)
-{
-  gzFile f;
-  int fd;
-
-  if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
-  else fd = open(name, flags, open_mode);
-
-  if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
-  f = gzdopen(fd, mode);
-  if (f == NULL) error(1, errno, "gzdopen %s (%s)", name, mode);
-  return f;
-}
-
-static void gzfatal(gzFile f, char *what)
-{
-  int err;
-  const char *msg = gzerror(f, &err);
-
-  error(1, (err == Z_ERRNO) ? errno : 0, "%s: %s", what, msg);
-}
-
-static void gunzip(char *arg)
-{
-  struct stat sb;
-  char buf[BUFSIZ];
-  int len, both_files;
-  char *in_name, *out_name;
-  gzFile in;
-  FILE *out;
-
-  // "gunzip x.gz" will decompress "x.gz" to "x".
-  len = strlen(arg);
-  if (len > 3 && !strcmp(arg+len-3, ".gz")) {
-    in_name = strdup(arg);
-    out_name = strdup(arg);
-    out_name[len-3] = '\0';
-  } else if (!strcmp(arg, "-")) {
-    // "-" means stdin; assume output to stdout.
-    // TODO: require -f to read compressed data from tty?
-    in_name = strdup("-");
-    out_name = strdup("-");
-  } else error(1, 0, "unknown suffix");
-
-  if (toys.optflags&FLAG_c) {
-    free(out_name);
-    out_name = strdup("-");
-  }
-
-  both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
-  if (both_files) xstat(in_name, &sb);
-
-  in = xgzopen(in_name, O_RDONLY, 0, "r");
-  out = xfdopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
-      both_files?sb.st_mode:0666, "w");
-
-  while ((len = gzread(in, buf, sizeof(buf))) > 0) {
-    if (fwrite(buf, 1, len, out) != (size_t) len) error(1, errno, "fwrite");
-  }
-  if (len < 0) gzfatal(in, "gzread");
-  if (fclose(out)) error(1, errno, "fclose");
-  if (gzclose(in) != Z_OK) error(1, 0, "gzclose");
-
-  if (both_files) fix_time(out_name, &sb);
-  if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
-  free(in_name);
-  free(out_name);
-}
-
-static void gzip(char *in_name)
-{
-  char buf[BUFSIZ];
-  size_t len;
-  char *out_name;
-  FILE *in;
-  gzFile out;
-  struct stat sb;
-  int both_files;
-
-  if (toys.optflags&FLAG_c) {
-    out_name = strdup("-");
-  } else {
-    if (asprintf(&out_name, "%s.gz", in_name) == -1) {
-      error(1, errno, "asprintf");
-    }
-  }
-
-  both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
-  if (both_files) xstat(in_name, &sb);
-
-  snprintf(buf, sizeof(buf), "w%d", TT.level);
-  in = xfdopen(in_name, O_RDONLY, 0, "r");
-  out = xgzopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
-      both_files?sb.st_mode:0, buf);
-
-  while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
-    if (gzwrite(out, buf, len) != (int) len) gzfatal(out, "gzwrite");
-  }
-  if (ferror(in)) error(1, errno, "fread");
-  if (fclose(in)) error(1, errno, "fclose");
-  if (gzclose(out) != Z_OK) error(1, 0, "gzclose");
-
-  if (both_files) fix_time(out_name, &sb);
-  if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
-  free(out_name);
-}
-
-static void do_file(char *arg)
-{
-  if (toys.optflags&FLAG_d) gunzip(arg);
-  else gzip(arg);
-}
-
-static void usage()
-{
-  char *cmd = basename(getprogname());
-
-  printf("usage: %s [-c] [-d] [-f] [-#] [FILE...]\n", cmd);
-  printf("\n");
-  if (!strcmp(cmd, "zcat")) {
-    printf("Decompress files to stdout. Like `gzip -dc`.\n");
-    printf("\n");
-    printf("-c\tOutput to stdout\n");
-    printf("-f\tForce: allow read from tty\n");
-  } else if (!strcmp(cmd, "gunzip")) {
-    printf("Decompress files. With no files, decompresses stdin to stdout.\n");
-    printf("On success, the input files are removed and replaced by new\n");
-    printf("files without the .gz suffix.\n");
-    printf("\n");
-    printf("-c\tOutput to stdout\n");
-    printf("-f\tForce: allow read from tty\n");
-    printf("-k\tKeep input files (don't remove)\n");
-  } else { // gzip
-    printf("Compress files. With no files, compresses stdin to stdout.\n");
-    printf("On success, the input files are removed and replaced by new\n");
-    printf("files with the .gz suffix.\n");
-    printf("\n");
-    printf("-c\tOutput to stdout\n");
-    printf("-d\tDecompress (act as gunzip)\n");
-    printf("-f\tForce: allow overwrite of output file\n");
-    printf("-k\tKeep input files (don't remove)\n");
-    printf("-#\tCompression level 1-9 (1:fastest, 6:default, 9:best)\n");
-  }
-  printf("\n");
-}
-
-int main(int argc, char *argv[])
-{
-  char *cmd = basename(argv[0]);
-  int opt_ch;
-
-  toys.optflags = 0;
-  TT.level = 6;
-
-  if (!strcmp(cmd, "gunzip")) {
-    // gunzip == gzip -d
-    toys.optflags = FLAG_d;
-  } else if (!strcmp(cmd, "zcat")) {
-    // zcat == gzip -dc
-    toys.optflags = (FLAG_c|FLAG_d);
-  }
-
-  while ((opt_ch = getopt(argc, argv, "cdfhk123456789")) != -1) {
-    switch (opt_ch) {
-    case 'c': toys.optflags |= FLAG_c; break;
-    case 'd': toys.optflags |= FLAG_d; break;
-    case 'f': toys.optflags |= FLAG_f; break;
-    case 'k': toys.optflags |= FLAG_k; break;
-
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
-    case '8':
-    case '9':
-      TT.level = opt_ch - '0';
-      break;
-
-    default:
-      usage();
-      return 1;
-    }
-  }
-
-  if (optind == argc) {
-    // With no arguments, we go from stdin to stdout.
-    toys.optflags |= FLAG_c;
-    do_file("-");
-    return 0;
-  }
-
-  // Otherwise process each file in turn.
-  while (optind < argc) do_file(argv[optind++]);
-  return 0;
-}