Merge "libsnapshot: Implement CowWriterV3::EmitCopy" into main
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 214eb1c..c5d7a02 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -206,5 +206,41 @@
     ASSERT_EQ(i, 5);
 }
 
+TEST_F(CowTestV3, CopyOp) {
+    CowOptions options;
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    ASSERT_TRUE(writer->AddCopy(10, 1000, 100));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 3);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    size_t i = 0;
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type, kCowCopyOp);
+        ASSERT_EQ(op->data_length, 0);
+        ASSERT_EQ(op->new_block, 10 + i);
+        ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+        iter->Next();
+        i += 1;
+    }
+
+    ASSERT_EQ(i, 100);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index d7aee13..aeb088d 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -168,9 +168,17 @@
 }
 
 bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
-    LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
-    if (new_block || old_block || num_blocks) return false;
-    return false;
+    for (size_t i = 0; i < num_blocks; i++) {
+        CowOperationV3 op = {};
+        op.type = kCowCopyOp;
+        op.new_block = new_block + i;
+        op.source_info = old_block + i;
+        if (!WriteOperation(op)) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {