Merge "init.rc: create /data/vendor* earlier"
diff --git a/OWNERS b/OWNERS
index 4f8e433..682a067 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1 @@
 enh@google.com
-baligh@google.com
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 43772ba..d9e69f7 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1149,7 +1149,7 @@
     // If we were using a specific transport ID, there's nothing we can wait for.
     if (previous_id == 0) {
         adb_set_transport(previous_type, previous_serial, 0);
-        wait_for_device("wait-for-device", 6000ms);
+        wait_for_device("wait-for-device", 12000ms);
     }
 
     return true;
diff --git a/adb/transport.cpp b/adb/transport.cpp
index c33d5af..93b4618 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -129,8 +129,8 @@
     };
 
     // Only retry for up to one minute.
-    static constexpr const std::chrono::seconds kDefaultTimeout = 10s;
-    static constexpr const size_t kMaxAttempts = 6;
+    static constexpr const std::chrono::seconds kDefaultTimeout = 3s;
+    static constexpr const size_t kMaxAttempts = 20;
 
     // Protects all members.
     std::mutex reconnect_mutex_;
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index fe05553..85a19e5 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/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 8788b5a..ef46eb9 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -284,6 +284,7 @@
     std::vector<std::string> argv;
     argv.push_back(std::to_string(start()));
     argv.push_back(std::to_string(size()));
+    argv.push_back(control_device());
     return android::base::Join(argv, " ");
 }
 
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 57e3884..478a3c6 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -311,10 +311,15 @@
 
 class DmTargetUser final : public DmTarget {
   public:
-    DmTargetUser(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+    DmTargetUser(uint64_t start, uint64_t length, std::string control_device)
+        : DmTarget(start, length), control_device_(control_device) {}
 
     std::string name() const override { return "user"; }
+    std::string control_device() const { return control_device_; }
     std::string GetParameterString() const override;
+
+  private:
+    std::string control_device_;
 };
 
 }  // namespace dm
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6e81c8b..0c5f3ba 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -77,6 +77,7 @@
         "snapshot_stats.cpp",
         "snapshot_stub.cpp",
         "snapshot_metadata_updater.cpp",
+        "snapshot_writer.cpp",
         "partition_cow_creator.cpp",
         "return.cpp",
         "utility.cpp",
@@ -247,6 +248,7 @@
         "libgmock",
         "liblp",
         "libsnapshot",
+        "libsnapshot_cow",
         "libsnapshot_test_helpers",
         "libsparse",
     ],
@@ -275,6 +277,7 @@
     static_libs: [
         "libfstab",
         "libsnapshot",
+        "libsnapshot_cow",
         "update_metadata-protos",
     ],
     shared_libs: [
@@ -338,6 +341,7 @@
         "libgmock", // from libsnapshot_test_helpers
         "liblog",
         "liblp",
+        "libsnapshot_cow",
         "libsnapshot_test_helpers",
         "libprotobuf-mutator",
     ],
@@ -540,9 +544,11 @@
 	"libsnapshot_snapuserd",
         "libcutils_sockets",
         "libz",
+	"libdm",
     ],
     header_libs: [
         "libstorage_literals_headers",
+        "libfiemap_headers",
     ],
     test_min_api_level: 30,
     auto_gen_config: true,
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 75e54f7..1d6c104 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <fcntl.h>
 #include <linux/fs.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <iostream>
 #include <memory>
 #include <string_view>
@@ -25,6 +25,7 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
+#include <libdm/loop_control.h>
 #include <libsnapshot/cow_writer.h>
 #include <libsnapshot/snapuserd_client.h>
 #include <storage_literals/storage_literals.h>
@@ -34,6 +35,8 @@
 
 using namespace android::storage_literals;
 using android::base::unique_fd;
+using LoopDevice = android::dm::LoopDevice;
+using namespace std::chrono_literals;
 
 class SnapuserdTest : public ::testing::Test {
   protected:
@@ -50,6 +53,18 @@
         cow_product_1_ = std::make_unique<TemporaryFile>();
         ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno);
 
