Merge "newfs_msdos: switch to external/newfs_msdos."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e6f8716..0e43dae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -77,3 +77,4 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
diff --git a/adb/Android.bp b/adb/Android.bp
index 553473f..97c9762 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -104,6 +104,7 @@
     "socket_spec.cpp",
     "sysdeps/errno.cpp",
     "transport.cpp",
+    "transport_fd.cpp",
     "transport_local.cpp",
     "transport_usb.cpp",
 ]
@@ -302,8 +303,6 @@
     name: "adbd",
     defaults: ["adb_defaults"],
 
-    // adbd must be static, as it is copied into the recovery image.
-    static_executable: true,
     recovery_available: true,
 
     srcs: [
@@ -344,7 +343,6 @@
         "libselinux",
         "libsquashfs_utils",
         "libqemu_pipe",
-        "libdebuggerd_handler",
 
         "libbase",
         "libcutils",
@@ -371,6 +369,7 @@
         "liblog",
         "libusb",
         "libmdnssd",
+        "libselinux",
     ],
     test_suites: ["device-tests"],
 }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 39983ab..e07dba7 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1136,8 +1136,7 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    // No need for shell protocol with logcat, always disable for simplicity.
-    return send_shell_command(cmd, true);
+    return send_shell_command(cmd);
 }
 
 static void write_zeros(int bytes, int fd) {
@@ -1796,6 +1795,11 @@
     }
     else if (!strcmp(argv[0], "track-devices")) {
         return adb_connect_command("host:track-devices");
+    } else if (!strcmp(argv[0], "raw")) {
+        if (argc != 2) {
+            return syntax_error("adb raw SERVICE");
+        }
+        return adb_connect_command(argv[1]);
     }
 
 
@@ -1861,7 +1865,7 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(cmd, false);
+    return send_shell_command(cmd);
 }
 
 static int install_app(int argc, const char** argv) {
@@ -2049,7 +2053,7 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(cmd, false);
+    return send_shell_command(cmd);
 }
 
 static int uninstall_app_legacy(int argc, const char** argv) {
@@ -2073,7 +2077,7 @@
 
 static int delete_file(const std::string& filename) {
     std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(cmd, false);
+    return send_shell_command(cmd);
 }
 
 static int install_app_legacy(int argc, const char** argv) {
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 36cd798..3d10030 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -92,7 +92,7 @@
 // resulting output.
 // if |callback| is non-null, stdout/stderr output will be handled by it.
 int send_shell_command(
-    const std::string& command, bool disable_shell_protocol,
-    StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+        const std::string& command, bool disable_shell_protocol = false,
+        StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
 
 #endif  // COMMANDLINE_H
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 232d9c5..c02cafa 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -38,7 +38,6 @@
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
 #include "selinux/android.h"
 
 #include "adb.h"
@@ -274,7 +273,6 @@
 
     close_stdin();
 
-    debuggerd_init(nullptr);
     adb_trace_init(argv);
 
     D("Handling main()");
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 401c99c..56d647a 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,6 +98,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <private/android_logger.h>
+#include <selinux/android.h>
 
 #include "adb.h"
 #include "adb_io.h"
@@ -343,6 +344,24 @@
                 adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
         }
 
+#ifdef __ANDROID_RECOVERY__
+        // Special routine for recovery. Switch to shell domain when adbd is
+        // is running with dropped privileged (i.e. not running as root) and
+        // is built for the recovery mode. This is required because recovery
+        // rootfs is not labeled and everything is labeled just as rootfs.
+        char* con = nullptr;
+        if (getcon(&con) == 0) {
+            if (!strcmp(con, "u:r:adbd:s0")) {
+                if (selinux_android_setcon("u:r:shell:s0") < 0) {
+                    LOG(FATAL) << "Could not set SELinux context for subprocess";
+                }
+            }
+            freecon(con);
+        } else {
+            LOG(FATAL) << "Failed to get SELinux context";
+        }
+#endif
+
         if (command_.empty()) {
             // Spawn a login shell if we don't have a command.
             execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1b7758c..f98c11a 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,6 +21,7 @@
 #include "fdevent.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +35,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
@@ -75,6 +77,8 @@
 static bool main_thread_valid;
 static uint64_t main_thread_id;
 
+static uint64_t fdevent_id;
+
 static bool run_needs_flush = false;
 static auto& run_queue_notify_fd = *new unique_fd();
 static auto& run_queue_mutex = *new std::mutex();
@@ -111,7 +115,8 @@
     if (fde->state & FDE_ERROR) {
         state += "E";
     }
-    return android::base::StringPrintf("(fdevent %d %s)", fde->fd.get(), state.c_str());
+    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+                                       state.c_str());
 }
 
 void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
@@ -125,6 +130,7 @@
     CHECK_GE(fd, 0);
 
     fdevent* fde = new fdevent();
+    fde->id = fdevent_id++;
     fde->state = FDE_ACTIVE;
     fde->fd.reset(fd);
     fde->func = func;
@@ -352,10 +358,56 @@
     }
 }
 
+static void fdevent_check_spin(uint64_t cycle) {
+    // Check to see if we're spinning because we forgot about an fdevent
+    // by keeping track of how long fdevents have been continuously pending.
+    struct SpinCheck {
+        fdevent* fde;
+        std::chrono::steady_clock::time_point timestamp;
+        uint64_t cycle;
+    };
+    static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+
+    auto now = std::chrono::steady_clock::now();
+    for (auto* fde : g_pending_list) {
+        auto it = g_continuously_pending.find(fde->id);
+        if (it == g_continuously_pending.end()) {
+            g_continuously_pending[fde->id] =
+                    SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+        } else {
+            it->second.cycle = cycle;
+        }
+    }
+
+    for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+        if (it->second.cycle != cycle) {
+            it = g_continuously_pending.erase(it);
+        } else {
+            // Use an absurdly long window, since all we really care about is
+            // getting a bugreport eventually.
+            if (now - it->second.timestamp > std::chrono::minutes(5)) {
+                LOG(FATAL_WITHOUT_ABORT) << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+                int fd = it->second.fde->fd.get();
+                std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+                std::string path;
+                if (!android::base::Readlink(fd_path, &path)) {
+                    PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+                }
+                LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+                abort();
+            }
+            ++it;
+        }
+    }
+}
+
 void fdevent_loop() {
     set_main_thread();
     fdevent_run_setup();
 
+    uint64_t cycle = 0;
     while (true) {
         if (terminate_loop) {
             return;
@@ -365,6 +417,8 @@
 
         fdevent_process();
 
+        fdevent_check_spin(cycle++);
+
         while (!g_pending_list.empty()) {
             fdevent* fde = g_pending_list.front();
             g_pending_list.pop_front();
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 69c4072..d501b86 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -32,8 +32,7 @@
 typedef void (*fd_func)(int fd, unsigned events, void *userdata);
 
 struct fdevent {
-    fdevent* next = nullptr;
-    fdevent* prev = nullptr;
+    uint64_t id;
 
     unique_fd fd;
     int force_eof = 0;
diff --git a/adb/services.cpp b/adb/services.cpp
index a757d90..b613d83 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -181,6 +181,29 @@
     kick_transport(t);
 }
 
+static void spin_service(int fd, void*) {
+    unique_fd sfd(fd);
+
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(sfd.get(), "refusing to spin on non-debuggable build\n");
+        return;
+    }
+
+    // A service that creates an fdevent that's always pending, and then ignores it.
+    unique_fd pipe_read, pipe_write;
+    if (!Pipe(&pipe_read, &pipe_write)) {
+        WriteFdExactly(sfd.get(), "failed to create pipe\n");
+        return;
+    }
+
+    fdevent_run_on_main_thread([fd = pipe_read.release()]() {
+        fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
+        fdevent_add(fde, FDE_READ);
+    });
+
+    WriteFdExactly(sfd.get(), "spinning\n");
+}
+
 int reverse_service(const char* command, atransport* transport) {
     int s[2];
     if (adb_socketpair(s)) {
@@ -328,6 +351,8 @@
                                     reinterpret_cast<void*>(1));
     } else if (!strcmp(name, "reconnect")) {
         ret = create_service_thread("reconnect", reconnect_service, transport);
+    } else if (!strcmp(name, "spin")) {
+        ret = create_service_thread("spin", spin_service, nullptr);
 #endif
     }
     if (ret >= 0) {
diff --git a/adb/transport.h b/adb/transport.h
index ae9cc02..cb20615 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -92,6 +92,8 @@
     std::string transport_name_;
     ReadCallback read_callback_;
     ErrorCallback error_callback_;
+
+    static std::unique_ptr<Connection> FromFd(unique_fd fd);
 };
 
 // Abstraction for a blocking packet transport.
diff --git a/adb/transport_benchmark.cpp b/adb/transport_benchmark.cpp
index da24aa7..022808f 100644
--- a/adb/transport_benchmark.cpp
+++ b/adb/transport_benchmark.cpp
@@ -24,13 +24,19 @@
 #include "sysdeps.h"
 #include "transport.h"
 
-#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...)               \
-    BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__) \
-        ->Arg(1)                                                    \
-        ->Arg(16384)                                                \
-        ->Arg(MAX_PAYLOAD)                                          \
+#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...)                          \
+    BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__)            \
+        ->Arg(1)                                                               \
+        ->Arg(16384)                                                           \
+        ->Arg(MAX_PAYLOAD)                                                     \
+        ->UseRealTime();                                                       \
+    BENCHMARK_TEMPLATE(benchmark_name, NonblockingFdConnection, ##__VA_ARGS__) \
+        ->Arg(1)                                                               \
+        ->Arg(16384)                                                           \
+        ->Arg(MAX_PAYLOAD)                                                     \
         ->UseRealTime()
 
+struct NonblockingFdConnection;
 template <typename ConnectionType>
 std::unique_ptr<Connection> MakeConnection(unique_fd fd);
 
@@ -40,6 +46,11 @@
     return std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
 }
 
+template <>
+std::unique_ptr<Connection> MakeConnection<NonblockingFdConnection>(unique_fd fd) {
+    return Connection::FromFd(std::move(fd));
+}
+
 template <typename ConnectionType>
 void BM_Connection_Unidirectional(benchmark::State& state) {
     int fds[2];
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
new file mode 100644
index 0000000..85f3c52
--- /dev/null
+++ b/adb/transport_fd.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+
+#include <deque>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "sysdeps/memory.h"
+#include "transport.h"
+#include "types.h"
+
+static void CreateWakeFds(unique_fd* read, unique_fd* write) {
+    // TODO: eventfd on linux?
+    int wake_fds[2];
+    int rc = adb_socketpair(wake_fds);
+    set_file_block_mode(wake_fds[0], false);
+    set_file_block_mode(wake_fds[1], false);
+    CHECK_EQ(0, rc);
+    *read = unique_fd(wake_fds[0]);
+    *write = unique_fd(wake_fds[1]);
+}
+
+struct NonblockingFdConnection : public Connection {
+    NonblockingFdConnection(unique_fd fd) : started_(false), fd_(std::move(fd)) {
+        set_file_block_mode(fd_.get(), false);
+        CreateWakeFds(&wake_fd_read_, &wake_fd_write_);
+    }
+
+    void SetRunning(bool value) {
+        std::lock_guard<std::mutex> lock(run_mutex_);
+        running_ = value;
+    }
+
+    bool IsRunning() {
+        std::lock_guard<std::mutex> lock(run_mutex_);
+        return running_;
+    }
+
+    void Run(std::string* error) {
+        SetRunning(true);
+        while (IsRunning()) {
+            adb_pollfd pfds[2] = {
+                {.fd = fd_.get(), .events = POLLIN},
+                {.fd = wake_fd_read_.get(), .events = POLLIN},
+            };
+
+            {
+                std::lock_guard<std::mutex> lock(this->write_mutex_);
+                if (!writable_) {
+                    pfds[0].events |= POLLOUT;
+                }
+            }
+
+            int rc = adb_poll(pfds, 2, -1);
+            if (rc == -1) {
+                *error = android::base::StringPrintf("poll failed: %s", strerror(errno));
+                return;
+            } else if (rc == 0) {
+                LOG(FATAL) << "poll timed out with an infinite timeout?";
+            }
+
+            if (pfds[0].revents) {
+                if ((pfds[0].revents & POLLOUT)) {
+                    std::lock_guard<std::mutex> lock(this->write_mutex_);
+                    WriteResult result = DispatchWrites();
+                    switch (result) {
+                        case WriteResult::Error:
+                            *error = "write failed";
+                            return;
+
+                        case WriteResult::Completed:
+                            writable_ = true;
+                            break;
+
+                        case WriteResult::TryAgain:
+                            break;
+                    }
+                }
+
+                if (pfds[0].revents & POLLIN) {
+                    // TODO: Should we be getting blocks from a free list?
+                    auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
+                    rc = adb_read(fd_.get(), &(*block)[0], block->size());
+                    if (rc == -1) {
+                        *error = std::string("read failed: ") + strerror(errno);
+                        return;
+                    } else if (rc == 0) {
+                        *error = "read failed: EOF";
+                        return;
+                    }
+                    block->resize(rc);
+                    read_buffer_.append(std::move(block));
+
+                    if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
+                        auto header_buf = read_buffer_.take_front(sizeof(amessage)).coalesce();
+                        CHECK_EQ(sizeof(amessage), header_buf.size());
+                        read_header_ = std::make_unique<amessage>();
+                        memcpy(read_header_.get(), header_buf.data(), sizeof(amessage));
+                    }
+
+                    if (read_header_ && read_buffer_.size() >= read_header_->data_length) {
+                        auto data_chain = read_buffer_.take_front(read_header_->data_length);
+
+                        // TODO: Make apacket carry around a IOVector instead of coalescing.
+                        auto payload = data_chain.coalesce<apacket::payload_type>();
+                        auto packet = std::make_unique<apacket>();
+                        packet->msg = *read_header_;
+                        packet->payload = std::move(payload);
+                        read_header_ = nullptr;
+                        read_callback_(this, std::move(packet));
+                    }
+                }
+            }
+
+            if (pfds[1].revents) {
+                uint64_t buf;
+                rc = adb_read(wake_fd_read_.get(), &buf, sizeof(buf));
+                CHECK_EQ(static_cast<int>(sizeof(buf)), rc);
+
+                // We were woken up either to add POLLOUT to our events, or to exit.
+                // Do nothing.
+            }
+        }
+    }
+
+    void Start() override final {
+        if (started_.exchange(true)) {
+            LOG(FATAL) << "Connection started multiple times?";
+        }
+
+        thread_ = std::thread([this]() {
+            std::string error = "connection closed";
+            Run(&error);
+            this->error_callback_(this, error);
+        });
+    }
+
+    void Stop() override final {
+        SetRunning(false);
+        WakeThread();
+        thread_.join();
+    }
+
+    void WakeThread() {
+        uint64_t buf = 0;
+        if (TEMP_FAILURE_RETRY(adb_write(wake_fd_write_.get(), &buf, sizeof(buf))) != sizeof(buf)) {
+            LOG(FATAL) << "failed to wake up thread";
+        }
+    }
+
+    enum class WriteResult {
+        Error,
+        Completed,
+        TryAgain,
+    };
+
+    WriteResult DispatchWrites() REQUIRES(write_mutex_) {
+        CHECK(!write_buffer_.empty());
+        if (!writable_) {
+            return WriteResult::TryAgain;
+        }
+
+        auto iovs = write_buffer_.iovecs();
+        ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
+        if (rc == -1) {
+            return WriteResult::Error;
+        } else if (rc == 0) {
+            errno = 0;
+            return WriteResult::Error;
+        }
+
+        // TODO: Implement a more efficient drop_front?
+        write_buffer_.take_front(rc);
+        if (write_buffer_.empty()) {
+            return WriteResult::Completed;
+        }
+
+        // There's data left in the range, which means our write returned early.
+        return WriteResult::TryAgain;
+    }
+
+    bool Write(std::unique_ptr<apacket> packet) final {
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
+        const char* header_end = header_begin + sizeof(packet->msg);
+        auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+        write_buffer_.append(std::move(header_block));
+        if (!packet->payload.empty()) {
+            write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+        }
+        return DispatchWrites() != WriteResult::Error;
+    }
+
+    std::thread thread_;
+
+    std::atomic<bool> started_;
+    std::mutex run_mutex_;
+    bool running_ GUARDED_BY(run_mutex_);
+
+    std::unique_ptr<amessage> read_header_;
+    IOVector read_buffer_;
+
+    unique_fd fd_;
+    unique_fd wake_fd_read_;
+    unique_fd wake_fd_write_;
+
+    std::mutex write_mutex_;
+    bool writable_ GUARDED_BY(write_mutex_) = true;
+    IOVector write_buffer_ GUARDED_BY(write_mutex_);
+
+    IOVector incoming_queue_;
+};
+
+std::unique_ptr<Connection> Connection::FromFd(unique_fd fd) {
+    return std::make_unique<NonblockingFdConnection>(std::move(fd));
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd9ba88..8fc2171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,7 +63,6 @@
     name: "bootstat",
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
-    shared_libs: ["liblogcat"],
     init_rc: ["bootstat.rc"],
     product_variables: {
         pdk: {
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 8ed92a6..ddd1caf 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -480,23 +480,25 @@
 - (wait until screen is up, boot has completed)
 - adb shell getprop ro.boot.bootreason (bootloader reason)
 - adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason.last (last last reason)
 - adb shell getprop sys.boot.reason (system reason)
 - NB: all should have a value that is compliant with our known set." ]
 test_properties() {
   duration_test 1
   wait_for_screen
   retval=0
-  check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+  check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
   bootloader=""
   # NB: this test could fail if performed _after_ optional_factory_reset test
   # and will report
   #  ERROR: expected "reboot" got ""
-  #        for Android property persist.sys.boot.reason
+  #        for Android property sys.boot.reason.last
   # following is mitigation for the persist.sys.boot.reason, skip it
   if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
     check_set="ro.boot.bootreason sys.boot.reason"
     bootloader="bootloader"
   fi
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   for prop in ${check_set}; do
     reason=`validate_property ${prop}`
     EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
@@ -554,7 +556,8 @@
   popd >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
-  EXPECT_PROPERTY persist.sys.boot.reason bootloader
+  EXPECT_PROPERTY sys.boot.reason.last bootloader
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,ota bootloader
 }
 
@@ -568,7 +571,8 @@
   adb reboot ota
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,ota
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+  EXPECT_PROPERTY sys.boot.reason.last reboot,ota
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,ota
 }
 
@@ -593,9 +597,15 @@
   wait_for_screen
   bootloader_reason=`validate_property ro.boot.bootreason`
   EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
-  EXPECT_PROPERTY sys.boot.reason ${reason}
-  EXPECT_PROPERTY persist.sys.boot.reason ${reason}
-  report_bootstat_logs ${reason}
+  # to make sys.boot.reason report user friendly
+  reasons=${reason}
+  if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then
+    reasons="\(${reason}\|${bootloader_reason}\)"
+  fi
+  EXPECT_PROPERTY sys.boot.reason ${reasons}
+  EXPECT_PROPERTY sys.boot.reason.last ${reason}
+  EXPECT_PROPERTY persist.sys.boot.reason ""
+  report_bootstat_logs ${reason} ${bootloader_reason}
 }
 
 [ "USAGE: test_cold
@@ -627,7 +637,8 @@
   adb reboot >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+  EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
     "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
     "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
@@ -658,6 +669,7 @@
   wait_for_screen
   ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+  EXPECT_PROPERTY sys.boot.reason.last ""
   EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,factory_reset bootloader \
     "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -731,7 +743,8 @@
     )
 
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason cold
+  EXPECT_PROPERTY sys.boot.reason.last cold
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
   exitPstore
 }
@@ -752,7 +765,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs shutdown,battery
 }
 
@@ -772,7 +786,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs shutdown,thermal,battery
 }
 
@@ -808,7 +823,8 @@
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs kernel_panic,sysrq
   exitPstore
 }
@@ -835,7 +851,8 @@
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs kernel_panic,sysrq,test \
     "-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
   exitPstore
@@ -865,7 +882,8 @@
   adb reboot warm
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs kernel_panic,hung
   exitPstore
 }
@@ -897,7 +915,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,thermal
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs shutdown,thermal
 }
 
@@ -917,7 +936,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs shutdown,userrequested
 }
 
@@ -933,7 +953,8 @@
   adb shell reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+  EXPECT_PROPERTY sys.boot.reason.last reboot,shell
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,shell
 }
 
@@ -949,7 +970,8 @@
   adb reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,adb
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+  EXPECT_PROPERTY sys.boot.reason.last reboot,adb
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,adb
 }
 
@@ -984,23 +1006,8 @@
   adb shell 'reboot "Its Just So Hard"'
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
-  EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
-  # Do not leave this test with an illegal value in persist.sys.boot.reason
-  save_ret=${?}           # hold on to error code from above two lines
-  if isDebuggable; then   # can do this easy, or we can do this hard.
-    adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
-    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
-  else
-    report_bootstat_logs reboot,its_just_so_hard  # report what we have so far
-    # user build mitigation
-    adb shell reboot its_just_so_hard
-    wait_for_screen
-    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
-    EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
-  fi
-  # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
-  # that we could choke up following tests.  For example test_properties.
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+  EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs reboot,its_just_so_hard
 }
 
@@ -1049,7 +1056,8 @@
   # Check values
   EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
   EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
-  EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+  EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
+  EXPECT_PROPERTY persist.sys.boot.reason ""
   report_bootstat_logs "${sys_expected}"
 }
 
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 32cc0cd..7ec57ec 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -43,7 +43,6 @@
 #include <android/log.h>
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
-#include <log/logcat.h>
 #include <metricslogger/metrics_logger.h>
 
 #include "boot_event_record_store.h"
@@ -735,6 +734,7 @@
 
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
 const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
 
 // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
@@ -869,86 +869,7 @@
       }
     }
 
-    // The following battery test should migrate to a default system health HAL
-
-    // Let us not worry if the reboot command was issued, for the cases of
-    // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
-    // Same for bootloader and ro.boot.bootreasons of this set, but a dead
-    // battery could conceivably lead to these, so worthy of override.
-    if (isBluntRebootReason(ret)) {
-      // Heuristic to determine if shutdown possibly because of a dead battery?
-      // Really a hail-mary pass to find it in last klog content ...
-      static const int battery_dead_threshold = 2;  // percent
-      static const char battery[] = "healthd: battery l=";
-      const pstoreConsole console(content);
-      size_t pos = console.rfind(battery);  // last one
-      std::string digits;
-      if (pos != std::string::npos) {
-        digits = content.substr(pos + strlen(battery), strlen("100 "));
-        // correct common errors
-        correctForBitError(digits, "100 ");
-        if (digits[0] == '!') digits[0] = '1';
-        if (digits[1] == '!') digits[1] = '1';
-      }
-      const char* endptr = digits.c_str();
-      unsigned level = 0;
-      while (::isdigit(*endptr)) {
-        level *= 10;
-        level += *endptr++ - '0';
-        // make sure no leading zeros, except zero itself, and range check.
-        if ((level == 0) || (level > 100)) break;
-      }
-      // example bit error rate issues for 10%
-      //   'l=10 ' no bits in error
-      //   'l=00 ' single bit error (fails above)
-      //   'l=1  ' single bit error
-      //   'l=0  ' double bit error
-      // There are others, not typically critical because of 2%
-      // battery_dead_threshold. KISS check, make sure second
-      // character after digit sequence is not a space.
-      if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
-        LOG(INFO) << "Battery level at shutdown " << level << "%";
-        if (level <= battery_dead_threshold) {
-          ret = "shutdown,battery";
-        }
-      } else {        // Most likely
-        digits = "";  // reset digits
-
-        // Content buffer no longer will have console data. Beware if more
-        // checks added below, that depend on parsing console content.
-        content = "";
-
-        LOG(DEBUG) << "Can not find last low battery in last console messages";
-        android_logcat_context ctx = create_android_logcat();
-        FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
-        if (fp != nullptr) {
-          android::base::ReadFdToString(fileno(fp), &content);
-        }
-        android_logcat_pclose(&ctx, fp);
-        static const char logcat_battery[] = "W/healthd (    0): battery l=";
-
-        pos = content.find(logcat_battery);  // The first one it finds.
-        if (pos != std::string::npos) {
-          digits = content.substr(pos + strlen(logcat_battery), strlen("100 "));
-        }
-        endptr = digits.c_str();
-        level = 0;
-        while (::isdigit(*endptr)) {
-          level *= 10;
-          level += *endptr++ - '0';
-          // make sure no leading zeros, except zero itself, and range check.
-          if ((level == 0) || (level > 100)) break;
-        }
-        if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
-          LOG(INFO) << "Battery level at startup " << level << "%";
-          if (level <= battery_dead_threshold) {
-            ret = "shutdown,battery";
-          }
-        } else {
-          LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
-        }
-      }
-    }
+    // TODO: use the HAL to get battery level (http://b/77725702).
 
     // Is there a controlled shutdown hint in last_reboot_reason_property?
     if (isBluntRebootReason(ret)) {
@@ -985,10 +906,6 @@
   }
 
   LOG(INFO) << "Canonical boot reason: " << ret;
-  if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
-    // Rewrite as it must be old news, kernel reasons trump user space.
-    SetProperty(last_reboot_reason_property, ret);
-  }
   return ret;
 }
 
@@ -1118,6 +1035,15 @@
   const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
   // Record the scrubbed system_boot_reason to the property
   SetProperty(system_reboot_reason_property, system_boot_reason);
+  // Shift last_reboot_reason_property to last_last_reboot_reason_property
+  std::string last_boot_reason(GetProperty(last_reboot_reason_property));
+  if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
+    last_boot_reason = system_boot_reason;
+  } else {
+    transformReason(last_boot_reason);
+  }
+  SetProperty(last_last_reboot_reason_property, last_boot_reason);
+  SetProperty(last_reboot_reason_property, "");
 }
 
 // Records several metrics related to the time it takes to boot the device,
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0b13662..1b20157 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -47,6 +47,7 @@
 cc_library_static {
     name: "libtombstoned_client_static",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "tombstoned/tombstoned_client.cpp",
         "util.cpp",
@@ -90,7 +91,6 @@
 cc_library_static {
     name: "libdebuggerd_handler",
     defaults: ["debuggerd_defaults"],
-    recovery_available: true,
     srcs: ["handler/debuggerd_fallback_nop.cpp"],
 
     whole_static_libs: [
@@ -104,6 +104,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_fallback",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "handler/debuggerd_fallback.cpp",
     ],
@@ -120,6 +121,12 @@
         "liblzma",
         "libcutils",
     ],
+    target: {
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_static_libs: ["libdexfile"],
+        },
+    },
 
     export_include_dirs: ["include"],
 }
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b016e23..360ea95 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include <limits>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -33,9 +34,10 @@
 using android::base::unique_fd;
 
 static void usage(int exit_code) {
-  fprintf(stderr, "usage: debuggerd [-b] PID\n");
+  fprintf(stderr, "usage: debuggerd [-bj] PID\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "-b, --backtrace    just a backtrace rather than a full tombstone\n");
+  fprintf(stderr, "-j                 collect java traces\n");
   _exit(exit_code);
 }
 
@@ -58,8 +60,19 @@
 int main(int argc, char* argv[]) {
   if (argc <= 1) usage(0);
   if (argc > 3) usage(1);
-  if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
-  bool backtrace_only = argc == 3;
+
+  DebuggerdDumpType dump_type = kDebuggerdTombstone;
+
+  if (argc == 3) {
+    std::string_view flag = argv[1];
+    if (flag == "-b" || flag == "--backtrace") {
+      dump_type = kDebuggerdNativeBacktrace;
+    } else if (flag == "-j") {
+      dump_type = kDebuggerdJavaBacktrace;
+    } else {
+      usage(1);
+    }
+  }
 
   pid_t pid;
   if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
@@ -90,8 +103,7 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
-                              0, std::move(pipewrite))) {
+  if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bc3b04b..b0b4839 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -56,12 +56,18 @@
         "libselinux",
         "libavb",
         "libfstab",
+        "libdm",
+        "liblp",
     ],
     export_static_lib_headers: [
         "libfstab",
+        "libdm",
+        "liblp",
     ],
     whole_static_libs: [
+        "liblp",
         "liblogwrap",
+        "libdm",
         "libfstab",
     ],
     cppflags: [
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index b2f3a68..ed42d40 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -37,6 +37,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <liblp/reader.h>
 
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_dm_ioctl.h"
@@ -137,5 +138,30 @@
     return nullptr;
 }
 
+bool CreateLogicalPartitions(const std::string& block_device) {
+    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+    auto metadata = ReadMetadata(block_device.c_str(), slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
+    }
+
+    LogicalPartitionTable table;
+    for (const auto& partition : metadata->partitions) {
+        LogicalPartition new_partition;
+        new_partition.name = GetPartitionName(partition);
+        new_partition.attributes = partition.attributes;
+        for (size_t i = 0; i < partition.num_extents; i++) {
+            const auto& extent = metadata->extents[partition.first_extent_index + i];
+            new_partition.extents.emplace_back(new_partition.num_sectors, extent.target_data,
+                                               extent.num_sectors, block_device.c_str());
+            new_partition.num_sectors += extent.num_sectors;
+        }
+        table.partitions.push_back(new_partition);
+    }
+
+    return CreateLogicalPartitions(table);
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 7f928e5..9772c4b 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -92,6 +92,7 @@
 //   /dev/block/dm-<name> where |name| is the partition name.
 //
 bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+bool CreateLogicalPartitions(const std::string& block_device);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..672d401
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_library_static {
+    name: "libdm",
+    recovery_available: true,
+
+    export_include_dirs: ["include"],
+    cflags: [
+        // TODO(b/110035986): Allows us to create a skeleton of required classes
+        "-Wno-unused-private-field",
+    ],
+
+    srcs: [
+        "dm_table.cpp",
+        "dm_target.cpp",
+        "dm.cpp"
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..57c1270
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#include "dm.h"
+
+namespace android {
+namespace dm {
+
+DeviceMapper& DeviceMapper::Instance() {
+    static DeviceMapper instance;
+    return instance;
+}
+// Creates a new device mapper device
+bool DeviceMapper::CreateDevice(const std::string& name) {
+    if (name.empty()) {
+        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+        return false;
+    }
+
+    if (name.size() >= DM_NAME_LEN) {
+        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+        return false;
+    }
+
+    std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
+            static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
+    if (io == nullptr) {
+        LOG(ERROR) << "Failed to allocate dm_ioctl";
+        return false;
+    }
+    InitIo(io.get(), name);
+
+    if (ioctl(fd_, DM_DEV_CREATE, io.get())) {
+        PLOG(ERROR) << "DM_DEV_CREATE failed to create [" << name << "]";
+        return false;
+    }
+
+    // Check to make sure the newly created device doesn't already have targets
+    // added or opened by someone
+    CHECK(io->target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+    CHECK(io->open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+
+    // Creates a new device mapper device with the name passed in
+    return true;
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+    if (name.empty()) {
+        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+        return false;
+    }
+
+    if (name.size() >= DM_NAME_LEN) {
+        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+        return false;
+    }
+
+    std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
+            static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
+    if (io == nullptr) {
+        LOG(ERROR) << "Failed to allocate dm_ioctl";
+        return false;
+    }
+    InitIo(io.get(), name);
+
+    if (ioctl(fd_, DM_DEV_REMOVE, io.get())) {
+        PLOG(ERROR) << "DM_DEV_REMOVE failed to create [" << name << "]";
+        return false;
+    }
+
+    // Check to make sure appropriate uevent is generated so ueventd will
+    // do the right thing and remove the corresponding device node and symlinks.
+    CHECK(io->flags & DM_UEVENT_GENERATED_FLAG)
+            << "Didn't generate uevent for [" << name << "] removal";
+
+    return true;
+}
+
+const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
+    // TODO(b/110035986): Return the table, as read from the kernel instead
+    return nullptr;
+}
+
+DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
+    // TODO(b/110035986): Return the state, as read from the kernel instead
+    return DmDeviceState::INVALID;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) {
+    return false;
+}
+
+// Reads all the available device mapper targets and their corresponding
+// versions from the kernel and returns in a vector
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTarget>* targets) {
+    targets->clear();
+
+    // calculate the space needed to read a maximum of kMaxPossibleDmTargets
+    uint32_t payload_size = sizeof(struct dm_target_versions);
+    payload_size += DM_MAX_TYPE_NAME;
+    // device mapper wants every target spec to be aligned at 8-byte boundary
+    payload_size = DM_ALIGN(payload_size);
+    payload_size *= kMaxPossibleDmTargets;
+
+    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory";
+        return false;
+    }
+
+    // Sets appropriate data size and data_start to make sure we tell kernel
+    // about the total size of the buffer we are passing and where to start
+    // writing the list of targets.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    InitIo(io);
+    io->data_size = data_size;
+    io->data_start = sizeof(*io);
+
+    if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
+        PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel";
+        return false;
+    }
+
+    // If the provided buffer wasn't enough to list all targets, note that
+    // any data beyond sizeof(*io) must not be read in this case
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        LOG(INFO) << data_size << " is not enough memory to list all dm targets";
+        return false;
+    }
+
+    // if there are no targets registered, return success with empty vector
+    if (io->data_size == sizeof(*io)) {
+        return true;
+    }
+
+    // Parse each target and list the name and version
+    // TODO(b/110035986): Templatize this
+    uint32_t next = sizeof(*io);
+    data_size = io->data_size - next;
+    struct dm_target_versions* vers =
+            reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+    while (next && data_size) {
+        targets->emplace_back((vers));
+        if (vers->next == 0) {
+            break;
+        }
+        next += vers->next;
+        data_size -= vers->next;
+        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+    }
+
+    return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
+    devices->clear();
+
+    // calculate the space needed to read a maximum of 256 targets, each with
+    // name with maximum length of 16 bytes
+    uint32_t payload_size = sizeof(struct dm_name_list);
+    // 128-bytes for the name
+    payload_size += DM_NAME_LEN;
+    // dm wants every device spec to be aligned at 8-byte boundary
+    payload_size = DM_ALIGN(payload_size);
+    payload_size *= kMaxPossibleDmDevices;
+    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory";
+        return false;
+    }
+
+    // Sets appropriate data size and data_start to make sure we tell kernel
+    // about the total size of the buffer we are passing and where to start
+    // writing the list of targets.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    InitIo(io);
+    io->data_size = data_size;
+    io->data_start = sizeof(*io);
+
+    if (ioctl(fd_, DM_LIST_DEVICES, io)) {
+        PLOG(ERROR) << "Failed to get DM_LIST_DEVICES from kernel";
+        return false;
+    }
+
+    // If the provided buffer wasn't enough to list all devices any data
+    // beyond sizeof(*io) must not be read.
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        LOG(INFO) << data_size << " is not enough memory to list all dm devices";
+        return false;
+    }
+
+    // if there are no devices created yet, return success with empty vector
+    if (io->data_size == sizeof(*io)) {
+        return true;
+    }
+
+    // Parse each device and add a new DmBlockDevice to the vector
+    // created from the kernel data.
+    uint32_t next = sizeof(*io);
+    data_size = io->data_size - next;
+    struct dm_name_list* dm_dev =
+            reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+
+    while (next && data_size) {
+        devices->emplace_back((dm_dev));
+        if (dm_dev->next == 0) {
+            break;
+        }
+        next += dm_dev->next;
+        data_size -= dm_dev->next;
+        dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+    }
+
+    return true;
+}
+
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns the path to it's device node (or symlink to the device node)
+std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) {
+    return "";
+}
+
+// private methods of DeviceMapper
+void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
+    CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
+    memset(io, 0, sizeof(*io));
+
+    io->version[0] = DM_VERSION0;
+    io->version[1] = DM_VERSION1;
+    io->version[2] = DM_VERSION2;
+    io->data_size = sizeof(*io);
+    io->data_start = 0;
+    if (!name.empty()) {
+        strlcpy(io->name, name.c_str(), sizeof(io->name));
+    }
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..14b3932
--- /dev/null
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_table.h"
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
+    return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
+    return true;
+}
+
+bool DmTable::valid() const {
+    return true;
+}
+
+uint64_t DmTable::size() const {
+    return valid() ? size_ : 0;
+}
+
+// Returns a string represnetation of the table that is ready to be passed
+// down to the kernel for loading
+//
+// Implementation must verify there are no gaps in the table, table starts
+// with sector == 0, and iterate over each target to get its table
+// serialized.
+std::string DmTable::Serialize() const {
+    return "";
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
new file mode 100644
index 0000000..8bcd526
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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 <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h
new file mode 100644
index 0000000..52a9a11
--- /dev/null
+++ b/fs_mgr/libdm/include/dm.h
@@ -0,0 +1,152 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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.
+ */
+
+#ifndef _LIBDM_DM_H_
+#define _LIBDM_DM_H_
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+#include <dm_table.h>
+
+// The minimum expected device mapper major.minor version
+#define DM_VERSION0 (4)
+#define DM_VERSION1 (0)
+#define DM_VERSION2 (0)
+
+#define DM_ALIGN_MASK (7)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+
+namespace android {
+namespace dm {
+
+enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
+
+class DeviceMapper final {
+  public:
+    class DmBlockDevice final {
+      public:
+        // only allow creating this with dm_name_list
+        DmBlockDevice() = delete;
+
+        explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};
+
+        // Returs device mapper name associated with the block device
+        const std::string& name() const { return name_; }
+
+        // Return major number for the block device
+        uint32_t Major() const { return major(dev_); }
+
+        // Return minor number for the block device
+        uint32_t Minor() const { return minor(dev_); }
+        ~DmBlockDevice() = default;
+
+      private:
+        std::string name_;
+        uint64_t dev_;
+    };
+
+    // Creates a device mapper device with given name.
+    // Return 'true' on success and 'false' on failure to
+    // create OR if a device mapper device with the same name already
+    // exists.
+    // TODO(b/110035986): Make this method private and to be only
+    // called through LoadTableAndActivate() below.
+    bool CreateDevice(const std::string& name);
+
+    // Removes a device mapper device with the given name.
+    // Returns 'true' on success, false otherwise.
+    bool DeleteDevice(const std::string& name);
+
+    // Reads the device mapper table from the device with given anme and
+    // returns it in a DmTable object.
+    const std::unique_ptr<DmTable> table(const std::string& name) const;
+
+    // Returns the current state of the underlying device mapper device
+    // with given name.
+    // One of INVALID, SUSPENDED or ACTIVE.
+    DmDeviceState state(const std::string& name) const;
+
+    // Loads the device mapper table from parameter into the underlying
+    // device mapper device with given name and activate / resumes the device in the process.
+    // If a device mapper device with the 'name', doesn't exist, it will be created.
+    // Returns 'true' on success, false otherwise.
+    bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+
+    // Returns true if a list of available device mapper targets registered in the kernel was
+    // successfully read and stored in 'targets'. Returns 'false' otherwise.
+    bool GetAvailableTargets(std::vector<DmTarget>* targets);
+
+    // Return 'true' if it can successfully read the list of device mapper block devices
+    // currently created. 'devices' will be empty if the kernel interactions
+    // were successful and there are no block devices at the moment. Returns
+    // 'false' in case of any failure along the way.
+    bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
+
+    // Returns the path to the device mapper device node in '/dev' corresponding to
+    // 'name'.
+    std::string GetDmDevicePathByName(const std::string& name);
+
+    // The only way to create a DeviceMapper object.
+    static DeviceMapper& Instance();
+
+    ~DeviceMapper() {
+        if (fd_ != -1) {
+            ::close(fd_);
+        }
+    }
+
+  private:
+    // Maximum possible device mapper targets registered in the kernel.
+    // This is only used to read the list of targets from kernel so we allocate
+    // a finite amount of memory. This limit is in no way enforced by the kernel.
+    static constexpr uint32_t kMaxPossibleDmTargets = 256;
+
+    // Maximum possible device mapper created block devices. Note that this is restricted by
+    // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer
+    // kernels. In Android systems however, we never expect these to grow beyond the artificial
+    // limit we are imposing here of 256.
+    static constexpr uint32_t kMaxPossibleDmDevices = 256;
+
+    void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+    DeviceMapper() : fd_(-1) {
+        fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open device-mapper";
+        }
+    }
+
+    int fd_;
+    // Non-copyable & Non-movable
+    DeviceMapper(const DeviceMapper&) = delete;
+    DeviceMapper& operator=(const DeviceMapper&) = delete;
+    DeviceMapper& operator=(DeviceMapper&&) = delete;
+    DeviceMapper(DeviceMapper&&) = delete;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DM_H_ */
diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/dm_table.h
new file mode 100644
index 0000000..0b1685d
--- /dev/null
+++ b/fs_mgr/libdm/include/dm_table.h
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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.
+ */
+
+#ifndef _LIBDM_DMTABLE_H_
+#define _LIBDM_DMTABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {
+
+class DmTable {
+  public:
+    DmTable() : size_(0){};
+
+    // Adds a target to the device mapper table for a range specified in the target object.
+    // The function will return 'true' if the target was successfully added and doesn't overlap with
+    // any of the existing targets in the table. Gaps are allowed. The final check, including
+    // overlaps and gaps are done before loading the table. Returns 'false' on failure.
+    bool AddTarget(std::unique_ptr<DmTarget>&& target);
+
+    // Removes a target from the table for the range specified in the target object. Returns 'false'
+    // if the target name doesn't match with the one in the table. Returns 'true' if target is
+    // successfully removed.
+    bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+
+    // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
+    // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
+    // table is malformed.
+    bool valid() const;
+
+    // Returns the total size represented by the table in terms of number of 512-byte sectors.
+    // NOTE: This function will overlook if there are any gaps in the targets added in the table.
+    uint64_t size() const;
+
+    // Returns the string represntation of the table that is ready to be passed into the kernel
+    // as part of the DM_TABLE_LOAD ioctl.
+    std::string Serialize() const;
+
+    ~DmTable() = default;
+
+  private:
+    // list of targets defined in this table sorted by
+    // their start and end sectors.
+    // Note: Overlapping targets MUST never be added in this list.
+    std::vector<std::unique_ptr<DmTarget>> targets_;
+
+    // Total size in terms of # of sectors, as calculated by looking at the last and the first
+    // target in 'target_'.
+    uint64_t size_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DMTABLE_H_ */
diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h
new file mode 100644
index 0000000..31b0cb6
--- /dev/null
+++ b/fs_mgr/libdm/include/dm_target.h
@@ -0,0 +1,77 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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.
+ */
+
+#ifndef _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <android-base/logging.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+class DmTarget {
+  public:
+    DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0)
+        : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){};
+
+    // Creates a DmTarget object from dm_target_version as read from kernel
+    // with DM_LIST_VERSION ioctl.
+    DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) {
+        CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr";
+        v0_ = vers->version[0];
+        v1_ = vers->version[1];
+        v2_ = vers->version[2];
+        name_ = vers->name;
+    }
+
+    virtual ~DmTarget() = default;
+
+    // Returns name of the target.
+    const std::string& name() const { return name_; }
+
+    // Returns size in number of sectors when this target is part of
+    // a DmTable, return 0 otherwise.
+    uint64_t size() const { return length_; }
+
+    // Return string representation of the device mapper target version.
+    std::string version() const {
+        return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_);
+    }
+
+    // Function that converts this object to a string of arguments that can
+    // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+    // must implement this, for it to be used on a device.
+    virtual std::string Serialize() const { return ""; }
+
+  private:
+    // Name of the target.
+    std::string name_;
+    // Target version.
+    uint32_t v0_, v1_, v2_;
+    // logical sector number start and total length (in terms of 512-byte sectors) represented
+    // by this target within a DmTable.
+    uint64_t start_, length_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..0ca8938
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_library_static {
+    name: "liblp",
+    host_supported: true,
+    recovery_available: true,
+    defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
+    srcs: [
+        "builder.cpp",
+        "reader.cpp",
+        "utility.cpp",
+        "writer.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "liblog",
+        "libcrypto",
+        "libcrypto_utils",
+    ],
+    whole_static_libs: [
+        "libext2_uuid",
+        "libext4_utils",
+        "libsparse",
+        "libz",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..a084893
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2018 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 "liblp/builder.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include <uuid/uuid.h>
+
+#include "liblp/metadata_format.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Align a byte count up to the nearest 512-byte sector.
+template <typename T>
+static inline T AlignToSector(T value) {
+    return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
+}
+
+void LinearExtent::AddTo(LpMetadata* out) const {
+    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+}
+
+void ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+}
+
+Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
+    : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+    size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+    extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+    size_ = 0;
+    extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t requested_size) {
+    uint64_t aligned_size = AlignToSector(requested_size);
+    if (size_ <= aligned_size) {
+        return;
+    }
+    if (aligned_size == 0) {
+        RemoveExtents();
+        return;
+    }
+
+    // Remove or shrink extents of any kind until the total partition size is
+    // equal to the requested size.
+    uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+    while (sectors_to_remove) {
+        Extent* extent = extents_.back().get();
+        if (extent->num_sectors() > sectors_to_remove) {
+            size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+            extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+            break;
+        }
+        size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+        sectors_to_remove -= extent->num_sectors();
+        extents_.pop_back();
+    }
+    DCHECK(size_ == requested_size);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
+                                                      uint32_t metadata_max_size,
+                                                      uint32_t metadata_slot_count) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(metadata)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+MetadataBuilder::MetadataBuilder() {
+    memset(&geometry_, 0, sizeof(geometry_));
+    geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+    geometry_.struct_size = sizeof(geometry_);
+
+    memset(&header_, 0, sizeof(header_));
+    header_.magic = LP_METADATA_HEADER_MAGIC;
+    header_.major_version = LP_METADATA_MAJOR_VERSION;
+    header_.minor_version = LP_METADATA_MINOR_VERSION;
+    header_.header_size = sizeof(header_);
+    header_.partitions.entry_size = sizeof(LpMetadataPartition);
+    header_.extents.entry_size = sizeof(LpMetadataExtent);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+    geometry_ = metadata.geometry;
+
+    for (const auto& partition : metadata.partitions) {
+        Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
+                                          partition.attributes);
+        if (!builder) {
+            return false;
+        }
+
+        for (size_t i = 0; i < partition.num_extents; i++) {
+            const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
+            if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+                builder->AddExtent(std::move(copy));
+            } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+                auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+                builder->AddExtent(std::move(copy));
+            }
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
+                           uint32_t metadata_slot_count) {
+    if (metadata_max_size < sizeof(LpMetadataHeader)) {
+        LERROR << "Invalid metadata maximum size.";
+        return false;
+    }
+    if (metadata_slot_count == 0) {
+        LERROR << "Invalid metadata slot count.";
+        return false;
+    }
+
+    // Align the metadata size up to the nearest sector.
+    metadata_max_size = AlignToSector(metadata_max_size);
+
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    uint64_t reserved =
+            LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
+    uint64_t total_reserved = reserved * 2;
+
+    if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
+        LERROR << "Attempting to create metadata on a block device that is too small.";
+        return false;
+    }
+
+    // The last sector is inclusive. We subtract one to make sure that logical
+    // partitions won't overlap with the same sector as the backup metadata,
+    // which could happen if the block device was not aligned to LP_SECTOR_SIZE.
+    geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
+    geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
+    geometry_.metadata_max_size = metadata_max_size;
+    geometry_.metadata_slot_count = metadata_slot_count;
+    DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+                                         uint32_t attributes) {
+    if (name.empty()) {
+        LERROR << "Partition must have a non-empty name.";
+        return nullptr;
+    }
+    if (FindPartition(name)) {
+        LERROR << "Attempting to create duplication partition with name: " << name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+    return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
+    for (const auto& partition : partitions_) {
+        if (partition->name() == name) {
+            return partition.get();
+        }
+    }
+    return nullptr;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+        if ((*iter)->name() == name) {
+            partitions_.erase(iter);
+            return;
+        }
+    }
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
+    // Align the space needed up to the nearest sector.
+    uint64_t aligned_size = AlignToSector(requested_size);
+    if (partition->size() >= aligned_size) {
+        return true;
+    }
+
+    // Figure out how much we need to allocate.
+    uint64_t space_needed = aligned_size - partition->size();
+    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+    struct Interval {
+        uint64_t start;
+        uint64_t end;
+
+        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        bool operator<(const Interval& other) const { return start < other.start; }
+    };
+    std::vector<Interval> intervals;
+
+    // Collect all extents in the partition table.
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            intervals.emplace_back(linear->physical_sector(),
+                                   linear->physical_sector() + extent->num_sectors());
+        }
+    }
+
+    // Sort extents by starting sector.
+    std::sort(intervals.begin(), intervals.end());
+
+    // Find gaps that we can use for new extents. Note we store new extents in a
+    // temporary vector, and only commit them if we are guaranteed enough free
+    // space.
+    std::vector<std::unique_ptr<LinearExtent>> new_extents;
+    for (size_t i = 1; i < intervals.size(); i++) {
+        const Interval& previous = intervals[i - 1];
+        const Interval& current = intervals[i];
+
+        if (previous.end >= current.start) {
+            // There is no gap between these two extents, try the next one. Note that
+            // extents may never overlap, but just for safety, we ignore them if they
+            // do.
+            DCHECK(previous.end == current.start);
+            continue;
+        }
+
+        // This gap is enough to hold the remainder of the space requested, so we
+        // can allocate what we need and return.
+        if (current.start - previous.end >= sectors_needed) {
+            auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
+            sectors_needed -= extent->num_sectors();
+            new_extents.push_back(std::move(extent));
+            break;
+        }
+
+        // This gap is not big enough to fit the remainder of the space requested,
+        // so consume the whole thing and keep looking for more.
+        auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
+        sectors_needed -= extent->num_sectors();
+        new_extents.push_back(std::move(extent));
+    }
+
+    // If we still have more to allocate, take it from the remaining free space
+    // in the allocatable region.
+    if (sectors_needed) {
+        uint64_t first_sector;
+        if (intervals.empty()) {
+            first_sector = geometry_.first_logical_sector;
+        } else {
+            first_sector = intervals.back().end;
+        }
+        DCHECK(first_sector <= geometry_.last_logical_sector);
+
+        // Note: the last usable sector is inclusive.
+        if (first_sector + sectors_needed > geometry_.last_logical_sector) {
+            LERROR << "Not enough free space to expand partition: " << partition->name();
+            return false;
+        }
+        auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
+        new_extents.push_back(std::move(extent));
+    }
+
+    for (auto& extent : new_extents) {
+        partition->AddExtent(std::move(extent));
+    }
+    return true;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
+    partition->ShrinkTo(requested_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    metadata->header = header_;
+    metadata->geometry = geometry_;
+
+    // Flatten the partition and extent structures into an LpMetadata, which
+    // makes it very easy to validate, serialize, or pass on to device-mapper.
+    for (const auto& partition : partitions_) {
+        LpMetadataPartition part;
+        memset(&part, 0, sizeof(part));
+
+        if (partition->name().size() > sizeof(part.name)) {
+            LERROR << "Partition name is too long: " << partition->name();
+            return nullptr;
+        }
+        if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+            LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+            return nullptr;
+        }
+
+        strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+        if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
+            LERROR << "Could not parse guid " << partition->guid() << " for partition "
+                   << partition->name();
+            return nullptr;
+        }
+
+        part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+        part.num_extents = static_cast<uint32_t>(partition->extents().size());
+        part.attributes = partition->attributes();
+
+        for (const auto& extent : partition->extents()) {
+            extent->AddTo(metadata.get());
+        }
+        metadata->partitions.push_back(part);
+    }
+
+    metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+    metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+    return metadata;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
new file mode 100644
index 0000000..fb982e2
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,169 @@
+//
+// Copyright (C) 2018 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.
+//
+
+#ifndef LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+  public:
+    explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+    virtual ~Extent() {}
+
+    virtual void AddTo(LpMetadata* out) const = 0;
+    virtual LinearExtent* AsLinearExtent() { return nullptr; }
+
+    uint64_t num_sectors() const { return num_sectors_; }
+    void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+  protected:
+    uint64_t num_sectors_;
+};
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+  public:
+    LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
+        : Extent(num_sectors), physical_sector_(physical_sector) {}
+
+    void AddTo(LpMetadata* metadata) const override;
+    LinearExtent* AsLinearExtent() override { return this; }
+
+    uint64_t physical_sector() const { return physical_sector_; }
+
+  private:
+    uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+  public:
+    explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+    void AddTo(LpMetadata* out) const override;
+};
+
+class Partition final {
+  public:
+    Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+    // Add a raw extent.
+    void AddExtent(std::unique_ptr<Extent>&& extent);
+
+    // Remove all extents from this partition.
+    void RemoveExtents();
+
+    // Remove and/or shrink extents until the partition is the requested size.
+    // See MetadataBuilder::ShrinkPartition for more information.
+    void ShrinkTo(uint64_t requested_size);
+
+    const std::string& name() const { return name_; }
+    uint32_t attributes() const { return attributes_; }
+    const std::string& guid() const { return guid_; }
+    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+    uint64_t size() const { return size_; }
+
+  private:
+    std::string name_;
+    std::string guid_;
+    std::vector<std::unique_ptr<Extent>> extents_;
+    uint32_t attributes_;
+    uint64_t size_;
+};
+
+class MetadataBuilder {
+  public:
+    // Construct an empty logical partition table builder. The block device size
+    // and maximum metadata size must be specified, as this will determine which
+    // areas of the physical partition can be flashed for metadata vs for logical
+    // partitions.
+    //
+    // If the parameters would yield invalid metadata, nullptr is returned. This
+    // could happen if the block device size is too small to store the metadata
+    // and backup copies.
+    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count);
+
+    // Import an existing table for modification. If the table is not valid, for
+    // example it contains duplicate partition names, then nullptr is returned.
+    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+    // Export metadata so it can be serialized to an image, to disk, or mounted
+    // via device-mapper.
+    std::unique_ptr<LpMetadata> Export();
+
+    // Add a partition, returning a handle so it can be sized as needed. If a
+    // partition with the given name already exists, nullptr is returned.
+    Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+    // Delete a partition by name if it exists.
+    void RemovePartition(const std::string& name);
+
+    // Find a partition by name. If no partition is found, nullptr is returned.
+    Partition* FindPartition(const std::string& name);
+
+    // Grow a partition to the requested size. If the partition's size is already
+    // greater or equal to the requested size, this will return true and the
+    // partition table will not be changed. Otherwise, a greedy algorithm is
+    // used to find free gaps in the partition table and allocate them for this
+    // partition. If not enough space can be allocated, false is returned, and
+    // the parition table will not be modified.
+    //
+    // The size will be rounded UP to the nearest sector.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    bool GrowPartition(Partition* partition, uint64_t requested_size);
+
+    // Shrink a partition to the requested size. If the partition is already
+    // smaller than the given size, this will return and the partition table
+    // will not be changed. Otherwise, extents will be removed and/or shrunk
+    // from the end of the partition until it is the requested size.
+    //
+    // The size will be rounded UP to the nearest sector.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    void ShrinkPartition(Partition* partition, uint64_t requested_size);
+
+  private:
+    MetadataBuilder();
+    bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const LpMetadata& metadata);
+
+    LpMetadataGeometry geometry_;
+    LpMetadataHeader header_;
+    std::vector<std::unique_ptr<Partition>> partitions_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
new file mode 100644
index 0000000..6a2c655
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MINOR_VERSION 0
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_READONLY 0x1
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ *     +--------------------+
+ *     | Disk Geometry      |
+ *     +--------------------+
+ *     | Metadata           |
+ *     +--------------------+
+ *     | Logical Partitions |
+ *     +--------------------+
+ *     | Backup Metadata    |
+ *     +--------------------+
+ *     | Geometry Backup    |
+ *     +--------------------+
+ */
+#define LP_METADATA_PARTITION_NAME "android"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* This structure is stored at sector 0 in the first 4096 bytes of the
+ * partition, and again in the very last 4096 bytes. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+    uint32_t magic;
+
+    /*  4: Size of the LpMetadataGeometry struct. */
+    uint32_t struct_size;
+
+    /*  8: SHA256 checksum of this struct, with this field set to 0. */
+    uint8_t checksum[32];
+
+    /* 40: Maximum amount of space a single copy of the metadata can use. */
+    uint32_t metadata_max_size;
+
+    /* 44: Number of copies of the metadata to keep. For A/B devices, this
+     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+     * it will be 1. A backup copy of each slot is kept, so if this is "2",
+     * there will be four copies total.
+     */
+    uint32_t metadata_slot_count;
+
+    /* 48: First usable sector for allocating logical partitions. this will be
+     * the first sector after the initial 4096 geometry block, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count.
+     */
+    uint64_t first_logical_sector;
+
+    /* 56: Last usable sector, inclusive, for allocating logical partitions.
+     * At the end of this sector will follow backup metadata slots and the
+     * backup geometry block at the very end.
+     */
+    uint64_t last_logical_sector;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+    /*  0: Location of the table, relative to the metadata header. */
+    uint32_t offset;
+    /*  4: Number of entries in the table. */
+    uint32_t num_entries;
+    /*  8: Size of each entry in the table, in bytes. */
+    uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ *  +-----------------------------------------+
+ *  | Header data - fixed size                |
+ *  +-----------------------------------------+
+ *  | Partition table - variable size         |
+ *  +-----------------------------------------+
+ *  | Partition table extents - variable size |
+ *  +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+    uint32_t magic;
+
+    /*  4: Version number required to read this metadata. If the version is not
+     * equal to the library version, the metadata should be considered
+     * incompatible.
+     */
+    uint16_t major_version;
+
+    /*  6: Minor version. A library supporting newer features should be able to
+     * read metadata with an older minor version. However, an older library
+     * should not support reading metadata if its minor version is higher.
+     */
+    uint16_t minor_version;
+
+    /*  8: The size of this header struct. */
+    uint32_t header_size;
+
+    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+     * if this field were set to 0.
+     */
+    uint8_t header_checksum[32];
+
+    /* 44: The total size of all tables. This size is contiguous; tables may not
+     * have gaps in between, and they immediately follow the header.
+     */
+    uint32_t tables_size;
+
+    /* 48: SHA256 checksum of all table contents. */
+    uint8_t tables_checksum[32];
+
+    /* 80: Partition table descriptor. */
+    LpMetadataTableDescriptor partitions;
+    /* 92: Extent table descriptor. */
+    LpMetadataTableDescriptor extents;
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+    /*  0: Name of this partition in ASCII characters. Any unused characters in
+     * the buffer must be set to 0. Characters may only be alphanumeric or _.
+     * The name must include at least one ASCII character, and it must be unique
+     * across all partition names. The length (36) is the same as the maximum
+     * length of a GPT partition name.
+     */
+    char name[36];
+
+    /* 36: Globally unique identifier (GUID) of this partition. */
+    uint8_t guid[16];
+
+    /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    uint32_t attributes;
+
+    /* 56: Index of the first extent owned by this partition. The extent will
+     * start at logical sector 0. Gaps between extents are not allowed.
+     */
+    uint32_t first_extent_index;
+
+    /* 60: Number of extents in the partition. Every partition must have at
+     * least one extent.
+     */
+    uint32_t num_extents;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+    /*  0: Length of this extent, in 512-byte sectors. */
+    uint64_t num_sectors;
+
+    /*  8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+    uint32_t target_type;
+
+    /* 12: Contents depends on target_type.
+     *
+     * LINEAR: The sector on the physical partition that this extent maps onto.
+     * ZERO: This field must be 0.
+     */
+    uint64_t target_data;
+} __attribute__((packed)) LpMetadataExtent;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#ifdef __cplusplus
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+    LpMetadataGeometry geometry;
+    LpMetadataHeader header;
+    std::vector<LpMetadataPartition> partitions;
+    std::vector<LpMetadataExtent> extents;
+};
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+}  // namespace fs_mgr
+}  // namespace android
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/include/liblp/reader.h
new file mode 100644
index 0000000..e7fa46d
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/reader.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+
+// Read and validate the logical partition geometry from a block device.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
+
+// Read logical partition metadata from an image file that was created with
+// WriteToImageFile().
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
new file mode 100644
index 0000000..02fb21f
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/writer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// When flashing the initial logical partition layout, we also write geometry
+// information at the start and end of the big physical partition. This helps
+// locate metadata and backup metadata in the case of corruption or a failed
+// update. For normal changes to the metadata, we never modify the geometry.
+enum class SyncMode {
+    // Write geometry information.
+    Flash,
+    // Normal update of a single slot.
+    Update
+};
+
+// Write the given partition table to the given block device, writing only
+// copies according to the given sync mode.
+//
+// This will perform some verification, such that the device has enough space
+// to store the metadata as well as all of its extents.
+//
+// The slot number indicates which metadata slot to use.
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+                         uint32_t slot_number);
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..328fe37
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2018 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 "liblp/reader.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+    static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+    memcpy(geometry, buffer, sizeof(*geometry));
+
+    // Check the magic signature.
+    if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+        LERROR << "Logical partition metadata has invalid geometry magic signature.";
+        return false;
+    }
+    // Recompute and check the CRC32.
+    {
+        LpMetadataGeometry temp = *geometry;
+        memset(&temp.checksum, 0, sizeof(temp.checksum));
+        SHA256(&temp, sizeof(temp), temp.checksum);
+        if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid geometry checksum.";
+            return false;
+        }
+    }
+    // Check that the struct size is equal (this will have to change if we ever
+    // change the struct size in a release).
+    if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has invalid struct size.";
+        return false;
+    }
+    if (geometry->metadata_slot_count == 0) {
+        LERROR << "Logical partition metadata has invalid slot count.";
+        return false;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    int64_t end_of_metadata =
+            GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
+            geometry->metadata_max_size;
+    if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return false;
+    }
+    return true;
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+static bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+    // Read the first 4096 bytes.
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+        return false;
+    }
+    if (ParseGeometry(buffer.get(), geometry)) {
+        return true;
+    }
+
+    // Try the backup copy in the last 4096 bytes.
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+               << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+// Helper function to read geometry from a device without an open descriptor.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
+    android::base::unique_fd fd(open(block_device, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return ReadLogicalPartitionGeometry(fd, geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+                                const LpMetadataTableDescriptor& table) {
+    if (table.offset > header.tables_size) {
+        return false;
+    }
+    uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+    if (header.tables_size - table.offset < table_size) {
+        return false;
+    }
+    return true;
+}
+
+static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
+    // To compute the header's checksum, we have to temporarily set its checksum
+    // field to 0.
+    {
+        LpMetadataHeader temp = header;
+        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+        SHA256(&temp, sizeof(temp), temp.header_checksum);
+        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid checksum.";
+            return false;
+        }
+    }
+
+    // Do basic validation of key metadata bits.
+    if (header.magic != LP_METADATA_HEADER_MAGIC) {
+        LERROR << "Logical partition metadata has invalid magic value.";
+        return false;
+    }
+    // Check that the version is compatible.
+    if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+        header.minor_version > LP_METADATA_MINOR_VERSION) {
+        LERROR << "Logical partition metadata has incompatible version.";
+        return false;
+    }
+    if (!ValidateTableBounds(header, header.partitions) ||
+        !ValidateTableBounds(header, header.extents)) {
+        LERROR << "Logical partition metadata has invalid table bounds.";
+        return false;
+    }
+    // Check that table entry sizes can accomodate their respective structs. If
+    // table sizes change, these checks will have to be adjusted.
+    if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+        LERROR << "Logical partition metadata has invalid partition table entry size.";
+        return false;
+    }
+    if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+        LERROR << "Logical partition metadata has invalid extent table entry size.";
+        return false;
+    }
+    return true;
+}
+
+using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+    // First read and validate the header.
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+        return nullptr;
+    }
+    if (!ValidateMetadataHeader(metadata->header)) {
+        return nullptr;
+    }
+
+    LpMetadataHeader& header = metadata->header;
+
+    // Read the metadata payload. Allocation is fallible in case the metadata is
+    // corrupt and has some huge value.
+    std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+    if (!buffer) {
+        LERROR << "Out of memory reading logical partition tables.";
+        return nullptr;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+        return nullptr;
+    }
+
+    uint8_t checksum[32];
+    SHA256(buffer.get(), header.tables_size, checksum);
+    if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+        LERROR << "Logical partition metadata has invalid table checksum.";
+        return nullptr;
+    }
+
+    // ValidateTableSize ensured that |cursor| is valid for the number of
+    // entries in the table.
+    uint8_t* cursor = buffer.get() + header.partitions.offset;
+    for (size_t i = 0; i < header.partitions.num_entries; i++) {
+        LpMetadataPartition partition;
+        memcpy(&partition, cursor, sizeof(partition));
+        cursor += header.partitions.entry_size;
+
+        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+            LERROR << "Logical partition has invalid attribute set.";
+            return nullptr;
+        }
+        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+            LERROR << "Logical partition has invalid extent list.";
+            return nullptr;
+        }
+
+        metadata->partitions.push_back(partition);
+    }
+
+    cursor = buffer.get() + header.extents.offset;
+    for (size_t i = 0; i < header.extents.num_entries; i++) {
+        LpMetadataExtent extent;
+        memcpy(&extent, cursor, sizeof(extent));
+        cursor += header.extents.entry_size;
+
+        metadata->extents.push_back(extent);
+    }
+
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return nullptr;
+    }
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+
+    // First try the primary copy.
+    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    if (std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd)) {
+        return metadata;
+    }
+
+    // Next try the backup copy.
+    offset = GetBackupMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+    android::base::unique_fd fd(open(file, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return nullptr;
+    }
+
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+        return nullptr;
+    }
+    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+    if (!metadata) {
+        return nullptr;
+    }
+    metadata->geometry = geometry;
+    return metadata;
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+    // If the end of the buffer has a null character, it's safe to assume the
+    // buffer is null terminated. Otherwise, we cap the string to the input
+    // buffer size.
+    if (name[buffer_size - 1] == '\0') {
+        return std::string(name);
+    }
+    return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+    return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..217d802
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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 <fcntl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+    struct stat s;
+    if (fstat(fd, &s) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "fstat failed";
+        return false;
+    }
+
+    if (S_ISBLK(s.st_mode)) {
+        return get_block_device_size(fd);
+    }
+
+    int64_t result = SeekFile64(fd, 0, SEEK_END);
+    if (result == -1) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        return false;
+    }
+
+    *size = result;
+    return true;
+}
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+    static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+    return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+
+    int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+    CHECK(offset + geometry.metadata_max_size <=
+          int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+    return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    SHA256_Update(&c, data, length);
+    SHA256_Final(out, &c);
+}
+
+std::string GetPartitionGuid(const LpMetadataPartition& partition) {
+    // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
+    // macro to assist with buffer sizing.
+    static const size_t kGuidLen = 36;
+    char buffer[kGuidLen + 1];
+    uuid_unparse(partition.guid, buffer);
+    return buffer;
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+    if (suffix.empty()) {
+        return 0;
+    }
+    if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+        LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+               << "' does not have a recognized format.";
+        return 0;
+    }
+    return suffix[1] - 'a';
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
new file mode 100644
index 0000000..09ed314
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "liblp/metadata_format.h"
+
+#define LP_TAG "[liblp]"
+#define LERROR LOG(ERROR) << LP_TAG
+#define PERROR PLOG(ERROR) << LP_TAG
+
+namespace android {
+namespace fs_mgr {
+
+// Determine the size of a block device (or file). Logs and returns false on
+// error. After calling this, the position of |fd| may have changed.
+bool GetDescriptorSize(int fd, uint64_t* size);
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_UTILITY_H
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..6a9c124
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2007 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 <inttypes.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "liblp/reader.h"
+#include "liblp/writer.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+static std::string SerializeGeometry(const LpMetadataGeometry& input) {
+    LpMetadataGeometry geometry = input;
+    memset(geometry.checksum, 0, sizeof(geometry.checksum));
+    SHA256(&geometry, sizeof(geometry), geometry.checksum);
+    return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+    return g1.metadata_max_size == g2.metadata_max_size &&
+           g1.metadata_slot_count == g2.metadata_slot_count &&
+           g1.first_logical_sector == g2.first_logical_sector &&
+           g1.last_logical_sector == g2.last_logical_sector;
+}
+
+static std::string SerializeMetadata(const LpMetadata& input) {
+    LpMetadata metadata = input;
+    LpMetadataHeader& header = metadata.header;
+
+    // Serialize individual tables.
+    std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+                           metadata.partitions.size() * sizeof(LpMetadataPartition));
+    std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+                        metadata.extents.size() * sizeof(LpMetadataExtent));
+
+    // Compute positions of tables.
+    header.partitions.offset = 0;
+    header.extents.offset = header.partitions.offset + partitions.size();
+    header.tables_size = header.extents.offset + extents.size();
+
+    // Compute payload checksum.
+    std::string tables = partitions + extents;
+    SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+    // Compute header checksum.
+    memset(header.header_checksum, 0, sizeof(header.header_checksum));
+    SHA256(&header, sizeof(header), header.header_checksum);
+
+    std::string header_blob =
+            std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+    return header_blob + tables;
+}
+
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
+static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
+                                        uint64_t metadata_size) {
+    const LpMetadataHeader& header = metadata.header;
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    // Validate the usable sector range.
+    if (geometry.first_logical_sector > geometry.last_logical_sector) {
+        LERROR << "Logical partition metadata has invalid sector range.";
+        return false;
+    }
+    // Make sure we're writing within the space reserved.
+    if (metadata_size > geometry.metadata_max_size) {
+        LERROR << "Logical partition metadata is too large.";
+        return false;
+    }
+
+    // Make sure the device has enough space to store two backup copies of the
+    // metadata.
+    uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+                             uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    if (reserved_size > blockdevice_size ||
+        reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to store all logical partition metadata slots.";
+        return false;
+    }
+    if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to backup all logical partition metadata slots.";
+        return false;
+    }
+
+    // Make sure all partition entries reference valid extents.
+    for (const auto& partition : metadata.partitions) {
+        if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+            LERROR << "Partition references invalid extent.";
+            return false;
+        }
+    }
+
+    // Make sure all linear extents have a valid range.
+    for (const auto& extent : metadata.extents) {
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            uint64_t physical_sector = extent.target_data;
+            if (physical_sector < geometry.first_logical_sector ||
+                physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+                LERROR << "Extent table entry is out of bounds.";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+                         uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+
+    uint64_t size;
+    if (!GetDescriptorSize(fd, &size)) {
+        return false;
+    }
+
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    if (sync_mode != SyncMode::Flash) {
+        // Verify that the old geometry is identical. If it's not, then we've
+        // based this new metadata on invalid assumptions.
+        LpMetadataGeometry old_geometry;
+        if (!ReadLogicalPartitionGeometry(block_device, &old_geometry)) {
+            return false;
+        }
+        if (!CompareGeometry(geometry, old_geometry)) {
+            LERROR << "Incompatible geometry in new logical partition metadata";
+            return false;
+        }
+    }
+
+    // Make sure we're writing to a valid metadata slot.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string blob = SerializeMetadata(metadata);
+    if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
+        return false;
+    }
+
+    // First write geometry if this is a flash operation. It gets written to
+    // the first and last 4096-byte regions of the device.
+    if (sync_mode == SyncMode::Flash) {
+        std::string blob = SerializeGeometry(metadata.geometry);
+        if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+            return false;
+        }
+        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+            PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
+                   << " bytes failed: " << block_device;
+            return false;
+        }
+        if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+            return false;
+        }
+        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+            PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
+                   << " bytes failed: " << block_device;
+            return false;
+        }
+    }
+
+    // Write the primary copy of the metadata.
+    int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
+               << " bytes failed: " << block_device;
+        return false;
+    }
+
+    // Write the backup copy of the metadata.
+    int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
+    int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
+    if (abs_offset == (int64_t)-1) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+        return false;
+    }
+    if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+               << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
+               << " bytes failed: " << block_device;
+        return false;
+    }
+    return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+    std::string geometry = SerializeGeometry(input.geometry);
+    std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
+    std::string metadata = SerializeMetadata(input);
+
+    std::string everything = geometry + padding + metadata;
+
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed: " << file;
+        return false;
+    }
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
new file mode 100644
index 0000000..4d4aae4
--- /dev/null
+++ b/fs_mgr/tools/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_binary {
+    name: "dmctl",
+    srcs: ["dmctl.cpp"],
+
+    static_libs: [
+        "libfs_mgr",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
new file mode 100644
index 0000000..b40b83a
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTarget = ::android::dm::DmTarget;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+    std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+    std::cerr << "commands:" << std::endl;
+    std::cerr << "  create <dm-name> [<dm-target> [-lo <filename>] <dm-target-args>]" << std::endl;
+    std::cerr << "  delete <dm-name>" << std::endl;
+    std::cerr << "  list <devices | targets>" << std::endl;
+    std::cerr << "  help" << std::endl;
+    return -EINVAL;
+}
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+        return -EINVAL;
+    }
+
+    std::string name = argv[0];
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.CreateDevice(name)) {
+        std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+        return -EIO;
+    }
+
+    // if we also have target specified
+    if (argc > 1) {
+        // fall through for now. This will eventually create a DmTarget() based on the target name
+        // passing it the table that is specified at the command line
+    }
+
+    return 0;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+        return -EINVAL;
+    }
+
+    std::string name = argv[0];
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.DeleteDevice(name)) {
+        std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+    std::vector<DmTarget> targets;
+    if (!dm.GetAvailableTargets(&targets)) {
+        std::cerr << "Failed to read available device mapper targets" << std::endl;
+        return -errno;
+    }
+
+    std::cout << "Available Device Mapper Targets:" << std::endl;
+    if (targets.empty()) {
+        std::cout << "  <empty>" << std::endl;
+        return 0;
+    }
+
+    for (const auto& target : targets) {
+        std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
+                  << std::endl;
+    }
+
+    return 0;
+}
+
+static int DmListDevices(DeviceMapper& dm) {
+    std::vector<DmBlockDevice> devices;
+    if (!dm.GetAvailableDevices(&devices)) {
+        std::cerr << "Failed to read available device mapper devices" << std::endl;
+        return -errno;
+    }
+    std::cout << "Available Device Mapper Devices:" << std::endl;
+    if (devices.empty()) {
+        std::cout << "  <empty>" << std::endl;
+        return 0;
+    }
+
+    for (const auto& dev : devices) {
+        std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+                  << dev.Minor() << std::endl;
+    }
+
+    return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+        {"targets", DmListTargets},
+        {"devices", DmListDevices},
+};
+
+static int DmListCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    for (const auto& l : listmap) {
+        if (l.first == argv[0]) return l.second(dm);
+    }
+
+    std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
+    return -EINVAL;
+}
+
+static int HelpCmdHandler(int /* argc */, char** /* argv */) {
+    Usage();
+    return 0;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+        {"create", DmCreateCmdHandler},
+        {"delete", DmDeleteCmdHandler},
+        {"list", DmListCmdHandler},
+        {"help", HelpCmdHandler},
+};
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    if (argc < 2) {
+        return Usage();
+    }
+
+    for (const auto& cmd : cmdmap) {
+        if (cmd.first == argv[1]) {
+            return cmd.second(argc - 2, argv + 2);
+        }
+    }
+
+    return Usage();
+}
diff --git a/init/Android.bp b/init/Android.bp
index 25877c0..cf7637f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -67,18 +67,9 @@
         "libsquashfs_utils",
         "liblogwrap",
         "libext4_utils",
-        "libcutils",
-        "libbase",
-        "libc",
         "libseccomp_policy",
-        "libselinux",
-        "liblog",
         "libcrypto_utils",
-        "libcrypto",
-        "libc++_static",
-        "libdl",
         "libsparse",
-        "libz",
         "libprocessgroup",
         "libavb",
         "libkeyutils",
@@ -86,6 +77,17 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
     ],
+    shared_libs: [
+        "libcutils",
+        "libbase",
+        "libc",
+        "liblog",
+        "libcrypto",
+        "libc++",
+        "libdl",
+        "libz",
+        "libselinux",
+    ],
 }
 
 cc_library_static {
@@ -166,7 +168,6 @@
 cc_test {
     name: "init_tests",
     defaults: ["init_defaults"],
-    static_executable: true,
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
@@ -187,7 +188,6 @@
 
 cc_benchmark {
     name: "init_benchmarks",
-    static_executable: true,
     defaults: ["init_defaults"],
     srcs: [
         "subcontext_benchmark.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index c4a6a50..a81a0f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,7 +45,6 @@
 
 LOCAL_MODULE:= init
 
-LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
 
@@ -59,18 +58,9 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
-    libcutils \
-    libbase \
-    libc \
     libseccomp_policy \
-    libselinux \
-    liblog \
     libcrypto_utils \
-    libcrypto \
-    libc++_static \
-    libdl \
     libsparse \
-    libz \
     libprocessgroup \
     libavb \
     libkeyutils \
@@ -78,6 +68,26 @@
     libpropertyinfoserializer \
     libpropertyinfoparser \
 
+shared_libs := \
+    libcutils \
+    libbase \
+    liblog \
+    libcrypto \
+    libdl \
+    libz \
+    libselinux \
+
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+# init is static executable for non-system-as-root devices, because the dynamic linker
+# and shared libs are not available before /system is mounted, but init has to run
+# before the partition is mounted.
+LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+else
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
+endif
+shared_libs :=
+
 LOCAL_REQUIRED_MODULES := \
     e2fsdroid \
     mke2fs \
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
index faf6fc1..93e363f 100644
--- a/init/host_import_parser.cpp
+++ b/init/host_import_parser.cpp
@@ -23,22 +23,17 @@
 namespace android {
 namespace init {
 
-Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
-                                               const std::string& filename, int line) {
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+                                               int) {
     if (args.size() != 2) {
         return Error() << "single argument needed for import\n";
     }
 
-    auto import_path = args[1];
+    return Success();
+}
 
-    if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
-        StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
-        import_path = out_dir_ + "/" + import_path;
-    } else {
-        import_path = out_dir_ + "/root/" + import_path;
-    }
-
-    return ImportParser::ParseSection({"import", import_path}, filename, line);
+Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+    return Error() << "Unexpected line found after import statement";
 }
 
 }  // namespace init
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
index e2980b2..52b8891 100644
--- a/init/host_import_parser.h
+++ b/init/host_import_parser.h
@@ -19,21 +19,16 @@
 #include <string>
 #include <vector>
 
-#include "import_parser.h"
 #include "parser.h"
 
 namespace android {
 namespace init {
 
-class HostImportParser : public ImportParser {
+class HostImportParser : public SectionParser {
   public:
-    HostImportParser(const std::string& out_dir, Parser* parser)
-        : ImportParser(parser), out_dir_(out_dir) {}
-    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line) override;
-
-  private:
-    std::string out_dir_;
+    HostImportParser() {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
 };
 
 }  // namespace init
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index d6884af..8407729 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <pwd.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #include <iostream>
 #include <string>
@@ -45,11 +46,11 @@
 using android::base::ReadFileToString;
 using android::base::Split;
 
-static std::string out_dir;
+static std::string passwd_file;
 
 static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
     std::string passwd;
-    if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+    if (!ReadFileToString(passwd_file, &passwd)) {
         return {};
     }
 
@@ -102,6 +103,14 @@
         }
     }
 
+    unsigned int oem_uid;
+    if (sscanf(login, "oem_%u", &oem_uid) == 1) {
+        snprintf(static_name, sizeof(static_name), "%s", login);
+        static_passwd.pw_uid = oem_uid;
+        static_passwd.pw_gid = oem_uid;
+        return &static_passwd;
+    }
+
     errno = ENOENT;
     return nullptr;
 }
@@ -118,20 +127,14 @@
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::StdioLogger);
     android::base::SetMinimumLogSeverity(android::base::ERROR);
-    if (argc != 3) {
-        LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
-        return -1;
+
+    if (argc != 2 && argc != 3) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
+        return EXIT_FAILURE;
     }
 
-    out_dir = argv[1];
-
-    auto properties = Split(argv[2], ",");
-    for (const auto& property : properties) {
-        auto split_property = Split(property, "=");
-        if (split_property.size() != 2) {
-            continue;
-        }
-        property_set(split_property[0], split_property[1]);
+    if (argc == 3) {
+        passwd_file = argv[2];
     }
 
     const BuiltinFunctionMap function_map;
@@ -141,22 +144,23 @@
     Parser parser;
     parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-    parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+    parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
-    if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
-        LOG(ERROR) << "Failed to find root init.rc script";
-        return -1;
+    if (!parser.ParseConfigFileInsecure(argv[1])) {
+        LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+        return EXIT_FAILURE;
     }
     if (parser.parse_error_count() > 0) {
-        LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
-        return -1;
+        LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+                   << parser.parse_error_count() << " errors";
+        return EXIT_FAILURE;
     }
-    return 0;
+    return EXIT_SUCCESS;
 }
 
 }  // namespace init
 }  // namespace android
 
 int main(int argc, char** argv) {
-    android::init::main(argc, argv);
+    return android::init::main(argc, argv);
 }
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index db60ce1..2bc9f3a 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -29,6 +29,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <liblp/metadata_format.h>
 
 #include "devices.h"
 #include "fs_mgr.h"
@@ -75,6 +76,7 @@
 
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
     std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
+    std::string lp_metadata_partition_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
     std::unique_ptr<DeviceHandler> device_handler_;
@@ -120,13 +122,17 @@
 }
 
 static inline bool IsDmLinearEnabled() {
-    bool enabled = false;
-    import_kernel_cmdline(
-        false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
-            if (key == "androidboot.logical_partitions" && value == "1") {
-                enabled = true;
-            }
-        });
+    static bool checked = false;
+    static bool enabled = false;
+    if (checked) {
+        return enabled;
+    }
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+        if (key == "androidboot.logical_partitions" && value == "1") {
+            enabled = true;
+        }
+    });
+    checked = true;
     return enabled;
 }
 
@@ -163,7 +169,7 @@
 }
 
 bool FirstStageMount::DoFirstStageMount() {
-    if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
+    if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
         return true;
@@ -184,14 +190,18 @@
 
 bool FirstStageMount::GetBackingDmLinearDevices() {
     // Add any additional devices required for dm-linear mappings.
-    if (!dm_linear_table_) {
+    if (!IsDmLinearEnabled()) {
         return true;
     }
 
-    for (const auto& partition : dm_linear_table_->partitions) {
-        for (const auto& extent : partition.extents) {
-            const std::string& partition_name = android::base::Basename(extent.block_device());
-            required_devices_partition_names_.emplace(partition_name);
+    required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+
+    if (dm_linear_table_) {
+        for (const auto& partition : dm_linear_table_->partitions) {
+            for (const auto& extent : partition.extents) {
+                const std::string& partition_name = android::base::Basename(extent.block_device());
+                required_devices_partition_names_.emplace(partition_name);
+            }
         }
     }
     return true;
@@ -205,7 +215,7 @@
         return true;
     }
 
-    if (dm_linear_table_ || need_dm_verity_) {
+    if (IsDmLinearEnabled() || need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
@@ -253,10 +263,21 @@
 }
 
 bool FirstStageMount::CreateLogicalPartitions() {
-    if (!dm_linear_table_) {
+    if (!IsDmLinearEnabled()) {
         return true;
     }
-    return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+
+    if (lp_metadata_partition_.empty()) {
+        LOG(ERROR) << "Could not locate logical partition tables in partition "
+                   << LP_METADATA_PARTITION_NAME;
+        return false;
+    }
+    if (dm_linear_table_) {
+        if (!android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get())) {
+            return false;
+        }
+    }
+    return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
 }
 
 ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -266,6 +287,10 @@
     auto iter = required_devices_partition_names_.find(name);
     if (iter != required_devices_partition_names_.end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+        if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+            std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+            lp_metadata_partition_ = links[0];
+        }
         required_devices_partition_names_.erase(iter);
         device_handler_->HandleDeviceEvent(uevent);
         if (required_devices_partition_names_.empty()) {
diff --git a/init/parser.cpp b/init/parser.cpp
index 4f1cac4..fa0fd11 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -39,14 +40,13 @@
     line_callbacks_.emplace_back(prefix, callback);
 }
 
-void Parser::ParseData(const std::string& filename, const std::string& data) {
-    // TODO: Use a parser with const input and remove this copy
-    std::vector<char> data_copy(data.begin(), data.end());
-    data_copy.push_back('\0');
+void Parser::ParseData(const std::string& filename, std::string* data) {
+    data->push_back('\n');  // TODO: fix tokenizer
+    data->push_back('\0');
 
     parse_state state;
     state.line = 0;
-    state.ptr = &data_copy[0];
+    state.ptr = data->data();
     state.nexttoken = 0;
 
     SectionParser* section_parser = nullptr;
@@ -69,6 +69,11 @@
         switch (next_token(&state)) {
             case T_EOF:
                 end_section();
+
+                for (const auto& [section_name, section_parser] : section_parsers_) {
+                    section_parser->EndFile();
+                }
+
                 return;
             case T_NEWLINE: {
                 state.line++;
@@ -118,6 +123,16 @@
     }
 }
 
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+    std::string config_contents;
+    if (!android::base::ReadFileToString(path, &config_contents)) {
+        return false;
+    }
+
+    ParseData(path, &config_contents);
+    return true;
+}
+
 bool Parser::ParseConfigFile(const std::string& path) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
@@ -127,11 +142,7 @@
         return false;
     }
 
-    config_contents->push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, *config_contents);
-    for (const auto& [section_name, section_parser] : section_parsers_) {
-        section_parser->EndFile();
-    }
+    ParseData(path, &config_contents.value());
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
     return true;
diff --git a/init/parser.h b/init/parser.h
index 3501d8c..2454b6a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -75,10 +75,13 @@
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
+    // Host init verifier check file permissions.
+    bool ParseConfigFileInsecure(const std::string& path);
+
     size_t parse_error_count() const { return parse_error_count_; }
 
   private:
-    void ParseData(const std::string& filename, const std::string& data);
+    void ParseData(const std::string& filename, std::string* data);
     bool ParseConfigFile(const std::string& path);
     bool ParseConfigDir(const std::string& path);
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597a6bb..a8a9a12 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3180,113 +3180,6 @@
 }
 #endif  // USING_LOGGER_DEFAULT
 
-#ifdef USING_LOGGER_DEFAULT  // Do not retest event mapping functionality
-#ifdef __ANDROID__
-// must be: '<needle:> 0 kB'
-static bool isZero(const std::string& content, std::string::size_type pos,
-                   const char* needle) {
-  std::string::size_type offset = content.find(needle, pos);
-  return (offset != std::string::npos) &&
-         ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
-          std::string::npos) &&
-         (content.find_first_not_of('0', offset) != offset);
-}
-
-// must not be: '<needle:> 0 kB'
-static bool isNotZero(const std::string& content, std::string::size_type pos,
-                      const char* needle) {
-  std::string::size_type offset = content.find(needle, pos);
-  return (offset != std::string::npos) &&
-         ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
-          std::string::npos) &&
-         (content.find_first_not_of("123456789", offset) != offset);
-}
-
-static void event_log_tags_test_smap(pid_t pid) {
-  std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
-
-  std::string content;
-  if (!android::base::ReadFileToString(filename, &content)) return;
-
-  bool shared_ok = false;
-  bool private_ok = false;
-  bool anonymous_ok = false;
-  bool pass_ok = false;
-
-  static const char event_log_tags[] = "event-log-tags";
-  std::string::size_type pos = 0;
-  while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
-    pos += strlen(event_log_tags);
-
-    // must not be: 'Shared_Clean: 0 kB'
-    bool ok =
-        isNotZero(content, pos, "Shared_Clean:") ||
-        // If not /etc/event-log-tags, thus r/w, then half points
-        // back for not 'Shared_Dirty: 0 kB'
-        ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
-         isNotZero(content, pos, "Shared_Dirty:"));
-    if (ok && !pass_ok) {
-      shared_ok = true;
-    } else if (!ok) {
-      shared_ok = false;
-    }
-
-    // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
-    ok = isZero(content, pos, "Private_Dirty:") ||
-         isZero(content, pos, "Private_Clean:");
-    if (ok && !pass_ok) {
-      private_ok = true;
-    } else if (!ok) {
-      private_ok = false;
-    }
-
-    // must be: 'Anonymous: 0 kB'
-    ok = isZero(content, pos, "Anonymous:");
-    if (ok && !pass_ok) {
-      anonymous_ok = true;
-    } else if (!ok) {
-      anonymous_ok = false;
-    }
-
-    pass_ok = true;
-  }
-  content = "";
-
-  if (!pass_ok) return;
-  if (shared_ok && anonymous_ok && private_ok) return;
-
-  filename = android::base::StringPrintf("/proc/%d/comm", pid);
-  android::base::ReadFileToString(filename, &content);
-  content = android::base::StringPrintf(
-      "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
-
-  EXPECT_TRUE(IsOk(shared_ok, content));
-  EXPECT_TRUE(IsOk(private_ok, content));
-  EXPECT_TRUE(IsOk(anonymous_ok, content));
-}
-#endif  // __ANDROID__
-
-TEST(liblog, event_log_tags) {
-#ifdef __ANDROID__
-  std::unique_ptr<DIR, int (*)(DIR*)> proc_dir(opendir("/proc"), closedir);
-  ASSERT_FALSE(!proc_dir);
-
-  dirent* e;
-  while ((e = readdir(proc_dir.get()))) {
-    if (e->d_type != DT_DIR) continue;
-    if (!isdigit(e->d_name[0])) continue;
-    long long id = atoll(e->d_name);
-    if (id <= 0) continue;
-    pid_t pid = id;
-    if (id != pid) continue;
-    event_log_tags_test_smap(pid);
-  }
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif  // USING_LOGGER_DEFAULT
-
 #ifdef USING_LOGGER_DEFAULT  // Do not retest ratelimit
 TEST(liblog, __android_log_ratelimit) {
   time_t state = 0;
diff --git a/libsparse/.clang-format b/libsparse/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libsparse/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index c7c089f..8ad339f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -6,11 +6,11 @@
     recovery_available: true,
     unique_host_soname: true,
     srcs: [
-        "backed_block.c",
-        "output_file.c",
-        "sparse.c",
-        "sparse_crc32.c",
-        "sparse_err.c",
+        "backed_block.cpp",
+        "output_file.cpp",
+        "sparse.cpp",
+        "sparse_crc32.cpp",
+        "sparse_err.cpp",
         "sparse_read.cpp",
     ],
     cflags: ["-Werror"],
@@ -31,8 +31,8 @@
     name: "simg2img",
     host_supported: true,
     srcs: [
-        "simg2img.c",
-        "sparse_crc32.c",
+        "simg2img.cpp",
+        "sparse_crc32.cpp",
     ],
     static_libs: [
         "libsparse",
@@ -46,7 +46,7 @@
 cc_binary {
     name: "img2simg",
     host_supported: true,
-    srcs: ["img2simg.c"],
+    srcs: ["img2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
@@ -58,7 +58,7 @@
 
 cc_binary_host {
     name: "append2simg",
-    srcs: ["append2simg.c"],
+    srcs: ["append2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
diff --git a/libsparse/OWNERS b/libsparse/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libsparse/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
deleted file mode 100644
index eef8764..0000000
--- a/libsparse/append2simg.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-#include "sparse_file.h"
-#include "backed_block.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#endif
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
-    fprintf(stderr, "Usage: append2simg <output> <input>\n");
-}
-
-int main(int argc, char *argv[])
-{
-    int output;
-    int output_block;
-    char *output_path;
-    struct sparse_file *sparse_output;
-
-    int input;
-    char *input_path;
-    off64_t input_len;
-
-    int tmp_fd;
-    char *tmp_path;
-
-    int ret;
-
-    if (argc == 3) {
-        output_path = argv[1];
-        input_path = argv[2];
-    } else {
-        usage();
-        exit(-1);
-    }
-
-    ret = asprintf(&tmp_path, "%s.append2simg", output_path);
-    if (ret < 0) {
-        fprintf(stderr, "Couldn't allocate filename\n");
-        exit(-1);
-    }
-
-    output = open(output_path, O_RDWR | O_BINARY);
-    if (output < 0) {
-        fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    sparse_output = sparse_file_import_auto(output, false, true);
-    if (!sparse_output) {
-        fprintf(stderr, "Couldn't import output file\n");
-        exit(-1);
-    }
-
-    input = open(input_path, O_RDONLY | O_BINARY);
-    if (input < 0) {
-        fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    input_len = lseek64(input, 0, SEEK_END);
-    if (input_len < 0) {
-        fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
-        exit(-1);
-    } else if (input_len % sparse_output->block_size) {
-        fprintf(stderr, "Input file is not a multiple of the output file's block size");
-        exit(-1);
-    }
-    lseek64(input, 0, SEEK_SET);
-
-    output_block = sparse_output->len / sparse_output->block_size;
-    if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
-        fprintf(stderr, "Couldn't add input file\n");
-        exit(-1);
-    }
-    sparse_output->len += input_len;
-
-    tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
-    if (tmp_fd < 0) {
-        fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    lseek64(output, 0, SEEK_SET);
-    if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
-        fprintf(stderr, "Failed to write sparse file\n");
-        exit(-1);
-    }
-
-    sparse_file_destroy(sparse_output);
-    close(tmp_fd);
-    close(output);
-    close(input);
-
-    ret = rename(tmp_path, output_path);
-    if (ret < 0) {
-        fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    free(tmp_path);
-
-    exit(0);
-}
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
new file mode 100644
index 0000000..99f4339
--- /dev/null
+++ b/libsparse/append2simg.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "backed_block.h"
+#include "sparse_file.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int output;
+  int output_block;
+  char* output_path;
+  struct sparse_file* sparse_output;
+
+  int input;
+  char* input_path;
+  off64_t input_len;
+
+  int tmp_fd;
+  char* tmp_path;
+
+  int ret;
+
+  if (argc == 3) {
+    output_path = argv[1];
+    input_path = argv[2];
+  } else {
+    usage();
+    exit(-1);
+  }
+
+  ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+  if (ret < 0) {
+    fprintf(stderr, "Couldn't allocate filename\n");
+    exit(-1);
+  }
+
+  output = open(output_path, O_RDWR | O_BINARY);
+  if (output < 0) {
+    fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  sparse_output = sparse_file_import_auto(output, false, true);
+  if (!sparse_output) {
+    fprintf(stderr, "Couldn't import output file\n");
+    exit(-1);
+  }
+
+  input = open(input_path, O_RDONLY | O_BINARY);
+  if (input < 0) {
+    fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  input_len = lseek64(input, 0, SEEK_END);
+  if (input_len < 0) {
+    fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+    exit(-1);
+  } else if (input_len % sparse_output->block_size) {
+    fprintf(stderr, "Input file is not a multiple of the output file's block size");
+    exit(-1);
+  }
+  lseek64(input, 0, SEEK_SET);
+
+  output_block = sparse_output->len / sparse_output->block_size;
+  if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+    fprintf(stderr, "Couldn't add input file\n");
+    exit(-1);
+  }
+  sparse_output->len += input_len;
+
+  tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+  if (tmp_fd < 0) {
+    fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  lseek64(output, 0, SEEK_SET);
+  if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+    fprintf(stderr, "Failed to write sparse file\n");
+    exit(-1);
+  }
+
+  sparse_file_destroy(sparse_output);
+  close(tmp_fd);
+  close(output);
+  close(input);
+
+  ret = rename(tmp_path, output_path);
+  if (ret < 0) {
+    fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  free(tmp_path);
+
+  exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
deleted file mode 100644
index 794cd6b..0000000
--- a/libsparse/backed_block.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2010 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 <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "backed_block.h"
-#include "sparse_defs.h"
-
-struct backed_block {
-	unsigned int block;
-	unsigned int len;
-	enum backed_block_type type;
-	union {
-		struct {
-			void *data;
-		} data;
-		struct {
-			char *filename;
-			int64_t offset;
-		} file;
-		struct {
-			int fd;
-			int64_t offset;
-		} fd;
-		struct {
-			uint32_t val;
-		} fill;
-	};
-	struct backed_block *next;
-};
-
-struct backed_block_list {
-	struct backed_block *data_blocks;
-	struct backed_block *last_used;
-	unsigned int block_size;
-};
-
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
-{
-	return bbl->data_blocks;
-}
-
-struct backed_block *backed_block_iter_next(struct backed_block *bb)
-{
-	return bb->next;
-}
-
-unsigned int backed_block_len(struct backed_block *bb)
-{
-	return bb->len;
-}
-
-unsigned int backed_block_block(struct backed_block *bb)
-{
-	return bb->block;
-}
-
-void *backed_block_data(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_DATA);
-	return bb->data.data;
-}
-
-const char *backed_block_filename(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILE);
-	return bb->file.filename;
-}
-
-int backed_block_fd(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FD);
-	return bb->fd.fd;
-}
-
-int64_t backed_block_file_offset(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
-	if (bb->type == BACKED_BLOCK_FILE) {
-		return bb->file.offset;
-	} else { /* bb->type == BACKED_BLOCK_FD */
-		return bb->fd.offset;
-	}
-}
-
-uint32_t backed_block_fill_val(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILL);
-	return bb->fill.val;
-}
-
-enum backed_block_type backed_block_type(struct backed_block *bb)
-{
-	return bb->type;
-}
-
-void backed_block_destroy(struct backed_block *bb)
-{
-	if (bb->type == BACKED_BLOCK_FILE) {
-		free(bb->file.filename);
-	}
-
-	free(bb);
-}
-
-struct backed_block_list *backed_block_list_new(unsigned int block_size)
-{
-	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
-	b->block_size = block_size;
-	return b;
-}
-
-void backed_block_list_destroy(struct backed_block_list *bbl)
-{
-	if (bbl->data_blocks) {
-		struct backed_block *bb = bbl->data_blocks;
-		while (bb) {
-			struct backed_block *next = bb->next;
-			backed_block_destroy(bb);
-			bb = next;
-		}
-	}
-
-	free(bbl);
-}
-
-void backed_block_list_move(struct backed_block_list *from,
-		struct backed_block_list *to, struct backed_block *start,
-		struct backed_block *end)
-{
-	struct backed_block *bb;
-
-	if (start == NULL) {
-		start = from->data_blocks;
-	}
-
-	if (!end) {
-		for (end = start; end && end->next; end = end->next)
-			;
-	}
-
-	if (start == NULL || end == NULL) {
-		return;
-	}
-
-	from->last_used = NULL;
-	to->last_used = NULL;
-	if (from->data_blocks == start) {
-		from->data_blocks = end->next;
-	} else {
-		for (bb = from->data_blocks; bb; bb = bb->next) {
-			if (bb->next == start) {
-				bb->next = end->next;
-				break;
-			}
-		}
-	}
-
-	if (!to->data_blocks) {
-		to->data_blocks = start;
-		end->next = NULL;
-	} else {
-		for (bb = to->data_blocks; bb; bb = bb->next) {
-			if (!bb->next || bb->next->block > start->block) {
-				end->next = bb->next;
-				bb->next = start;
-				break;
-			}
-		}
-	}
-}
-
-/* may free b */
-static int merge_bb(struct backed_block_list *bbl,
-		struct backed_block *a, struct backed_block *b)
-{
-	unsigned int block_len;
-
-	/* Block doesn't exist (possible if one block is the last block) */
-	if (!a || !b) {
-		return -EINVAL;
-	}
-
-	assert(a->block < b->block);
-
-	/* Blocks are of different types */
-	if (a->type != b->type) {
-		return -EINVAL;
-	}
-
-	/* Blocks are not adjacent */
-	block_len = a->len / bbl->block_size; /* rounds down */
-	if (a->block + block_len != b->block) {
-		return -EINVAL;
-	}
-
-	switch (a->type) {
-	case BACKED_BLOCK_DATA:
-		/* Don't support merging data for now */
-		return -EINVAL;
-	case BACKED_BLOCK_FILL:
-		if (a->fill.val != b->fill.val) {
-			return -EINVAL;
-		}
-		break;
-	case BACKED_BLOCK_FILE:
-		/* Already make sure b->type is BACKED_BLOCK_FILE */
-		if (strcmp(a->file.filename, b->file.filename) ||
-				a->file.offset + a->len != b->file.offset) {
-			return -EINVAL;
-		}
-		break;
-	case BACKED_BLOCK_FD:
-		if (a->fd.fd != b->fd.fd ||
-				a->fd.offset + a->len != b->fd.offset) {
-			return -EINVAL;
-		}
-		break;
-	}
-
-	/* Blocks are compatible and adjacent, with a before b.  Merge b into a,
-	 * and free b */
-	a->len += b->len;
-	a->next = b->next;
-
-	backed_block_destroy(b);
-
-	return 0;
-}
-
-static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
-{
-	struct backed_block *bb;
-
-	if (bbl->data_blocks == NULL) {
-		bbl->data_blocks = new_bb;
-		return 0;
-	}
-
-	if (bbl->data_blocks->block > new_bb->block) {
-		new_bb->next = bbl->data_blocks;
-		bbl->data_blocks = new_bb;
-		return 0;
-	}
-
-	/* Optimization: blocks are mostly queued in sequence, so save the
-	   pointer to the last bb that was added, and start searching from
-	   there if the next block number is higher */
-	if (bbl->last_used && new_bb->block > bbl->last_used->block)
-		bb = bbl->last_used;
-	else
-		bb = bbl->data_blocks;
-	bbl->last_used = new_bb;
-
-	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
-		;
-
-	if (bb->next == NULL) {
-		bb->next = new_bb;
-	} else {
-		new_bb->next = bb->next;
-		bb->next = new_bb;
-	}
-
-	merge_bb(bbl, new_bb, new_bb->next);
-	if (!merge_bb(bbl, bb, new_bb)) {
-		/* new_bb destroyed, point to retained as last_used */
-		bbl->last_used = bb;
-	}
-
-	return 0;
-}
-
-/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FILL;
-	bb->fill.val = fill_val;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_DATA;
-	bb->data.data = data;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a file on disk to be written to the specified data blocks */
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
-		int64_t offset, unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FILE;
-	bb->file.filename = strdup(filename);
-	bb->file.offset = offset;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FD;
-	bb->fd.fd = fd;
-	bb->fd.offset = offset;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
-		unsigned int max_len)
-{
-	struct backed_block *new_bb;
-
-	max_len = ALIGN_DOWN(max_len, bbl->block_size);
-
-	if (bb->len <= max_len) {
-		return 0;
-	}
-
-	new_bb = malloc(sizeof(struct backed_block));
-	if (new_bb == NULL) {
-		return -ENOMEM;
-	}
-
-	*new_bb = *bb;
-
-	new_bb->len = bb->len - max_len;
-	new_bb->block = bb->block + max_len / bbl->block_size;
-	new_bb->next = bb->next;
-	bb->next = new_bb;
-	bb->len = max_len;
-
-	switch (bb->type) {
-	case BACKED_BLOCK_DATA:
-		new_bb->data.data = (char *)bb->data.data + max_len;
-		break;
-	case BACKED_BLOCK_FILE:
-		new_bb->file.offset += max_len;
-		break;
-	case BACKED_BLOCK_FD:
-		new_bb->fd.offset += max_len;
-		break;
-	case BACKED_BLOCK_FILL:
-		break;
-	}
-
-	return 0;
-}
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
new file mode 100644
index 0000000..7f5632e
--- /dev/null
+++ b/libsparse/backed_block.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+  unsigned int block;
+  unsigned int len;
+  enum backed_block_type type;
+  union {
+    struct {
+      void* data;
+    } data;
+    struct {
+      char* filename;
+      int64_t offset;
+    } file;
+    struct {
+      int fd;
+      int64_t offset;
+    } fd;
+    struct {
+      uint32_t val;
+    } fill;
+  };
+  struct backed_block* next;
+};
+
+struct backed_block_list {
+  struct backed_block* data_blocks;
+  struct backed_block* last_used;
+  unsigned int block_size;
+};
+
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {
+  return bbl->data_blocks;
+}
+
+struct backed_block* backed_block_iter_next(struct backed_block* bb) {
+  return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block* bb) {
+  return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block* bb) {
+  return bb->block;
+}
+
+void* backed_block_data(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_DATA);
+  return bb->data.data;
+}
+
+const char* backed_block_filename(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILE);
+  return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FD);
+  return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+  if (bb->type == BACKED_BLOCK_FILE) {
+    return bb->file.offset;
+  } else { /* bb->type == BACKED_BLOCK_FD */
+    return bb->fd.offset;
+  }
+}
+
+uint32_t backed_block_fill_val(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILL);
+  return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block* bb) {
+  return bb->type;
+}
+
+void backed_block_destroy(struct backed_block* bb) {
+  if (bb->type == BACKED_BLOCK_FILE) {
+    free(bb->file.filename);
+  }
+
+  free(bb);
+}
+
+struct backed_block_list* backed_block_list_new(unsigned int block_size) {
+  struct backed_block_list* b =
+      reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));
+  b->block_size = block_size;
+  return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list* bbl) {
+  if (bbl->data_blocks) {
+    struct backed_block* bb = bbl->data_blocks;
+    while (bb) {
+      struct backed_block* next = bb->next;
+      backed_block_destroy(bb);
+      bb = next;
+    }
+  }
+
+  free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+                            struct backed_block* start, struct backed_block* end) {
+  struct backed_block* bb;
+
+  if (start == NULL) {
+    start = from->data_blocks;
+  }
+
+  if (!end) {
+    for (end = start; end && end->next; end = end->next)
+      ;
+  }
+
+  if (start == NULL || end == NULL) {
+    return;
+  }
+
+  from->last_used = NULL;
+  to->last_used = NULL;
+  if (from->data_blocks == start) {
+    from->data_blocks = end->next;
+  } else {
+    for (bb = from->data_blocks; bb; bb = bb->next) {
+      if (bb->next == start) {
+        bb->next = end->next;
+        break;
+      }
+    }
+  }
+
+  if (!to->data_blocks) {
+    to->data_blocks = start;
+    end->next = NULL;
+  } else {
+    for (bb = to->data_blocks; bb; bb = bb->next) {
+      if (!bb->next || bb->next->block > start->block) {
+        end->next = bb->next;
+        bb->next = start;
+        break;
+      }
+    }
+  }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {
+  unsigned int block_len;
+
+  /* Block doesn't exist (possible if one block is the last block) */
+  if (!a || !b) {
+    return -EINVAL;
+  }
+
+  assert(a->block < b->block);
+
+  /* Blocks are of different types */
+  if (a->type != b->type) {
+    return -EINVAL;
+  }
+
+  /* Blocks are not adjacent */
+  block_len = a->len / bbl->block_size; /* rounds down */
+  if (a->block + block_len != b->block) {
+    return -EINVAL;
+  }
+
+  switch (a->type) {
+    case BACKED_BLOCK_DATA:
+      /* Don't support merging data for now */
+      return -EINVAL;
+    case BACKED_BLOCK_FILL:
+      if (a->fill.val != b->fill.val) {
+        return -EINVAL;
+      }
+      break;
+    case BACKED_BLOCK_FILE:
+      /* Already make sure b->type is BACKED_BLOCK_FILE */
+      if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {
+        return -EINVAL;
+      }
+      break;
+    case BACKED_BLOCK_FD:
+      if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {
+        return -EINVAL;
+      }
+      break;
+  }
+
+  /* Blocks are compatible and adjacent, with a before b.  Merge b into a,
+   * and free b */
+  a->len += b->len;
+  a->next = b->next;
+
+  backed_block_destroy(b);
+
+  return 0;
+}
+
+static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
+  struct backed_block* bb;
+
+  if (bbl->data_blocks == NULL) {
+    bbl->data_blocks = new_bb;
+    return 0;
+  }
+
+  if (bbl->data_blocks->block > new_bb->block) {
+    new_bb->next = bbl->data_blocks;
+    bbl->data_blocks = new_bb;
+    return 0;
+  }
+
+  /* Optimization: blocks are mostly queued in sequence, so save the
+     pointer to the last bb that was added, and start searching from
+     there if the next block number is higher */
+  if (bbl->last_used && new_bb->block > bbl->last_used->block)
+    bb = bbl->last_used;
+  else
+    bb = bbl->data_blocks;
+  bbl->last_used = new_bb;
+
+  for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+    ;
+
+  if (bb->next == NULL) {
+    bb->next = new_bb;
+  } else {
+    new_bb->next = bb->next;
+    bb->next = new_bb;
+  }
+
+  merge_bb(bbl, new_bb, new_bb->next);
+  if (!merge_bb(bbl, bb, new_bb)) {
+    /* new_bb destroyed, point to retained as last_used */
+    bbl->last_used = bb;
+  }
+
+  return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+                          unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FILL;
+  bb->fill.val = fill_val;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+                          unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_DATA;
+  bb->data.data = data;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+                          unsigned int len, unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FILE;
+  bb->file.filename = strdup(filename);
+  bb->file.offset = offset;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                        unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FD;
+  bb->fd.fd = fd;
+  bb->fd.offset = offset;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
+                       unsigned int max_len) {
+  struct backed_block* new_bb;
+
+  max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+  if (bb->len <= max_len) {
+    return 0;
+  }
+
+  new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
+  if (new_bb == NULL) {
+    return -ENOMEM;
+  }
+
+  *new_bb = *bb;
+
+  new_bb->len = bb->len - max_len;
+  new_bb->block = bb->block + max_len / bbl->block_size;
+  new_bb->next = bb->next;
+  bb->next = new_bb;
+  bb->len = max_len;
+
+  switch (bb->type) {
+    case BACKED_BLOCK_DATA:
+      new_bb->data.data = (char*)bb->data.data + max_len;
+      break;
+    case BACKED_BLOCK_FILE:
+      new_bb->file.offset += max_len;
+      break;
+    case BACKED_BLOCK_FD:
+      new_bb->fd.offset += max_len;
+      break;
+    case BACKED_BLOCK_FILL:
+      break;
+  }
+
+  return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 1a159be..3a75460 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -23,42 +23,40 @@
 struct backed_block;
 
 enum backed_block_type {
-	BACKED_BLOCK_DATA,
-	BACKED_BLOCK_FILE,
-	BACKED_BLOCK_FD,
-	BACKED_BLOCK_FILL,
+  BACKED_BLOCK_DATA,
+  BACKED_BLOCK_FILE,
+  BACKED_BLOCK_FD,
+  BACKED_BLOCK_FILL,
 };
 
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
-		unsigned int len, unsigned int block);
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
-		unsigned int len, unsigned int block);
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
-		int64_t offset, unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list *bbl, int fd,
-		int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+                          unsigned int block);
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+                          unsigned int block);
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+                          unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                        unsigned int block);
 
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
-unsigned int backed_block_len(struct backed_block *bb);
-unsigned int backed_block_block(struct backed_block *bb);
-void *backed_block_data(struct backed_block *bb);
-const char *backed_block_filename(struct backed_block *bb);
-int backed_block_fd(struct backed_block *bb);
-int64_t backed_block_file_offset(struct backed_block *bb);
-uint32_t backed_block_fill_val(struct backed_block *bb);
-enum backed_block_type backed_block_type(struct backed_block *bb);
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
-		unsigned int max_len);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
+unsigned int backed_block_block(struct backed_block* bb);
+void* backed_block_data(struct backed_block* bb);
+const char* backed_block_filename(struct backed_block* bb);
+int backed_block_fd(struct backed_block* bb);
+int64_t backed_block_file_offset(struct backed_block* bb);
+uint32_t backed_block_fill_val(struct backed_block* bb);
+enum backed_block_type backed_block_type(struct backed_block* bb);
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);
 
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
 
-struct backed_block_list *backed_block_list_new(unsigned int block_size);
-void backed_block_list_destroy(struct backed_block_list *bbl);
+struct backed_block_list* backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list* bbl);
 
-void backed_block_list_move(struct backed_block_list *from,
-		struct backed_block_list *to, struct backed_block *start,
-		struct backed_block *end);
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+                            struct backed_block* start, struct backed_block* end);
 
 #endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
index 34e63c5..28e5cab 100644
--- a/libsparse/defs.h
+++ b/libsparse/defs.h
@@ -17,7 +17,7 @@
 #ifndef _LIBSPARSE_DEFS_H_
 
 #ifndef __unused
-#define __unused        __attribute__((__unused__))
+#define __unused __attribute__((__unused__))
 #endif
 
 #endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
deleted file mode 100644
index a0db36f..0000000
--- a/libsparse/img2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
-    fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int ret;
-	struct sparse_file *s;
-	unsigned int block_size = 4096;
-	off64_t len;
-
-	if (argc < 3 || argc > 4) {
-		usage();
-		exit(-1);
-	}
-
-	if (argc == 4) {
-		block_size = atoi(argv[3]);
-	}
-
-	if (block_size < 1024 || block_size % 4 != 0) {
-		usage();
-		exit(-1);
-	}
-
-	if (strcmp(argv[1], "-") == 0) {
-		in = STDIN_FILENO;
-	} else {
-		in = open(argv[1], O_RDONLY | O_BINARY);
-		if (in < 0) {
-			fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-			exit(-1);
-		}
-	}
-
-	if (strcmp(argv[2], "-") == 0) {
-		out = STDOUT_FILENO;
-	} else {
-		out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-		if (out < 0) {
-			fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-			exit(-1);
-		}
-	}
-
-	len = lseek64(in, 0, SEEK_END);
-	lseek64(in, 0, SEEK_SET);
-
-	s = sparse_file_new(block_size, len);
-	if (!s) {
-		fprintf(stderr, "Failed to create sparse file\n");
-		exit(-1);
-	}
-
-	sparse_file_verbose(s);
-	ret = sparse_file_read(s, in, false, false);
-	if (ret) {
-		fprintf(stderr, "Failed to read file\n");
-		exit(-1);
-	}
-
-	ret = sparse_file_write(s, out, false, true, false);
-	if (ret) {
-		fprintf(stderr, "Failed to write sparse file\n");
-		exit(-1);
-	}
-
-	close(in);
-	close(out);
-
-	exit(0);
-}
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
new file mode 100644
index 0000000..4c2c6ca
--- /dev/null
+++ b/libsparse/img2simg.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int ret;
+  struct sparse_file* s;
+  unsigned int block_size = 4096;
+  off64_t len;
+
+  if (argc < 3 || argc > 4) {
+    usage();
+    exit(-1);
+  }
+
+  if (argc == 4) {
+    block_size = atoi(argv[3]);
+  }
+
+  if (block_size < 1024 || block_size % 4 != 0) {
+    usage();
+    exit(-1);
+  }
+
+  if (strcmp(argv[1], "-") == 0) {
+    in = STDIN_FILENO;
+  } else {
+    in = open(argv[1], O_RDONLY | O_BINARY);
+    if (in < 0) {
+      fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+      exit(-1);
+    }
+  }
+
+  if (strcmp(argv[2], "-") == 0) {
+    out = STDOUT_FILENO;
+  } else {
+    out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    if (out < 0) {
+      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      exit(-1);
+    }
+  }
+
+  len = lseek64(in, 0, SEEK_END);
+  lseek64(in, 0, SEEK_SET);
+
+  s = sparse_file_new(block_size, len);
+  if (!s) {
+    fprintf(stderr, "Failed to create sparse file\n");
+    exit(-1);
+  }
+
+  sparse_file_verbose(s);
+  ret = sparse_file_read(s, in, false, false);
+  if (ret) {
+    fprintf(stderr, "Failed to read file\n");
+    exit(-1);
+  }
+
+  ret = sparse_file_write(s, out, false, true, false);
+  if (ret) {
+    fprintf(stderr, "Failed to write sparse file\n");
+    exit(-1);
+  }
+
+  close(in);
+  close(out);
+
+  exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 1b91ead..3d5fb0c 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -246,9 +246,24 @@
 int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
 
 /**
- * sparse_file_import - import an existing sparse file
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
  *
  * @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. If crc is true, the
+ * crc of the sparse file will be verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @fd - file descriptor to read from
  * @verbose - print verbose errors while reading the sparse file
  * @crc - verify the crc of a file in the Android sparse file format
  *
@@ -261,6 +276,21 @@
 struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
 
 /**
+ * sparse_file_import_buf - import an existing sparse file from a buffer
+ *
+ * @buf - buffer to read from
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads existing sparse file data into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it.  If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+
+/**
  * sparse_file_import_auto - import an existing sparse or normal file
  *
  * @fd - file descriptor to read from
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
deleted file mode 100644
index 002ad27..0000000
--- a/libsparse/output_file.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "defs.h"
-#include "output_file.h"
-#include "sparse_crc32.h"
-#include "sparse_format.h"
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#define O_BINARY 0
-#else
-#define ftruncate64 ftruncate
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define ftruncate64 ftruncate
-#define mmap64 mmap
-#define off64_t off_t
-#endif
-
-#define min(a, b) \
-	({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
-
-#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_MINOR_VER 0
-#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
-#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-
-#define container_of(inner, outer_t, elem) \
-	((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
-
-struct output_file_ops {
-	int (*open)(struct output_file *, int fd);
-	int (*skip)(struct output_file *, int64_t);
-	int (*pad)(struct output_file *, int64_t);
-	int (*write)(struct output_file *, void *, size_t);
-	void (*close)(struct output_file *);
-};
-
-struct sparse_file_ops {
-	int (*write_data_chunk)(struct output_file *out, unsigned int len,
-			void *data);
-	int (*write_fill_chunk)(struct output_file *out, unsigned int len,
-			uint32_t fill_val);
-	int (*write_skip_chunk)(struct output_file *out, int64_t len);
-	int (*write_end_chunk)(struct output_file *out);
-};
-
-struct output_file {
-	int64_t cur_out_ptr;
-	unsigned int chunk_cnt;
-	uint32_t crc32;
-	struct output_file_ops *ops;
-	struct sparse_file_ops *sparse_ops;
-	int use_crc;
-	unsigned int block_size;
-	int64_t len;
-	char *zero_buf;
-	uint32_t *fill_buf;
-	char *buf;
-};
-
-struct output_file_gz {
-	struct output_file out;
-	gzFile gz_fd;
-};
-
-#define to_output_file_gz(_o) \
-	container_of((_o), struct output_file_gz, out)
-
-struct output_file_normal {
-	struct output_file out;
-	int fd;
-};
-
-#define to_output_file_normal(_o) \
-	container_of((_o), struct output_file_normal, out)
-
-struct output_file_callback {
-	struct output_file out;
-	void *priv;
-	int (*write)(void *priv, const void *buf, size_t len);
-};
-
-#define to_output_file_callback(_o) \
-	container_of((_o), struct output_file_callback, out)
-
-static int file_open(struct output_file *out, int fd)
-{
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	outn->fd = fd;
-	return 0;
-}
-
-static int file_skip(struct output_file *out, int64_t cnt)
-{
-	off64_t ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	ret = lseek64(outn->fd, cnt, SEEK_CUR);
-	if (ret < 0) {
-		error_errno("lseek64");
-		return -1;
-	}
-	return 0;
-}
-
-static int file_pad(struct output_file *out, int64_t len)
-{
-	int ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	ret = ftruncate64(outn->fd, len);
-	if (ret < 0) {
-		return -errno;
-	}
-
-	return 0;
-}
-
-static int file_write(struct output_file *out, void *data, size_t len)
-{
-	ssize_t ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	while (len > 0) {
-		ret = write(outn->fd, data, len);
-		if (ret < 0) {
-			if (errno == EINTR) {
-				continue;
-			}
-			error_errno("write");
-			return -1;
-		}
-
-		data = (char *)data + ret;
-		len -= ret;
-	}
-
-	return 0;
-}
-
-static void file_close(struct output_file *out)
-{
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	free(outn);
-}
-
-static struct output_file_ops file_ops = {
-	.open = file_open,
-	.skip = file_skip,
-	.pad = file_pad,
-	.write = file_write,
-	.close = file_close,
-};
-
-static int gz_file_open(struct output_file *out, int fd)
-{
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	outgz->gz_fd = gzdopen(fd, "wb9");
-	if (!outgz->gz_fd) {
-		error_errno("gzopen");
-		return -errno;
-	}
-
-	return 0;
-}
-
-
-static int gz_file_skip(struct output_file *out, int64_t cnt)
-{
-	off64_t ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
-	if (ret < 0) {
-		error_errno("gzseek");
-		return -1;
-	}
-	return 0;
-}
-
-static int gz_file_pad(struct output_file *out, int64_t len)
-{
-	off64_t ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	ret = gztell(outgz->gz_fd);
-	if (ret < 0) {
-		return -1;
-	}
-
-	if (ret >= len) {
-		return 0;
-	}
-
-	ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
-	if (ret < 0) {
-		return -1;
-	}
-
-	gzwrite(outgz->gz_fd, "", 1);
-
-	return 0;
-}
-
-static int gz_file_write(struct output_file *out, void *data, size_t len)
-{
-	int ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	while (len > 0) {
-		ret = gzwrite(outgz->gz_fd, data,
-			      min(len, (unsigned int)INT_MAX));
-		if (ret == 0) {
-			error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
-			return -1;
-		}
-		len -= ret;
-		data = (char *)data + ret;
-	}
-
-	return 0;
-}
-
-static void gz_file_close(struct output_file *out)
-{
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	gzclose(outgz->gz_fd);
-	free(outgz);
-}
-
-static struct output_file_ops gz_file_ops = {
-	.open = gz_file_open,
-	.skip = gz_file_skip,
-	.pad = gz_file_pad,
-	.write = gz_file_write,
-	.close = gz_file_close,
-};
-
-static int callback_file_open(struct output_file *out __unused, int fd __unused)
-{
-	return 0;
-}
-
-static int callback_file_skip(struct output_file *out, int64_t off)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-	int to_write;
-	int ret;
-
-	while (off > 0) {
-		to_write = min(off, (int64_t)INT_MAX);
-		ret = outc->write(outc->priv, NULL, to_write);
-		if (ret < 0) {
-			return ret;
-		}
-		off -= to_write;
-	}
-
-	return 0;
-}
-
-static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
-{
-	return -1;
-}
-
-static int callback_file_write(struct output_file *out, void *data, size_t len)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-
-	return outc->write(outc->priv, data, len);
-}
-
-static void callback_file_close(struct output_file *out)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-
-	free(outc);
-}
-
-static struct output_file_ops callback_file_ops = {
-	.open = callback_file_open,
-	.skip = callback_file_skip,
-	.pad = callback_file_pad,
-	.write = callback_file_write,
-	.close = callback_file_close,
-};
-
-int read_all(int fd, void *buf, size_t len)
-{
-	size_t total = 0;
-	int ret;
-	char *ptr = buf;
-
-	while (total < len) {
-		ret = read(fd, ptr, len - total);
-
-		if (ret < 0)
-			return -errno;
-
-		if (ret == 0)
-			return -EINVAL;
-
-		ptr += ret;
-		total += ret;
-	}
-
-	return 0;
-}
-
-static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
-{
-	chunk_header_t chunk_header;
-	int ret;
-
-	if (skip_len % out->block_size) {
-		error("don't care size %"PRIi64" is not a multiple of the block size %u",
-				skip_len, out->block_size);
-		return -1;
-	}
-
-	/* We are skipping data, so emit a don't care chunk. */
-	chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = skip_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN;
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-	if (ret < 0)
-		return -1;
-
-	out->cur_out_ptr += skip_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	chunk_header_t chunk_header;
-	int rnd_up_len, count;
-	int ret;
-
-	/* Round up the fill length to a multiple of the block size */
-	rnd_up_len = ALIGN(len, out->block_size);
-
-	/* Finally we can safely emit a chunk of data */
-	chunk_header.chunk_type = CHUNK_TYPE_FILL;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = rnd_up_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
-	if (ret < 0)
-		return -1;
-	ret = out->ops->write(out, &fill_val, sizeof(fill_val));
-	if (ret < 0)
-		return -1;
-
-	if (out->use_crc) {
-		count = out->block_size / sizeof(uint32_t);
-		while (count--)
-			out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
-	}
-
-	out->cur_out_ptr += rnd_up_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
-		void *data)
-{
-	chunk_header_t chunk_header;
-	int rnd_up_len, zero_len;
-	int ret;
-
-	/* Round up the data length to a multiple of the block size */
-	rnd_up_len = ALIGN(len, out->block_size);
-	zero_len = rnd_up_len - len;
-
-	/* Finally we can safely emit a chunk of data */
-	chunk_header.chunk_type = CHUNK_TYPE_RAW;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = rnd_up_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
-	if (ret < 0)
-		return -1;
-	ret = out->ops->write(out, data, len);
-	if (ret < 0)
-		return -1;
-	if (zero_len) {
-		ret = out->ops->write(out, out->zero_buf, zero_len);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (out->use_crc) {
-		out->crc32 = sparse_crc32(out->crc32, data, len);
-		if (zero_len)
-			out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
-	}
-
-	out->cur_out_ptr += rnd_up_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-int write_sparse_end_chunk(struct output_file *out)
-{
-	chunk_header_t chunk_header;
-	int ret;
-
-	if (out->use_crc) {
-		chunk_header.chunk_type = CHUNK_TYPE_CRC32;
-		chunk_header.reserved1 = 0;
-		chunk_header.chunk_sz = 0;
-		chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
-
-		ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-		if (ret < 0) {
-			return ret;
-		}
-		out->ops->write(out, &out->crc32, 4);
-		if (ret < 0) {
-			return ret;
-		}
-
-		out->chunk_cnt++;
-	}
-
-	return 0;
-}
-
-static struct sparse_file_ops sparse_file_ops = {
-		.write_data_chunk = write_sparse_data_chunk,
-		.write_fill_chunk = write_sparse_fill_chunk,
-		.write_skip_chunk = write_sparse_skip_chunk,
-		.write_end_chunk = write_sparse_end_chunk,
-};
-
-static int write_normal_data_chunk(struct output_file *out, unsigned int len,
-		void *data)
-{
-	int ret;
-	unsigned int rnd_up_len = ALIGN(len, out->block_size);
-
-	ret = out->ops->write(out, data, len);
-	if (ret < 0) {
-		return ret;
-	}
-
-	if (rnd_up_len > len) {
-		ret = out->ops->skip(out, rnd_up_len - len);
-	}
-
-	return ret;
-}
-
-static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	int ret;
-	unsigned int i;
-	unsigned int write_len;
-
-	/* Initialize fill_buf with the fill_val */
-	for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
-		out->fill_buf[i] = fill_val;
-	}
-
-	while (len) {
-		write_len = min(len, out->block_size);
-		ret = out->ops->write(out, out->fill_buf, write_len);
-		if (ret < 0) {
-			return ret;
-		}
-
-		len -= write_len;
-	}
-
-	return 0;
-}
-
-static int write_normal_skip_chunk(struct output_file *out, int64_t len)
-{
-	return out->ops->skip(out, len);
-}
-
-int write_normal_end_chunk(struct output_file *out)
-{
-	return out->ops->pad(out, out->len);
-}
-
-static struct sparse_file_ops normal_file_ops = {
-		.write_data_chunk = write_normal_data_chunk,
-		.write_fill_chunk = write_normal_fill_chunk,
-		.write_skip_chunk = write_normal_skip_chunk,
-		.write_end_chunk = write_normal_end_chunk,
-};
-
-void output_file_close(struct output_file *out)
-{
-	out->sparse_ops->write_end_chunk(out);
-	out->ops->close(out);
-}
-
-static int output_file_init(struct output_file *out, int block_size,
-		int64_t len, bool sparse, int chunks, bool crc)
-{
-	int ret;
-
-	out->len = len;
-	out->block_size = block_size;
-	out->cur_out_ptr = 0ll;
-	out->chunk_cnt = 0;
-	out->crc32 = 0;
-	out->use_crc = crc;
-
-	out->zero_buf = calloc(block_size, 1);
-	if (!out->zero_buf) {
-		error_errno("malloc zero_buf");
-		return -ENOMEM;
-	}
-
-	out->fill_buf = calloc(block_size, 1);
-	if (!out->fill_buf) {
-		error_errno("malloc fill_buf");
-		ret = -ENOMEM;
-		goto err_fill_buf;
-	}
-
-	if (sparse) {
-		out->sparse_ops = &sparse_file_ops;
-	} else {
-		out->sparse_ops = &normal_file_ops;
-	}
-
-	if (sparse) {
-		sparse_header_t sparse_header = {
-				.magic = SPARSE_HEADER_MAGIC,
-				.major_version = SPARSE_HEADER_MAJOR_VER,
-				.minor_version = SPARSE_HEADER_MINOR_VER,
-				.file_hdr_sz = SPARSE_HEADER_LEN,
-				.chunk_hdr_sz = CHUNK_HEADER_LEN,
-				.blk_sz = out->block_size,
-				.total_blks = DIV_ROUND_UP(out->len, out->block_size),
-				.total_chunks = chunks,
-				.image_checksum = 0
-		};
-
-		if (out->use_crc) {
-			sparse_header.total_chunks++;
-		}
-
-		ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
-		if (ret < 0) {
-			goto err_write;
-		}
-	}
-
-	return 0;
-
-err_write:
-	free(out->fill_buf);
-err_fill_buf:
-	free(out->zero_buf);
-	return ret;
-}
-
-static struct output_file *output_file_new_gz(void)
-{
-	struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
-	if (!outgz) {
-		error_errno("malloc struct outgz");
-		return NULL;
-	}
-
-	outgz->out.ops = &gz_file_ops;
-
-	return &outgz->out;
-}
-
-static struct output_file *output_file_new_normal(void)
-{
-	struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
-	if (!outn) {
-		error_errno("malloc struct outn");
-		return NULL;
-	}
-
-	outn->out.ops = &file_ops;
-
-	return &outn->out;
-}
-
-struct output_file *output_file_open_callback(
-		int (*write)(void *, const void *, size_t),
-		void *priv, unsigned int block_size, int64_t len,
-		int gz __unused, int sparse, int chunks, int crc)
-{
-	int ret;
-	struct output_file_callback *outc;
-
-	outc = calloc(1, sizeof(struct output_file_callback));
-	if (!outc) {
-		error_errno("malloc struct outc");
-		return NULL;
-	}
-
-	outc->out.ops = &callback_file_ops;
-	outc->priv = priv;
-	outc->write = write;
-
-	ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
-	if (ret < 0) {
-		free(outc);
-		return NULL;
-	}
-
-	return &outc->out;
-}
-
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
-		int gz, int sparse, int chunks, int crc)
-{
-	int ret;
-	struct output_file *out;
-
-	if (gz) {
-		out = output_file_new_gz();
-	} else {
-		out = output_file_new_normal();
-	}
-	if (!out) {
-		return NULL;
-	}
-
-	out->ops->open(out, fd);
-
-	ret = output_file_init(out, block_size, len, sparse, chunks, crc);
-	if (ret < 0) {
-		free(out);
-		return NULL;
-	}
-
-	return out;
-}
-
-/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file *out, unsigned int len, void *data)
-{
-	return out->sparse_ops->write_data_chunk(out, len, data);
-}
-
-/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	return out->sparse_ops->write_fill_chunk(out, len, fill_val);
-}
-
-int write_fd_chunk(struct output_file *out, unsigned int len,
-		int fd, int64_t offset)
-{
-	int ret;
-	int64_t aligned_offset;
-	int aligned_diff;
-	uint64_t buffer_size;
-	char *ptr;
-
-	aligned_offset = offset & ~(4096 - 1);
-	aligned_diff = offset - aligned_offset;
-	buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
-	if (buffer_size > SIZE_MAX)
-		return -E2BIG;
-	char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
-			aligned_offset);
-	if (data == MAP_FAILED) {
-		return -errno;
-	}
-	ptr = data + aligned_diff;
-#else
-	off64_t pos;
-	char *data = malloc(len);
-	if (!data) {
-		return -errno;
-	}
-	pos = lseek64(fd, offset, SEEK_SET);
-	if (pos < 0) {
-                free(data);
-		return -errno;
-	}
-	ret = read_all(fd, data, len);
-	if (ret < 0) {
-                free(data);
-		return ret;
-	}
-	ptr = data;
-#endif
-
-	ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
-	munmap(data, buffer_size);
-#else
-	free(data);
-#endif
-
-	return ret;
-}
-
-/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file *out, unsigned int len,
-		const char *file, int64_t offset)
-{
-	int ret;
-
-	int file_fd = open(file, O_RDONLY | O_BINARY);
-	if (file_fd < 0) {
-		return -errno;
-	}
-
-	ret = write_fd_chunk(out, len, file_fd, offset);
-
-	close(file_fd);
-
-	return ret;
-}
-
-int write_skip_chunk(struct output_file *out, int64_t len)
-{
-	return out->sparse_ops->write_skip_chunk(out, len);
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
new file mode 100644
index 0000000..5388e77
--- /dev/null
+++ b/libsparse/output_file.cpp
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#endif
+
+#define min(a, b)        \
+  ({                     \
+    typeof(a) _a = (a);  \
+    typeof(b) _b = (b);  \
+    (_a < _b) ? _a : _b; \
+  })
+
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SPARSE_HEADER_MINOR_VER 0
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
+
+struct output_file_ops {
+  int (*open)(struct output_file*, int fd);
+  int (*skip)(struct output_file*, int64_t);
+  int (*pad)(struct output_file*, int64_t);
+  int (*write)(struct output_file*, void*, size_t);
+  void (*close)(struct output_file*);
+};
+
+struct sparse_file_ops {
+  int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+  int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
+  int (*write_skip_chunk)(struct output_file* out, int64_t len);
+  int (*write_end_chunk)(struct output_file* out);
+};
+
+struct output_file {
+  int64_t cur_out_ptr;
+  unsigned int chunk_cnt;
+  uint32_t crc32;
+  struct output_file_ops* ops;
+  struct sparse_file_ops* sparse_ops;
+  int use_crc;
+  unsigned int block_size;
+  int64_t len;
+  char* zero_buf;
+  uint32_t* fill_buf;
+  char* buf;
+};
+
+struct output_file_gz {
+  struct output_file out;
+  gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+  struct output_file out;
+  int fd;
+};
+
+#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+  struct output_file out;
+  void* priv;
+  int (*write)(void* priv, const void* buf, size_t len);
+};
+
+#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file* out, int fd) {
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  outn->fd = fd;
+  return 0;
+}
+
+static int file_skip(struct output_file* out, int64_t cnt) {
+  off64_t ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  ret = lseek64(outn->fd, cnt, SEEK_CUR);
+  if (ret < 0) {
+    error_errno("lseek64");
+    return -1;
+  }
+  return 0;
+}
+
+static int file_pad(struct output_file* out, int64_t len) {
+  int ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  ret = ftruncate64(outn->fd, len);
+  if (ret < 0) {
+    return -errno;
+  }
+
+  return 0;
+}
+
+static int file_write(struct output_file* out, void* data, size_t len) {
+  ssize_t ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  while (len > 0) {
+    ret = write(outn->fd, data, len);
+    if (ret < 0) {
+      if (errno == EINTR) {
+        continue;
+      }
+      error_errno("write");
+      return -1;
+    }
+
+    data = (char*)data + ret;
+    len -= ret;
+  }
+
+  return 0;
+}
+
+static void file_close(struct output_file* out) {
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  free(outn);
+}
+
+static struct output_file_ops file_ops = {
+    .open = file_open,
+    .skip = file_skip,
+    .pad = file_pad,
+    .write = file_write,
+    .close = file_close,
+};
+
+static int gz_file_open(struct output_file* out, int fd) {
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  outgz->gz_fd = gzdopen(fd, "wb9");
+  if (!outgz->gz_fd) {
+    error_errno("gzopen");
+    return -errno;
+  }
+
+  return 0;
+}
+
+static int gz_file_skip(struct output_file* out, int64_t cnt) {
+  off64_t ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+  if (ret < 0) {
+    error_errno("gzseek");
+    return -1;
+  }
+  return 0;
+}
+
+static int gz_file_pad(struct output_file* out, int64_t len) {
+  off64_t ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  ret = gztell(outgz->gz_fd);
+  if (ret < 0) {
+    return -1;
+  }
+
+  if (ret >= len) {
+    return 0;
+  }
+
+  ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+  if (ret < 0) {
+    return -1;
+  }
+
+  gzwrite(outgz->gz_fd, "", 1);
+
+  return 0;
+}
+
+static int gz_file_write(struct output_file* out, void* data, size_t len) {
+  int ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  while (len > 0) {
+    ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+    if (ret == 0) {
+      error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+      return -1;
+    }
+    len -= ret;
+    data = (char*)data + ret;
+  }
+
+  return 0;
+}
+
+static void gz_file_close(struct output_file* out) {
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  gzclose(outgz->gz_fd);
+  free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+    .open = gz_file_open,
+    .skip = gz_file_skip,
+    .pad = gz_file_pad,
+    .write = gz_file_write,
+    .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file* out __unused, int fd __unused) {
+  return 0;
+}
+
+static int callback_file_skip(struct output_file* out, int64_t off) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+  int to_write;
+  int ret;
+
+  while (off > 0) {
+    to_write = min(off, (int64_t)INT_MAX);
+    ret = outc->write(outc->priv, NULL, to_write);
+    if (ret < 0) {
+      return ret;
+    }
+    off -= to_write;
+  }
+
+  return 0;
+}
+
+static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
+  return -1;
+}
+
+static int callback_file_write(struct output_file* out, void* data, size_t len) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+
+  return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file* out) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+
+  free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+    .open = callback_file_open,
+    .skip = callback_file_skip,
+    .pad = callback_file_pad,
+    .write = callback_file_write,
+    .close = callback_file_close,
+};
+
+int read_all(int fd, void* buf, size_t len) {
+  size_t total = 0;
+  int ret;
+  char* ptr = reinterpret_cast<char*>(buf);
+
+  while (total < len) {
+    ret = read(fd, ptr, len - total);
+
+    if (ret < 0) return -errno;
+
+    if (ret == 0) return -EINVAL;
+
+    ptr += ret;
+    total += ret;
+  }
+
+  return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+  chunk_header_t chunk_header;
+  int ret;
+
+  if (skip_len % out->block_size) {
+    error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
+          out->block_size);
+    return -1;
+  }
+
+  /* We are skipping data, so emit a don't care chunk. */
+  chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = skip_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+  if (ret < 0) return -1;
+
+  out->cur_out_ptr += skip_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  chunk_header_t chunk_header;
+  int rnd_up_len, count;
+  int ret;
+
+  /* Round up the fill length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_FILL;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+  if (ret < 0) return -1;
+
+  if (out->use_crc) {
+    count = out->block_size / sizeof(uint32_t);
+    while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  chunk_header_t chunk_header;
+  int rnd_up_len, zero_len;
+  int ret;
+
+  /* Round up the data length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+  zero_len = rnd_up_len - len;
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_RAW;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  ret = out->ops->write(out, data, len);
+  if (ret < 0) return -1;
+  if (zero_len) {
+    ret = out->ops->write(out, out->zero_buf, zero_len);
+    if (ret < 0) return -1;
+  }
+
+  if (out->use_crc) {
+    out->crc32 = sparse_crc32(out->crc32, data, len);
+    if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+int write_sparse_end_chunk(struct output_file* out) {
+  chunk_header_t chunk_header;
+  int ret;
+
+  if (out->use_crc) {
+    chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+    chunk_header.reserved1 = 0;
+    chunk_header.chunk_sz = 0;
+    chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+    ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+    if (ret < 0) {
+      return ret;
+    }
+    out->ops->write(out, &out->crc32, 4);
+    if (ret < 0) {
+      return ret;
+    }
+
+    out->chunk_cnt++;
+  }
+
+  return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+    .write_data_chunk = write_sparse_data_chunk,
+    .write_fill_chunk = write_sparse_fill_chunk,
+    .write_skip_chunk = write_sparse_skip_chunk,
+    .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  int ret;
+  unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+  ret = out->ops->write(out, data, len);
+  if (ret < 0) {
+    return ret;
+  }
+
+  if (rnd_up_len > len) {
+    ret = out->ops->skip(out, rnd_up_len - len);
+  }
+
+  return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  int ret;
+  unsigned int i;
+  unsigned int write_len;
+
+  /* Initialize fill_buf with the fill_val */
+  for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+    out->fill_buf[i] = fill_val;
+  }
+
+  while (len) {
+    write_len = min(len, out->block_size);
+    ret = out->ops->write(out, out->fill_buf, write_len);
+    if (ret < 0) {
+      return ret;
+    }
+
+    len -= write_len;
+  }
+
+  return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+  return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file* out) {
+  return out->ops->pad(out, out->len);
+}
+
+static struct sparse_file_ops normal_file_ops = {
+    .write_data_chunk = write_normal_data_chunk,
+    .write_fill_chunk = write_normal_fill_chunk,
+    .write_skip_chunk = write_normal_skip_chunk,
+    .write_end_chunk = write_normal_end_chunk,
+};
+
+void output_file_close(struct output_file* out) {
+  out->sparse_ops->write_end_chunk(out);
+  out->ops->close(out);
+}
+
+static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
+                            int chunks, bool crc) {
+  int ret;
+
+  out->len = len;
+  out->block_size = block_size;
+  out->cur_out_ptr = 0ll;
+  out->chunk_cnt = 0;
+  out->crc32 = 0;
+  out->use_crc = crc;
+
+  out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+  if (!out->zero_buf) {
+    error_errno("malloc zero_buf");
+    return -ENOMEM;
+  }
+
+  out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+  if (!out->fill_buf) {
+    error_errno("malloc fill_buf");
+    ret = -ENOMEM;
+    goto err_fill_buf;
+  }
+
+  if (sparse) {
+    out->sparse_ops = &sparse_file_ops;
+  } else {
+    out->sparse_ops = &normal_file_ops;
+  }
+
+  if (sparse) {
+    sparse_header_t sparse_header = {
+        .magic = SPARSE_HEADER_MAGIC,
+        .major_version = SPARSE_HEADER_MAJOR_VER,
+        .minor_version = SPARSE_HEADER_MINOR_VER,
+        .file_hdr_sz = SPARSE_HEADER_LEN,
+        .chunk_hdr_sz = CHUNK_HEADER_LEN,
+        .blk_sz = out->block_size,
+        .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
+        .total_chunks = static_cast<unsigned>(chunks),
+        .image_checksum = 0};
+
+    if (out->use_crc) {
+      sparse_header.total_chunks++;
+    }
+
+    ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+    if (ret < 0) {
+      goto err_write;
+    }
+  }
+
+  return 0;
+
+err_write:
+  free(out->fill_buf);
+err_fill_buf:
+  free(out->zero_buf);
+  return ret;
+}
+
+static struct output_file* output_file_new_gz(void) {
+  struct output_file_gz* outgz =
+      reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
+  if (!outgz) {
+    error_errno("malloc struct outgz");
+    return NULL;
+  }
+
+  outgz->out.ops = &gz_file_ops;
+
+  return &outgz->out;
+}
+
+static struct output_file* output_file_new_normal(void) {
+  struct output_file_normal* outn =
+      reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
+  if (!outn) {
+    error_errno("malloc struct outn");
+    return NULL;
+  }
+
+  outn->out.ops = &file_ops;
+
+  return &outn->out;
+}
+
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+                                              unsigned int block_size, int64_t len, int gz __unused,
+                                              int sparse, int chunks, int crc) {
+  int ret;
+  struct output_file_callback* outc;
+
+  outc =
+      reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
+  if (!outc) {
+    error_errno("malloc struct outc");
+    return NULL;
+  }
+
+  outc->out.ops = &callback_file_ops;
+  outc->priv = priv;
+  outc->write = write;
+
+  ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+  if (ret < 0) {
+    free(outc);
+    return NULL;
+  }
+
+  return &outc->out;
+}
+
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+                                        int sparse, int chunks, int crc) {
+  int ret;
+  struct output_file* out;
+
+  if (gz) {
+    out = output_file_new_gz();
+  } else {
+    out = output_file_new_normal();
+  }
+  if (!out) {
+    return NULL;
+  }
+
+  out->ops->open(out, fd);
+
+  ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+  if (ret < 0) {
+    free(out);
+    return NULL;
+  }
+
+  return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+  int ret;
+  int64_t aligned_offset;
+  int aligned_diff;
+  uint64_t buffer_size;
+  char* ptr;
+
+  aligned_offset = offset & ~(4096 - 1);
+  aligned_diff = offset - aligned_offset;
+  buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+  if (buffer_size > SIZE_MAX) return -E2BIG;
+  char* data =
+      reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+  if (data == MAP_FAILED) {
+    return -errno;
+  }
+  ptr = data + aligned_diff;
+#else
+  off64_t pos;
+  char* data = reinterpret_cast<char*>(malloc(len));
+  if (!data) {
+    return -errno;
+  }
+  pos = lseek64(fd, offset, SEEK_SET);
+  if (pos < 0) {
+    free(data);
+    return -errno;
+  }
+  ret = read_all(fd, data, len);
+  if (ret < 0) {
+    free(data);
+    return ret;
+  }
+  ptr = data;
+#endif
+
+  ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef _WIN32
+  munmap(data, buffer_size);
+#else
+  free(data);
+#endif
+
+  return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+  int ret;
+
+  int file_fd = open(file, O_RDONLY | O_BINARY);
+  if (file_fd < 0) {
+    return -errno;
+  }
+
+  ret = write_fd_chunk(out, len, file_fd, offset);
+
+  close(file_fd);
+
+  return ret;
+}
+
+int write_skip_chunk(struct output_file* out, int64_t len) {
+  return out->sparse_ops->write_skip_chunk(out, len);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 690f610..278430b 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -25,23 +25,19 @@
 
 struct output_file;
 
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
-		int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(
-		int (*write)(void *, const void *, size_t),
-		void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
-		int chunks, int crc);
-int write_data_chunk(struct output_file *out, unsigned int len, void *data);
-int write_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val);
-int write_file_chunk(struct output_file *out, unsigned int len,
-		const char *file, int64_t offset);
-int write_fd_chunk(struct output_file *out, unsigned int len,
-		int fd, int64_t offset);
-int write_skip_chunk(struct output_file *out, int64_t len);
-void output_file_close(struct output_file *out);
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+                                        int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+                                              unsigned int block_size, int64_t len, int gz,
+                                              int sparse, int chunks, int crc);
+int write_data_chunk(struct output_file* out, unsigned int len, void* data);
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
+void output_file_close(struct output_file* out);
 
-int read_all(int fd, void *buf, size_t len);
+int read_all(int fd, void* buf, size_t len);
 
 #ifdef __cplusplus
 }
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
deleted file mode 100644
index b9b438e..0000000
--- a/libsparse/simg2img.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 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 <sparse/sparse.h>
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
-  fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int i;
-	struct sparse_file *s;
-
-	if (argc < 3) {
-		usage();
-		exit(-1);
-	}
-
-	out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-	if (out < 0) {
-		fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
-		exit(-1);
-	}
-
-	for (i = 1; i < argc - 1; i++) {
-		if (strcmp(argv[i], "-") == 0) {
-			in = STDIN_FILENO;
-		} else {
-			in = open(argv[i], O_RDONLY | O_BINARY);
-			if (in < 0) {
-				fprintf(stderr, "Cannot open input file %s\n", argv[i]);
-				exit(-1);
-			}
-		}
-
-		s = sparse_file_import(in, true, false);
-		if (!s) {
-			fprintf(stderr, "Failed to read sparse file\n");
-			exit(-1);
-		}
-
-		if (lseek(out, 0, SEEK_SET) == -1) {
-			perror("lseek failed");
-			exit(EXIT_FAILURE);
-		}
-
-		if (sparse_file_write(s, out, false, false, false) < 0) {
-			fprintf(stderr, "Cannot write output file\n");
-			exit(-1);
-		}
-		sparse_file_destroy(s);
-		close(in);
-	}
-
-	close(out);
-
-	exit(0);
-}
-
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
new file mode 100644
index 0000000..8ba5f69
--- /dev/null
+++ b/libsparse/simg2img.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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 <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int i;
+  struct sparse_file* s;
+
+  if (argc < 3) {
+    usage();
+    exit(-1);
+  }
+
+  out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+  if (out < 0) {
+    fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+    exit(-1);
+  }
+
+  for (i = 1; i < argc - 1; i++) {
+    if (strcmp(argv[i], "-") == 0) {
+      in = STDIN_FILENO;
+    } else {
+      in = open(argv[i], O_RDONLY | O_BINARY);
+      if (in < 0) {
+        fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+        exit(-1);
+      }
+    }
+
+    s = sparse_file_import(in, true, false);
+    if (!s) {
+      fprintf(stderr, "Failed to read sparse file\n");
+      exit(-1);
+    }
+
+    if (lseek(out, 0, SEEK_SET) == -1) {
+      perror("lseek failed");
+      exit(EXIT_FAILURE);
+    }
+
+    if (sparse_file_write(s, out, false, false, false) < 0) {
+      fprintf(stderr, "Cannot write output file\n");
+      exit(-1);
+    }
+    sparse_file_destroy(s);
+    close(in);
+  }
+
+  close(out);
+
+  exit(0);
+}
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
deleted file mode 100644
index 5f9ccf6..0000000
--- a/libsparse/simg2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
-  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int i;
-	int ret;
-	struct sparse_file *s;
-	int64_t max_size;
-	struct sparse_file **out_s;
-	int files;
-	char filename[4096];
-
-	if (argc != 4) {
-		usage();
-		exit(-1);
-	}
-
-	max_size = atoll(argv[3]);
-
-	in = open(argv[1], O_RDONLY | O_BINARY);
-	if (in < 0) {
-		fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-		exit(-1);
-	}
-
-	s = sparse_file_import(in, true, false);
-	if (!s) {
-		fprintf(stderr, "Failed to import sparse file\n");
-		exit(-1);
-	}
-
-	files = sparse_file_resparse(s, max_size, NULL, 0);
-	if (files < 0) {
-		fprintf(stderr, "Failed to resparse\n");
-		exit(-1);
-	}
-
-	out_s = calloc(sizeof(struct sparse_file *), files);
-	if (!out_s) {
-		fprintf(stderr, "Failed to allocate sparse file array\n");
-		exit(-1);
-	}
-
-	files = sparse_file_resparse(s, max_size, out_s, files);
-	if (files < 0) {
-		fprintf(stderr, "Failed to resparse\n");
-		exit(-1);
-	}
-
-	for (i = 0; i < files; i++) {
-		ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
-		if (ret >= (int)sizeof(filename)) {
-			fprintf(stderr, "Filename too long\n");
-			exit(-1);
-		}
-
-		out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-		if (out < 0) {
-			fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-			exit(-1);
-		}
-
-		ret = sparse_file_write(out_s[i], out, false, true, false);
-		if (ret) {
-			fprintf(stderr, "Failed to write sparse file\n");
-			exit(-1);
-		}
-		close(out);
-	}
-
-	close(in);
-
-	exit(0);
-}
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
new file mode 100644
index 0000000..7e65701
--- /dev/null
+++ b/libsparse/simg2simg.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int i;
+  int ret;
+  struct sparse_file* s;
+  int64_t max_size;
+  struct sparse_file** out_s;
+  int files;
+  char filename[4096];
+
+  if (argc != 4) {
+    usage();
+    exit(-1);
+  }
+
+  max_size = atoll(argv[3]);
+
+  in = open(argv[1], O_RDONLY | O_BINARY);
+  if (in < 0) {
+    fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+    exit(-1);
+  }
+
+  s = sparse_file_import(in, true, false);
+  if (!s) {
+    fprintf(stderr, "Failed to import sparse file\n");
+    exit(-1);
+  }
+
+  files = sparse_file_resparse(s, max_size, NULL, 0);
+  if (files < 0) {
+    fprintf(stderr, "Failed to resparse\n");
+    exit(-1);
+  }
+
+  out_s = calloc(sizeof(struct sparse_file*), files);
+  if (!out_s) {
+    fprintf(stderr, "Failed to allocate sparse file array\n");
+    exit(-1);
+  }
+
+  files = sparse_file_resparse(s, max_size, out_s, files);
+  if (files < 0) {
+    fprintf(stderr, "Failed to resparse\n");
+    exit(-1);
+  }
+
+  for (i = 0; i < files; i++) {
+    ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+    if (ret >= (int)sizeof(filename)) {
+      fprintf(stderr, "Filename too long\n");
+      exit(-1);
+    }
+
+    out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    if (out < 0) {
+      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      exit(-1);
+    }
+
+    ret = sparse_file_write(out_s[i], out, false, true, false);
+    if (ret) {
+      fprintf(stderr, "Failed to write sparse file\n");
+      exit(-1);
+    }
+    close(out);
+  }
+
+  close(in);
+
+  exit(0);
+}
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
deleted file mode 100644
index 466435f..0000000
--- a/libsparse/sparse.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2012 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 <assert.h>
-#include <stdlib.h>
-
-#include <sparse/sparse.h>
-
-#include "defs.h"
-#include "sparse_file.h"
-
-#include "output_file.h"
-#include "backed_block.h"
-#include "sparse_defs.h"
-#include "sparse_format.h"
-
-struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
-{
-	struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
-	if (!s) {
-		return NULL;
-	}
-
-	s->backed_block_list = backed_block_list_new(block_size);
-	if (!s->backed_block_list) {
-		free(s);
-		return NULL;
-	}
-
-	s->block_size = block_size;
-	s->len = len;
-
-	return s;
-}
-
-void sparse_file_destroy(struct sparse_file *s)
-{
-	backed_block_list_destroy(s->backed_block_list);
-	free(s);
-}
-
-int sparse_file_add_data(struct sparse_file *s,
-		void *data, unsigned int len, unsigned int block)
-{
-	return backed_block_add_data(s->backed_block_list, data, len, block);
-}
-
-int sparse_file_add_fill(struct sparse_file *s,
-		uint32_t fill_val, unsigned int len, unsigned int block)
-{
-	return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
-}
-
-int sparse_file_add_file(struct sparse_file *s,
-		const char *filename, int64_t file_offset, unsigned int len,
-		unsigned int block)
-{
-	return backed_block_add_file(s->backed_block_list, filename, file_offset,
-			len, block);
-}
-
-int sparse_file_add_fd(struct sparse_file *s,
-		int fd, int64_t file_offset, unsigned int len, unsigned int block)
-{
-	return backed_block_add_fd(s->backed_block_list, fd, file_offset,
-			len, block);
-}
-unsigned int sparse_count_chunks(struct sparse_file *s)
-{
-	struct backed_block *bb;
-	unsigned int last_block = 0;
-	unsigned int chunks = 0;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		if (backed_block_block(bb) > last_block) {
-			/* If there is a gap between chunks, add a skip chunk */
-			chunks++;
-		}
-		chunks++;
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
-	}
-	if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
-		chunks++;
-	}
-
-	return chunks;
-}
-
-static int sparse_file_write_block(struct output_file *out,
-		struct backed_block *bb)
-{
-	int ret = -EINVAL;
-
-	switch (backed_block_type(bb)) {
-	case BACKED_BLOCK_DATA:
-		ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
-		break;
-	case BACKED_BLOCK_FILE:
-		ret = write_file_chunk(out, backed_block_len(bb),
-				       backed_block_filename(bb),
-				       backed_block_file_offset(bb));
-		break;
-	case BACKED_BLOCK_FD:
-		ret = write_fd_chunk(out, backed_block_len(bb),
-				     backed_block_fd(bb),
-				     backed_block_file_offset(bb));
-		break;
-	case BACKED_BLOCK_FILL:
-		ret = write_fill_chunk(out, backed_block_len(bb),
-				       backed_block_fill_val(bb));
-		break;
-	}
-
-	return ret;
-}
-
-static int write_all_blocks(struct sparse_file *s, struct output_file *out)
-{
-	struct backed_block *bb;
-	unsigned int last_block = 0;
-	int64_t pad;
-	int ret = 0;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		if (backed_block_block(bb) > last_block) {
-			unsigned int blocks = backed_block_block(bb) - last_block;
-			write_skip_chunk(out, (int64_t)blocks * s->block_size);
-		}
-		ret = sparse_file_write_block(out, bb);
-		if (ret)
-			return ret;
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
-	}
-
-	pad = s->len - (int64_t)last_block * s->block_size;
-	assert(pad >= 0);
-	if (pad > 0) {
-		write_skip_chunk(out, pad);
-	}
-
-	return 0;
-}
-
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
-		bool crc)
-{
-	int ret;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	return ret;
-}
-
-int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
-		int (*write)(void *priv, const void *data, size_t len), void *priv)
-{
-	int ret;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_callback(write, priv, s->block_size, s->len, false,
-			sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	return ret;
-}
-
-struct chunk_data {
-	void		*priv;
-	unsigned int	block;
-	unsigned int	nr_blocks;
-	int (*write)(void *priv, const void *data, size_t len,
-		     unsigned int block, unsigned int nr_blocks);
-};
-
-static int foreach_chunk_write(void *priv, const void *data, size_t len)
-{
-	struct chunk_data *chk = priv;
-
-	return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
-}
-
-int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
-	int (*write)(void *priv, const void *data, size_t len, unsigned int block,
-		     unsigned int nr_blocks),
-	void *priv)
-{
-	int ret;
-	int chunks;
-	struct chunk_data chk;
-	struct output_file *out;
-	struct backed_block *bb;
-
-	chk.priv = priv;
-	chk.write = write;
-	chk.block = chk.nr_blocks = 0;
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_callback(foreach_chunk_write, &chk,
-					s->block_size, s->len, false, sparse,
-					chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		chk.block = backed_block_block(bb);
-		chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
-		ret = sparse_file_write_block(out, bb);
-		if (ret)
-			return ret;
-	}
-
-	output_file_close(out);
-
-	return ret;
-}
-
-static int out_counter_write(void *priv, const void *data __unused, size_t len)
-{
-	int64_t *count = priv;
-	*count += len;
-	return 0;
-}
-
-int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
-{
-	int ret;
-	int chunks = sparse_count_chunks(s);
-	int64_t count = 0;
-	struct output_file *out;
-
-	out = output_file_open_callback(out_counter_write, &count,
-			s->block_size, s->len, false, sparse, chunks, crc);
-	if (!out) {
-		return -1;
-	}
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	if (ret < 0) {
-		return -1;
-	}
-
-	return count;
-}
-
-unsigned int sparse_file_block_size(struct sparse_file *s)
-{
-	return s->block_size;
-}
-
-static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
-		struct sparse_file *to, unsigned int len)
-{
-	int64_t count = 0;
-	struct output_file *out_counter;
-	struct backed_block *last_bb = NULL;
-	struct backed_block *bb;
-	struct backed_block *start;
-	unsigned int last_block = 0;
-	int64_t file_len = 0;
-	int ret;
-
-	/*
-	 * overhead is sparse file header, the potential end skip
-	 * chunk and crc chunk.
-	 */
-	int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
-			sizeof(uint32_t);
-	len -= overhead;
-
-	start = backed_block_iter_new(from->backed_block_list);
-	out_counter = output_file_open_callback(out_counter_write, &count,
-			to->block_size, to->len, false, true, 0, false);
-	if (!out_counter) {
-		return NULL;
-	}
-
-	for (bb = start; bb; bb = backed_block_iter_next(bb)) {
-		count = 0;
-		if (backed_block_block(bb) > last_block)
-			count += sizeof(chunk_header_t);
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), to->block_size);
-
-		/* will call out_counter_write to update count */
-		ret = sparse_file_write_block(out_counter, bb);
-		if (ret) {
-			bb = NULL;
-			goto out;
-		}
-		if (file_len + count > len) {
-			/*
-			 * If the remaining available size is more than 1/8th of the
-			 * requested size, split the chunk.  Results in sparse files that
-			 * are at least 7/8ths of the requested size
-			 */
-			file_len += sizeof(chunk_header_t);
-			if (!last_bb || (len - file_len > (len / 8))) {
-				backed_block_split(from->backed_block_list, bb, len - file_len);
-				last_bb = bb;
-			}
-			goto move;
-		}
-		file_len += count;
-		last_bb = bb;
-	}
-
-move:
-	backed_block_list_move(from->backed_block_list,
-		to->backed_block_list, start, last_bb);
-
-out:
-	output_file_close(out_counter);
-
-	return bb;
-}
-
-int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
-		struct sparse_file **out_s, int out_s_count)
-{
-	struct backed_block *bb;
-	struct sparse_file *s;
-	struct sparse_file *tmp;
-	int c = 0;
-
-	tmp = sparse_file_new(in_s->block_size, in_s->len);
-	if (!tmp) {
-		return -ENOMEM;
-	}
-
-	do {
-		s = sparse_file_new(in_s->block_size, in_s->len);
-
-		bb = move_chunks_up_to_len(in_s, s, max_len);
-
-		if (c < out_s_count) {
-			out_s[c] = s;
-		} else {
-			backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
-					NULL, NULL);
-			sparse_file_destroy(s);
-		}
-		c++;
-	} while (bb);
-
-	backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
-			NULL, NULL);
-
-	sparse_file_destroy(tmp);
-
-	return c;
-}
-
-void sparse_file_verbose(struct sparse_file *s)
-{
-	s->verbose = true;
-}
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
new file mode 100644
index 0000000..6ff97b6
--- /dev/null
+++ b/libsparse/sparse.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 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 <assert.h>
+#include <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "backed_block.h"
+#include "output_file.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
+  struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
+  if (!s) {
+    return NULL;
+  }
+
+  s->backed_block_list = backed_block_list_new(block_size);
+  if (!s->backed_block_list) {
+    free(s);
+    return NULL;
+  }
+
+  s->block_size = block_size;
+  s->len = len;
+
+  return s;
+}
+
+void sparse_file_destroy(struct sparse_file* s) {
+  backed_block_list_destroy(s->backed_block_list);
+  free(s);
+}
+
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+  return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+                         unsigned int block) {
+  return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+                         unsigned int len, unsigned int block) {
+  return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+                       unsigned int block) {
+  return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file* s) {
+  struct backed_block* bb;
+  unsigned int last_block = 0;
+  unsigned int chunks = 0;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    if (backed_block_block(bb) > last_block) {
+      /* If there is a gap between chunks, add a skip chunk */
+      chunks++;
+    }
+    chunks++;
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+  }
+  if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+    chunks++;
+  }
+
+  return chunks;
+}
+
+static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
+  int ret = -EINVAL;
+
+  switch (backed_block_type(bb)) {
+    case BACKED_BLOCK_DATA:
+      ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+      break;
+    case BACKED_BLOCK_FILE:
+      ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
+                             backed_block_file_offset(bb));
+      break;
+    case BACKED_BLOCK_FD:
+      ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
+                           backed_block_file_offset(bb));
+      break;
+    case BACKED_BLOCK_FILL:
+      ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
+      break;
+  }
+
+  return ret;
+}
+
+static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
+  struct backed_block* bb;
+  unsigned int last_block = 0;
+  int64_t pad;
+  int ret = 0;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    if (backed_block_block(bb) > last_block) {
+      unsigned int blocks = backed_block_block(bb) - last_block;
+      write_skip_chunk(out, (int64_t)blocks * s->block_size);
+    }
+    ret = sparse_file_write_block(out, bb);
+    if (ret) return ret;
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+  }
+
+  pad = s->len - (int64_t)last_block * s->block_size;
+  assert(pad >= 0);
+  if (pad > 0) {
+    write_skip_chunk(out, pad);
+  }
+
+  return 0;
+}
+
+int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
+                         int (*write)(void* priv, const void* data, size_t len), void* priv) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+struct chunk_data {
+  void* priv;
+  unsigned int block;
+  unsigned int nr_blocks;
+  int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void* priv, const void* data, size_t len) {
+  struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
+
+  return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
+                              int (*write)(void* priv, const void* data, size_t len,
+                                           unsigned int block, unsigned int nr_blocks),
+                              void* priv) {
+  int ret;
+  int chunks;
+  struct chunk_data chk;
+  struct output_file* out;
+  struct backed_block* bb;
+
+  chk.priv = priv;
+  chk.write = write;
+  chk.block = chk.nr_blocks = 0;
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
+                                  chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    chk.block = backed_block_block(bb);
+    chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+    ret = sparse_file_write_block(out, bb);
+    if (ret) return ret;
+  }
+
+  output_file_close(out);
+
+  return ret;
+}
+
+static int out_counter_write(void* priv, const void* data __unused, size_t len) {
+  int64_t* count = reinterpret_cast<int64_t*>(priv);
+  *count += len;
+  return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
+  int ret;
+  int chunks = sparse_count_chunks(s);
+  int64_t count = 0;
+  struct output_file* out;
+
+  out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
+                                  chunks, crc);
+  if (!out) {
+    return -1;
+  }
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  if (ret < 0) {
+    return -1;
+  }
+
+  return count;
+}
+
+unsigned int sparse_file_block_size(struct sparse_file* s) {
+  return s->block_size;
+}
+
+static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
+                                                  unsigned int len) {
+  int64_t count = 0;
+  struct output_file* out_counter;
+  struct backed_block* last_bb = NULL;
+  struct backed_block* bb;
+  struct backed_block* start;
+  unsigned int last_block = 0;
+  int64_t file_len = 0;
+  int ret;
+
+  /*
+   * overhead is sparse file header, the potential end skip
+   * chunk and crc chunk.
+   */
+  int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
+  len -= overhead;
+
+  start = backed_block_iter_new(from->backed_block_list);
+  out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
+                                          true, 0, false);
+  if (!out_counter) {
+    return NULL;
+  }
+
+  for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+    count = 0;
+    if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+    /* will call out_counter_write to update count */
+    ret = sparse_file_write_block(out_counter, bb);
+    if (ret) {
+      bb = NULL;
+      goto out;
+    }
+    if (file_len + count > len) {
+      /*
+       * If the remaining available size is more than 1/8th of the
+       * requested size, split the chunk.  Results in sparse files that
+       * are at least 7/8ths of the requested size
+       */
+      file_len += sizeof(chunk_header_t);
+      if (!last_bb || (len - file_len > (len / 8))) {
+        backed_block_split(from->backed_block_list, bb, len - file_len);
+        last_bb = bb;
+      }
+      goto move;
+    }
+    file_len += count;
+    last_bb = bb;
+  }
+
+move:
+  backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
+
+out:
+  output_file_close(out_counter);
+
+  return bb;
+}
+
+int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
+                         int out_s_count) {
+  struct backed_block* bb;
+  struct sparse_file* s;
+  struct sparse_file* tmp;
+  int c = 0;
+
+  tmp = sparse_file_new(in_s->block_size, in_s->len);
+  if (!tmp) {
+    return -ENOMEM;
+  }
+
+  do {
+    s = sparse_file_new(in_s->block_size, in_s->len);
+
+    bb = move_chunks_up_to_len(in_s, s, max_len);
+
+    if (c < out_s_count) {
+      out_s[c] = s;
+    } else {
+      backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL);
+      sparse_file_destroy(s);
+    }
+    c++;
+  } while (bb);
+
+  backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL);
+
+  sparse_file_destroy(tmp);
+
+  return c;
+}
+
+void sparse_file_verbose(struct sparse_file* s) {
+  s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
deleted file mode 100644
index 38bfe4a..0000000
--- a/libsparse/sparse_crc32.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
- *  code or tables extracted from it, as desired without restriction.
- */
-
-/*
- *  First, the polynomial itself and its table of feedback terms.  The
- *  polynomial is
- *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- *  Note that we take it "backwards" and put the highest-order term in
- *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
- *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
- *  the MSB being 1
- *
- *  Note that the usual hardware shift register implementation, which
- *  is what we're using (we're merely optimizing it by doing eight-bit
- *  chunks at a time) shifts bits into the lowest-order term.  In our
- *  implementation, that means shifting towards the right.  Why do we
- *  do it this way?  Because the calculated CRC must be transmitted in
- *  order from highest-order term to lowest-order term.  UARTs transmit
- *  characters in order from LSB to MSB.  By storing the CRC this way
- *  we hand it to the UART in the order low-byte to high-byte; the UART
- *  sends each low-bit to hight-bit; and the result is transmission bit
- *  by bit from highest- to lowest-order term without requiring any bit
- *  shuffling on our part.  Reception works similarly
- *
- *  The feedback terms table consists of 256, 32-bit entries.  Notes
- *
- *      The table can be generated at runtime if desired; code to do so
- *      is shown later.  It might not be obvious, but the feedback
- *      terms simply represent the results of eight shift/xor opera
- *      tions for all combinations of data and CRC register values
- *
- *      The values must be right-shifted by eight bits by the "updcrc
- *      logic; the shift must be unsigned (bring in zeroes).  On some
- *      hardware you could probably optimize the shift in assembler by
- *      using byte-swap instructions
- *      polynomial $edb88320
- *
- *
- * CRC32 code derived from work by Gary S. Brown.
- */
-
-/* Code taken from FreeBSD 8 */
-#include <stdint.h>
-
-static uint32_t crc32_tab[] = {
-        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
-        0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
-        0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
-        0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
-        0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
-        0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
-        0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
-        0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
-        0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
-        0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
-        0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
-        0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
-        0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
-        0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
-        0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
-        0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
-        0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
-        0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
-        0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
-        0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
-        0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
-        0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
-        0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
-        0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
-        0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
-        0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
-        0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
-        0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
-        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
-        0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
-        0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
-        0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
-        0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
-        0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
-        0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
-        0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
-        0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
-        0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
-        0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
-        0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
-        0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
-        0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-        0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/*
- * A function that calculates the CRC-32 based on the table above is
- * given below for documentation purposes. An equivalent implementation
- * of this function that's actually used in the kernel can be found
- * in sys/libkern.h, where it can be inlined.
- */
-
-uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
-{
-        const uint8_t *p = buf;
-        uint32_t crc;
-
-        crc = crc_in ^ ~0U;
-        while (size--)
-                crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
-        return crc ^ ~0U;
-}
-
diff --git a/libsparse/sparse_crc32.cpp b/libsparse/sparse_crc32.cpp
new file mode 100644
index 0000000..267322c
--- /dev/null
+++ b/libsparse/sparse_crc32.cpp
@@ -0,0 +1,97 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+#include <stdio.h>
+
+static uint32_t crc32_tab[] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {
+  const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);
+  uint32_t crc;
+
+  crc = crc_in ^ ~0U;
+  while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+  return crc ^ ~0U;
+}
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 50cd9e9..2702c4f 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -19,14 +19,6 @@
 
 #include <stdint.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
-
-#ifdef __cplusplus
-}
-#endif
+uint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);
 
 #endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
index b99cfd5..9137805 100644
--- a/libsparse/sparse_defs.h
+++ b/libsparse/sparse_defs.h
@@ -39,11 +39,14 @@
 typedef unsigned short int u16;
 typedef unsigned char u8;
 
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))
 #define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
 
-#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...)                                    \
+  do {                                                         \
+    fprintf(stderr, "error: %s: " fmt "\n", __func__, ##args); \
+  } while (0)
 #define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
 
 #endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.cpp
similarity index 73%
rename from libsparse/sparse_err.c
rename to libsparse/sparse_err.cpp
index 0f392ad..6886d31 100644
--- a/libsparse/sparse_err.c
+++ b/libsparse/sparse_err.cpp
@@ -20,14 +20,13 @@
 #include <stdio.h>
 #include <unistd.h>
 
-void sparse_default_print(const char *fmt, ...)
-{
-	va_list argp;
+void sparse_default_print(const char* fmt, ...) {
+  va_list argp;
 
-	va_start(argp, fmt);
-	vfprintf(stderr, fmt, argp);
-	va_end(argp);
+  va_start(argp, fmt);
+  vfprintf(stderr, fmt, argp);
+  va_end(argp);
 }
 
-void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
-void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
+void (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 763f43f..e565f63 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -24,12 +24,12 @@
 #include <sparse/sparse.h>
 
 struct sparse_file {
-	unsigned int block_size;
-	int64_t len;
-	bool verbose;
+  unsigned int block_size;
+  int64_t len;
+  bool verbose;
 
-	struct backed_block_list *backed_block_list;
-	struct output_file *out;
+  struct backed_block_list* backed_block_list;
+  struct output_file* out;
 };
 
 #ifdef __cplusplus
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index 779e038..a8a721e 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -23,31 +23,31 @@
 #endif
 
 typedef struct sparse_header {
-  __le32	magic;		/* 0xed26ff3a */
-  __le16	major_version;	/* (0x1) - reject images with higher major versions */
-  __le16	minor_version;	/* (0x0) - allow images with higer minor versions */
-  __le16	file_hdr_sz;	/* 28 bytes for first revision of the file format */
-  __le16	chunk_hdr_sz;	/* 12 bytes for first revision of the file format */
-  __le32	blk_sz;		/* block size in bytes, must be a multiple of 4 (4096) */
-  __le32	total_blks;	/* total blocks in the non-sparse output image */
-  __le32	total_chunks;	/* total chunks in the sparse input image */
-  __le32	image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
-				/* as 0. Standard 802.3 polynomial, use a Public Domain */
-				/* table implementation */
+  __le32 magic;          /* 0xed26ff3a */
+  __le16 major_version;  /* (0x1) - reject images with higher major versions */
+  __le16 minor_version;  /* (0x0) - allow images with higer minor versions */
+  __le16 file_hdr_sz;    /* 28 bytes for first revision of the file format */
+  __le16 chunk_hdr_sz;   /* 12 bytes for first revision of the file format */
+  __le32 blk_sz;         /* block size in bytes, must be a multiple of 4 (4096) */
+  __le32 total_blks;     /* total blocks in the non-sparse output image */
+  __le32 total_chunks;   /* total chunks in the sparse input image */
+  __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+                         /* as 0. Standard 802.3 polynomial, use a Public Domain */
+                         /* table implementation */
 } sparse_header_t;
 
-#define SPARSE_HEADER_MAGIC	0xed26ff3a
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
 
-#define CHUNK_TYPE_RAW		0xCAC1
-#define CHUNK_TYPE_FILL		0xCAC2
-#define CHUNK_TYPE_DONT_CARE	0xCAC3
-#define CHUNK_TYPE_CRC32    0xCAC4
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
 
 typedef struct chunk_header {
-  __le16	chunk_type;	/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
-  __le16	reserved1;
-  __le32	chunk_sz;	/* in blocks in output image */
-  __le32	total_sz;	/* in bytes of chunk input file including chunk header and data */
+  __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+  __le16 reserved1;
+  __le32 chunk_sz; /* in blocks in output image */
+  __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
 } chunk_header_t;
 
 /* Following a Raw or Fill or CRC32 chunk is data.
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 4379635..56e2c9a 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -17,16 +17,16 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
-#include <algorithm>
-#include <inttypes.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <string>
 #include <unistd.h>
+#include <algorithm>
+#include <string>
 
 #include <sparse/sparse.h>
 
@@ -37,447 +37,541 @@
 #include "sparse_file.h"
 #include "sparse_format.h"
 
-
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define off64_t off_t
 #endif
 
 #define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
 static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
-static char *copybuf;
+static char* copybuf;
 
-static std::string ErrorString(int err)
-{
-	if (err == -EOVERFLOW) return "EOF while reading file";
-	if (err == -EINVAL) return "Invalid sparse file format";
-	if (err == -ENOMEM) return "Failed allocation while reading file";
-	return android::base::StringPrintf("Unknown error %d", err);
+static std::string ErrorString(int err) {
+  if (err == -EOVERFLOW) return "EOF while reading file";
+  if (err == -EINVAL) return "Invalid sparse file format";
+  if (err == -ENOMEM) return "Failed allocation while reading file";
+  return android::base::StringPrintf("Unknown error %d", err);
 }
 
-static void verbose_error(bool verbose, int err, const char *fmt, ...)
-{
-	if (!verbose) return;
+class SparseFileSource {
+ public:
+  /* Seeks the source ahead by the given offset. */
+  virtual void Seek(int64_t offset) = 0;
 
-	std::string msg = ErrorString(err);
-	if (fmt) {
-		msg += " at ";
-		va_list argp;
-		va_start(argp, fmt);
-		android::base::StringAppendV(&msg, fmt, argp);
-		va_end(argp);
-	}
-	sparse_print_verbose("%s\n", msg.c_str());
+  /* Return the current offset. */
+  virtual int64_t GetOffset() = 0;
+
+  /* Set the current offset. Return 0 if successful. */
+  virtual int SetOffset(int64_t offset) = 0;
+
+  /* Adds the given length from the current offset of the source to the file at the given block.
+   * Return 0 if successful. */
+  virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
+
+  /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
+  virtual int ReadValue(void* ptr, int len) = 0;
+
+  /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
+  virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
+
+  virtual ~SparseFileSource(){};
+};
+
+class SparseFileFdSource : public SparseFileSource {
+ private:
+  int fd;
+
+ public:
+  SparseFileFdSource(int fd) : fd(fd) {}
+  ~SparseFileFdSource() override {}
+
+  void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+
+  int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
+
+  int SetOffset(int64_t offset) override {
+    return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+  }
+
+  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+    return sparse_file_add_fd(s, fd, GetOffset(), len, block);
+  }
+
+  int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
+
+  int GetCrc32(uint32_t* crc32, int64_t len) override {
+    int chunk;
+    int ret;
+    while (len) {
+      chunk = std::min(len, COPY_BUF_SIZE);
+      ret = read_all(fd, copybuf, chunk);
+      if (ret < 0) {
+        return ret;
+      }
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+    return 0;
+  }
+};
+
+class SparseFileBufSource : public SparseFileSource {
+ private:
+  char* buf;
+  int64_t offset;
+
+ public:
+  SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+  ~SparseFileBufSource() override {}
+
+  void Seek(int64_t off) override {
+    buf += off;
+    offset += off;
+  }
+
+  int64_t GetOffset() override { return offset; }
+
+  int SetOffset(int64_t off) override {
+    buf += off - offset;
+    offset = off;
+    return 0;
+  }
+
+  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+    return sparse_file_add_data(s, buf, len, block);
+  }
+
+  int ReadValue(void* ptr, int len) override {
+    memcpy(ptr, buf, len);
+    Seek(len);
+    return 0;
+  }
+
+  int GetCrc32(uint32_t* crc32, int64_t len) override {
+    *crc32 = sparse_crc32(*crc32, buf, len);
+    Seek(len);
+    return 0;
+  }
+};
+
+static void verbose_error(bool verbose, int err, const char* fmt, ...) {
+  if (!verbose) return;
+
+  std::string msg = ErrorString(err);
+  if (fmt) {
+    msg += " at ";
+    va_list argp;
+    va_start(argp, fmt);
+    android::base::StringAppendV(&msg, fmt, argp);
+    va_end(argp);
+  }
+  sparse_print_verbose("%s\n", msg.c_str());
 }
 
-static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, int64_t offset, unsigned int blocks, unsigned int block,
-		uint32_t *crc32)
-{
-	int ret;
-	int chunk;
-	int64_t len = blocks * s->block_size;
+static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
+                             SparseFileSource* source, unsigned int blocks, unsigned int block,
+                             uint32_t* crc32) {
+  int ret;
+  int64_t len = blocks * s->block_size;
 
-	if (chunk_size % s->block_size != 0) {
-		return -EINVAL;
-	}
+  if (chunk_size % s->block_size != 0) {
+    return -EINVAL;
+  }
 
-	if (chunk_size / s->block_size != blocks) {
-		return -EINVAL;
-	}
+  if (chunk_size / s->block_size != blocks) {
+    return -EINVAL;
+  }
 
-	ret = sparse_file_add_fd(s, fd, offset, len, block);
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->AddToSparseFile(s, len, block);
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32) {
-		while (len) {
-			chunk = std::min(len, COPY_BUF_SIZE);
-			ret = read_all(fd, copybuf, chunk);
-			if (ret < 0) {
-				return ret;
-			}
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	} else {
-		lseek64(fd, len, SEEK_CUR);
-	}
+  if (crc32) {
+    ret = source->GetCrc32(crc32, len);
+    if (ret < 0) {
+      return ret;
+    }
+  } else {
+    source->Seek(len);
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
-{
-	int ret;
-	int chunk;
-	int64_t len = (int64_t)blocks * s->block_size;
-	uint32_t fill_val;
-	uint32_t *fillbuf;
-	unsigned int i;
+static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
+                              SparseFileSource* source, unsigned int blocks, unsigned int block,
+                              uint32_t* crc32) {
+  int ret;
+  int chunk;
+  int64_t len = (int64_t)blocks * s->block_size;
+  uint32_t fill_val;
+  uint32_t* fillbuf;
+  unsigned int i;
 
-	if (chunk_size != sizeof(fill_val)) {
-		return -EINVAL;
-	}
+  if (chunk_size != sizeof(fill_val)) {
+    return -EINVAL;
+  }
 
-	ret = read_all(fd, &fill_val, sizeof(fill_val));
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->ReadValue(&fill_val, sizeof(fill_val));
+  if (ret < 0) {
+    return ret;
+  }
 
-	ret = sparse_file_add_fill(s, fill_val, len, block);
-	if (ret < 0) {
-		return ret;
-	}
+  ret = sparse_file_add_fill(s, fill_val, len, block);
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32) {
-		/* Fill copy_buf with the fill value */
-		fillbuf = (uint32_t *)copybuf;
-		for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
-			fillbuf[i] = fill_val;
-		}
+  if (crc32) {
+    /* Fill copy_buf with the fill value */
+    fillbuf = (uint32_t*)copybuf;
+    for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+      fillbuf[i] = fill_val;
+    }
 
-		while (len) {
-			chunk = std::min(len, COPY_BUF_SIZE);
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	}
+    while (len) {
+      chunk = std::min(len, COPY_BUF_SIZE);
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd __unused, unsigned int blocks,
-		unsigned int block __unused, uint32_t *crc32)
-{
-	if (chunk_size != 0) {
-		return -EINVAL;
-	}
+static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
+                              SparseFileSource* source __unused, unsigned int blocks,
+                              unsigned int block __unused, uint32_t* crc32) {
+  if (chunk_size != 0) {
+    return -EINVAL;
+  }
 
-	if (crc32) {
-	        int64_t len = (int64_t)blocks * s->block_size;
-		memset(copybuf, 0, COPY_BUF_SIZE);
+  if (crc32) {
+    int64_t len = (int64_t)blocks * s->block_size;
+    memset(copybuf, 0, COPY_BUF_SIZE);
 
-		while (len) {
-			int chunk = std::min(len, COPY_BUF_SIZE);
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	}
+    while (len) {
+      int chunk = std::min(len, COPY_BUF_SIZE);
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
-{
-	uint32_t file_crc32;
-	int ret;
+static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
+  uint32_t file_crc32;
 
-	if (chunk_size != sizeof(file_crc32)) {
-		return -EINVAL;
-	}
+  if (chunk_size != sizeof(file_crc32)) {
+    return -EINVAL;
+  }
 
-	ret = read_all(fd, &file_crc32, sizeof(file_crc32));
-	if (ret < 0) {
-		return ret;
-	}
+  int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32 != NULL && file_crc32 != *crc32) {
-		return -EINVAL;
-	}
+  if (crc32 != NULL && file_crc32 != *crc32) {
+    return -EINVAL;
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
-		unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
-		unsigned int cur_block, uint32_t *crc_ptr)
-{
-	int ret;
-	unsigned int chunk_data_size;
+static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
+                         chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
+  int ret;
+  unsigned int chunk_data_size;
+  int64_t offset = source->GetOffset();
 
-	chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+  chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
 
-	switch (chunk_header->chunk_type) {
-		case CHUNK_TYPE_RAW:
-			ret = process_raw_chunk(s, chunk_data_size, fd, offset,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
-				return ret;
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_FILL:
-			ret = process_fill_chunk(s, chunk_data_size, fd,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
-				return ret;
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_DONT_CARE:
-			ret = process_skip_chunk(s, chunk_data_size, fd,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (chunk_data_size != 0) {
-				if (ret < 0) {
-					verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
-					return ret;
-				}
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_CRC32:
-			ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
-						offset);
-				return ret;
-			}
-			return 0;
-		default:
-			verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
-					chunk_header->chunk_type, offset);
-	}
+  switch (chunk_header->chunk_type) {
+    case CHUNK_TYPE_RAW:
+      ret =
+          process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
+        return ret;
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_FILL:
+      ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+                               crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
+        return ret;
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_DONT_CARE:
+      ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+                               crc_ptr);
+      if (chunk_data_size != 0) {
+        if (ret < 0) {
+          verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
+          return ret;
+        }
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_CRC32:
+      ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
+        return ret;
+      }
+      return 0;
+    default:
+      verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
+                    offset);
+  }
 
-	return 0;
+  return 0;
 }
 
-static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
-{
-	int ret;
-	unsigned int i;
-	sparse_header_t sparse_header;
-	chunk_header_t chunk_header;
-	uint32_t crc32 = 0;
-	uint32_t *crc_ptr = 0;
-	unsigned int cur_block = 0;
-	off64_t offset;
+static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
+  int ret;
+  unsigned int i;
+  sparse_header_t sparse_header;
+  chunk_header_t chunk_header;
+  uint32_t crc32 = 0;
+  uint32_t* crc_ptr = 0;
+  unsigned int cur_block = 0;
 
-	if (!copybuf) {
-		copybuf = (char *)malloc(COPY_BUF_SIZE);
-	}
+  if (!copybuf) {
+    copybuf = (char*)malloc(COPY_BUF_SIZE);
+  }
 
-	if (!copybuf) {
-		return -ENOMEM;
-	}
+  if (!copybuf) {
+    return -ENOMEM;
+  }
 
-	if (crc) {
-		crc_ptr = &crc32;
-	}
+  if (crc) {
+    crc_ptr = &crc32;
+  }
 
-	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
-		return -EINVAL;
-	}
+  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
-		return -EINVAL;
-	}
+  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-		return -EINVAL;
-	}
+  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
-		return -EINVAL;
-	}
+  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
-		/* Skip the remaining bytes in a header that is longer than
-		 * we expected.
-		 */
-		lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
-	}
+  if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+    /* Skip the remaining bytes in a header that is longer than
+     * we expected.
+     */
+    source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+  }
 
-	for (i = 0; i < sparse_header.total_chunks; i++) {
-		ret = read_all(fd, &chunk_header, sizeof(chunk_header));
-		if (ret < 0) {
-			return ret;
-		}
+  for (i = 0; i < sparse_header.total_chunks; i++) {
+    ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
+    if (ret < 0) {
+      return ret;
+    }
 
-		if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
-			/* Skip the remaining bytes in a header that is longer than
-			 * we expected.
-			 */
-			lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
-		}
+    if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+      /* Skip the remaining bytes in a header that is longer than
+       * we expected.
+       */
+      source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+    }
 
-		offset = lseek64(fd, 0, SEEK_CUR);
+    ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
+    if (ret < 0) {
+      return ret;
+    }
 
-		ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
-				cur_block, crc_ptr);
-		if (ret < 0) {
-			return ret;
-		}
+    cur_block += ret;
+  }
 
-		cur_block += ret;
-	}
+  if (sparse_header.total_blks != cur_block) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.total_blks != cur_block) {
-		return -EINVAL;
-	}
-
-	return 0;
+  return 0;
 }
 
-static int sparse_file_read_normal(struct sparse_file *s, int fd)
-{
-	int ret;
-	uint32_t *buf = (uint32_t *)malloc(s->block_size);
-	unsigned int block = 0;
-	int64_t remain = s->len;
-	int64_t offset = 0;
-	unsigned int to_read;
-	unsigned int i;
-	bool sparse_block;
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+  int ret;
+  uint32_t* buf = (uint32_t*)malloc(s->block_size);
+  unsigned int block = 0;
+  int64_t remain = s->len;
+  int64_t offset = 0;
+  unsigned int to_read;
+  unsigned int i;
+  bool sparse_block;
 
-	if (!buf) {
-		return -ENOMEM;
-	}
+  if (!buf) {
+    return -ENOMEM;
+  }
 
-	while (remain > 0) {
-		to_read = std::min(remain, (int64_t)(s->block_size));
-		ret = read_all(fd, buf, to_read);
-		if (ret < 0) {
-			error("failed to read sparse file");
-			free(buf);
-			return ret;
-		}
+  while (remain > 0) {
+    to_read = std::min(remain, (int64_t)(s->block_size));
+    ret = read_all(fd, buf, to_read);
+    if (ret < 0) {
+      error("failed to read sparse file");
+      free(buf);
+      return ret;
+    }
 
-		if (to_read == s->block_size) {
-			sparse_block = true;
-			for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
-				if (buf[0] != buf[i]) {
-					sparse_block = false;
-					break;
-				}
-			}
-		} else {
-			sparse_block = false;
-		}
+    if (to_read == s->block_size) {
+      sparse_block = true;
+      for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+        if (buf[0] != buf[i]) {
+          sparse_block = false;
+          break;
+        }
+      }
+    } else {
+      sparse_block = false;
+    }
 
-		if (sparse_block) {
-			/* TODO: add flag to use skip instead of fill for buf[0] == 0 */
-			sparse_file_add_fill(s, buf[0], to_read, block);
-		} else {
-			sparse_file_add_fd(s, fd, offset, to_read, block);
-		}
+    if (sparse_block) {
+      /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+      sparse_file_add_fill(s, buf[0], to_read, block);
+    } else {
+      sparse_file_add_fd(s, fd, offset, to_read, block);
+    }
 
-		remain -= to_read;
-		offset += to_read;
-		block++;
-	}
+    remain -= to_read;
+    offset += to_read;
+    block++;
+  }
 
-	free(buf);
-	return 0;
+  free(buf);
+  return 0;
 }
 
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
-{
-	if (crc && !sparse) {
-		return -EINVAL;
-	}
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+  if (crc && !sparse) {
+    return -EINVAL;
+  }
 
-	if (sparse) {
-		return sparse_file_read_sparse(s, fd, crc);
-	} else {
-		return sparse_file_read_normal(s, fd);
-	}
+  if (sparse) {
+    SparseFileFdSource source(fd);
+    return sparse_file_read_sparse(s, &source, crc);
+  } else {
+    return sparse_file_read_normal(s, fd);
+  }
 }
 
-struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
-{
-	int ret;
-	sparse_header_t sparse_header;
-	int64_t len;
-	struct sparse_file *s;
-
-	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
-	if (ret < 0) {
-		verbose_error(verbose, ret, "header");
-		return NULL;
-	}
-
-	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
-		verbose_error(verbose, -EINVAL, "header magic");
-		return NULL;
-	}
-
-	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
-		verbose_error(verbose, -EINVAL, "header major version");
-		return NULL;
-	}
-
-	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-		return NULL;
-	}
-
-	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
-		return NULL;
-	}
-
-	len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
-	s = sparse_file_new(sparse_header.blk_sz, len);
-	if (!s) {
-		verbose_error(verbose, -EINVAL, NULL);
-		return NULL;
-	}
-
-	ret = lseek64(fd, 0, SEEK_SET);
-	if (ret < 0) {
-		verbose_error(verbose, ret, "seeking");
-		sparse_file_destroy(s);
-		return NULL;
-	}
-
-	s->verbose = verbose;
-
-	ret = sparse_file_read(s, fd, true, crc);
-	if (ret < 0) {
-		sparse_file_destroy(s);
-		return NULL;
-	}
-
-	return s;
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_read_sparse(s, &source, crc);
 }
 
-struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
-{
-	struct sparse_file *s;
-	int64_t len;
-	int ret;
+static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
+                                                     bool crc) {
+  int ret;
+  sparse_header_t sparse_header;
+  int64_t len;
+  struct sparse_file* s;
 
-	s = sparse_file_import(fd, verbose, crc);
-	if (s) {
-		return s;
-	}
+  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+  if (ret < 0) {
+    verbose_error(verbose, ret, "header");
+    return NULL;
+  }
 
-	len = lseek64(fd, 0, SEEK_END);
-	if (len < 0) {
-		return NULL;
-	}
+  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+    verbose_error(verbose, -EINVAL, "header magic");
+    return NULL;
+  }
 
-	lseek64(fd, 0, SEEK_SET);
+  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+    verbose_error(verbose, -EINVAL, "header major version");
+    return NULL;
+  }
 
-	s = sparse_file_new(4096, len);
-	if (!s) {
-		return NULL;
-	}
+  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+    return NULL;
+  }
 
-	ret = sparse_file_read_normal(s, fd);
-	if (ret < 0) {
-		sparse_file_destroy(s);
-		return NULL;
-	}
+  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+    return NULL;
+  }
 
-	return s;
+  len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+  s = sparse_file_new(sparse_header.blk_sz, len);
+  if (!s) {
+    verbose_error(verbose, -EINVAL, NULL);
+    return NULL;
+  }
+
+  ret = source->SetOffset(0);
+  if (ret < 0) {
+    verbose_error(verbose, ret, "seeking");
+    sparse_file_destroy(s);
+    return NULL;
+  }
+
+  s->verbose = verbose;
+
+  ret = sparse_file_read_sparse(s, source, crc);
+  if (ret < 0) {
+    sparse_file_destroy(s);
+    return NULL;
+  }
+
+  return s;
+}
+
+struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
+  SparseFileFdSource source(fd);
+  return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
+  struct sparse_file* s;
+  int64_t len;
+  int ret;
+
+  s = sparse_file_import(fd, verbose, crc);
+  if (s) {
+    return s;
+  }
+
+  len = lseek64(fd, 0, SEEK_END);
+  if (len < 0) {
+    return NULL;
+  }
+
+  lseek64(fd, 0, SEEK_SET);
+
+  s = sparse_file_new(4096, len);
+  if (!s) {
+    return NULL;
+  }
+
+  ret = sparse_file_read_normal(s, fd);
+  if (ret < 0) {
+    sparse_file_destroy(s);
+    return NULL;
+  }
+
+  return s;
 }
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
 
 __BEGIN_DECLS
 
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
 /* timeout in msecs */
 int sync_wait(int fd, int timeout);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
-                                  struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
 
 __END_DECLS
 
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index a786d3e..49f01e1 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -32,8 +32,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= __ANDROID_API_O__
-
 /* Fences indicate the status of an asynchronous task. They are initially
  * in unsignaled state (0), and make a one-time transition to either signaled
  * (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -63,14 +61,14 @@
  * The original fences remain valid, and the caller is responsible for closing
  * them.
  */
-int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
 
 /**
  * Retrieve detailed information about a sync file and its fences.
  *
  * The returned sync_file_info must be freed by calling sync_file_info_free().
  */
-struct sync_file_info *sync_file_info(int32_t fd);
+struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
 
 /**
  * Get the array of fence infos from the sync file's info.
@@ -88,9 +86,7 @@
 }
 
 /** Free a struct sync_file_info structure */
-void sync_file_info_free(struct sync_file_info *info);
-
-#endif // __ANDROID_API__ >= __ANDROID_API_O__
+void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
 
 __END_DECLS
 
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
 
 #include <android/sync.h>
 
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
 /* Legacy Sync API */
 
 struct sync_legacy_merge_data {
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -15,6 +15,35 @@
 #include <random>
 #include <unordered_map>
 
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+}  // extern "C"
+
 // TODO: better stress tests?
 // Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
 // Handle wraparound in timelines like nvidia.
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9395ef8..bbfa9d8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -47,6 +47,7 @@
 cc_defaults {
     name: "libutils_defaults",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -90,6 +91,10 @@
             },
         },
 
+        recovery: {
+            exclude_shared_libs: ["libvndksupport"],
+        },
+
         host: {
             cflags: ["-DLIBUTILS_NATIVE=1"],
 
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index da28dfa..f074341 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -23,7 +23,7 @@
 #include <utils/Log.h>
 #include <utils/Vector.h>
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
 #include <dlfcn.h>
 #include <vndksupport/linker.h>
 #endif
@@ -70,7 +70,7 @@
 void add_sysprop_change_callback(sysprop_change_callback, int) {}
 #endif
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
 void (*get_report_sysprop_change_func())() {
     void (*func)() = nullptr;
     void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
@@ -85,7 +85,7 @@
 void report_sysprop_change() {
     do_report_sysprop_change();
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
     // libutils.so is double loaded; from the default namespace and from the
     // 'sphal' namespace. Redirect the sysprop change event to the other instance
     // of libutils.so loaded in the 'sphal' namespace so that listeners attached
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 6c06618..2606aa9 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,7 @@
     name: "libziparchive",
     host_supported: true,
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/logcat/.clang-format b/logcat/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logcat/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 01beb53..b0563a6 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2006-2017 The Android Open Source Project
+// Copyright (C) 2006 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.
@@ -31,25 +31,13 @@
     logtags: ["event.logtags"],
 }
 
-cc_library {
-    name: "liblogcat",
-
-    defaults: ["logcat_defaults"],
-    srcs: [
-        "logcat.cpp",
-        "getopt_long.cpp",
-        "logcat_system.cpp",
-    ],
-    export_include_dirs: ["include"],
-}
-
 cc_binary {
     name: "logcat",
 
     defaults: ["logcat_defaults"],
-    shared_libs: ["liblogcat"],
     srcs: [
         "logcat_main.cpp",
+        "logcat.cpp",
     ],
 }
 
@@ -57,9 +45,9 @@
     name: "logcatd",
 
     defaults: ["logcat_defaults"],
-    shared_libs: ["liblogcat"],
     srcs: [
         "logcatd_main.cpp",
+        "logcat.cpp",
     ],
 }
 
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
deleted file mode 100644
index da99906..0000000
--- a/logcat/getopt_long.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
-/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $       */
-
-/*
- * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-/*-
- * Copyright (c) 2000 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Dieter Baron and Thomas Klausner.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include <log/getopt.h>
-
-#define PRINT_ERROR ((context->opterr) && (*options != ':'))
-
-#define FLAG_PERMUTE 0x01  // permute non-options to the end of argv
-#define FLAG_ALLARGS 0x02  // treat non-options as args to option "-1"
-
-// return values
-#define BADCH (int)'?'
-#define BADARG ((*options == ':') ? (int)':' : (int)'?')
-#define INORDER (int)1
-
-#define D_PREFIX 0
-#define DD_PREFIX 1
-#define W_PREFIX 2
-
-// Compute the greatest common divisor of a and b.
-static int gcd(int a, int b) {
-    int c = a % b;
-    while (c) {
-        a = b;
-        b = c;
-        c = a % b;
-    }
-    return b;
-}
-
-// Exchange the block from nonopt_start to nonopt_end with the block from
-// nonopt_end to opt_end (keeping the same order of arguments in each block).
-// Returns optind - (nonopt_end - nonopt_start) for convenience.
-static int permute_args(getopt_context* context, char* const* nargv) {
-    // compute lengths of blocks and number and size of cycles
-    int nnonopts = context->nonopt_end - context->nonopt_start;
-    int nopts = context->optind - context->nonopt_end;
-    int ncycle = gcd(nnonopts, nopts);
-    int cyclelen = (context->optind - context->nonopt_start) / ncycle;
-
-    for (int i = 0; i < ncycle; i++) {
-        int cstart = context->nonopt_end + i;
-        int pos = cstart;
-        for (int j = 0; j < cyclelen; j++) {
-            if (pos >= context->nonopt_end) {
-                pos -= nnonopts;
-            } else {
-                pos += nopts;
-            }
-            char* swap = nargv[pos];
-            const_cast<char**>(nargv)[pos] = nargv[cstart];
-            const_cast<char**>(nargv)[cstart] = swap;
-        }
-    }
-    return context->optind - (context->nonopt_end - context->nonopt_start);
-}
-
-// parse_long_options_r --
-//    Parse long options in argc/argv argument vector.
-// Returns -1 if short_too is set and the option does not match long_options.
-static int parse_long_options_r(char* const* nargv, const char* options,
-                                const struct option* long_options, int* idx,
-                                bool short_too, struct getopt_context* context) {
-    const char* current_argv = context->place;
-    const char* current_dash;
-    switch (context->dash_prefix) {
-        case D_PREFIX:
-            current_dash = "-";
-            break;
-        case DD_PREFIX:
-            current_dash = "--";
-            break;
-        case W_PREFIX:
-            current_dash = "-W ";
-            break;
-        default:
-            current_dash = "";
-            break;
-    }
-    context->optind++;
-
-    const char* has_equal;
-    size_t current_argv_len;
-    if (!!(has_equal = strchr(current_argv, '='))) {
-        // argument found (--option=arg)
-        current_argv_len = has_equal - current_argv;
-        has_equal++;
-    } else {
-        current_argv_len = strlen(current_argv);
-    }
-
-    int match = -1;
-    bool exact_match = false;
-    bool second_partial_match = false;
-    for (int i = 0; long_options[i].name; i++) {
-        // find matching long option
-        if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
-            continue;
-        }
-
-        if (strlen(long_options[i].name) == current_argv_len) {
-            // exact match
-            match = i;
-            exact_match = true;
-            break;
-        }
-        // If this is a known short option, don't allow
-        // a partial match of a single character.
-        if (short_too && current_argv_len == 1) continue;
-
-        if (match == -1) {  // first partial match
-            match = i;
-        } else if (long_options[i].has_arg != long_options[match].has_arg ||
-                   long_options[i].flag != long_options[match].flag ||
-                   long_options[i].val != long_options[match].val) {
-            second_partial_match = true;
-        }
-    }
-    if (!exact_match && second_partial_match) {
-        // ambiguous abbreviation
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr,
-                    "option `%s%.*s' is ambiguous", current_dash,
-                    (int)current_argv_len, current_argv);
-        }
-        context->optopt = 0;
-        return BADCH;
-    }
-    if (match != -1) {  // option found
-        if (long_options[match].has_arg == no_argument && has_equal) {
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr,
-                        "option `%s%.*s' doesn't allow an argument",
-                        current_dash, (int)current_argv_len, current_argv);
-            }
-            // XXX: GNU sets optopt to val regardless of flag
-            context->optopt =
-                long_options[match].flag ? 0 : long_options[match].val;
-            return BADCH;
-        }
-        if (long_options[match].has_arg == required_argument ||
-            long_options[match].has_arg == optional_argument) {
-            if (has_equal) {
-                context->optarg = has_equal;
-            } else if (long_options[match].has_arg == required_argument) {
-                // optional argument doesn't use next nargv
-                context->optarg = nargv[context->optind++];
-            }
-        }
-        if ((long_options[match].has_arg == required_argument) &&
-            !context->optarg) {
-            // Missing argument; leading ':' indicates no error
-            // should be generated.
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr,
-                        "option `%s%s' requires an argument", current_dash,
-                        current_argv);
-            }
-            // XXX: GNU sets optopt to val regardless of flag
-            context->optopt =
-                long_options[match].flag ? 0 : long_options[match].val;
-            context->optind--;
-            return BADARG;
-        }
-    } else {  // unknown option
-        if (short_too) {
-            context->optind--;
-            return -1;
-        }
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
-                    current_dash, current_argv);
-        }
-        context->optopt = 0;
-        return BADCH;
-    }
-    if (idx) *idx = match;
-    if (long_options[match].flag) {
-        *long_options[match].flag = long_options[match].val;
-        return 0;
-    }
-    return long_options[match].val;
-}
-
-// getopt_long_r --
-//    Parse argc/argv argument vector.
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
-                  const struct option* long_options, int* idx,
-                  struct getopt_context* context) {
-    if (!options) return -1;
-
-    // XXX Some GNU programs (like cvs) set optind to 0 instead of
-    // XXX using optreset.  Work around this braindamage.
-    if (!context->optind) context->optind = context->optreset = 1;
-
-    // Disable GNU extensions if options string begins with a '+'.
-    int flags = FLAG_PERMUTE;
-    if (*options == '-') {
-        flags |= FLAG_ALLARGS;
-    } else if (*options == '+') {
-        flags &= ~FLAG_PERMUTE;
-    }
-    if (*options == '+' || *options == '-') options++;
-
-    context->optarg = nullptr;
-    if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
-start:
-    if (context->optreset || !*context->place) {  // update scanning pointer
-        context->optreset = 0;
-        if (context->optind >= nargc) {  // end of argument vector
-            context->place = EMSG;
-            if (context->nonopt_end != -1) {
-                // do permutation, if we have to
-                context->optind = permute_args(context, nargv);
-            } else if (context->nonopt_start != -1) {
-                // If we skipped non-options, set optind to the first of them.
-                context->optind = context->nonopt_start;
-            }
-            context->nonopt_start = context->nonopt_end = -1;
-            return -1;
-        }
-        if (*(context->place = nargv[context->optind]) != '-' ||
-            context->place[1] == '\0') {
-            context->place = EMSG;  // found non-option
-            if (flags & FLAG_ALLARGS) {
-                // GNU extension: return non-option as argument to option 1
-                context->optarg = nargv[context->optind++];
-                return INORDER;
-            }
-            if (!(flags & FLAG_PERMUTE)) {
-                // If no permutation wanted, stop parsing at first non-option.
-                return -1;
-            }
-            // do permutation
-            if (context->nonopt_start == -1) {
-                context->nonopt_start = context->optind;
-            } else if (context->nonopt_end != -1) {
-                context->nonopt_start = permute_args(context, nargv);
-                context->nonopt_end = -1;
-            }
-            context->optind++;
-            // process next argument
-            goto start;
-        }
-        if (context->nonopt_start != -1 && context->nonopt_end == -1) {
-            context->nonopt_end = context->optind;
-        }
-
-        // If we have "-" do nothing, if "--" we are done.
-        if (context->place[1] != '\0' && *++(context->place) == '-' &&
-            context->place[1] == '\0') {
-            context->optind++;
-            context->place = EMSG;
-            // We found an option (--), so if we skipped
-            // non-options, we have to permute.
-            if (context->nonopt_end != -1) {
-                context->optind = permute_args(context, nargv);
-            }
-            context->nonopt_start = context->nonopt_end = -1;
-            return -1;
-        }
-    }
-
-    int optchar;
-    // Check long options if:
-    //  1) we were passed some
-    //  2) the arg is not just "-"
-    //  3) either the arg starts with -- we are getopt_long_only()
-    if (long_options && context->place != nargv[context->optind] &&
-        (*context->place == '-')) {
-        bool short_too = false;
-        context->dash_prefix = D_PREFIX;
-        if (*context->place == '-') {
-            context->place++;  // --foo long option
-            context->dash_prefix = DD_PREFIX;
-        } else if (*context->place != ':' && strchr(options, *context->place)) {
-            short_too = true;  // could be short option too
-        }
-
-        optchar = parse_long_options_r(nargv, options, long_options, idx,
-                                       short_too, context);
-        if (optchar != -1) {
-            context->place = EMSG;
-            return optchar;
-        }
-    }
-
-    const char* oli;  // option letter list index
-    if ((optchar = (int)*(context->place)++) == (int)':' ||
-        (optchar == (int)'-' && *context->place != '\0') ||
-        !(oli = strchr(options, optchar))) {
-        // If the user specified "-" and  '-' isn't listed in
-        // options, return -1 (non-option) as per POSIX.
-        // Otherwise, it is an unknown option character (or ':').
-        if (optchar == (int)'-' && *context->place == '\0') return -1;
-        if (!*context->place) context->optind++;
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr, "invalid option -- %c",
-                    optchar);
-        }
-        context->optopt = optchar;
-        return BADCH;
-    }
-
-    static const char recargchar[] = "option requires an argument -- %c";
-    if (long_options && optchar == 'W' && oli[1] == ';') {
-        // -W long-option
-        if (*context->place) {                      // no space
-            ;                                       // NOTHING
-        } else if (++(context->optind) >= nargc) {  // no arg
-            context->place = EMSG;
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr, recargchar, optchar);
-            }
-            context->optopt = optchar;
-            return BADARG;
-        } else {  // white space
-            context->place = nargv[context->optind];
-        }
-        context->dash_prefix = W_PREFIX;
-        optchar = parse_long_options_r(nargv, options, long_options, idx, false,
-                                       context);
-        context->place = EMSG;
-        return optchar;
-    }
-    if (*++oli != ':') {  // doesn't take argument
-        if (!*context->place) context->optind++;
-    } else {  // takes (optional) argument
-        context->optarg = nullptr;
-        if (*context->place) {  // no white space
-            context->optarg = context->place;
-        } else if (oli[1] != ':') {              // arg not optional
-            if (++(context->optind) >= nargc) {  // no arg
-                context->place = EMSG;
-                if (PRINT_ERROR) {
-                    fprintf(context->optstderr ?: stderr, recargchar, optchar);
-                }
-                context->optopt = optchar;
-                return BADARG;
-            }
-            context->optarg = nargv[context->optind];
-        }
-        context->place = EMSG;
-        context->optind++;
-    }
-    // dump back option letter
-    return optchar;
-}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
deleted file mode 100644
index 0da2b10..0000000
--- a/logcat/include/log/getopt.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _LOG_GETOPT_H_
-#define _LOG_GETOPT_H_
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-#include <getopt.h>
-#include <sys/cdefs.h>
-
-struct getopt_context {
-    int opterr;
-    int optind;
-    int optopt;
-    int optreset;
-    const char* optarg;
-    FILE* optstderr; /* NULL defaults to stderr */
-    /* private */
-    const char* place;
-    int nonopt_start;
-    int nonopt_end;
-    int dash_prefix;
-    /* expansion space */
-    int __extra__;
-    void* __stuff__;
-};
-
-#define EMSG ""
-#define NO_PREFIX (-1)
-
-#define INIT_GETOPT_CONTEXT(context) \
-    context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
-
-__BEGIN_DECLS
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
-                  const struct option* long_options, int* idx,
-                  struct getopt_context* context);
-
-__END_DECLS
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
deleted file mode 100644
index 009672c..0000000
--- a/logcat/include/log/logcat.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2005-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.
- */
-
-#ifndef _LIBS_LOGCAT_H /* header boilerplate */
-#define _LIBS_LOGCAT_H
-
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-/* For managing an in-process logcat function, rather than forking/execing
- *
- * It also serves as the basis for the logcat command.
- *
- * The following C API allows a logcat instance to be created, run
- * to completion, and then release all the associated resources.
- */
-
-/*
- * The opaque context
- */
-#ifndef __android_logcat_context_defined /* typedef boilerplate */
-#define __android_logcat_context_defined
-typedef struct android_logcat_context_internal* android_logcat_context;
-#endif
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command.  The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection.  Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error,
-                               int argc, char* const* argv, char* const* envp);
-
-/* Will not block, performed in-process
- *
- * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
- * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
- * scripted error (stderr) redirection.
- */
-int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
-                                      char* const* argv, char* const* envp);
-int android_logcat_run_command_thread_running(android_logcat_context ctx);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
-
-/* derived helpers */
-
-/*
- * In-process thread that acts like somewhat like libc-like system and popen
- * respectively.  Can not handle shell scripting, only pure calls to the
- * logcat operations. The android_logcat_system is a wrapper for the
- * create_android_logcat, android_logcat_run_command and android_logcat_destroy
- * API above.  The android_logcat_popen is a wrapper for the
- * android_logcat_run_command_thread API above.  The android_logcat_pclose is
- * a wrapper for a reasonable wait until output has subsided for command
- * completion, fclose on the FILE pointer and the android_logcat_destroy API.
- */
-int android_logcat_system(const char* command);
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ff85f54..0f56337 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+#include "logcat.h"
+
 #include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <math.h>
 #include <pthread.h>
 #include <sched.h>
@@ -47,8 +50,6 @@
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
-#include <log/getopt.h>
-#include <log/logcat.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <system/thread_defs.h>
@@ -854,14 +855,8 @@
     // net for stability dealing with possible mistaken inputs.
     static const char delimiters[] = ",:; \t\n\r\f";
 
-    struct getopt_context optctx;
-    INIT_GETOPT_CONTEXT(optctx);
-    optctx.opterr = !!context->error;
-    optctx.optstderr = context->error;
-
-    for (;;) {
-        int ret;
-
+    optind = 0;
+    while (true) {
         int option_index = 0;
         // list of long-argument only strings for later comparison
         static const char pid_str[] = "pid";
@@ -902,19 +897,18 @@
         };
         // clang-format on
 
-        ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
-                            long_options, &option_index, &optctx);
-        if (ret < 0) break;
+        int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+                            &option_index);
+        if (c == -1) break;
 
-        switch (ret) {
+        switch (c) {
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     // ToDo: determine runtime PID_MAX?
-                    if (!getSizeTArg(optctx.optarg, &pid, 1)) {
+                    if (!getSizeTArg(optarg, &pid, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name,
-                                     optctx.optarg);
+                                     long_options[option_index].name, optarg);
                         goto exit;
                     }
                     break;
@@ -924,11 +918,9 @@
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
-                    if (optctx.optarg &&
-                        !getSizeTArg(optctx.optarg, &dummy, 1)) {
+                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name,
-                                     optctx.optarg);
+                                     long_options[option_index].name, optarg);
                         goto exit;
                     }
                     if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
@@ -949,8 +941,7 @@
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
-                    setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
-                                                                : nullptr;
+                    setId = (optarg && optarg[0]) ? optarg : nullptr;
                 }
                 break;
 
@@ -978,32 +969,27 @@
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
             // FALLTHRU
             case 'T':
-                if (strspn(optctx.optarg, "0123456789") !=
-                    strlen(optctx.optarg)) {
-                    char* cp = parseTime(tail_time, optctx.optarg);
+                if (strspn(optarg, "0123456789") != strlen(optarg)) {
+                    char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        logcat_panic(context, HELP_FALSE,
-                                     "-%c \"%s\" not in time format\n", ret,
-                                     optctx.optarg);
+                        logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
+                                     optarg);
                         goto exit;
                     }
                     if (*cp) {
-                        char c = *cp;
+                        char ch = *cp;
                         *cp = '\0';
                         if (context->error) {
-                            fprintf(
-                                context->error,
-                                "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
-                                ret, optctx.optarg, c, cp + 1);
+                            fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+                                    c, optarg, ch, cp + 1);
                         }
-                        *cp = c;
+                        *cp = ch;
                     }
                 } else {
-                    if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
+                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
                         if (context->error) {
-                            fprintf(context->error,
-                                    "WARNING: -%c %s invalid, setting to 1\n",
-                                    ret, optctx.optarg);
+                            fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
+                                    optarg);
                         }
                         tail_lines = 1;
                     }
@@ -1015,21 +1001,19 @@
                 break;
 
             case 'e':
-                context->regex = new pcrecpp::RE(optctx.optarg);
+                context->regex = new pcrecpp::RE(optarg);
                 break;
 
             case 'm': {
-                if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
+                if (!getSizeTArg(optarg, &context->maxCount)) {
                     logcat_panic(context, HELP_FALSE,
-                                 "-%c \"%s\" isn't an "
-                                 "integer greater than zero\n",
-                                 ret, optctx.optarg);
+                                 "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
                     goto exit;
                 }
             } break;
 
             case 'g':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getLogSize = true;
                     break;
                 }
@@ -1037,8 +1021,8 @@
 
             case 'G': {
                 char* cp;
-                if (strtoll(optctx.optarg, &cp, 0) > 0) {
-                    setLogSize = strtoll(optctx.optarg, &cp, 0);
+                if (strtoll(optarg, &cp, 0) > 0) {
+                    setLogSize = strtoll(optarg, &cp, 0);
                 } else {
                     setLogSize = 0;
                 }
@@ -1071,19 +1055,18 @@
             } break;
 
             case 'p':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getPruneList = true;
                     break;
                 }
             // FALLTHRU
 
             case 'P':
-                setPruneList = optctx.optarg;
+                setPruneList = optarg;
                 break;
 
             case 'b': {
-                std::unique_ptr<char, void (*)(void*)> buffers(
-                    strdup(optctx.optarg), free);
+                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
                 char* arg = buffers.get();
                 unsigned idMask = 0;
                 char* sv = nullptr;  // protect against -ENOMEM above
@@ -1147,40 +1130,33 @@
 
             case 'f':
                 if ((tail_time == log_time::EPOCH) && !tail_lines) {
-                    tail_time = lastLogTime(optctx.optarg);
+                    tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
-                context->outputFileName = optctx.optarg;
+                context->outputFileName = optarg;
                 break;
 
             case 'r':
-                if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
-                                 1)) {
-                    logcat_panic(context, HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -r\n",
-                                 optctx.optarg);
+                if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
                     goto exit;
                 }
                 break;
 
             case 'n':
-                if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
-                    logcat_panic(context, HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -n\n",
-                                 optctx.optarg);
+                if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
                     goto exit;
                 }
                 break;
 
             case 'v': {
-                if (!strcmp(optctx.optarg, "help") ||
-                    !strcmp(optctx.optarg, "--help")) {
+                if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
                     show_format_help(context);
                     context->retval = EXIT_SUCCESS;
                     goto exit;
                 }
-                std::unique_ptr<char, void (*)(void*)> formats(
-                    strdup(optctx.optarg), free);
+                std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
                 char* arg = formats.get();
                 char* sv = nullptr;  // protect against -ENOMEM above
                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
@@ -1300,8 +1276,7 @@
                 break;
 
             case ':':
-                logcat_panic(context, HELP_TRUE,
-                             "Option -%c needs an argument\n", optctx.optopt);
+                logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
                 goto exit;
 
             case 'h':
@@ -1310,8 +1285,7 @@
                 goto exit;
 
             default:
-                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
-                             optctx.optopt);
+                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
                 goto exit;
         }
     }
@@ -1400,7 +1374,7 @@
                          "Invalid filter expression in logcat args\n");
             goto exit;
         }
-    } else if (argc == optctx.optind) {
+    } else if (argc == optind) {
         // Add from environment variable
         const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
@@ -1416,7 +1390,7 @@
         }
     } else {
         // Add from commandline
-        for (int i = optctx.optind ; i < argc ; i++) {
+        for (int i = optind ; i < argc ; i++) {
             // skip stderr redirections of _all_ kinds
             if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
             // skip stdout redirections of _all_ kinds
@@ -1707,105 +1681,6 @@
     return __logcat(context);
 }
 
-// starts a thread, opens a pipe, returns reading end.
-int android_logcat_run_command_thread(android_logcat_context ctx,
-                                      int argc, char* const* argv,
-                                      char* const* envp) {
-    android_logcat_context_internal* context = ctx;
-
-    int save_errno = EBUSY;
-    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
-
-    if (pipe(context->fds) < 0) {
-        save_errno = errno;
-        goto exit;
-    }
-
-    pthread_attr_t attr;
-    if (pthread_attr_init(&attr)) {
-        save_errno = errno;
-        goto close_exit;
-    }
-
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
-    pthread_attr_setschedparam(&attr, &param);
-    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
-    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
-        save_errno = errno;
-        goto pthread_attr_exit;
-    }
-
-    context->stop = false;
-    context->thread_stopped = false;
-    context->output_fd = context->fds[1];
-    // save off arguments so they remain while thread is active.
-    for (int i = 0; i < argc; ++i) {
-        context->args.push_back(std::string(argv[i]));
-    }
-    // save off environment so they remain while thread is active.
-    if (envp) for (size_t i = 0; envp[i]; ++i) {
-        context->envs.push_back(std::string(envp[i]));
-    }
-
-    for (auto& str : context->args) {
-        context->argv_hold.push_back(str.c_str());
-    }
-    context->argv_hold.push_back(nullptr);
-    for (auto& str : context->envs) {
-        context->envp_hold.push_back(str.c_str());
-    }
-    context->envp_hold.push_back(nullptr);
-
-    context->argc = context->argv_hold.size() - 1;
-    context->argv = (char* const*)&context->argv_hold[0];
-    context->envp = (char* const*)&context->envp_hold[0];
-
-#ifdef DEBUG
-    fprintf(stderr, "argv[%d] = {", context->argc);
-    for (auto str : context->argv_hold) {
-        fprintf(stderr, " \"%s\"", str ?: "nullptr");
-    }
-    fprintf(stderr, " }\n");
-    fflush(stderr);
-#endif
-    context->retval = EXIT_SUCCESS;
-    if (pthread_create(&context->thr, &attr,
-                       (void*(*)(void*))__logcat, context)) {
-        save_errno = errno;
-        goto argv_exit;
-    }
-    pthread_attr_destroy(&attr);
-
-    return context->fds[0];
-
-argv_exit:
-    context->argv_hold.clear();
-    context->args.clear();
-    context->envp_hold.clear();
-    context->envs.clear();
-pthread_attr_exit:
-    pthread_attr_destroy(&attr);
-close_exit:
-    close(context->fds[0]);
-    context->fds[0] = -1;
-    close(context->fds[1]);
-    context->fds[1] = -1;
-exit:
-    errno = save_errno;
-    context->stop = true;
-    context->thread_stopped = true;
-    context->retval = EXIT_FAILURE;
-    return -1;
-}
-
-// test if the thread is still doing 'stuff'
-int android_logcat_run_command_thread_running(android_logcat_context ctx) {
-    android_logcat_context_internal* context = ctx;
-
-    return context->thread_stopped == false;
-}
-
 // Finished with context
 int android_logcat_destroy(android_logcat_context* ctx) {
     android_logcat_context_internal* context = *ctx;
diff --git a/logcat/logcat.h b/logcat/logcat.h
new file mode 100644
index 0000000..85ed7da
--- /dev/null
+++ b/logcat/logcat.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-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.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+/*
+ * The opaque context
+ */
+typedef struct android_logcat_context_internal* android_logcat_context;
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command.  The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection.  Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
+                               char* const* argv, char* const* envp);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
index 9477e79..ecfa2ba 100644
--- a/logcat/logcat_main.cpp
+++ b/logcat/logcat_main.cpp
@@ -17,7 +17,7 @@
 #include <signal.h>
 #include <stdlib.h>
 
-#include <log/logcat.h>
+#include "logcat.h"
 
 int main(int argc, char** argv, char** envp) {
     android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
deleted file mode 100644
index 6dfd110..0000000
--- a/logcat/logcat_system.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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 <ctype.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include <log/logcat.h>
-
-static std::string unquote(const char*& cp, const char*& delim) {
-    if ((*cp == '\'') || (*cp == '"')) {
-        // KISS: Simple quotes. Do not handle the case
-        //       of concatenation like "blah"foo'bar'
-        char quote = *cp++;
-        delim = strchr(cp, quote);
-        if (!delim) delim = cp + strlen(cp);
-        std::string str(cp, delim);
-        if (*delim) ++delim;
-        return str;
-    }
-    delim = strpbrk(cp, " \t\f\r\n");
-    if (!delim) delim = cp + strlen(cp);
-    return std::string(cp, delim);
-}
-
-static bool __android_logcat_parse(const char* command,
-                                   std::vector<std::string>& args,
-                                   std::vector<std::string>& envs) {
-    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
-        while (isspace(*cp)) ++cp;
-        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
-            const char* env = cp;
-            while (isalnum(*cp) || (*cp == '_')) ++cp;
-            if (cp && (*cp == '=')) {
-                std::string str(env, ++cp);
-                str += unquote(cp, delim);
-                envs.push_back(str);
-                continue;
-            }
-            cp = env;
-        }
-        args.push_back(unquote(cp, delim));
-        if ((args.size() == 1) && (args[0] != "logcat") &&
-            (args[0] != "/system/bin/logcat")) {
-            return false;
-        }
-    }
-    return args.size() != 0;
-}
-
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
-    *ctx = NULL;
-
-    std::vector<std::string> args;
-    std::vector<std::string> envs;
-    if (!__android_logcat_parse(command, args, envs)) return NULL;
-
-    std::vector<const char*> argv;
-    for (auto& str : args) {
-        argv.push_back(str.c_str());
-    }
-    argv.push_back(NULL);
-
-    std::vector<const char*> envp;
-    for (auto& str : envs) {
-        envp.push_back(str.c_str());
-    }
-    envp.push_back(NULL);
-
-    *ctx = create_android_logcat();
-    if (!*ctx) return NULL;
-
-    int fd = android_logcat_run_command_thread(
-        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
-    argv.clear();
-    args.clear();
-    envp.clear();
-    envs.clear();
-    if (fd < 0) {
-        android_logcat_destroy(ctx);
-        return NULL;
-    }
-
-    int duped = dup(fd);
-    FILE* retval = fdopen(duped, "reb");
-    if (!retval) {
-        close(duped);
-        android_logcat_destroy(ctx);
-    }
-    return retval;
-}
-
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
-    if (*ctx) {
-        static const useconds_t wait_sample = 20000;
-        // Wait two seconds maximum
-        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
-             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
-            usleep(wait_sample);
-        }
-    }
-
-    if (output) fclose(output);
-    return android_logcat_destroy(ctx);
-}
-
-int android_logcat_system(const char* command) {
-    std::vector<std::string> args;
-    std::vector<std::string> envs;
-    if (!__android_logcat_parse(command, args, envs)) return -1;
-
-    std::vector<const char*> argv;
-    for (auto& str : args) {
-        argv.push_back(str.c_str());
-    }
-    argv.push_back(NULL);
-
-    std::vector<const char*> envp;
-    for (auto& str : envs) {
-        envp.push_back(str.c_str());
-    }
-    envp.push_back(NULL);
-
-    android_logcat_context ctx = create_android_logcat();
-    if (!ctx) return -1;
-    /* Command return value */
-    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
-                                            (char* const*)&argv[0],
-                                            (char* const*)&envp[0]);
-    /* destroy return value */
-    int ret = android_logcat_destroy(&ctx);
-    /* Paranoia merging any discrepancies between the two return values */
-    if (!ret) ret = retval;
-    return ret;
-}
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
index 9109eb1..c131846 100644
--- a/logcat/logcatd_main.cpp
+++ b/logcat/logcatd_main.cpp
@@ -21,7 +21,7 @@
 #include <string>
 #include <vector>
 
-#include <log/logcat.h>
+#include "logcat.h"
 
 int main(int argc, char** argv, char** envp) {
     android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index defd3c4..66f6724 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -32,7 +32,6 @@
 
 benchmark_src_files := \
     logcat_benchmark.cpp \
-    exec_benchmark.cpp \
 
 # Build benchmarks for the device. Run with:
 #   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -41,7 +40,7 @@
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
 LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase liblogcat
+LOCAL_SHARED_LIBRARIES := libbase
 include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
@@ -51,7 +50,6 @@
 test_src_files := \
     logcat_test.cpp \
     logcatd_test.cpp \
-    liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -59,6 +57,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
+LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
deleted file mode 100644
index c30a5f5..0000000
--- a/logcat/tests/exec_benchmark.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 <stdio.h>
-
-#include <android-base/file.h>
-#include <benchmark/benchmark.h>
-#include <log/logcat.h>
-
-// Dump the statistics and report results
-
-static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        FILE* fp = popen(cmd, "r");
-        std::string ret;
-        android::base::ReadFdToString(fileno(fp), &ret);
-        pclose(fp);
-    }
-}
-
-static void BM_logcat_stat_popen_libc(benchmark::State& state) {
-    logcat_popen_libc(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_libc);
-
-static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        android_logcat_context ctx;
-        FILE* fp = android_logcat_popen(&ctx, cmd);
-        std::string ret;
-        android::base::ReadFdToString(fileno(fp), &ret);
-        android_logcat_pclose(&ctx, fp);
-    }
-}
-
-static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
-    logcat_popen_liblogcat(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_liblogcat);
-
-static void logcat_system_libc(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        system(cmd);
-    }
-}
-
-static void BM_logcat_stat_system_libc(benchmark::State& state) {
-    logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_libc);
-
-static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        android_logcat_system(cmd);
-    }
-}
-
-static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
-    logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_liblogcat);
-
-// Dump the logs and report results
-
-static void BM_logcat_dump_popen_libc(benchmark::State& state) {
-    logcat_popen_libc(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_libc);
-
-static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
-    logcat_popen_liblogcat(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_liblogcat);
-
-static void BM_logcat_dump_system_libc(benchmark::State& state) {
-    logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_libc);
-
-static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
-    logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
deleted file mode 100644
index c8a00da..0000000
--- a/logcat/tests/liblogcat_test.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 <log/logcat.h>
-
-#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&(context), command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
-#define logcat_system(command) android_logcat_system(command)
-#define logcat liblogcat
-
-#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 786fb14..cc1632a 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -37,12 +37,6 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
-#ifndef logcat_popen
-#define logcat_define(context)
-#define logcat_popen(context, command) popen((command), "r")
-#define logcat_pclose(context, fp) pclose(fp)
-#define logcat_system(command) system(command)
-#endif
 #ifndef logcat_executable
 #define USING_LOGCAT_EXECUTABLE_DEFAULT
 #define logcat_executable "logcat"
@@ -78,7 +72,6 @@
 
 TEST(logcat, buckets) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.buckets"
@@ -90,10 +83,9 @@
     __android_log_bswrite(0, logcat_executable ".inject.buckets");
     rest();
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(
-                     ctx, logcat_executable
-                     " -b radio -b events -b system -b main -d 2>/dev/null")));
+    ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+                                    " -b radio -b events -b system -b main -d 2>/dev/null",
+                                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -111,7 +103,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_EQ(ids, 15);
 
@@ -120,7 +112,6 @@
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.filter"
@@ -135,7 +126,7 @@
         logcat_executable
         " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
         getpid());
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
+    ASSERT_TRUE(NULL != (fp = popen(command.c_str(), "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -145,7 +136,7 @@
         if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     // logcat, liblogcat and logcatd test instances result in the progression
     // of 3, 6 and 9 for our counts as each round is performed.
@@ -191,7 +182,6 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
         char needle[32];
         time_t now;
@@ -205,9 +195,8 @@
 #endif
         strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-        ASSERT_TRUE(NULL != (fp = logcat_popen(
-                                 ctx, logcat_executable
-                                 " -v long -v year -b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL !=
+                    (fp = popen(logcat_executable " -v long -v year -b all -t 3 2>/dev/null", "r")));
 
         char buffer[BIG_BUFFER];
 
@@ -218,7 +207,7 @@
                 ++count;
             }
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -268,12 +257,10 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
-        ASSERT_TRUE(NULL !=
-                    (fp = logcat_popen(ctx, logcat_executable
-                                       " -v long -v America/Los_Angeles "
-                                       "-b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+                                        " -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+                                        "r")));
 
         char buffer[BIG_BUFFER];
 
@@ -287,7 +274,7 @@
             }
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -296,11 +283,11 @@
 
 TEST(logcat, ntz) {
     FILE* fp;
-    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
-                                           " -v long -v America/Los_Angeles -v "
-                                           "zone -b all -t 3 2>/dev/null")));
+    ASSERT_TRUE(NULL !=
+                (fp = popen(logcat_executable
+                            " -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
+                            "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -312,7 +299,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(0, count);
 }
@@ -330,8 +317,7 @@
                  "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
 
         FILE* fp;
-        logcat_define(ctx);
-        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
         count = 0;
 
@@ -339,7 +325,7 @@
             ++count;
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < num) && --tries && inject(num - count));
 
@@ -377,8 +363,7 @@
 
     do {
         snprintf(buffer, sizeof(buffer), "%s -t 10 2>&1", cmd);
-        logcat_define(ctx);
-        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
         count = 0;
 
         while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -391,7 +376,7 @@
             free(last_timestamp);
             last_timestamp = strdup(input);
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 10) && --tries && inject(10 - count));
 
@@ -401,8 +386,7 @@
     EXPECT_TRUE(second_timestamp != NULL);
 
     snprintf(buffer, sizeof(buffer), "%s -t '%s' 2>&1", cmd, first_timestamp);
-    logcat_define(ctx);
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     int second_count = 0;
     int last_timestamp_count = -1;
@@ -442,7 +426,7 @@
             last_timestamp_count = second_count;
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_TRUE(found);
     if (!found) {
@@ -483,10 +467,8 @@
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
     FILE* fp;
-    logcat_define(ctx);
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx, logcat_executable
-                                   " -v brief -b events -t 100 2>/dev/null")));
+                (fp = popen(logcat_executable " -v brief -b events -t 100 2>/dev/null", "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -507,7 +489,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(1, count);
 }
@@ -521,12 +503,9 @@
 
     FILE* fp[256];  // does this count as a multitude!
     memset(fp, 0, sizeof(fp));
-    logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
     size_t num = 0;
     do {
-        EXPECT_TRUE(NULL !=
-                    (fp[num] = logcat_popen(ctx[num], logcat_executable
-                                            " -v brief -b events -t 100")));
+        EXPECT_TRUE(NULL != (fp[num] = popen(logcat_executable " -v brief -b events -t 100", "r")));
         if (!fp[num]) {
             fprintf(stderr,
                     "WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -556,7 +535,7 @@
             }
         }
 
-        logcat_pclose(ctx[idx], fp[idx]);
+        pclose(fp[idx]);
     }
 
     ASSERT_EQ(num, count);
@@ -564,10 +543,9 @@
 
 static int get_groups(const char* cmd) {
     FILE* fp;
-    logcat_define(ctx);
 
     // NB: crash log only available in user space
-    EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
+    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
 
     if (fp == NULL) {
         return 0;
@@ -631,7 +609,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     return count;
 }
@@ -815,7 +793,7 @@
     snprintf(command, sizeof(command), comm, buf);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
@@ -861,7 +839,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
@@ -920,7 +898,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -969,7 +947,7 @@
     // re-run the command, it should only add a few lines more content if it
     // continues where it left off.
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -1052,7 +1030,7 @@
                  tmp_out_dir, log_filename, num_val);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+        EXPECT_FALSE(IsFalse(ret = system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
@@ -1082,7 +1060,7 @@
         strcat(command, clear_cmd);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+        EXPECT_FALSE(IsFalse(ret = system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(system(command));
@@ -1120,7 +1098,7 @@
 
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
-    int ret = logcat_system(command);
+    int ret = system(command);
     if (ret) {
         fprintf(stderr, "system(\"%s\")=%d", command, ret);
         return -1;
@@ -1194,7 +1172,7 @@
         " -b all -d"
         " -f /das/nein/gerfingerpoken/logcat/log.txt"
         " -n 256 -r 1024";
-    EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(0 == system(command), command));
 }
 
 #ifndef logcat
@@ -1329,10 +1307,7 @@
 #endif
 
 static bool get_white_black(char** list) {
-    FILE* fp;
-    logcat_define(ctx);
-
-    fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
+    FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1360,19 +1335,15 @@
             asprintf(list, "%s", buf);
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
     return *list != NULL;
 }
 
 static bool set_white_black(const char* list) {
-    FILE* fp;
-    logcat_define(ctx);
-
     char buffer[BIG_BUFFER];
-
     snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
              list ? list : "");
-    fp = logcat_popen(ctx, buffer);
+    FILE* fp = popen(buffer, "r");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
         return false;
@@ -1391,10 +1362,10 @@
             continue;
         }
         fprintf(stderr, "%s\n", buf);
-        logcat_pclose(ctx, fp);
+        pclose(fp);
         return false;
     }
-    return logcat_pclose(ctx, fp) == 0;
+    return pclose(fp) == 0;
 }
 
 TEST(logcat, white_black_adjust) {
@@ -1429,7 +1400,6 @@
 
 TEST(logcat, regex) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1450,7 +1420,7 @@
     // Let the logs settle
     rest();
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1462,14 +1432,13 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(2, count);
 }
 
 TEST(logcat, maxcount) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1488,7 +1457,7 @@
 
     rest();
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1498,7 +1467,7 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1510,13 +1479,7 @@
     ;
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, logcat_executable
-                            " -v brief"
-                            " -b events"
-                            " -v descriptive"
-                            " -t 100"
-                            " 2>/dev/null");
+    FILE* fp = popen(logcat_executable " -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
     if (!fp) {
         fprintf(stderr, "End_to_End: popen failed");
         return false;
@@ -1551,7 +1514,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // Help us pinpoint where things went wrong ...
@@ -1741,13 +1704,12 @@
 }
 
 static bool reportedSecurity(const char* command) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, command);
+    FILE* fp = popen(command, "r");
     if (!fp) return true;
 
     std::string ret;
     bool val = android::base::ReadFdToString(fileno(fp), &ret);
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if (!val) return true;
     return std::string::npos != ret.find("'security'");
@@ -1762,13 +1724,12 @@
 }
 
 static size_t commandOutputSize(const char* command) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, command);
+    FILE* fp = popen(command, "r");
     if (!fp) return 0;
 
     std::string ret;
     if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
-    if (logcat_pclose(ctx, fp) != 0) return 0;
+    if (pclose(fp) != 0) return 0;
 
     return ret.size();
 }
diff --git a/logd/main.cpp b/logd/main.cpp
index 5a588be..b697d44 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -87,30 +87,25 @@
 //
 
 static int drop_privs(bool klogd, bool auditd) {
-    // Tricky, if ro.build.type is "eng" then this is true because of the
-    // side effect that ro.debuggable == 1 as well, else it is false.
-    bool eng =
-        __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
-
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
+    sched_param param = {};
 
     if (set_sched_policy(0, SP_BACKGROUND) < 0) {
         android::prdebug("failed to set background scheduling policy");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
         android::prdebug("failed to set batch scheduler");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
         android::prdebug("failed to set background cgroup");
-        if (!eng) return -1;
+        return -1;
     }
 
-    if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
+    if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+        prctl(PR_SET_DUMPABLE, 0) == -1) {
         android::prdebug("failed to clear PR_SET_DUMPABLE");
         return -1;
     }
@@ -133,24 +128,24 @@
         android::prdebug(
             "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
             errno);
-        if (!eng) return -1;
+        return -1;
     }
 
     gid_t groups[] = { AID_READPROC };
 
     if (setgroups(arraysize(groups), groups) == -1) {
         android::prdebug("failed to set AID_READPROC groups");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setgid(AID_LOGD) != 0) {
         android::prdebug("failed to set AID_LOGD gid");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setuid(AID_LOGD) != 0) {
         android::prdebug("failed to set AID_LOGD uid");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
@@ -161,7 +156,7 @@
     }
     if (cap_set_proc(caps.get()) < 0) {
         android::prdebug("failed to clear CAP_SETGID (%d)", errno);
-        if (!eng) return -1;
+        return -1;
     }
 
     return 0;
@@ -468,7 +463,7 @@
     bool auditd =
         __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
     if (drop_privs(klogd, auditd) != 0) {
-        return -1;
+        return EXIT_FAILURE;
     }
 
     // Serves the purpose of managing the last logs times read on a
@@ -496,7 +491,7 @@
 
     LogReader* reader = new LogReader(logBuf);
     if (reader->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogListener listens on /dev/socket/logdw for client
@@ -506,7 +501,7 @@
     LogListener* swl = new LogListener(logBuf, reader);
     // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
     if (swl->startListener(600)) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
@@ -514,7 +509,7 @@
 
     CommandListener* cl = new CommandListener(logBuf, reader, swl);
     if (cl->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogAudit listens on NETLINK_AUDIT socket for selinux
@@ -549,5 +544,5 @@
 
     TEMP_FAILURE_RETRY(pause());
 
-    exit(0);
+    return EXIT_SUCCESS;
 }
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
index 39448cf..3a00860 100644
--- a/mkbootimg/OWNERS
+++ b/mkbootimg/OWNERS
@@ -1 +1,2 @@
+hridya@google.com
 tbao@google.com
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3c9e5f3..478b8d0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -201,6 +201,7 @@
 	$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
 	$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
 	$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+	$$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
 
 llndk_libraries_list :=
 vndksp_libraries_list :=
@@ -306,6 +307,16 @@
 _enforce_vndk_lite_at_runtime :=
 
 #######################################
+# ld.config.txt for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.recovery.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := etc/ld.config.recovery.txt
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
 # llndk.libraries.txt
 include $(CLEAR_VARS)
 LOCAL_MODULE := llndk.libraries.txt
diff --git a/rootdir/etc/ld.config.recovery.txt b/rootdir/etc/ld.config.recovery.txt
new file mode 100644
index 0000000..5d6c01a
--- /dev/null
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Bionic loader config file for recovery mode
+#
+
+dir.recovery = /system/bin
+
+[recovery]
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index a0b1996..42dc7ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,7 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -39,7 +39,7 @@
 namespace.default.isolated = true
 
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
 
 # We can't have entire /system/${LIB} as permitted paths because doing so
 # makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -51,7 +51,7 @@
 namespace.default.permitted.paths  = /system/${LIB}/drm
 namespace.default.permitted.paths += /system/${LIB}/extractors
 namespace.default.permitted.paths += /system/${LIB}/hw
-namespace.default.permitted.paths += /product/${LIB}
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
 # These are where odex files are located. libart has to be able to dlopen the files
 namespace.default.permitted.paths += /system/framework
 namespace.default.permitted.paths += /system/app
@@ -63,9 +63,9 @@
 namespace.default.permitted.paths += /odm/app
 namespace.default.permitted.paths += /odm/priv-app
 namespace.default.permitted.paths += /oem/app
-namespace.default.permitted.paths += /product/framework
-namespace.default.permitted.paths += /product/app
-namespace.default.permitted.paths += /product/priv-app
+namespace.default.permitted.paths += /%PRODUCT%/framework
+namespace.default.permitted.paths += /%PRODUCT%/app
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
 
@@ -88,10 +88,10 @@
 namespace.default.asan.permitted.paths += /odm/app
 namespace.default.asan.permitted.paths += /odm/priv-app
 namespace.default.asan.permitted.paths += /oem/app
-namespace.default.asan.permitted.paths += /product/${LIB}
-namespace.default.asan.permitted.paths += /product/framework
-namespace.default.asan.permitted.paths += /product/app
-namespace.default.asan.permitted.paths += /product/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
 
 ###############################################################################
@@ -327,7 +327,7 @@
 namespace.system.isolated = false
 
 namespace.system.search.paths  = /system/${LIB}
-namespace.system.search.paths += /product/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
 
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
@@ -345,4 +345,4 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index c1cd3e0..dfb88f7 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -13,10 +13,13 @@
         "newfs_msdos",
         "reboot",
         "sh",
+        "sh.recovery",
         "sh_vendor",
         "toolbox",
+        "toolbox.recovery",
         "toolbox_vendor",
         "toybox",
+        "toybox.recovery",
         "toybox_vendor",
     ],
 }
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 97f2310..fc51705 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -44,6 +44,7 @@
 cc_binary {
     name: "toolbox",
     defaults: ["toolbox_binary_defaults"],
+    recovery_available: true,
 }
 
 cc_binary {