Merge "Add //visibility:any_system_partition" into main
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 2021348..4456b26 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -209,6 +209,48 @@
     ASSERT_EQ(sink, data);
 }
 
+TEST_F(CowTestV3, BigReplaceOp) {
+    CowOptions options;
+    options.op_count_max = 10000;
+    options.batch_write = true;
+    options.cluster_ops = 2048;
+
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size * 4096, '\0');
+    for (int i = 0; i < data.size(); i++) {
+        data[i] = static_cast<char>('A' + i / options.block_size);
+    }
+    ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.op_count, 4096);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    size_t i = 0;
+
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+        std::string sink(options.block_size, '\0');
+        ASSERT_EQ(op->type(), kCowReplaceOp);
+        ASSERT_EQ(op->data_length, options.block_size);
+        ASSERT_EQ(op->new_block, 5 + i);
+        ASSERT_TRUE(ReadData(reader, op, sink.data(), options.block_size));
+        ASSERT_EQ(std::string_view(sink),
+                  std::string_view(data).substr(i * options.block_size, options.block_size))
+                << " readback data for " << i << "th block does not match";
+        iter->Next();
+        i++;
+    }
+}
+
 TEST_F(CowTestV3, ConsecutiveReplaceOp) {
     CowOptions options;
     options.op_count_max = 20;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index ea1da4b..8cc9964 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -717,13 +717,27 @@
         return false;
     }
     if (!data.empty()) {
-        const auto ret = pwritev(fd_, data.data(), data.size(), next_data_pos_);
-        if (ret != total_data_size) {
+        int total_written = 0;
+        int i = 0;
+        while (i < data.size()) {
+            int chunk = std::min(static_cast<int>(data.size() - i), IOV_MAX);
+
+            const auto ret = pwritev(fd_, data.data() + i, chunk, next_data_pos_ + total_written);
+            if (ret < 0) {
+                PLOG(ERROR) << "write failed chunk size of: " << chunk
+                            << " at offset: " << next_data_pos_ + total_written << " " << errno;
+                return false;
+            }
+            total_written += ret;
+            i += chunk;
+        }
+        if (total_written != total_data_size) {
             PLOG(ERROR) << "write failed for data of size: " << data.size()
-                        << " at offset: " << next_data_pos_ << " " << ret;
+                        << " at offset: " << next_data_pos_ << " " << errno;
             return false;
         }
     }
+
     header_.op_count += ops.size();
     next_data_pos_ += total_data_size;