+        // Create temp files in the PWD as selinux
+        // allows kernel domin to read from that directory only
+        // on userdebug/eng builds. Creating files under /data/local/tmp
+        // will have selinux denials.
+        std::string path = android::base::GetExecutableDirectory();
+
+        system_a_ = std::make_unique<TemporaryFile>(path);
+        ASSERT_GE(system_a_->fd, 0) << strerror(errno);
+
+        product_a_ = std::make_unique<TemporaryFile>(path);
+        ASSERT_GE(product_a_->fd, 0) << strerror(errno);
+
         size_ = 100_MiB;
     }
 
@@ -61,6 +76,12 @@
         cow_product_1_ = nullptr;
     }
 
+    std::unique_ptr<TemporaryFile> system_a_;
+    std::unique_ptr<TemporaryFile> product_a_;
+
+    std::unique_ptr<LoopDevice> system_a_loop_;
+    std::unique_ptr<LoopDevice> product_a_loop_;
+
     std::unique_ptr<TemporaryFile> cow_system_;
     std::unique_ptr<TemporaryFile> cow_product_;
 
@@ -76,6 +97,9 @@
     std::string system_device_name_;
     std::string product_device_name_;
 
+    std::string system_device_ctrl_name_;
+    std::string product_device_ctrl_name_;
+
     std::unique_ptr<uint8_t[]> random_buffer_1_;
     std::unique_ptr<uint8_t[]> random_buffer_2_;
     std::unique_ptr<uint8_t[]> zero_buffer_;
@@ -86,10 +110,18 @@
     void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
     void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
     void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
+    void DeleteDmUser(std::unique_ptr<TemporaryFile>& cow, std::string snapshot_device);
     void StartSnapuserdDaemon();
     void CreateSnapshotDevices();
     void SwitchSnapshotDevices();
 
+    std::string GetSystemControlPath() {
+        return std::string("/dev/dm-user-") + system_device_ctrl_name_;
+    }
+    std::string GetProductControlPath() {
+        return std::string("/dev/dm-user-") + product_device_ctrl_name_;
+    }
+
     void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
     SnapuserdClient client_;
 };
@@ -97,6 +129,7 @@
 void SnapuserdTest::Init() {
     unique_fd rnd_fd;
     loff_t offset = 0;
+    std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -118,10 +151,27 @@
         offset += 1_MiB;
     }
 
-    sys_fd_.reset(open("/dev/block/mapper/system_a", O_RDONLY));
+    for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
+        ASSERT_EQ(android::base::WriteFully(system_a_->fd, random_buffer.get(), 1_MiB), true);
+    }
+
+    for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
+        ASSERT_EQ(android::base::WriteFully(product_a_->fd, random_buffer.get(), 1_MiB), true);
+    }
+
+    // Create loopback devices
+    system_a_loop_ = std::make_unique<LoopDevice>(std::string(system_a_->path), 10s);
+    ASSERT_TRUE(system_a_loop_->valid());
+
+    product_a_loop_ = std::make_unique<LoopDevice>(std::string(product_a_->path), 10s);
+    ASSERT_TRUE(product_a_loop_->valid());
+
+    sys_fd_.reset(open(system_a_loop_->device().c_str(), O_RDONLY));
     ASSERT_TRUE(sys_fd_ > 0);
 
-    product_fd_.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+    product_fd_.reset(open(product_a_loop_->device().c_str(), O_RDONLY));
     ASSERT_TRUE(product_fd_ > 0);
 
     // Read from system partition from offset 0 of size 100MB
@@ -183,47 +233,65 @@
 }
 
 void SnapuserdTest::CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow) {
-    unique_fd system_a_fd;
     std::string cmd;
     system_device_name_.clear();
+    system_device_ctrl_name_.clear();
 
     // Create a COW device. Number of sectors is chosen random which can
     // hold at least 400MB of data
 
-    system_a_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
-    ASSERT_TRUE(system_a_fd > 0);
-
-    int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
+    int err = ioctl(sys_fd_.get(), BLKGETSIZE, &system_blksize_);
     ASSERT_GE(err, 0);
 
     std::string str(cow->path);
     std::size_t found = str.find_last_of("/\\");
     ASSERT_NE(found, std::string::npos);
     system_device_name_ = str.substr(found + 1);
+
+    // Create a control device
+    system_device_ctrl_name_ = system_device_name_ + "-ctrl";
     cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);
