Merge "libunwindstack: don't save pseudoregisters while evaluating Dwarf"
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index eb28668..8c41c5e 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -108,9 +108,12 @@
     // AID_NET_BW_STATS to read out qtaguid statistics
     // AID_READPROC for reading /proc entries across UID boundaries
     // AID_UHID for using 'hid' command to read/write to /dev/uhid
+    // AID_EXT_DATA_RW for writing to /sdcard/Android/data (devices without sdcardfs)
+    // AID_EXT_OBB_RW for writing to /sdcard/Android/obb (devices without sdcardfs)
     gid_t groups[] = {AID_ADB,          AID_LOG,          AID_INPUT,    AID_INET,
                       AID_NET_BT,       AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
-                      AID_NET_BW_STATS, AID_READPROC,     AID_UHID};
+                      AID_NET_BW_STATS, AID_READPROC,     AID_UHID,     AID_EXT_DATA_RW,
+                      AID_EXT_OBB_RW};
     minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
 
     // Don't listen on a port (default 5037) if running in secure mode.
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 54102ec..233d15f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -640,13 +640,14 @@
             entry.fs_mgr_flags.wait = true;
             entry.fs_mgr_flags.logical = true;
             entry.fs_mgr_flags.first_stage_mount = true;
+            fstab->emplace_back(entry);
         } else {
             // If the corresponding partition exists, transform all its Fstab
             // by pointing .blk_device to the DSU partition.
             for (auto&& entry : entries) {
                 entry->blk_device = partition;
                 // AVB keys for DSU should always be under kDsuKeysDir.
-                entry->avb_keys += kDsuKeysDir;
+                entry->avb_keys = kDsuKeysDir;
             }
             // Make sure the ext4 is included to support GSI.
             auto partition_ext4 =
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index f32e0eb..93fc131 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -640,16 +640,22 @@
         return false;
     }
 
+    bool ok = true;
     for (const auto& partition : metadata->partitions) {
         auto name = GetPartitionName(partition);
         auto image_path = GetImageHeaderPath(name);
         auto fiemap = SplitFiemap::Open(image_path);
-        if (!fiemap || !fiemap->HasPinnedExtents()) {
-            LOG(ERROR) << "Image is missing or was moved: " << image_path;
-            return false;
+        if (fiemap == nullptr) {
+            LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+            ok = false;
+            continue;
+        }
+        if (!fiemap->HasPinnedExtents()) {
+            LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+            ok = false;
         }
     }
-    return true;
+    return ok;
 }
 
 bool ImageManager::DisableImage(const std::string& name) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index eaef180..db50e58 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -122,6 +122,39 @@
     ],
 }
 
+cc_defaults {
+    name: "libsnapshot_cow_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: ["include"],
+    srcs: [
+        "cow_reader.cpp",
+        "cow_writer.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libsnapshot_cow",
+    defaults: [
+        "libsnapshot_cow_defaults",
+    ],
+    host_supported: true,
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "liblog",
+    ],
+    static_libs: [
+        "libz",
+    ],
+}
+
 cc_library_static {
     name: "libsnapshot_test_helpers",
     defaults: ["libsnapshot_defaults"],
@@ -343,3 +376,57 @@
     static_executable: true,
     system_shared_libs: [],
 }
+
+cc_test {
+    name: "cow_api_test",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "cow_api_test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "liblog",
+        "libz",
+    ],
+    static_libs: [
+        "libgtest",
+        "libsnapshot_cow",
+    ],
+    test_min_api_level: 30,
+    auto_gen_config: true,
+    require_root: false,
+}
+
+cc_binary {
+    name: "make_cow_from_ab_ota",
+    host_supported: true,
+    device_supported: false,
+    static_libs: [
+        "libbase",
+        "libbspatch",
+        "libbrotli",
+        "libbz",
+        "libchrome",
+        "libcrypto",
+        "libgflags",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libpuffpatch",
+        "libsnapshot_cow",
+        "libsparse",
+        "libxz",
+        "libz",
+        "libziparchive",
+        "update_metadata-protos",
+    ],
+    srcs: [
+        "make_cow_from_ab_ota.cpp",
+    ],
+}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
new file mode 100644
index 0000000..3b3fc47
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -0,0 +1,244 @@
+// 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 <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        cow_ = std::make_unique<TemporaryFile>();
+        ASSERT_GE(cow_->fd, 0) << strerror(errno);
+    }
+
+    void TearDown() override { cow_ = nullptr; }
+
+    std::unique_ptr<TemporaryFile> cow_;
+};
+
+// Sink that always appends to the end of a string.
+class StringSink : public IByteSink {
+  public:
+    void* GetBuffer(size_t requested, size_t* actual) override {
+        size_t old_size = stream_.size();
+        stream_.resize(old_size + requested, '\0');
+        *actual = requested;
+        return stream_.data() + old_size;
+    }
+    bool ReturnData(void*, size_t) override { return true; }
+    void Reset() { stream_.clear(); }
+
+    std::string& stream() { return stream_; }
+
+  private:
+    std::string stream_;
+};
+
+TEST_F(CowTest, ReadWrite) {
+    CowOptions options;
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddCopy(10, 20));
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    CowHeader header;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetHeader(&header));
+    ASSERT_EQ(header.magic, kCowMagicNumber);
+    ASSERT_EQ(header.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.num_ops, 4);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+    auto op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowCopyOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 10);
+    ASSERT_EQ(op->source, 20);
+
+    StringSink sink;
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 4096);
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_EQ(op->source, 104);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    // Note: the zero operation gets split into two blocks.
+    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_EQ(op->source, 0);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->compression, kCowCompressNone);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 52);
+    ASSERT_EQ(op->source, 0);
+
+    iter->Next();
+    ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressGz) {
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+    auto op = &iter->Get();
+
+    StringSink sink;
+
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->compression, kCowCompressGz);
+    ASSERT_EQ(op->data_length, 56);  // compressed!
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_EQ(op->source, 104);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data);
+
+    iter->Next();
+    ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressTwoBlocks) {
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size * 2, '\0');
+
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+
+    StringSink sink;
+
+    auto op = &iter->Get();
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->compression, kCowCompressGz);
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+}
+
+// Only return 1-byte buffers, to stress test the partial read logic in
+// CowReader.
+class HorribleStringSink : public StringSink {
+  public:
+    void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
+};
+
+TEST_F(CowTest, HorribleSink) {
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+
+    HorribleStringSink sink;
+    auto op = &iter->Get();
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data);
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
new file mode 100644
index 0000000..86565c4
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -0,0 +1,264 @@
+//
+// Copyright (C) 2020 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 <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_reader.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+
+static 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);
+}
+
+bool CowReader::Parse(android::base::unique_fd&& fd) {
+    owned_fd_ = std::move(fd);
+    return Parse(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowReader::Parse(android::base::borrowed_fd fd) {
+    fd_ = fd;
+
+    auto pos = lseek(fd_.get(), 0, SEEK_END);
+    if (pos < 0) {
+        PLOG(ERROR) << "lseek end failed";
+        return false;
+    }
+    fd_size_ = pos;
+
+    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek header failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+        PLOG(ERROR) << "read header failed";
+        return false;
+    }
+
+    // Validity check the ops range.
+    if (header_.ops_offset >= fd_size_) {
+        LOG(ERROR) << "ops offset " << header_.ops_offset << " larger than fd size " << fd_size_;
+        return false;
+    }
+    if (fd_size_ - header_.ops_offset < header_.ops_size) {
+        LOG(ERROR) << "ops size " << header_.ops_size << " is too large";
+        return false;
+    }
+
+    uint8_t header_csum[32];
+    {
+        CowHeader tmp = header_;
+        memset(&tmp.header_checksum, 0, sizeof(tmp.header_checksum));
+        SHA256(&tmp, sizeof(tmp), header_csum);
+    }
+    if (memcmp(header_csum, header_.header_checksum, sizeof(header_csum)) != 0) {
+        LOG(ERROR) << "header checksum is invalid";
+        return false;
+    }
+    return true;
+}
+
+bool CowReader::GetHeader(CowHeader* header) {
+    *header = header_;
+    return true;
+}
+
+class CowOpIter final : public ICowOpIter {
+  public:
+    CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len);
+
+    bool Done() override;
+    const CowOperation& Get() override;
+    void Next() override;
+
+  private:
+    bool HasNext();
+
+    std::unique_ptr<uint8_t[]> ops_;
+    const uint8_t* pos_;
+    const uint8_t* end_;
+    bool done_;
+};
+
+CowOpIter::CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len)
+    : ops_(std::move(ops)), pos_(ops_.get()), end_(pos_ + len), done_(!HasNext()) {}
+
+bool CowOpIter::Done() {
+    return done_;
+}
+
+bool CowOpIter::HasNext() {
+    return pos_ < end_ && size_t(end_ - pos_) >= sizeof(CowOperation);
+}
+
+void CowOpIter::Next() {
+    CHECK(!Done());
+
+    pos_ += sizeof(CowOperation);
+    if (!HasNext()) done_ = true;
+}
+
+const CowOperation& CowOpIter::Get() {
+    CHECK(!Done());
+    CHECK(HasNext());
+    return *reinterpret_cast<const CowOperation*>(pos_);
+}
+
+std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
+    if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek ops failed";
+        return nullptr;
+    }
+    auto ops_buffer = std::make_unique<uint8_t[]>(header_.ops_size);
+    if (!android::base::ReadFully(fd_, ops_buffer.get(), header_.ops_size)) {
+        PLOG(ERROR) << "read ops failed";
+        return nullptr;
+    }
+
+    uint8_t csum[32];
+    SHA256(ops_buffer.get(), header_.ops_size, csum);
+    if (memcmp(csum, header_.ops_checksum, sizeof(csum)) != 0) {
+        LOG(ERROR) << "ops checksum does not match";
+        return nullptr;
+    }
+
+    return std::make_unique<CowOpIter>(std::move(ops_buffer), header_.ops_size);
+}
+
+bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len) {
+    // Validate the offset, taking care to acknowledge possible overflow of offset+len.
+    if (offset < sizeof(header_) || offset >= header_.ops_offset || len >= fd_size_ ||
+        offset + len > header_.ops_offset) {
+        LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
+        return false;
+    }
+    if (lseek(fd_.get(), offset, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek to read raw bytes failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd_, buffer, len)) {
+        PLOG(ERROR) << "read raw bytes failed";
+        return false;
+    }
+    return true;
+}
+
+bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
+    uint64_t offset = op.source;
+
+    switch (op.compression) {
+        case kCowCompressNone: {
+            size_t remaining = op.data_length;
+            while (remaining) {
+                size_t amount = remaining;
+                void* buffer = sink->GetBuffer(amount, &amount);
+                if (!buffer) {
+                    LOG(ERROR) << "Could not acquire buffer from sink";
+                    return false;
+                }
+                if (!GetRawBytes(offset, buffer, amount)) {
+                    return false;
+                }
+                if (!sink->ReturnData(buffer, amount)) {
+                    LOG(ERROR) << "Could not return buffer to sink";
+                    return false;
+                }
+                remaining -= amount;
+                offset += amount;
+            }
+            return true;
+        }
+        case kCowCompressGz: {
+            auto input = std::make_unique<Bytef[]>(op.data_length);
+            if (!GetRawBytes(offset, input.get(), op.data_length)) {
+                return false;
+            }
+
+            z_stream z = {};
+            z.next_in = input.get();
+            z.avail_in = op.data_length;
+            if (int rv = inflateInit(&z); rv != Z_OK) {
+                LOG(ERROR) << "inflateInit returned error code " << rv;
+                return false;
+            }
+
+            while (z.total_out < header_.block_size) {
+                // If no more output buffer, grab a new buffer.
+                if (z.avail_out == 0) {
+                    size_t amount = header_.block_size - z.total_out;
+                    z.next_out = reinterpret_cast<Bytef*>(sink->GetBuffer(amount, &amount));
+                    if (!z.next_out) {
+                        LOG(ERROR) << "Could not acquire buffer from sink";
+                        return false;
+                    }
+                    z.avail_out = amount;
+                }
+
+                // Remember the position of the output buffer so we can call ReturnData.
+                auto buffer = z.next_out;
+                auto avail_out = z.avail_out;
+
+                // Decompress.
+                int rv = inflate(&z, Z_NO_FLUSH);
+                if (rv != Z_OK && rv != Z_STREAM_END) {
+                    LOG(ERROR) << "inflate returned error code " << rv;
+                    return false;
+                }
+
+                // Return the section of the buffer that was updated.
+                if (z.avail_out < avail_out && !sink->ReturnData(buffer, avail_out - z.avail_out)) {
+                    LOG(ERROR) << "Could not return buffer to sink";
+                    return false;
+                }
+
+                if (rv == Z_STREAM_END) {
+                    // Error if the stream has ended, but we didn't fill the entire block.
+                    if (z.total_out != header_.block_size) {
+                        LOG(ERROR) << "Reached gz stream end but did not read a full block of data";
+                        return false;
+                    }
+                    break;
+                }
+
+                CHECK(rv == Z_OK);
+
+                // Error if the stream is expecting more data, but we don't have any to read.
+                if (z.avail_in == 0) {
+                    LOG(ERROR) << "Gz stream ended prematurely";
+                    return false;
+                }
+            }
+            return true;
+        }
+        default:
+            LOG(ERROR) << "Unknown compression type: " << op.compression;
+            return false;
+    }
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
new file mode 100644
index 0000000..ea8e534
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -0,0 +1,230 @@
+//
+// Copyright (C) 2020 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 <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_writer.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+    SetupHeaders();
+}
+
+void CowWriter::SetupHeaders() {
+    header_ = {};
+    header_.magic = kCowMagicNumber;
+    header_.major_version = kCowVersionMajor;
+    header_.minor_version = kCowVersionMinor;
+    header_.block_size = options_.block_size;
+}
+
+bool CowWriter::Initialize(android::base::unique_fd&& fd) {
+    owned_fd_ = std::move(fd);
+    return Initialize(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowWriter::Initialize(android::base::borrowed_fd fd) {
+    fd_ = fd;
+
+    // This limitation is tied to the data field size in CowOperation.
+    if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+        LOG(ERROR) << "Block size is too large";
+        return false;
+    }
+
+    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+
+    if (options_.compression == "gz") {
+        compression_ = kCowCompressGz;
+    } else if (!options_.compression.empty()) {
+        LOG(ERROR) << "unrecognized compression: " << options_.compression;
+        return false;
+    }
+
+    // Headers are not complete, but this ensures the file is at the right
+    // position.
+    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+        PLOG(ERROR) << "write failed";
+        return false;
+    }
+    return true;
+}
+
+bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+    header_.num_ops++;
+
+    CowOperation op = {};
+    op.type = kCowCopyOp;
+    op.new_block = new_block;
+    op.source = old_block;
+    ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+
+    return true;
+}
+
+bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+    if (size % header_.block_size != 0) {
+        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+                   << header_.block_size;
+        return false;
+    }
+
+    uint64_t pos;
+    if (!GetDataPos(&pos)) {
+        return false;
+    }
+
+    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+    for (size_t i = 0; i < size / header_.block_size; i++) {
+        header_.num_ops++;
+
+        CowOperation op = {};
+        op.type = kCowReplaceOp;
+        op.new_block = new_block_start + i;
+        op.source = pos;
+
+        if (compression_) {
+            auto data = Compress(iter, header_.block_size);
+            if (data.empty()) {
+                PLOG(ERROR) << "AddRawBlocks: compression failed";
+                return false;
+            }
+            if (data.size() > std::numeric_limits<uint16_t>::max()) {
+                LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
+                return false;
+            }
+            if (!android::base::WriteFully(fd_, data.data(), data.size())) {
+                PLOG(ERROR) << "AddRawBlocks: write failed";
+                return false;
+            }
+            op.compression = compression_;
+            op.data_length = static_cast<uint16_t>(data.size());
+            pos += data.size();
+        } else {
+            op.data_length = static_cast<uint16_t>(header_.block_size);
+            pos += header_.block_size;
+        }
+
+        ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+        iter += header_.block_size;
+    }
+
+    if (!compression_ && !android::base::WriteFully(fd_, data, size)) {
+        PLOG(ERROR) << "AddRawBlocks: write failed";
+        return false;
+    }
+    return true;
+}
+
+bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+    for (uint64_t i = 0; i < num_blocks; i++) {
+        header_.num_ops++;
+
+        CowOperation op = {};
+        op.type = kCowZeroOp;
+        op.new_block = new_block_start + i;
+        op.source = 0;
+        ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+    }
+    return true;
+}
+
+std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
+    switch (compression_) {
+        case kCowCompressGz: {
+            auto bound = compressBound(length);
+            auto buffer = std::make_unique<uint8_t[]>(bound);
+
+            uLongf dest_len = bound;
+            auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+                                length, Z_BEST_COMPRESSION);
+            if (rv != Z_OK) {
+                LOG(ERROR) << "compress2 returned: " << rv;
+                return {};
+            }
+            return std::basic_string<uint8_t>(buffer.get(), dest_len);
+        }
+        default:
+            LOG(ERROR) << "unhandled compression type: " << compression_;
+            break;
+    }
+    return {};
+}
+
+static 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);
+}
+
+bool CowWriter::Finalize() {
+    auto offs = lseek(fd_.get(), 0, SEEK_CUR);
+    if (offs < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+    header_.ops_offset = offs;
+    header_.ops_size = ops_.size();
+
+    SHA256(ops_.data(), ops_.size(), header_.ops_checksum);
+    SHA256(&header_, sizeof(header_), header_.header_checksum);
+
+    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek start failed";
+        return false;
+    }
+    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+        PLOG(ERROR) << "write header failed";
+        return false;
+    }
+    if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek ops failed";
+        return false;
+    }
+    if (!android::base::WriteFully(fd_, ops_.data(), ops_.size())) {
+        PLOG(ERROR) << "write ops failed";
+        return false;
+    }
+    return true;
+}
+
+bool CowWriter::GetDataPos(uint64_t* pos) {
+    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
+    if (offs < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+    *pos = offs;
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
new file mode 100644
index 0000000..6d500e7
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
+static constexpr uint32_t kCowVersionMajor = 1;
+static constexpr uint32_t kCowVersionMinor = 0;
+
+// This header appears as the first sequence of bytes in the COW. All fields
+// in the layout are little-endian encoded. The on-disk layout is:
+//
+//      +-----------------------+
+//      |     Header (fixed)    |
+//      +-----------------------+
+//      |  Raw Data (variable)  |
+//      +-----------------------+
+//      | Operations (variable) |
+//      +-----------------------+
+//
+// The "raw data" occurs immediately after the header, and the operation
+// sequence occurs after the raw data. This ordering is intentional. While
+// streaming an OTA, we can immediately write compressed data, but store the
+// metadata in memory. At the end, we can simply append the metadata and flush
+// the file. There is no need to create separate files to store the metadata
+// and block data.
+struct CowHeader {
+    uint64_t magic;
+    uint16_t major_version;
+    uint16_t minor_version;
+
+    // Offset to the location of the operation sequence, and size of the
+    // operation sequence buffer. |ops_offset| is also the end of the
+    // raw data region.
+    uint64_t ops_offset;
+    uint64_t ops_size;
+    uint64_t num_ops;
+
+    // The size of block operations, in bytes.
+    uint32_t block_size;
+
+    // SHA256 checksums of this header, with this field set to 0.
+    uint8_t header_checksum[32];
+
+    // SHA256 of the operation sequence.
+    uint8_t ops_checksum[32];
+} __attribute__((packed));
+
+// Cow operations are currently fixed-size entries, but this may change if
+// needed.
+struct CowOperation {
+    // The operation code (see the constants and structures below).
+    uint8_t type;
+
+    // If this operation reads from the data section of the COW, this contains
+    // the compression type of that data (see constants below).
+    uint8_t compression;
+
+    // If this operation reads from the data section of the COW, this contains
+    // the length.
+    uint16_t data_length;
+
+    // The block of data in the new image that this operation modifies.
+    uint64_t new_block;
+
+    // The value of |source| depends on the operation code.
+    //
+    // For copy operations, this is a block location in the source image.
+    //
+    // For replace operations, this is a byte offset within the COW's data
+    // section (eg, not landing within the header or metadata). It is an
+    // absolute position within the image.
+    //
+    // For zero operations (replace with all zeroes), this is unused and must
+    // be zero.
+    uint64_t source;
+} __attribute__((packed));
+
+static constexpr uint8_t kCowCopyOp = 1;
+static constexpr uint8_t kCowReplaceOp = 2;
+static constexpr uint8_t kCowZeroOp = 3;
+
+static constexpr uint8_t kCowCompressNone = 0;
+static constexpr uint8_t kCowCompressGz = 1;
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
new file mode 100644
index 0000000..a3b1291
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -0,0 +1,107 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+class ICowOpIter;
+
+// A ByteSink object handles requests for a buffer of a specific size. It
+// always owns the underlying buffer. It's designed to minimize potential
+// copying as we parse or decompress the COW.
+class IByteSink {
+  public:
+    virtual ~IByteSink() {}
+
+    // Called when the reader has data. The size of the request is given. The
+    // sink must return a valid pointer (or null on failure), and return the
+    // maximum number of bytes that can be written to the returned buffer.
+    //
+    // The returned buffer is owned by IByteSink, but must remain valid until
+    // the ready operation has completed (or the entire buffer has been
+    // covered by calls to ReturnData).
+    //
+    // After calling GetBuffer(), all previous buffers returned are no longer
+    // valid.
+    virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
+
+    // Called when a section returned by |GetBuffer| has been filled with data.
+    virtual bool ReturnData(void* buffer, size_t length) = 0;
+};
+
+// Interface for reading from a snapuserd COW.
+class ICowReader {
+  public:
+    virtual ~ICowReader() {}
+
+    // Return the file header.
+    virtual bool GetHeader(CowHeader* header) = 0;
+
+    // Return an iterator for retrieving CowOperation entries.
+    virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+
+    // Get raw bytes from the data section.
+    virtual bool GetRawBytes(uint64_t offset, void* buffer, size_t len) = 0;
+
+    // Get decoded bytes from the data section, handling any decompression.
+    // All retrieved data is passed to the sink.
+    virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
+};
+
+// Iterate over a sequence of COW operations.
+class ICowOpIter {
+  public:
+    virtual ~ICowOpIter() {}
+
+    // True if there are more items to read, false otherwise.
+    virtual bool Done() = 0;
+
+    // Read the current operation.
+    virtual const CowOperation& Get() = 0;
+
+    // Advance to the next item.
+    virtual void Next() = 0;
+};
+
+class CowReader : public ICowReader {
+  public:
+    CowReader();
+
+    bool Parse(android::base::unique_fd&& fd);
+    bool Parse(android::base::borrowed_fd fd);
+
+    bool GetHeader(CowHeader* header) override;
+    std::unique_ptr<ICowOpIter> GetOpIter() override;
+    bool GetRawBytes(uint64_t offset, void* buffer, size_t len) override;
+    bool ReadData(const CowOperation& op, IByteSink* sink) override;
+
+  private:
+    android::base::unique_fd owned_fd_;
+    android::base::borrowed_fd fd_;
+    CowHeader header_;
+    uint64_t fd_size_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
new file mode 100644
index 0000000..5a2cbd6
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct CowOptions {
+    uint32_t block_size = 4096;
+    std::string compression;
+};
+
+// Interface for writing to a snapuserd COW. All operations are ordered; merges
+// will occur in the sequence they were added to the COW.
+class ICowWriter {
+  public:
+    explicit ICowWriter(const CowOptions& options) : options_(options) {}
+
+    virtual ~ICowWriter() {}
+
+    // Encode an operation that copies the contents of |old_block| to the
+    // location of |new_block|.
+    virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0;
+
+    // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
+    virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+
+    // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
+    virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+  protected:
+    CowOptions options_;
+};
+
+class CowWriter : public ICowWriter {
+  public:
+    explicit CowWriter(const CowOptions& options);
+
+    // Set up the writer.
+    bool Initialize(android::base::unique_fd&& fd);
+    bool Initialize(android::base::borrowed_fd fd);
+
+    bool AddCopy(uint64_t new_block, uint64_t old_block) override;
+    bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+    bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+
+    // Finalize all COW operations and flush pending writes.
+    bool Finalize();
+
+  private:
+    void SetupHeaders();
+    bool GetDataPos(uint64_t* pos);
+    std::basic_string<uint8_t> Compress(const void* data, size_t length);
+
+  private:
+    android::base::unique_fd owned_fd_;
+    android::base::borrowed_fd fd_;
+    CowHeader header_;
+    int compression_ = 0;
+
+    // :TODO: this is not efficient, but stringstream ubsan aborts because some
+    // bytes overflow a signed char.
+    std::basic_string<uint8_t> ops_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
new file mode 100644
index 0000000..0b40fd6
--- /dev/null
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -0,0 +1,690 @@
+//
+// Copyright (C) 2020 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 <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <limits>
+#include <string>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <bsdiff/bspatch.h>
+#include <bzlib.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+#include <puffin/puffpatch.h>
+#include <sparse/sparse.h>
+#include <update_engine/update_metadata.pb.h>
+#include <xz.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::Extent;
+using chromeos_update_engine::InstallOperation;
+using chromeos_update_engine::PartitionUpdate;
+
+static constexpr uint64_t kBlockSize = 4096;
+
+DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
+DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+              unsigned int, const char* message) {
+    if (severity == android::base::ERROR) {
+        fprintf(stderr, "%s\n", message);
+    } else {
+        fprintf(stdout, "%s\n", message);
+    }
+}
+
+uint64_t ToLittleEndian(uint64_t value) {
+    union {
+        uint64_t u64;
+        char bytes[8];
+    } packed;
+    packed.u64 = value;
+    std::swap(packed.bytes[0], packed.bytes[7]);
+    std::swap(packed.bytes[1], packed.bytes[6]);
+    std::swap(packed.bytes[2], packed.bytes[5]);
+    std::swap(packed.bytes[3], packed.bytes[4]);
+    return packed.u64;
+}
+
+class PayloadConverter final {
+  public:
+    PayloadConverter(const std::string& in_file, const std::string& out_dir)
+        : in_file_(in_file), out_dir_(out_dir), source_tf_zip_(nullptr, &CloseArchive) {}
+
+    bool Run();
+
+  private:
+    bool OpenPayload();
+    bool OpenSourceTargetFiles();
+    bool ProcessPartition(const PartitionUpdate& update);
+    bool ProcessOperation(const InstallOperation& op);
+    bool ProcessZero(const InstallOperation& op);
+    bool ProcessCopy(const InstallOperation& op);
+    bool ProcessReplace(const InstallOperation& op);
+    bool ProcessDiff(const InstallOperation& op);
+    borrowed_fd OpenSourceImage();
+
+    std::string in_file_;
+    std::string out_dir_;
+    unique_fd in_fd_;
+    uint64_t payload_offset_ = 0;
+    DeltaArchiveManifest manifest_;
+    std::unordered_set<std::string> dap_;
+    unique_fd source_tf_fd_;
+    std::unique_ptr<ZipArchive, decltype(&CloseArchive)> source_tf_zip_;
+
+    // Updated during ProcessPartition().
+    std::string partition_name_;
+    std::unique_ptr<CowWriter> writer_;
+    unique_fd source_image_;
+};
+
+bool PayloadConverter::Run() {
+    if (!OpenPayload()) {
+        return false;
+    }
+
+    if (manifest_.has_dynamic_partition_metadata()) {
+        const auto& dpm = manifest_.dynamic_partition_metadata();
+        for (const auto& group : dpm.groups()) {
+            for (const auto& partition : group.partition_names()) {
+                dap_.emplace(partition);
+            }
+        }
+    }
+
+    if (dap_.empty()) {
+        LOG(ERROR) << "No dynamic partitions found.";
+        return false;
+    }
+
+    if (!OpenSourceTargetFiles()) {
+        return false;
+    }
+
+    for (const auto& update : manifest_.partitions()) {
+        if (!ProcessPartition(update)) {
+            return false;
+        }
+        writer_ = nullptr;
+        source_image_.reset();
+    }
+    return true;
+}
+
+bool PayloadConverter::OpenSourceTargetFiles() {
+    if (FLAGS_source_tf.empty()) {
+        return true;
+    }
+
+    source_tf_fd_.reset(open(FLAGS_source_tf.c_str(), O_RDONLY));
+    if (source_tf_fd_ < 0) {
+        LOG(ERROR) << "open failed: " << FLAGS_source_tf;
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(source_tf_fd_.get(), &s) < 0) {
+        LOG(ERROR) << "fstat failed: " << FLAGS_source_tf;
+        return false;
+    }
+    if (S_ISDIR(s.st_mode)) {
+        return true;
+    }
+
+    // Otherwise, assume it's a zip file.
+    ZipArchiveHandle handle;
+    if (OpenArchiveFd(source_tf_fd_.get(), FLAGS_source_tf.c_str(), &handle, false)) {
+        LOG(ERROR) << "Could not open " << FLAGS_source_tf << " as a zip archive.";
+        return false;
+    }
+    source_tf_zip_.reset(handle);
+    return true;
+}
+
+bool PayloadConverter::ProcessPartition(const PartitionUpdate& update) {
+    auto partition_name = update.partition_name();
+    if (dap_.find(partition_name) == dap_.end()) {
+        // Skip non-DAP partitions.
+        return true;
+    }
+
+    auto path = out_dir_ + "/" + partition_name + ".cow";
+    unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+    if (fd < 0) {
+        PLOG(ERROR) << "open failed: " << path;
+        return false;
+    }
+
+    CowOptions options;
+    options.block_size = kBlockSize;
+    options.compression = FLAGS_compression;
+
+    writer_ = std::make_unique<CowWriter>(options);
+    if (!writer_->Initialize(std::move(fd))) {
+        LOG(ERROR) << "Unable to initialize COW writer";
+        return false;
+    }
+
+    partition_name_ = partition_name;
+
+    for (const auto& op : update.operations()) {
+        if (!ProcessOperation(op)) {
+            return false;
+        }
+    }
+
+    if (!writer_->Finalize()) {
+        LOG(ERROR) << "Unable to finalize COW for " << partition_name;
+        return false;
+    }
+    return true;
+}
+
+bool PayloadConverter::ProcessOperation(const InstallOperation& op) {
+    switch (op.type()) {
+        case InstallOperation::SOURCE_COPY:
+            return ProcessCopy(op);
+        case InstallOperation::BROTLI_BSDIFF:
+        case InstallOperation::PUFFDIFF:
+            return ProcessDiff(op);
+        case InstallOperation::REPLACE:
+        case InstallOperation::REPLACE_XZ:
+        case InstallOperation::REPLACE_BZ:
+            return ProcessReplace(op);
+        case InstallOperation::ZERO:
+            return ProcessZero(op);
+        default:
+            LOG(ERROR) << "Unsupported op: " << (int)op.type();
+            return false;
+    }
+    return true;
+}
+
+bool PayloadConverter::ProcessZero(const InstallOperation& op) {
+    for (const auto& extent : op.dst_extents()) {
+        if (!writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks())) {
+            LOG(ERROR) << "Could not add zero operation";
+            return false;
+        }
+    }
+    return true;
+}
+
+template <typename T>
+static uint64_t SizeOfAllExtents(const T& extents) {
+    uint64_t total = 0;
+    for (const auto& extent : extents) {
+        total += extent.num_blocks() * kBlockSize;
+    }
+    return total;
+}
+
+class PuffInputStream final : public puffin::StreamInterface {
+  public:
+    PuffInputStream(uint8_t* buffer, size_t length) : buffer_(buffer), length_(length) {}
+
+    bool GetSize(uint64_t* size) const override {
+        *size = length_;
+        return true;
+    }
+    bool GetOffset(uint64_t* offset) const override {
+        *offset = pos_;
+        return true;
+    }
+    bool Seek(uint64_t offset) override {
+        if (offset > length_) return false;
+        pos_ = offset;
+        return true;
+    }
+    bool Read(void* buffer, size_t length) override {
+        if (length_ - pos_ < length) return false;
+        memcpy(buffer, buffer_ + pos_, length);
+        pos_ += length;
+        return true;
+    }
+    bool Write(const void*, size_t) override { return false; }
+    bool Close() override { return true; }
+
+  private:
+    uint8_t* buffer_;
+    size_t length_;
+    size_t pos_;
+};
+
+class PuffOutputStream final : public puffin::StreamInterface {
+  public:
+    PuffOutputStream(std::vector<uint8_t>& stream) : stream_(stream), pos_(0) {}
+
+    bool GetSize(uint64_t* size) const override {
+        *size = stream_.size();
+        return true;
+    }
+    bool GetOffset(uint64_t* offset) const override {
+        *offset = pos_;
+        return true;
+    }
+    bool Seek(uint64_t offset) override {
+        if (offset > stream_.size()) {
+            return false;
+        }
+        pos_ = offset;
+        return true;
+    }
+    bool Read(void* buffer, size_t length) override {
+        if (stream_.size() - pos_ < length) {
+            return false;
+        }
+        memcpy(buffer, &stream_[0] + pos_, length);
+        pos_ += length;
+        return true;
+    }
+    bool Write(const void* buffer, size_t length) override {
+        auto remaining = stream_.size() - pos_;
+        if (remaining < length) {
+            stream_.resize(stream_.size() + (length - remaining));
+        }
+        memcpy(&stream_[0] + pos_, buffer, length);
+        pos_ += length;
+        return true;
+    }
+    bool Close() override { return true; }
+
+  private:
+    std::vector<uint8_t>& stream_;
+    size_t pos_;
+};
+
+bool PayloadConverter::ProcessDiff(const InstallOperation& op) {
+    auto source_image = OpenSourceImage();
+    if (source_image < 0) {
+        return false;
+    }
+
+    uint64_t src_length = SizeOfAllExtents(op.src_extents());
+    auto src = std::make_unique<uint8_t[]>(src_length);
+    size_t src_pos = 0;
+
+    // Read source bytes.
+    for (const auto& extent : op.src_extents()) {
+        uint64_t offset = extent.start_block() * kBlockSize;
+        if (lseek(source_image.get(), offset, SEEK_SET) < 0) {
+            PLOG(ERROR) << "lseek source image failed";
+            return false;
+        }
+
+        uint64_t size = extent.num_blocks() * kBlockSize;
+        CHECK(src_length - src_pos >= size);
+        if (!android::base::ReadFully(source_image, src.get() + src_pos, size)) {
+            PLOG(ERROR) << "read source image failed";
+            return false;
+        }
+        src_pos += size;
+    }
+    CHECK(src_pos == src_length);
+
+    // Read patch bytes.
+    auto patch = std::make_unique<uint8_t[]>(op.data_length());
+    if (lseek(in_fd_.get(), payload_offset_ + op.data_offset(), SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek payload failed";
+        return false;
+    }
+    if (!android::base::ReadFully(in_fd_, patch.get(), op.data_length())) {
+        PLOG(ERROR) << "read payload failed";
+        return false;
+    }
+
+    std::vector<uint8_t> dest(SizeOfAllExtents(op.dst_extents()));
+
+    // Apply the diff.
+    if (op.type() == InstallOperation::BROTLI_BSDIFF) {
+        size_t dest_pos = 0;
+        auto sink = [&](const uint8_t* data, size_t length) -> size_t {
+            CHECK(dest.size() - dest_pos >= length);
+            memcpy(&dest[dest_pos], data, length);
+            dest_pos += length;
+            return length;
+        };
+        if (int rv = bsdiff::bspatch(src.get(), src_pos, patch.get(), op.data_length(), sink)) {
+            LOG(ERROR) << "bspatch failed, error code " << rv;
+            return false;
+        }
+    } else if (op.type() == InstallOperation::PUFFDIFF) {
+        auto src_stream = std::make_unique<PuffInputStream>(src.get(), src_length);
+        auto dest_stream = std::make_unique<PuffOutputStream>(dest);
+        bool ok = PuffPatch(std::move(src_stream), std::move(dest_stream), patch.get(),
+                            op.data_length());
+        if (!ok) {
+            LOG(ERROR) << "puffdiff operation failed to apply";
+            return false;
+        }
+    } else {
+        LOG(ERROR) << "unsupported diff operation: " << op.type();
+        return false;
+    }
+
+    // Write the final blocks to the COW.
+    size_t dest_pos = 0;
+    for (const auto& extent : op.dst_extents()) {
+        uint64_t size = extent.num_blocks() * kBlockSize;
+        CHECK(dest.size() - dest_pos >= size);
+
+        if (!writer_->AddRawBlocks(extent.start_block(), &dest[dest_pos], size)) {
+            return false;
+        }
+        dest_pos += size;
+    }
+    return true;
+}
+
+borrowed_fd PayloadConverter::OpenSourceImage() {
+    if (source_image_ >= 0) {
+        return source_image_;
+    }
+
+    unique_fd unzip_fd;
+
+    auto local_path = "IMAGES/" + partition_name_ + ".img";
+    if (source_tf_zip_) {
+        {
+            TemporaryFile tmp;
+            if (tmp.fd < 0) {
+                PLOG(ERROR) << "mkstemp failed";
+                return -1;
+            }
+            unzip_fd.reset(tmp.release());
+        }
+
+        ZipEntry64 entry;
+        if (FindEntry(source_tf_zip_.get(), local_path, &entry)) {
+            LOG(ERROR) << "not found in archive: " << local_path;
+            return -1;
+        }
+        if (ExtractEntryToFile(source_tf_zip_.get(), &entry, unzip_fd.get())) {
+            LOG(ERROR) << "could not extract " << local_path;
+            return -1;
+        }
+        if (lseek(unzip_fd.get(), 0, SEEK_SET) < 0) {
+            PLOG(ERROR) << "lseek failed";
+            return -1;
+        }
+    } else if (source_tf_fd_ >= 0) {
+        unzip_fd.reset(openat(source_tf_fd_.get(), local_path.c_str(), O_RDONLY));
+        if (unzip_fd < 0) {
+            PLOG(ERROR) << "open failed: " << FLAGS_source_tf << "/" << local_path;
+            return -1;
+        }
+    } else {
+        LOG(ERROR) << "No source target files package was specified; need -source_tf";
+        return -1;
+    }
+
+    std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
+            sparse_file_import(unzip_fd.get(), false, false), &sparse_file_destroy);
+    if (s) {
+        TemporaryFile tmp;
+        if (tmp.fd < 0) {
+            PLOG(ERROR) << "mkstemp failed";
+            return -1;
+        }
+        if (sparse_file_write(s.get(), tmp.fd, false, false, false) < 0) {
+            LOG(ERROR) << "sparse_file_write failed";
+            return -1;
+        }
+        source_image_.reset(tmp.release());
+    } else {
+        source_image_ = std::move(unzip_fd);
+    }
+    return source_image_;
+}
+
+template <typename ContainerType>
+class ExtentIter final {
+  public:
+    ExtentIter(const ContainerType& container)
+        : iter_(container.cbegin()), end_(container.cend()) {}
+
+    bool GetNext(uint64_t* block) {
+        while (iter_ != end_) {
+            if (dst_index_ < iter_->num_blocks()) {
+                break;
+            }
+            iter_++;
+            dst_index_ = 0;
+        }
+        if (iter_ == end_) {
+            return false;
+        }
+        *block = iter_->start_block() + dst_index_;
+        dst_index_++;
+        return true;
+    }
+
+  private:
+    typename ContainerType::const_iterator iter_;
+    typename ContainerType::const_iterator end_;
+    uint64_t dst_index_;
+};
+
+bool PayloadConverter::ProcessCopy(const InstallOperation& op) {
+    ExtentIter dst_blocks(op.dst_extents());
+
+    for (const auto& extent : op.src_extents()) {
+        for (uint64_t i = 0; i < extent.num_blocks(); i++) {
+            uint64_t src_block = extent.start_block() + i;
+            uint64_t dst_block;
+            if (!dst_blocks.GetNext(&dst_block)) {
+                LOG(ERROR) << "SOURCE_COPY contained mismatching extents";
+                return false;
+            }
+            if (src_block == dst_block) continue;
+            if (!writer_->AddCopy(dst_block, src_block)) {
+                LOG(ERROR) << "Could not add copy operation";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool PayloadConverter::ProcessReplace(const InstallOperation& op) {
+    auto buffer_size = op.data_length();
+    auto buffer = std::make_unique<char[]>(buffer_size);
+    uint64_t offs = payload_offset_ + op.data_offset();
+    if (lseek(in_fd_.get(), offs, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek " << offs << " failed";
+        return false;
+    }
+    if (!android::base::ReadFully(in_fd_, buffer.get(), buffer_size)) {
+        PLOG(ERROR) << "read " << buffer_size << " bytes from offset " << offs << "failed";
+        return false;
+    }
+
+    uint64_t dst_size = 0;
+    for (const auto& extent : op.dst_extents()) {
+        dst_size += extent.num_blocks() * kBlockSize;
+    }
+
+    if (op.type() == InstallOperation::REPLACE_BZ) {
+        auto tmp = std::make_unique<char[]>(dst_size);
+
+        uint32_t actual_size;
+        if (dst_size > std::numeric_limits<typeof(actual_size)>::max()) {
+            LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+            return false;
+        }
+        actual_size = static_cast<uint32_t>(dst_size);
+
+        auto rv = BZ2_bzBuffToBuffDecompress(tmp.get(), &actual_size, buffer.get(), buffer_size, 0,
+                                             0);
+        if (rv) {
+            LOG(ERROR) << "bz2 decompress failed: " << rv;
+            return false;
+        }
+        if (actual_size != dst_size) {
+            LOG(ERROR) << "bz2 returned " << actual_size << " bytes, expected " << dst_size;
+            return false;
+        }
+        buffer = std::move(tmp);
+        buffer_size = dst_size;
+    } else if (op.type() == InstallOperation::REPLACE_XZ) {
+        constexpr uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
+
+        if (dst_size > std::numeric_limits<size_t>::max()) {
+            LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+            return false;
+        }
+
+        std::unique_ptr<struct xz_dec, decltype(&xz_dec_end)> s(
+                xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize), xz_dec_end);
+        if (!s) {
+            LOG(ERROR) << "xz_dec_init failed";
+            return false;
+        }
+
+        auto tmp = std::make_unique<char[]>(dst_size);
+
+        struct xz_buf args;
+        args.in = reinterpret_cast<const uint8_t*>(buffer.get());
+        args.in_pos = 0;
+        args.in_size = buffer_size;
+        args.out = reinterpret_cast<uint8_t*>(tmp.get());
+        args.out_pos = 0;
+        args.out_size = dst_size;
+
+        auto rv = xz_dec_run(s.get(), &args);
+        if (rv != XZ_STREAM_END) {
+            LOG(ERROR) << "xz decompress failed: " << (int)rv;
+            return false;
+        }
+        buffer = std::move(tmp);
+        buffer_size = dst_size;
+    }
+
+    uint64_t buffer_pos = 0;
+    for (const auto& extent : op.dst_extents()) {
+        uint64_t extent_size = extent.num_blocks() * kBlockSize;
+        if (buffer_size - buffer_pos < extent_size) {
+            LOG(ERROR) << "replace op ran out of input buffer";
+            return false;
+        }
+        if (!writer_->AddRawBlocks(extent.start_block(), buffer.get() + buffer_pos, extent_size)) {
+            LOG(ERROR) << "failed to add raw blocks from replace op";
+            return false;
+        }
+        buffer_pos += extent_size;
+    }
+    return true;
+}
+
+bool PayloadConverter::OpenPayload() {
+    in_fd_.reset(open(in_file_.c_str(), O_RDONLY));
+    if (in_fd_ < 0) {
+        PLOG(ERROR) << "open " << in_file_;
+        return false;
+    }
+
+    char magic[4];
+    if (!android::base::ReadFully(in_fd_, magic, sizeof(magic))) {
+        PLOG(ERROR) << "read magic";
+        return false;
+    }
+    if (std::string(magic, sizeof(magic)) != "CrAU") {
+        LOG(ERROR) << "Invalid magic in " << in_file_;
+        return false;
+    }
+
+    uint64_t version;
+    uint64_t manifest_size;
+    uint32_t manifest_signature_size = 0;
+    if (!android::base::ReadFully(in_fd_, &version, sizeof(version))) {
+        PLOG(ERROR) << "read version";
+        return false;
+    }
+    version = ToLittleEndian(version);
+    if (version < 2) {
+        LOG(ERROR) << "Only payload version 2 or higher is supported.";
+        return false;
+    }
+
+    if (!android::base::ReadFully(in_fd_, &manifest_size, sizeof(manifest_size))) {
+        PLOG(ERROR) << "read manifest_size";
+        return false;
+    }
+    manifest_size = ToLittleEndian(manifest_size);
+    if (!android::base::ReadFully(in_fd_, &manifest_signature_size,
+                                  sizeof(manifest_signature_size))) {
+        PLOG(ERROR) << "read manifest_signature_size";
+        return false;
+    }
+    manifest_signature_size = ntohl(manifest_signature_size);
+
+    auto manifest = std::make_unique<uint8_t[]>(manifest_size);
+    if (!android::base::ReadFully(in_fd_, manifest.get(), manifest_size)) {
+        PLOG(ERROR) << "read manifest";
+        return false;
+    }
+
+    // Skip past manifest signature.
+    auto offs = lseek(in_fd_, manifest_signature_size, SEEK_CUR);
+    if (offs < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+    payload_offset_ = offs;
+
+    if (!manifest_.ParseFromArray(manifest.get(), manifest_size)) {
+        LOG(ERROR) << "could not parse manifest";
+        return false;
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, android::snapshot::MyLogger);
+    gflags::SetUsageMessage("Convert OTA payload to a Virtual A/B COW");
+    int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+    xz_crc32_init();
+
+    if (argc - arg_start != 2) {
+        std::cerr << "Usage: [options] <payload.bin> <out-dir>\n";
+        return 1;
+    }
+
+    android::snapshot::PayloadConverter pc(argv[arg_start], argv[arg_start + 1]);
+    return pc.Run() ? 0 : 1;
+}
diff --git a/libcrypto_utils/.clang-format b/libcrypto_utils/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libcrypto_utils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index d7175e0..923b291 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -23,7 +23,7 @@
     },
     host_supported: true,
     srcs: [
-        "android_pubkey.c",
+        "android_pubkey.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/libcrypto_utils/android_pubkey.c b/libcrypto_utils/android_pubkey.cpp
similarity index 65%
rename from libcrypto_utils/android_pubkey.c
rename to libcrypto_utils/android_pubkey.cpp
index 3052e52..21e5663 100644
--- a/libcrypto_utils/android_pubkey.c
+++ b/libcrypto_utils/android_pubkey.cpp
@@ -35,37 +35,29 @@
 // little-endian 32 bit words. Note that Android only supports little-endian
 // processors, so we don't do any byte order conversions when parsing the binary
 // struct.
-typedef struct RSAPublicKey {
-    // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
-    uint32_t modulus_size_words;
+struct RSAPublicKey {
+  // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
+  uint32_t modulus_size_words;
 
-    // Precomputed montgomery parameter: -1 / n[0] mod 2^32
-    uint32_t n0inv;
+  // Precomputed montgomery parameter: -1 / n[0] mod 2^32
+  uint32_t n0inv;
 
-    // RSA modulus as a little-endian array.
-    uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
+  // RSA modulus as a little-endian array.
+  uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
 
-    // Montgomery parameter R^2 as a little-endian array of little-endian words.
-    uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
+  // Montgomery parameter R^2 as a little-endian array.
+  uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
 
-    // RSA modulus: 3 or 65537
-    uint32_t exponent;
-} RSAPublicKey;
-
-// Reverses byte order in |buffer|.
-static void reverse_bytes(uint8_t* buffer, size_t size) {
-  for (size_t i = 0; i < (size + 1) / 2; ++i) {
-    uint8_t tmp = buffer[i];
-    buffer[i] = buffer[size - i - 1];
-    buffer[size - i - 1] = tmp;
-  }
-}
+  // RSA modulus: 3 or 65537
+  uint32_t exponent;
+};
 
 bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
   const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
   bool ret = false;
-  uint8_t modulus_buffer[ANDROID_PUBKEY_MODULUS_SIZE];
   RSA* new_key = RSA_new();
+  BIGNUM* n = NULL;
+  BIGNUM* e = NULL;
   if (!new_key) {
     goto cleanup;
   }
@@ -79,19 +71,24 @@
   }
 
   // Convert the modulus to big-endian byte order as expected by BN_bin2bn.
-  memcpy(modulus_buffer, key_struct->modulus, sizeof(modulus_buffer));
-  reverse_bytes(modulus_buffer, sizeof(modulus_buffer));
-  new_key->n = BN_bin2bn(modulus_buffer, sizeof(modulus_buffer), NULL);
-  if (!new_key->n) {
+  n = BN_le2bn(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, NULL);
+  if (!n) {
     goto cleanup;
   }
 
   // Read the exponent.
-  new_key->e = BN_new();
-  if (!new_key->e || !BN_set_word(new_key->e, key_struct->exponent)) {
+  e = BN_new();
+  if (!e || !BN_set_word(e, key_struct->exponent)) {
     goto cleanup;
   }
 
+  if (!RSA_set0_key(new_key, n, e, NULL)) {
+    goto cleanup;
+  }
+  // RSA_set0_key takes ownership of its inputs on success.
+  n = NULL;
+  e = NULL;
+
   // Note that we don't extract the montgomery parameters n0inv and rr from
   // the RSAPublicKey structure. They assume a word size of 32 bits, but
   // BoringSSL may use a word size of 64 bits internally, so we're lacking the
@@ -101,24 +98,16 @@
   // pre-computed montgomery parameters.
 
   *key = new_key;
+  new_key = NULL;
   ret = true;
 
 cleanup:
-  if (!ret && new_key) {
-    RSA_free(new_key);
-  }
+  RSA_free(new_key);
+  BN_free(n);
+  BN_free(e);
   return ret;
 }
 
-static bool android_pubkey_encode_bignum(const BIGNUM* num, uint8_t* buffer) {
-  if (!BN_bn2bin_padded(buffer, ANDROID_PUBKEY_MODULUS_SIZE, num)) {
-    return false;
-  }
-
-  reverse_bytes(buffer, ANDROID_PUBKEY_MODULUS_SIZE);
-  return true;
-}
-
 bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
   RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
   bool ret = false;
@@ -127,8 +116,7 @@
   BIGNUM* n0inv = BN_new();
   BIGNUM* rr = BN_new();
 
-  if (sizeof(RSAPublicKey) > size ||
-      RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
+  if (sizeof(RSAPublicKey) > size || RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
     goto cleanup;
   }
 
@@ -136,27 +124,26 @@
   key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
 
   // Compute and store n0inv = -1 / N[0] mod 2^32.
-  if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) ||
-      !BN_mod(n0inv, key->n, r32, ctx) ||
+  if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) || !BN_mod(n0inv, RSA_get0_n(key), r32, ctx) ||
       !BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
     goto cleanup;
   }
   key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
 
   // Store the modulus.
-  if (!android_pubkey_encode_bignum(key->n, key_struct->modulus)) {
+  if (!BN_bn2le_padded(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, RSA_get0_n(key))) {
     goto cleanup;
   }
 
   // Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
   if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
-      !BN_mod_sqr(rr, rr, key->n, ctx) ||
-      !android_pubkey_encode_bignum(rr, key_struct->rr)) {
+      !BN_mod_sqr(rr, rr, RSA_get0_n(key), ctx) ||
+      !BN_bn2le_padded(key_struct->rr, ANDROID_PUBKEY_MODULUS_SIZE, rr)) {
     goto cleanup;
   }
 
   // Store the exponent.
-  key_struct->exponent = (uint32_t)BN_get_word(key->e);
+  key_struct->exponent = (uint32_t)BN_get_word(RSA_get0_e(key));
 
   ret = true;
 
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index c74ee3e..793e2ce 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
 #define ATRACE_TAG_AIDL             (1<<24)
 #define ATRACE_TAG_NNAPI            (1<<25)
 #define ATRACE_TAG_RRO              (1<<26)
-#define ATRACE_TAG_LAST             ATRACE_TAG_RRO
+#define ATRACE_TAG_SYSPROP          (1<<27)
+#define ATRACE_TAG_LAST             ATRACE_TAG_SYSPROP
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 0341902..4518487 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -39,19 +39,21 @@
       "Mode": "0755",
       "UID": "system",
       "GID": "system"
-    },
-    {
-      "Controller": "freezer",
-      "Path": "/dev/freezer",
-      "Mode": "0755",
-      "UID": "system",
-      "GID": "system"
     }
   ],
   "Cgroups2": {
-    "Path": "/dev/cg2_bpf",
-    "Mode": "0600",
-    "UID": "root",
-    "GID": "root"
+    "Path": "/sys/fs/cgroup",
+    "Mode": "0755",
+    "UID": "system",
+    "GID": "system",
+    "Controllers": [
+      {
+        "Controller": "freezer",
+        "Path": "freezer",
+        "Mode": "0755",
+        "UID": "system",
+        "GID": "system"
+      }
+    ]
   }
 }
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index f4070c5..13adcae 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,19 +24,24 @@
     Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
 }
 
-// Next: 6
+// Next: 7
 message Cgroup {
     string controller = 1 [json_name = "Controller"];
     string path = 2 [json_name = "Path"];
     string mode = 3 [json_name = "Mode"];
     string uid = 4 [json_name = "UID"];
     string gid = 5 [json_name = "GID"];
+// Booleans default to false when not specified. File reconstruction fails
+// when a boolean is specified as false, so leave unspecified in that case
+// https://developers.google.com/protocol-buffers/docs/proto3#default
+    bool needs_activation = 6 [json_name = "NeedsActivation"];
 }
 
-// Next: 5
+// Next: 6
 message Cgroups2 {
     string path = 1 [json_name = "Path"];
     string mode = 2 [json_name = "Mode"];
     string uid = 3 [json_name = "UID"];
     string gid = 4 [json_name = "GID"];
+    repeated Cgroup controllers = 5 [json_name = "Controllers"];
 }
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index a515e58..c4dbf8e 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -49,6 +49,11 @@
       "Name": "UClampMax",
       "Controller": "cpu",
       "File": "cpu.uclamp.max"
+    },
+    {
+      "Name": "FreezerState",
+      "Controller": "freezer",
+      "File": "cgroup.freeze"
     }
   ],
 
@@ -74,7 +79,7 @@
           "Params":
           {
             "Controller": "freezer",
-            "Path": "frozen"
+            "Path": ""
           }
         }
       ]
@@ -87,7 +92,7 @@
           "Params":
           {
             "Controller": "freezer",
-            "Path": ""
+            "Path": "../"
           }
         }
       ]
@@ -531,6 +536,32 @@
           }
         }
       ]
+    },
+    {
+      "Name": "FreezerDisabled",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "FreezerState",
+            "Value": "0"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "FreezerEnabled",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "FreezerState",
+            "Value": "1"
+          }
+        }
+      ]
     }
   ],
 
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
index ce3dc7b..b4d3a2f 100644
--- a/logd/ChattyLogBuffer.h
+++ b/logd/ChattyLogBuffer.h
@@ -25,7 +25,6 @@
 #include <android-base/thread_annotations.h>
 #include <android/log.h>
 #include <private/android_filesystem_config.h>
-#include <sysutils/SocketClient.h>
 
 #include "LogBuffer.h"
 #include "LogBufferElement.h"
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b85a584..a4e52c4 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -22,8 +22,6 @@
 #include "LogReaderList.h"
 #include "LogReaderThread.h"
 
-#define LOGD_SNDTIMEO 32
-
 class LogReader : public SocketListener {
   public:
     explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index 1855c0e..20624f2 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -27,7 +27,6 @@
 #include <memory>
 
 #include <log/log.h>
-#include <sysutils/SocketClient.h>
 
 #include "LogBuffer.h"
 #include "LogWriter.h"
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 1b7107f..6ab3b48 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -24,6 +24,7 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
 #include <string>
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index df78a50..c0f62d3 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,12 +20,13 @@
 #include <sys/types.h>
 
 #include <private/android_logger.h>
-#include <sysutils/SocketClient.h>
 #include <utils/FastStrcmp.h>
 
 // Hijack this header as a common include file used by most all sources
 // to report some utilities defined here and there.
 
+#define LOGD_SNDTIMEO 32
+
 namespace android {
 
 // Furnished in main.cpp. Caller must own and free returned value
diff --git a/logd/logd_test.cpp b/logd/logd_test.cpp
index 202ab06..828f580 100644
--- a/logd/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -40,7 +40,7 @@
 #include <selinux/selinux.h>
 #endif
 
-#include "LogReader.h"  // pickup LOGD_SNDTIMEO
+#include "LogUtils.h"  // For LOGD_SNDTIMEO.
 
 using android::base::unique_fd;
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6ef3bdc..37f911a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -324,16 +324,6 @@
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
 
-    # freezer cgroup entries
-    mkdir /dev/freezer/frozen
-    write /dev/freezer/frozen/freezer.state FROZEN
-    chown system system /dev/freezer/cgroup.procs
-    chown system system /dev/freezer/frozen
-    chown system system /dev/freezer/frozen/freezer.state
-    chown system system /dev/freezer/frozen/cgroup.procs
-
-    chmod 0444 /dev/freezer/frozen/freezer.state
-
     # make the PSI monitor accessible to others
     chown system system /proc/pressure/memory
     chmod 0664 /proc/pressure/memory
@@ -348,8 +338,6 @@
     # This is needed by any process that uses socket tagging.
     chmod 0644 /dev/xt_qtaguid
 
-    chown root root /dev/cg2_bpf
-    chmod 0600 /dev/cg2_bpf
     mount bpf bpf /sys/fs/bpf nodev noexec nosuid
 
     # Create location for fs_mgr to store abbreviated output from filesystem