Merge "Don't check permissions bits on init scripts for host_init_verifier"
diff --git a/adb/Android.bp b/adb/Android.bp
index 53bf7e3..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",
 ]
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 06fcf16..e07dba7 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1795,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]);
     }
 
 
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/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 923442f..b0b4839 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -57,12 +57,15 @@
         "libavb",
         "libfstab",
         "libdm",
+        "liblp",
     ],
     export_static_lib_headers: [
         "libfstab",
         "libdm",
+        "liblp",
     ],
     whole_static_libs: [
+        "liblp",
         "liblogwrap",
         "libdm",
         "libfstab",
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/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/init/Android.bp b/init/Android.bp
index 7d863c8..cf7637f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -76,7 +76,6 @@
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libselinux",
     ],
     shared_libs: [
         "libcutils",
@@ -87,6 +86,7 @@
         "libc++",
         "libdl",
         "libz",
+        "libselinux",
     ],
 }
 
diff --git a/init/Android.mk b/init/Android.mk
index da27a73..a81a0f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -61,7 +61,6 @@
     libseccomp_policy \
     libcrypto_utils \
     libsparse \
-    libselinux \
     libprocessgroup \
     libavb \
     libkeyutils \
@@ -76,6 +75,7 @@
     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
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/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/logcat/logcat.h b/logcat/logcat.h
index 989a459..85ed7da 100644
--- a/logcat/logcat.h
+++ b/logcat/logcat.h
@@ -14,42 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOGCAT_H /* header boilerplate */
-#define _LIBS_LOGCAT_H
+#pragma once
 
 #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
  *
@@ -86,11 +58,3 @@
  * non-zero for any errors.
  */
 int android_logcat_destroy(android_logcat_context* ctx);
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOGCAT_H */
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 70b6a3e..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 :=
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index eebad2b..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,8 +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 += /system/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
@@ -64,12 +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 += /system/product/framework
-namespace.default.permitted.paths += /system/product/app
-namespace.default.permitted.paths += /system/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
 
@@ -92,14 +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 += /system/product/${LIB}
-namespace.default.asan.permitted.paths += /system/product/framework
-namespace.default.asan.permitted.paths += /system/product/app
-namespace.default.asan.permitted.paths += /system/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
 
 ###############################################################################
@@ -335,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}
@@ -353,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}