+    cmd += " " + system_device_ctrl_name_;
+
+    system(cmd.c_str());
+}
+
+void SnapuserdTest::DeleteDmUser(std::unique_ptr<TemporaryFile>& cow, std::string snapshot_device) {
+    std::string cmd;
+
+    cmd = "dmctl delete " + snapshot_device;
+    system(cmd.c_str());
+
+    cmd.clear();
+
+    std::string str(cow->path);
+    std::size_t found = str.find_last_of("/\\");
+    ASSERT_NE(found, std::string::npos);
+    std::string device_name = str.substr(found + 1);
+
+    cmd = "dmctl delete " + device_name;
 
     system(cmd.c_str());
 }
 
 void SnapuserdTest::CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow) {
-    unique_fd product_a_fd;
     std::string cmd;
     product_device_name_.clear();
+    product_device_ctrl_name_.clear();
 
     // Create a COW device. Number of sectors is chosen random which can
     // hold at least 400MB of data
 
-    product_a_fd.reset(open("/dev/block/mapper/product_a", O_RDONLY));
-    ASSERT_TRUE(product_a_fd > 0);
-
-    int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
+    int err = ioctl(product_fd_.get(), BLKGETSIZE, &product_blksize_);
     ASSERT_GE(err, 0);
 
     std::string str(cow->path);
     std::size_t found = str.find_last_of("/\\");
     ASSERT_NE(found, std::string::npos);
     product_device_name_ = str.substr(found + 1);
+    product_device_ctrl_name_ = product_device_name_ + "-ctrl";
     cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);
+    cmd += " " + product_device_ctrl_name_;
 
     system(cmd.c_str());
 }
@@ -234,10 +302,12 @@
     ret = client_.StartSnapuserd();
     ASSERT_EQ(ret, 0);
 
-    ret = client_.InitializeSnapuserd(cow_system_->path, "/dev/block/mapper/system_a");
+    ret = client_.InitializeSnapuserd(cow_system_->path, system_a_loop_->device(),
+                                      GetSystemControlPath());
     ASSERT_EQ(ret, 0);
 
-    ret = client_.InitializeSnapuserd(cow_product_->path, "/dev/block/mapper/product_a");
+    ret = client_.InitializeSnapuserd(cow_product_->path, product_a_loop_->device(),
+                                      GetProductControlPath());
     ASSERT_EQ(ret, 0);
 }
 
@@ -245,7 +315,7 @@
     std::string cmd;
 
     cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
-    cmd += " /dev/block/mapper/system_a";
+    cmd += " " + system_a_loop_->device();
     cmd += " /dev/block/mapper/" + system_device_name_;
     cmd += " P 8";
 
@@ -254,7 +324,7 @@
     cmd.clear();
 
     cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
-    cmd += " /dev/block/mapper/product_a";
+    cmd += " " + product_a_loop_->device();
     cmd += " /dev/block/mapper/" + product_device_name_;
     cmd += " P 8";
 
@@ -265,7 +335,7 @@
     std::string cmd;
 
     cmd = "dmctl create system-snapshot-1 -ro snapshot 0 " + std::to_string(system_blksize_);
-    cmd += " /dev/block/mapper/system_a";
+    cmd += " " + system_a_loop_->device();
     cmd += " /dev/block/mapper/" + system_device_name_;
     cmd += " P 8";
 
@@ -274,7 +344,7 @@
     cmd.clear();
 
     cmd = "dmctl create product-snapshot-1 -ro snapshot 0 " + std::to_string(product_blksize_);
-    cmd += " /dev/block/mapper/product_a";
+    cmd += " " + product_a_loop_->device();
     cmd += " /dev/block/mapper/" + product_device_name_;
     cmd += " P 8";
 
