Merge "init: guarantee the ordering that actions are executed in"
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 81ebf43..e6f9ffa 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -147,6 +147,7 @@
     static_libs: [
         "libgtest_prod",
         "libhealthhalutils",
+        "libsnapshot_cow",
         "libsnapshot_nobinder",
         "update_metadata-protos",
     ],
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index aa41be3..046ea74 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -74,9 +74,11 @@
         "android/snapshot/snapshot.proto",
         "device_info.cpp",
         "snapshot.cpp",
+        "snapshot_reader.cpp",
         "snapshot_stats.cpp",
         "snapshot_stub.cpp",
         "snapshot_metadata_updater.cpp",
+        "snapshot_writer.cpp",
         "partition_cow_creator.cpp",
         "return.cpp",
         "utility.cpp",
@@ -215,6 +217,7 @@
         "libgmock",
         "liblp",
         "libsnapshot",
+        "libsnapshot_cow",
         "libsnapshot_test_helpers",
         "libsparse",
     ],
@@ -249,6 +252,7 @@
     static_libs: [
         "libfstab",
         "libsnapshot",
+        "libsnapshot_cow",
         "update_metadata-protos",
     ],
     shared_libs: [
@@ -312,6 +316,7 @@
         "libgmock", // from libsnapshot_test_helpers
         "liblog",
         "liblp",
+        "libsnapshot_cow",
         "libsnapshot_test_helpers",
         "libprotobuf-mutator",
     ],
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 4cf2119..f96f174 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -32,6 +32,48 @@
 
 static_assert(sizeof(off_t) == sizeof(uint64_t));
 
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+    if (!ValidateNewBlock(new_block)) {
+        return false;
+    }
+    return EmitCopy(new_block, old_block);
+}
+
+bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+    if (size % options_.block_size != 0) {
+        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+                   << options_.block_size;
+        return false;
+    }
+
+    uint64_t num_blocks = size / options_.block_size;
+    uint64_t last_block = new_block_start + num_blocks - 1;
+    if (!ValidateNewBlock(last_block)) {
+        return false;
+    }
+    return EmitRawBlocks(new_block_start, data, size);
+}
+
+bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+    uint64_t last_block = new_block_start + num_blocks - 1;
+    if (!ValidateNewBlock(last_block)) {
+        return false;
+    }
+    return EmitZeroBlocks(new_block_start, num_blocks);
+}
+
+bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
+    if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
+        LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
+                   << options_.max_blocks.value();
+        return false;
+    }
+    return true;
+}
+
 CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
     SetupHeaders();
 }
@@ -59,12 +101,12 @@
     return true;
 }
 
-bool CowWriter::Initialize(android::base::unique_fd&& fd, OpenMode mode) {
+bool CowWriter::Initialize(unique_fd&& fd, OpenMode mode) {
     owned_fd_ = std::move(fd);
-    return Initialize(android::base::borrowed_fd{owned_fd_}, mode);
+    return Initialize(borrowed_fd{owned_fd_}, mode);
 }
 
-bool CowWriter::Initialize(android::base::borrowed_fd fd, OpenMode mode) {
+bool CowWriter::Initialize(borrowed_fd fd, OpenMode mode) {
     fd_ = fd;
 
     if (!ParseOptions()) {
@@ -134,7 +176,7 @@
     return true;
 }
 
-bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
     CowOperation op = {};
     op.type = kCowCopyOp;
     op.new_block = new_block;
@@ -143,13 +185,7 @@
     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;
-    }
-
+bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
     uint64_t pos;
     if (!GetDataPos(&pos)) {
         return false;
@@ -195,7 +231,7 @@
     return true;
 }
 
-bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     for (uint64_t i = 0; i < num_blocks; i++) {
         CowOperation op = {};
         op.type = kCowZeroOp;
@@ -291,7 +327,7 @@
     return true;
 }
 
-size_t CowWriter::GetCowSize() {
+uint64_t CowWriter::GetCowSize() {
     return header_.ops_offset + header_.num_ops * sizeof(CowOperation);
 }
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 8569161..2bc0171 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -16,6 +16,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 
 #include <android-base/unique_fd.h>
@@ -27,6 +28,9 @@
 struct CowOptions {
     uint32_t block_size = 4096;
     std::string compression;
+
+    // Maximum number of blocks that can be written.
+    std::optional<uint64_t> max_blocks;
 };
 
 // Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -39,20 +43,32 @@
 
     // 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;
+    bool AddCopy(uint64_t new_block, uint64_t old_block);
 
     // 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;
+    bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
 
     // 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;
+    bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
 
     // Flush all pending writes. This must be called before closing the writer
     // to ensure that the correct headers and footers are written.
     virtual bool Flush() = 0;
 
     // Return number of bytes the cow image occupies on disk.
-    virtual size_t GetCowSize() = 0;
+    virtual uint64_t GetCowSize() = 0;
+
+    // Returns true if AddCopy() operations are supported.
+    virtual bool SupportsCopyOperation() const { return true; }
+
+    const CowOptions& options() { return options_; }
+
+  protected:
+    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
+    virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+    bool ValidateNewBlock(uint64_t new_block);
 
   protected:
     CowOptions options_;
@@ -68,13 +84,14 @@
     bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE);
     bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE);
 
-    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;
-
     bool Flush() override;
 
-    size_t GetCowSize() override;
+    uint64_t GetCowSize() override;
+
+  protected:
+    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
 
   private:
     void SetupHeaders();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index eb6ad05..13f19aa 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -38,9 +38,7 @@
                 (const android::fs_mgr::CreateLogicalPartitionParams& params,
                  std::string* snapshot_path),
                 (override));
-    MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
-                (const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
-    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenSnapshotReader,
+    MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
                 (const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
     MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
     MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6fef58a..1bc972e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -35,8 +35,8 @@
 #include <update_engine/update_metadata.pb.h>
 
 #include <libsnapshot/auto_device.h>
-#include <libsnapshot/cow_writer.h>
 #include <libsnapshot/return.h>
+#include <libsnapshot/snapshot_writer.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -44,10 +44,6 @@
 #define DEFINED_FRIEND_TEST
 #endif
 
-namespace chromeos_update_engine {
-class FileDescriptor;
-}  // namespace chromeos_update_engine
-
 namespace android {
 
 namespace fiemap {
@@ -110,8 +106,6 @@
     };
     virtual ~ISnapshotManager() = default;
 
-    using FileDescriptor = chromeos_update_engine::FileDescriptor;
-
     // Begin an update. This must be called before creating any snapshots. It
     // will fail if GetUpdateState() != None.
     virtual bool BeginUpdate() = 0;
@@ -187,19 +181,14 @@
     virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                                    std::string* snapshot_path) = 0;
 
-    // Create an ICowWriter to build a snapshot against a target partition. The partition name must
-    // be suffixed.
-    virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
-            const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
-
-    // Open a snapshot for reading. A file-like interface is provided through the FileDescriptor.
-    // In this mode, writes are not supported. The partition name must be suffixed.
-    virtual std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+    // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
+    // must be suffixed.
+    virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
 
     // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
-    // OpenSnapshotWriter, or OpenSnapshotReader. All outstanding open descriptors, writers,
-    // or readers must be deleted before this is called.
+    // OpenSnapshotWriter. All outstanding open descriptors, writers, or
+    // readers must be deleted before this is called.
     virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
 
     // If this returns true, first-stage mount must call
@@ -310,9 +299,7 @@
     Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
-    std::unique_ptr<ICowWriter> OpenSnapshotWriter(
-            const android::fs_mgr::CreateLogicalPartitionParams& params) override;
-    std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+    std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params) override;
     bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
     bool NeedSnapshotsInFirstStageMount() override;
@@ -532,9 +519,39 @@
     std::string GetSnapshotDeviceName(const std::string& snapshot_name,
                                       const SnapshotStatus& status);
 
+    // Reason for calling MapPartitionWithSnapshot.
+    enum class SnapshotContext {
+        // For writing or verification (during update_engine).
+        Update,
+
+        // For mounting a full readable device.
+        Mount,
+    };
+
+    struct SnapshotPaths {
+        // Target/base device (eg system_b), always present.
+        std::string target_device;
+
+        // COW path (eg system_cow). Not present if no COW is needed.
+        std::string cow_device;
+
+        // dm-snapshot instance. Not present in Update mode for VABC.
+        std::string snapshot_device;
+    };
+
+    // Helpers for OpenSnapshotWriter.
+    std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
+                                                                  const std::string& partition_name,
+                                                                  const SnapshotStatus& status,
+                                                                  const SnapshotPaths& paths);
+    std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(LockedFile* lock,
+                                                              const std::string& partition_name,
+                                                              const SnapshotStatus& status,
+                                                              const SnapshotPaths& paths);
+
     // Map the base device, COW devices, and snapshot device.
     bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
-                                  std::string* path);
+                                  SnapshotContext context, SnapshotPaths* paths);
 
     // Map the COW devices, including the partition in super and the images.
     // |params|:
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 149f463..cda2bee 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -36,9 +36,7 @@
             const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
-    std::unique_ptr<ICowWriter> OpenSnapshotWriter(
-            const android::fs_mgr::CreateLogicalPartitionParams& params) override;
-    std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+    std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params) override;
     bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
     bool NeedSnapshotsInFirstStageMount() override;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
new file mode 100644
index 0000000..bf57a00
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -0,0 +1,68 @@
+// 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.
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include <libsnapshot/cow_writer.h>
+
+namespace chromeos_update_engine {
+class FileDescriptor;
+}  // namespace chromeos_update_engine
+
+namespace android {
+namespace snapshot {
+
+class ISnapshotWriter : public ICowWriter {
+  public:
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
+    explicit ISnapshotWriter(const CowOptions& options);
+
+    // Set the source device. This is used for AddCopy() operations, if the
+    // underlying writer needs the original bytes (for example if backed by
+    // dm-snapshot or if writing directly to an unsnapshotted region).
+    void SetSourceDevice(android::base::unique_fd&& source_fd);
+
+    virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
+
+  protected:
+    android::base::unique_fd source_fd_;
+};
+
+// Write directly to a dm-snapshot device.
+class OnlineKernelSnapshotWriter : public ISnapshotWriter {
+  public:
+    OnlineKernelSnapshotWriter(const CowOptions& options);
+
+    // Set the device used for all writes.
+    void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
+
+    bool Flush() override;
+    uint64_t GetCowSize() override { return cow_size_; }
+    virtual std::unique_ptr<FileDescriptor> OpenReader() override;
+
+  protected:
+    bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+    bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+    bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+
+  private:
+    android::base::unique_fd snapshot_fd_;
+    uint64_t cow_size_ = 0;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 8e369b0..197aeaa 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -144,6 +144,7 @@
 // Expect space of |path| is multiple of 4K.
 bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
                      std::string* hash = nullptr);
+bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
 
 std::optional<std::string> GetHash(const std::string& path);
 
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0904fc7..b672d0e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -15,6 +15,7 @@
 #include <libsnapshot/snapshot.h>
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <math.h>
 #include <sys/file.h>
 #include <sys/types.h>
@@ -43,6 +44,7 @@
 #include "device_info.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
+#include "snapshot_reader.h"
 #include "utility.h"
 
 namespace android {
@@ -1570,7 +1572,8 @@
                 .timeout_ms = timeout_ms,
         };
         std::string ignore_path;
-        if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
+        if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
+                                      nullptr)) {
             return false;
         }
     }
@@ -1598,11 +1601,10 @@
 
 bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
                                                CreateLogicalPartitionParams params,
-                                               std::string* path) {
+                                               SnapshotContext context, SnapshotPaths* paths) {
     auto begin = std::chrono::steady_clock::now();
 
     CHECK(lock);
-    path->clear();
 
     if (params.GetPartitionName() != params.GetDeviceName()) {
         LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
@@ -1683,8 +1685,11 @@
     }
     created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
 
+    if (paths) {
+        paths->target_device = base_path;
+    }
+
     if (!live_snapshot_status.has_value()) {
-        *path = base_path;
         created_devices.Release();
         return true;
     }
@@ -1711,21 +1716,33 @@
         LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
         return false;
     }
+    if (paths) {
+        paths->cow_device = cow_device;
+    }
 
     remaining_time = GetRemainingTime(params.timeout_ms, begin);
     if (remaining_time.count() < 0) return false;
 
+    if (context == SnapshotContext::Update && IsCompressionEnabled()) {
+        // Stop here, we can't run dm-user yet, the COW isn't built.
+        return true;
+    }
+
+    std::string path;
     if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
-                     path)) {
+                     &path)) {
         LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
         return false;
     }
     // No need to add params.GetPartitionName() to created_devices since it is immediately released.
 
+    if (paths) {
+        paths->snapshot_device = path;
+    }
+
     created_devices.Release();
 
-    LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
-
+    LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << path;
     return true;
 }
 
@@ -2438,23 +2455,86 @@
                    << params.GetPartitionName();
         return false;
     }
-    return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
+
+    SnapshotPaths paths;
+    if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
+        return false;
+    }
+
+    if (paths.snapshot_device.empty()) {
+        *snapshot_path = paths.snapshot_device;
+    } else {
+        *snapshot_path = paths.target_device;
+    }
+    return true;
 }
 
-std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
+std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
         const android::fs_mgr::CreateLogicalPartitionParams& params) {
-    (void)params;
+    // First unmap any existing mapping.
+    auto lock = LockShared();
+    if (!lock) return nullptr;
+    if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
+        LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
+                   << params.GetPartitionName();
+        return nullptr;
+    }
 
-    LOG(ERROR) << "OpenSnapshotWriter not yet implemented";
+    SnapshotPaths paths;
+    if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
+        return nullptr;
+    }
+
+    SnapshotStatus status;
+    if (!paths.cow_device.empty()) {
+        if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
+            return nullptr;
+        }
+    } else {
+        // Currently, partition_cow_creator always creates snapshots. The
+        // reason is that if partition X shrinks while partition Y grows, we
+        // cannot bindly write to the newly freed extents in X. This would
+        // make the old slot unusable. So, the entire size of the target
+        // partition is currently considered snapshottable.
+        LOG(ERROR) << "No snapshot available for partition " << params.GetPartitionName();
+        return nullptr;
+    }
+
+    if (IsCompressionEnabled()) {
+        return OpenCompressedSnapshotWriter(lock.get(), params.GetPartitionName(), status, paths);
+    } else {
+        return OpenKernelSnapshotWriter(lock.get(), params.GetPartitionName(), status, paths);
+    }
+}
+
+std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
+        LockedFile*, const std::string&, const SnapshotStatus&, const SnapshotPaths&) {
+    LOG(ERROR) << "OpenSnapshotWriter not yet implemented for compression";
     return nullptr;
 }
 
-std::unique_ptr<FileDescriptor> SnapshotManager::OpenSnapshotReader(
-        const android::fs_mgr::CreateLogicalPartitionParams& params) {
-    (void)params;
+std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
+        LockedFile* lock, [[maybe_unused]] const std::string& partition_name,
+        const SnapshotStatus& status, const SnapshotPaths& paths) {
+    CHECK(lock);
 
-    LOG(ERROR) << "OpenSnapshotReader not yet implemented";
-    return nullptr;
+    CowOptions cow_options;
+    cow_options.max_blocks = {status.device_size() / cow_options.block_size};
+
+    auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
+
+    std::string_view path =
+            paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
+    unique_fd fd(open(path.data(), O_RDWR | O_CLOEXEC));
+    if (fd < 0) {
+        PLOG(ERROR) << "open failed: " << path;
+        return nullptr;
+    }
+
+    uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
+    writer->SetSnapshotDevice(std::move(fd), cow_size);
+
+    return writer;
 }
 
 bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
new file mode 100644
index 0000000..0d47468
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -0,0 +1,77 @@
+//
+// 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 <ext4_utils/ext4_utils.h>
+
+#include "snapshot_reader.h"
+
+namespace android {
+namespace snapshot {
+
+// Not supported.
+bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
+    errno = EINVAL;
+    return false;
+}
+
+bool ReadOnlyFileDescriptor::Open(const char*, int) {
+    errno = EINVAL;
+    return false;
+}
+
+ssize_t ReadOnlyFileDescriptor::Write(const void*, size_t) {
+    errno = EINVAL;
+    return false;
+}
+
+bool ReadOnlyFileDescriptor::BlkIoctl(int, uint64_t, uint64_t, int*) {
+    errno = EINVAL;
+    return false;
+}
+
+ReadFdFileDescriptor::ReadFdFileDescriptor(android::base::unique_fd&& fd) : fd_(std::move(fd)) {}
+
+ssize_t ReadFdFileDescriptor::Read(void* buf, size_t count) {
+    return read(fd_.get(), buf, count);
+}
+
+off64_t ReadFdFileDescriptor::Seek(off64_t offset, int whence) {
+    return lseek(fd_.get(), offset, whence);
+}
+
+uint64_t ReadFdFileDescriptor::BlockDevSize() {
+    return get_block_device_size(fd_.get());
+}
+
+bool ReadFdFileDescriptor::Close() {
+    fd_ = {};
+    return true;
+}
+
+bool ReadFdFileDescriptor::IsSettingErrno() {
+    return true;
+}
+
+bool ReadFdFileDescriptor::IsOpen() {
+    return fd_ >= 0;
+}
+
+bool ReadFdFileDescriptor::Flush() {
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/snapshot_reader.h
new file mode 100644
index 0000000..1f2ffe2
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_reader.h
@@ -0,0 +1,50 @@
+//
+// 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.
+//
+
+#pragma once
+
+#include <android-base/file.h>
+#include <payload_consumer/file_descriptor.h>
+
+namespace android {
+namespace snapshot {
+
+class ReadOnlyFileDescriptor : public chromeos_update_engine::FileDescriptor {
+  public:
+    bool Open(const char* path, int flags, mode_t mode) override;
+    bool Open(const char* path, int flags) override;
+    ssize_t Write(const void* buf, size_t count) override;
+    bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;
+};
+
+class ReadFdFileDescriptor : public ReadOnlyFileDescriptor {
+  public:
+    explicit ReadFdFileDescriptor(android::base::unique_fd&& fd);
+
+    ssize_t Read(void* buf, size_t count) override;
+    off64_t Seek(off64_t offset, int whence) override;
+    uint64_t BlockDevSize() override;
+    bool Close() override;
+    bool IsSettingErrno() override;
+    bool IsOpen() override;
+    bool Flush() override;
+
+  private:
+    android::base::unique_fd fd_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 8ae6305..41f5da4 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -130,13 +130,7 @@
     return &snapshot_merge_stats;
 }
 
-std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
-        const CreateLogicalPartitionParams&) {
-    LOG(ERROR) << __FUNCTION__ << " should never be called.";
-    return nullptr;
-}
-
-std::unique_ptr<FileDescriptor> SnapshotManagerStub::OpenSnapshotReader(
+std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
         const CreateLogicalPartitionParams&) {
     LOG(ERROR) << __FUNCTION__ << " should never be called.";
     return nullptr;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6ff935b..f2caaa4 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -80,6 +80,7 @@
 std::string fake_super;
 
 void MountMetadata();
+bool IsCompressionEnabled();
 
 class SnapshotTest : public ::testing::Test {
   public:
@@ -892,42 +893,39 @@
         return AssertionSuccess();
     }
 
-    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) {
-        std::string real_path;
-        if (!sm->MapUpdateSnapshot(
-                    CreateLogicalPartitionParams{
-                            .block_device = fake_super,
-                            .metadata_slot = 1,
-                            .partition_name = name,
-                            .timeout_ms = 10s,
-                            .partition_opener = opener_.get(),
-                    },
-                    &real_path)) {
-            return AssertionFailure() << "Unable to map snapshot " << name;
+    AssertionResult MapUpdateSnapshot(const std::string& name,
+                                      std::unique_ptr<ICowWriter>* writer = nullptr) {
+        CreateLogicalPartitionParams params{
+                .block_device = fake_super,
+                .metadata_slot = 1,
+                .partition_name = name,
+                .timeout_ms = 10s,
+                .partition_opener = opener_.get(),
+        };
+
+        auto result = sm->OpenSnapshotWriter(params);
+        if (!result) {
+            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
         }
-        if (path) {
-            *path = real_path;
+
+        if (writer) {
+            *writer = std::move(result);
         }
-        return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
+        return AssertionSuccess();
     }
 
-    AssertionResult WriteSnapshotAndHash(const std::string& name,
-                                         std::optional<size_t> size = std::nullopt) {
-        std::string path;
-        auto res = MapUpdateSnapshot(name, &path);
+    AssertionResult WriteSnapshotAndHash(const std::string& name) {
+        std::unique_ptr<ICowWriter> writer;
+        auto res = MapUpdateSnapshot(name, &writer);
         if (!res) {
             return res;
         }
 
-        std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
-
-        if (!WriteRandomData(path, size, &hashes_[name])) {
-            return AssertionFailure() << "Unable to write " << size_string << " to " << path
-                                      << " for partition " << name;
+        if (!WriteRandomData(writer.get(), &hashes_[name])) {
+            return AssertionFailure() << "Unable to write random data to snapshot " << name;
         }
 
-        return AssertionSuccess() << "Written " << size_string << " to " << path
-                                  << " for snapshot partition " << name
+        return AssertionSuccess() << "Written random data to snapshot " << name
                                   << ", hash: " << hashes_[name];
     }
 
@@ -1003,7 +1001,7 @@
 
     // Write some data to target partitions.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name, partition_size));
+        ASSERT_TRUE(WriteSnapshotAndHash(name));
     }
 
     // Assert that source partitions aren't affected.
@@ -1406,6 +1404,10 @@
     MetadataMountedTest().TearDown();
 }
 
+bool IsCompressionEnabled() {
+    return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+}
+
 TEST_F(MetadataMountedTest, Android) {
     auto device = sm->EnsureMetadataMounted();
     EXPECT_NE(nullptr, device);
@@ -1623,7 +1625,7 @@
 
     // Map and write some data to target partition.
     ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
 
     // Finish update.
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -1655,7 +1657,7 @@
 
     // Map and write some data to target partitions.
     ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b", actual_write_size));
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
 
     std::vector<android::dm::DeviceMapper::TargetInfo> table;
     ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
new file mode 100644
index 0000000..584f15e
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -0,0 +1,97 @@
+//
+// 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 <libsnapshot/snapshot_writer.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <payload_consumer/file_descriptor.h>
+#include "snapshot_reader.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+using chromeos_update_engine::FileDescriptor;
+
+ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
+
+void ISnapshotWriter::SetSourceDevice(android::base::unique_fd&& source_fd) {
+    source_fd_ = std::move(source_fd);
+}
+
+OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
+    : ISnapshotWriter(options) {}
+
+void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
+                                                   uint64_t cow_size) {
+    snapshot_fd_ = std::move(snapshot_fd);
+    cow_size_ = cow_size;
+}
+
+bool OnlineKernelSnapshotWriter::Flush() {
+    if (fsync(snapshot_fd_.get()) < 0) {
+        PLOG(ERROR) << "fsync";
+        return false;
+    }
+    return true;
+}
+
+bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
+                                               size_t size) {
+    uint64_t offset = new_block_start * options_.block_size;
+    if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
+        PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
+        return false;
+    }
+    if (!android::base::WriteFully(snapshot_fd_, data, size)) {
+        PLOG(ERROR) << "EmitRawBlocks write";
+        return false;
+    }
+    return true;
+}
+
+bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+    std::string zeroes(options_.block_size, 0);
+    for (uint64_t i = 0; i < num_blocks; i++) {
+        if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
+    std::string buffer(options_.block_size, 0);
+    uint64_t offset = old_block * options_.block_size;
+    if (!android::base::ReadFullyAtOffset(source_fd_, buffer.data(), buffer.size(), offset)) {
+        PLOG(ERROR) << "EmitCopy read";
+        return false;
+    }
+    return EmitRawBlocks(new_block, buffer.data(), buffer.size());
+}
+
+std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
+    unique_fd fd(dup(snapshot_fd_.get()));
+    if (fd < 0) {
+        PLOG(ERROR) << "dup2 failed in OpenReader";
+        return nullptr;
+    }
+    return std::make_unique<ReadFdFileDescriptor>(std::move(fd));
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b07bf91..6104c82 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -127,6 +127,48 @@
     return true;
 }
 
+bool WriteRandomData(ICowWriter* writer, std::string* hash) {
+    unique_fd rand(open("/dev/urandom", O_RDONLY));
+    if (rand < 0) {
+        PLOG(ERROR) << "open /dev/urandom";
+        return false;
+    }
+
+    SHA256_CTX ctx;
+    if (hash) {
+        SHA256_Init(&ctx);
+    }
+
+    if (!writer->options().max_blocks) {
+        LOG(ERROR) << "CowWriter must specify maximum number of blocks";
+        return false;
+    }
+    uint64_t num_blocks = writer->options().max_blocks.value();
+
+    size_t block_size = writer->options().block_size;
+    std::string block(block_size, '\0');
+    for (uint64_t i = 0; i < num_blocks; i++) {
+        if (!ReadFully(rand, block.data(), block.size())) {
+            PLOG(ERROR) << "read /dev/urandom";
+            return false;
+        }
+        if (!writer->AddRawBlocks(i, block.data(), block.size())) {
+            LOG(ERROR) << "Failed to add raw block " << i;
+            return false;
+        }
+        if (hash) {
+            SHA256_Update(&ctx, block.data(), block.size());
+        }
+    }
+
+    if (hash) {
+        uint8_t out[32];
+        SHA256_Final(out, &ctx);
+        *hash = ToHexString(out, sizeof(out));
+    }
+    return true;
+}
+
 std::optional<std::string> GetHash(const std::string& path) {
     std::string content;
     if (!android::base::ReadFileToString(path, &content, true)) {
diff --git a/init/Android.bp b/init/Android.bp
index 3f2cd07..c3dd7f6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -129,6 +129,7 @@
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
+        "libsnapshot_cow",
         "libsnapshot_init",
         "libxml2",
         "lib_apex_manifest_proto_lite",
diff --git a/init/Android.mk b/init/Android.mk
index da94daf..998e0fd 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -112,6 +112,7 @@
     libmodprobe \
     libext2_uuid \
     libprotobuf-cpp-lite \
+    libsnapshot_cow \
     libsnapshot_init \
     update_metadata-protos \
 
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index acd093b..fa90878 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -113,8 +113,8 @@
     if (total_size > max_size_[log_id]) {
         Prune(log_id, total_size - max_size_[log_id], 0);
         after_size = GetSizeUsed(log_id);
-        LOG(INFO) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
-                  << " after size: " << after_size;
+        LOG(VERBOSE) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
+                     << " after size: " << after_size;
     }
 
     stats_->set_overhead(log_id, after_size);
diff --git a/logd/SerializedLogChunk.cpp b/logd/SerializedLogChunk.cpp
index e4d8945..1ffe7a8 100644
--- a/logd/SerializedLogChunk.cpp
+++ b/logd/SerializedLogChunk.cpp
@@ -27,8 +27,9 @@
 void SerializedLogChunk::Compress() {
     CHECK_EQ(compressed_log_.size(), 0U);
     CompressionEngine::GetInstance().Compress(contents_, write_offset_, compressed_log_);
-    LOG(INFO) << "Compressed Log, buffer max size: " << contents_.size()
-              << " size used: " << write_offset_ << " compressed size: " << compressed_log_.size();
+    LOG(VERBOSE) << "Compressed Log, buffer max size: " << contents_.size()
+                 << " size used: " << write_offset_
+                 << " compressed size: " << compressed_log_.size();
 }
 
 // TODO: Develop a better reference counting strategy to guard against the case where the writer is
@@ -111,4 +112,4 @@
     write_offset_ += entry->total_len();
     highest_sequence_number_ = sequence;
     return entry;
-}
\ No newline at end of file
+}