@@ -318,7 +388,7 @@
     //
     // IO path:
     //
-    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_system_a_partition
+    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_(system_a/product_a) partition
     // (copy op) -> return
     ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
 
@@ -379,24 +449,33 @@
     ASSERT_TRUE(snapshot_fd > 0);
     TestIO(snapshot_fd, product_buffer_);
 
+    snapshot_fd.reset(-1);
+
     // Sequence of operations for transition
     CreateCowDevice(cow_system_1_);
     CreateCowDevice(cow_product_1_);
 
+    // Create dm-user which creates new control devices
     CreateSystemDmUser(cow_system_1_);
     CreateProductDmUser(cow_product_1_);
 
-    std::vector<std::pair<std::string, std::string>> vec;
-    vec.push_back(std::make_pair(cow_system_1_->path, "/dev/block/mapper/system_a"));
-    vec.push_back(std::make_pair(cow_product_1_->path, "/dev/block/mapper/product_a"));
+    // Send the path information to second stage daemon through vector
+    std::vector<std::vector<std::string>> vec{
+            {cow_system_1_->path, system_a_loop_->device(), GetSystemControlPath()},
+            {cow_product_1_->path, product_a_loop_->device(), GetProductControlPath()}};
 
-    // Start the second stage deamon and send the devices
+    // Start the second stage deamon and send the devices information through
+    // vector.
     ASSERT_EQ(client_.RestartSnapuserd(vec), 0);
 
     // TODO: This is not switching snapshot device but creates a new table;
-    // however, it should serve the testing purpose.
+    // Second stage daemon will be ready to serve the IO request. From now
+    // onwards, we can go ahead and shutdown the first stage daemon
     SwitchSnapshotDevices();
 
+    DeleteDmUser(cow_system_, "system-snapshot");
+    DeleteDmUser(cow_product_, "product-snapshot");
+
     // Stop the first stage daemon
     ASSERT_EQ(client_.StopSnapuserd(true), 0);
 
@@ -409,6 +488,11 @@
     ASSERT_TRUE(snapshot_fd > 0);
     TestIO(snapshot_fd, product_buffer_);
 
+    snapshot_fd.reset(-1);
+
+    DeleteDmUser(cow_system_1_, "system-snapshot-1");
+    DeleteDmUser(cow_product_1_, "product-snapshot-1");
+
     // Stop the second stage daemon
     ASSERT_EQ(client_.StopSnapuserd(false), 0);
 }
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 70fdac1..f96f174 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -32,6 +32,9 @@
 
 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;
@@ -98,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()) {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 245da0c..2bc0171 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -58,6 +58,9 @@
     // Return number of bytes the cow image occupies on disk.
     virtual uint64_t GetCowSize() = 0;
 
+    // Returns true if AddCopy() operations are supported.
+    virtual bool SupportsCopyOperation() const { return true; }
+
     const CowOptions& options() { return options_; }
 
   protected:
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/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index d495014..2f727d6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -60,9 +60,11 @@
 
 class Snapuserd final {
   public:
-    Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device)
+    Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device,
+              const std::string& in_control_device)
         : cow_device_(in_cow_device),
           backing_store_device_(in_backing_store_device),
+          control_device_(in_control_device),
           metadata_read_done_(false) {}
 
     bool Init();
@@ -75,6 +77,8 @@
     int ReadDiskExceptions(chunk_t chunk, size_t size);
     int ReadData(chunk_t chunk, size_t size);
 
+    std::string GetControlDevicePath() { return control_device_; }
+
   private:
     int ProcessReplaceOp(const CowOperation* cow_op);
     int ProcessCopyOp(const CowOperation* cow_op);
@@ -82,6 +86,7 @@
 
     std::string cow_device_;
     std::string backing_store_device_;
+    std::string control_device_;
 
     unique_fd cow_fd_;
     unique_fd backing_store_fd_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index 535e923..ab2149e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -51,8 +51,9 @@
   public:
     int StartSnapuserd();
     int StopSnapuserd(bool firstStageDaemon);
-    int RestartSnapuserd(std::vector<std::pair<std::string, std::string>>& vec);
-    int InitializeSnapuserd(std::string cow_device, std::string backing_device);
+    int RestartSnapuserd(std::vector<std::vector<std::string>>& vec);
+    int InitializeSnapuserd(std::string cow_device, std::string backing_device,
+                            std::string control_device);
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
index 1a6ba8f..1037c12 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -80,7 +80,6 @@
     __u64 flags;
     __u64 sector;
     __u64 len;
-    __u64 io_in_progress;
 } __attribute__((packed));
 
 struct dm_user_payload {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
index 584fe71..a1ebd3a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -60,7 +60,8 @@
 
     virtual ~Stoppable() {}
 
-    virtual void ThreadStart(std::string cow_device, std::string backing_device) = 0;
+    virtual void ThreadStart(std::string cow_device, std::string backing_device,
+                             std::string control_device) = 0;
 
     bool StopRequested() {
         // checks if value in future object is available
@@ -78,7 +79,8 @@
     bool terminating_;
     std::vector<std::unique_ptr<Client>> clients_vec_;
 
-    void ThreadStart(std::string cow_device, std::string backing_device) override;
+    void ThreadStart(std::string cow_device, std::string backing_device,
+                     std::string control_device) override;
     void ShutdownThreads();
     DaemonOperations Resolveop(std::string& input);
     std::string GetDaemonStatus();
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..2df2c94 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>
@@ -1569,8 +1570,8 @@
                 .partition_opener = &opener,
                 .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 +1599,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 +1683,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 +1714,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 +2453,85 @@
                    << 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;
+    }
+    DCHECK(!snapshot_path->empty());
+    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);
+    }
+    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 path = paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
+    unique_fd fd(open(path.c_str(), 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_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..77226ad 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,78 @@
         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) {
+        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);
-        if (!res) {
-            return res;
+    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
+        CreateLogicalPartitionParams params{
+                .block_device = fake_super,
+                .metadata_slot = 1,
+                .partition_name = name,
+                .timeout_ms = 10s,
+                .partition_opener = opener_.get(),
+        };
+
+        auto result = sm->MapUpdateSnapshot(params, path);
+        if (!result) {
+            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
+        }
+        return AssertionSuccess();
+    }
+
+    AssertionResult MapUpdateSnapshot(const std::string& name) {
+        if (IsCompressionEnabled()) {
+            std::unique_ptr<ICowWriter> writer;
+            return MapUpdateSnapshot(name, &writer);
+        } else {
+            std::string path;
+            return MapUpdateSnapshot(name, &path);
+        }
+    }
+
+    AssertionResult WriteSnapshotAndHash(const std::string& name) {
+        if (IsCompressionEnabled()) {
+            std::unique_ptr<ICowWriter> writer;
+            auto res = MapUpdateSnapshot(name, &writer);
+            if (!res) {
+                return res;
+            }
+            if (!WriteRandomData(writer.get(), &hashes_[name])) {
+                return AssertionFailure() << "Unable to write random data to snapshot " << name;
+            }
+        } else {
+            std::string path;
+            auto res = MapUpdateSnapshot(name, &path);
+            if (!res) {
+                return res;
+            }
+            if (!WriteRandomData(path, std::nullopt, &hashes_[name])) {
+                return AssertionFailure() << "Unable to write random data to snapshot " << name;
+            }
         }
 
-        std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
+        // Make sure updates to one device are seen by all devices.
+        sync();
 
-        if (!WriteRandomData(path, size, &hashes_[name])) {
-            return AssertionFailure() << "Unable to write " << size_string << " to " << path
-                                      << " for partition " << 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 +1040,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 +1443,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 +1664,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 +1696,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..1958f18
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -0,0 +1,91 @@
+//
+// 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>
+
+namespace android {
+namespace snapshot {
+
+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() {
+    LOG(ERROR) << "OnlineKernelSnapshotWriter::OpenReader not yet implemented";
+    return nullptr;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 3ed853f..62ef1b0 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -34,18 +34,6 @@
 
 static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);
 
-class Target {
-  public:
-    // Represents an already-created Target, which is referenced by UUID.
-    Target(std::string uuid) : uuid_(uuid) {}
-
-    const auto& uuid() { return uuid_; }
-    std::string control_path() { return std::string("/dev/dm-user-") + uuid(); }
-
-  private:
-    const std::string uuid_;
-};
-
 void BufferSink::Initialize(size_t size) {
     buffer_size_ = size;
     buffer_offset_ = 0;
@@ -498,26 +486,13 @@
         return false;
     }
 
-    std::string str(cow_device_);
-    std::size_t found = str.find_last_of("/\\");
-    CHECK(found != std::string::npos);
-    std::string device_name = str.substr(found + 1);
+    std::string control_path = GetControlDevicePath();
 
-    LOG(DEBUG) << "Fetching UUID for: " << device_name;
+    LOG(DEBUG) << "Opening control device " << control_path;
 
-    auto& dm = dm::DeviceMapper::Instance();
-    std::string uuid;
-    if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
-        LOG(ERROR) << "Unable to find UUID for " << cow_device_;
-        return false;
-    }
-
-    LOG(DEBUG) << "UUID: " << uuid;
-    Target t(uuid);
-
-    ctrl_fd_.reset(open(t.control_path().c_str(), O_RDWR));
+    ctrl_fd_.reset(open(control_path.c_str(), O_RDWR));
     if (ctrl_fd_ < 0) {
-        LOG(ERROR) << "Unable to open " << t.control_path();
+        LOG(ERROR) << "Unable to open " << control_path;
         return false;
     }
 
@@ -553,7 +528,6 @@
         case DM_USER_MAP_READ: {
             size_t remaining_size = header->len;
             loff_t offset = 0;
-            header->io_in_progress = 0;
             ret = 0;
             do {
                 size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
@@ -619,7 +593,6 @@
                 if (remaining_size) {
                     LOG(DEBUG) << "Write done ret: " << ret
                                << " remaining size: " << remaining_size;
-                    bufsink_.GetHeaderPtr()->io_in_progress = 1;
                 }
             } while (remaining_size);
 
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
index bef8f5c..78dbada 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -127,24 +127,6 @@
     return msgStr;
 }
 
-#if 0
-std::string SnapuserdClient::Receivemsg() {
-    char msg[PACKET_SIZE];
-    std::string msgStr("fail");
-    int ret;
-
-    ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, PACKET_SIZE, 0));
-    if (ret <= 0) {
-        LOG(ERROR) << "recv failed " << strerror(errno);
-        return msgStr;
-    }
-
-    msgStr.clear();
-    msgStr = msg;
-    return msgStr;
-}
-#endif
-
 int SnapuserdClient::StopSnapuserd(bool firstStageDaemon) {
     if (firstStageDaemon) {
         sockfd_ = socket_local_client(GetSocketNameFirstStage().c_str(),
@@ -209,7 +191,8 @@
     return 0;
 }
 
-int SnapuserdClient::InitializeSnapuserd(std::string cow_device, std::string backing_device) {
+int SnapuserdClient::InitializeSnapuserd(std::string cow_device, std::string backing_device,
+                                         std::string control_device) {
     int ret = 0;
 
     if (!ConnectToServer()) {
@@ -217,7 +200,7 @@
         return -1;
     }
 
-    std::string msg = "start," + cow_device + "," + backing_device;
+    std::string msg = "start," + cow_device + "," + backing_device + "," + control_device;
 
     ret = Sendmsg(msg.c_str(), msg.size());
     if (ret < 0) {
@@ -270,7 +253,7 @@
  * completely active to serve the IO and merging process.
  *
  */
-int SnapuserdClient::RestartSnapuserd(std::vector<std::pair<std::string, std::string>>& vec) {
+int SnapuserdClient::RestartSnapuserd(std::vector<std::vector<std::string>>& vec) {
     // Connect to first-stage daemon and send a terminate-request control
     // message. This will not terminate the daemon but will mark the daemon as
     // passive.
@@ -306,14 +289,19 @@
 
     LOG(DEBUG) << "Second stage Snapuserd daemon created successfully at socket "
                << GetSocketNameSecondStage();
-    CHECK(vec.size() % 2 == 0);
 
+    // Vector contains all the device information to be passed to the new
+    // daemon. Note that the caller can choose to initialize separately
+    // by calling InitializeSnapuserd() API as well. In that case, vector
+    // should be empty
     for (int i = 0; i < vec.size(); i++) {
-        std::string& cow_device = vec[i].first;
-        std::string& base_device = vec[i].second;
+        std::string& cow_device = vec[i][0];
+        std::string& base_device = vec[i][1];
+        std::string& control_device = vec[i][2];
 
-        InitializeSnapuserd(cow_device, base_device);
-        LOG(DEBUG) << "Daemon initialized with " << cow_device << " and " << base_device;
+        InitializeSnapuserd(cow_device, base_device, control_device);
+        LOG(DEBUG) << "Daemon initialized with " << cow_device << ", " << base_device << " and "
+                   << control_device;
     }
 
     return 0;
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 1f8dd63..53101aa 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -63,8 +63,9 @@
 }
 
 // new thread
-void SnapuserdServer::ThreadStart(std::string cow_device, std::string backing_device) {
-    Snapuserd snapd(cow_device, backing_device);
+void SnapuserdServer::ThreadStart(std::string cow_device, std::string backing_device,
+                                  std::string control_device) {
+    Snapuserd snapd(cow_device, backing_device, control_device);
     if (!snapd.Init()) {
         PLOG(ERROR) << "Snapuserd: Init failed";
         return;
@@ -73,10 +74,9 @@
     while (StopRequested() == false) {
         int ret = snapd.Run();
 
-        if (ret == -ETIMEDOUT) continue;
-
         if (ret < 0) {
-            PLOG(ERROR) << "snapd.Run() failed..." << ret;
+            LOG(ERROR) << "Snapuserd: Thread terminating as control device is de-registered";
+            break;
         }
     }
 }
@@ -159,12 +159,12 @@
         switch (op) {
             case DaemonOperations::START: {
                 // Message format:
-                // start,<cow_device_path>,<source_device_path>
+                // start,<cow_device_path>,<source_device_path>,<control_device>
                 //
                 // Start the new thread which binds to dm-user misc device
                 newClient = std::make_unique<Client>();
                 newClient->SetThreadHandler(
-                        std::bind(&SnapuserdServer::ThreadStart, this, out[1], out[2]));
+                        std::bind(&SnapuserdServer::ThreadStart, this, out[1], out[2], out[3]));
                 clients_vec_.push_back(std::move(newClient));
                 sprintf(msg, "success");
                 Sendmsg(fd, msg, MAX_PACKET_SIZE);
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/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 9edcda7..62ca162 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -177,7 +177,12 @@
             return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
                                                       cow_device, mode, chunk_size);
         } else if (target_type == "user") {
-            return std::make_unique<DmTargetUser>(start_sector, num_sectors);
+            if (!HasArgs(1)) {
+                std::cerr << "Expected \"user\" <control_device_name>" << std::endl;
+                return nullptr;
+            }
+            std::string control_device = NextArg();
+            return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
         } else {
             std::cerr << "Unrecognized target type: " << target_type << std::endl;
             return nullptr;
diff --git a/init/Android.bp b/init/Android.bp
index 13b1876..19ba21b 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..ac31ef1 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -80,6 +80,7 @@
     $(TARGET_RAMDISK_OUT)/dev \
     $(TARGET_RAMDISK_OUT)/mnt \
     $(TARGET_RAMDISK_OUT)/proc \
+    $(TARGET_RAMDISK_OUT)/second_stage_resources \
     $(TARGET_RAMDISK_OUT)/sys \
 
 LOCAL_STATIC_LIBRARIES := \
@@ -112,6 +113,7 @@
     libmodprobe \
     libext2_uuid \
     libprotobuf-cpp-lite \
+    libsnapshot_cow \
     libsnapshot_init \
     update_metadata-protos \
 
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 0215576..554f301 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -41,6 +41,7 @@
 #include "first_stage_console.h"
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
+#include "second_stage_resources.h"
 #include "switch_root.h"
 #include "util.h"
 
@@ -235,6 +236,11 @@
     // /debug_ramdisk is used to preserve additional files from the debug ramdisk
     CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
+
+    // /second_stage_resources is used to preserve files from first to second
+    // stage init
+    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"))
 #undef CHECKCALL
 
     SetStdioToDevNull(argv);
@@ -276,6 +282,20 @@
         StartConsole();
     }
 
+    if (access(kBootImageRamdiskProp, F_OK) == 0) {
+        std::string dest = GetRamdiskPropForSecondStage();
+        std::string dir = android::base::Dirname(dest);
+        std::error_code ec;
+        if (!fs::create_directories(dir, ec)) {
+            LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
+        }
+        if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {
+            LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "
+                       << ec.message();
+        }
+        LOG(INFO) << "Copied ramdisk prop to " << dest;
+    }
+
     if (ForceNormalBoot(cmdline)) {
         mkdir("/first_stage_ramdisk", 0755);
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
diff --git a/init/init.cpp b/init/init.cpp
index 7d00538..ea04494 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -71,6 +71,7 @@
 #include "proto_utils.h"
 #include "reboot.h"
 #include "reboot_utils.h"
+#include "second_stage_resources.h"
 #include "security.h"
 #include "selabel.h"
 #include "selinux.h"
@@ -668,6 +669,12 @@
     }
 }
 
+static void UmountSecondStageRes() {
+    if (umount(kSecondStageRes) != 0) {
+        PLOG(ERROR) << "Failed to umount " << kSecondStageRes;
+    }
+}
+
 static void MountExtraFilesystems() {
 #define CHECKCALL(x) \
     if ((x) != 0) PLOG(FATAL) << #x " failed.";
@@ -776,6 +783,9 @@
 
     PropertyInit();
 
+    // Umount second stage resources after property service has read the .prop files.
+    UmountSecondStageRes();
+
     // Umount the debug ramdisk after property service has read the .prop files when it means to.
     if (load_debug_prop) {
         UmountDebugRamdisk();
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 57e82fa..e71c386 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -67,6 +67,7 @@
 #include "persistent_properties.h"
 #include "property_type.h"
 #include "proto_utils.h"
+#include "second_stage_resources.h"
 #include "selinux.h"
 #include "subcontext.h"
 #include "system/core/init/property_service.pb.h"
@@ -745,6 +746,15 @@
     return true;
 }
 
+static void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {
+    std::string prop = GetRamdiskPropForSecondStage();
+    if (access(prop.c_str(), R_OK) != 0) {
+        CHECK(errno == ENOENT) << "Cannot access " << prop << ": " << strerror(errno);
+        return;
+    }
+    load_properties_from_file(prop.c_str(), nullptr, properties);
+}
+
 // persist.sys.usb.config values can't be combined on build-time when property
 // files are split into each partition.
 // So we need to apply the same rule of build/make/tools/post_process_props.py
@@ -933,6 +943,7 @@
 
     // Order matters here. The more the partition is specific to a product, the higher its
     // precedence is.
+    LoadPropertiesFromSecondStageRes(&properties);
     load_properties_from_file("/system/build.prop", nullptr, &properties);
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
diff --git a/init/second_stage_resources.h b/init/second_stage_resources.h
new file mode 100644
index 0000000..544d16f
--- /dev/null
+++ b/init/second_stage_resources.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <string>
+
+namespace android {
+namespace init {
+
+constexpr const char kSecondStageRes[] = "/second_stage_resources";
+constexpr const char kBootImageRamdiskProp[] = "/system/etc/ramdisk/build.prop";
+
+inline std::string GetRamdiskPropForSecondStage() {
+    return std::string(kSecondStageRes) + kBootImageRamdiskProp;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 77fa94e..2bceb75 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -78,7 +78,7 @@
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
     dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
-    linkerconfig $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    linkerconfig second_stage_resources $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \