Refactor off V2 Cow Ops

Refactor writer, reader + parser to work off v2 version of
CowOperations.

Test: m libsnapshot. ota on cuttlefish
Change-Id: Iec59be91e5f54782272b37702d645942df38c771
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 9359b9e..2a2cee2 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -22,6 +22,9 @@
 namespace android {
 namespace snapshot {
 
+struct CowOperationV3;
+typedef CowOperationV3 CowOperation;
+
 static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
 static constexpr uint32_t kCowVersionMajor = 2;
 static constexpr uint32_t kCowVersionMinor = 0;
@@ -109,43 +112,7 @@
     uint64_t num_ops;
 } __attribute__((packed));
 
-// Cow operations are currently fixed-size entries, but this may change if
-// needed.
-struct CowOperation {
-    // The operation code (see the constants and structures below).
-    uint8_t type;
-
-    // If this operation reads from the data section of the COW, this contains
-    // the compression type of that data (see constants below).
-    uint8_t compression;
-
-    // If this operation reads from the data section of the COW, this contains
-    // the length.
-    uint16_t data_length;
-
-    // The block of data in the new image that this operation modifies.
-    uint64_t new_block;
-
-    // The value of |source| depends on the operation code.
-    //
-    // For copy operations, this is a block location in the source image.
-    //
-    // For replace operations, this is a byte offset within the COW's data
-    // sections (eg, not landing within the header or metadata). It is an
-    // absolute position within the image.
-    //
-    // For zero operations (replace with all zeroes), this is unused and must
-    // be zero.
-    //
-    // For Label operations, this is the value of the applied label.
-    //
-    // For Cluster operations, this is the length of the following data region
-    //
-    // For Xor operations, this is the byte location in the source image.
-    uint64_t source;
-} __attribute__((packed));
-
-// The on disk format of cow (currently ==  CowOperation)
+// V2 version of COW. On disk format for older devices
 struct CowOperationV2 {
     // The operation code (see the constants and structures below).
     uint8_t type;
@@ -180,8 +147,33 @@
     uint64_t source;
 } __attribute__((packed));
 
-static_assert(sizeof(CowOperationV2) == sizeof(CowOperation));
-static_assert(sizeof(CowOperation) == sizeof(CowFooterOperation));
+// The on disk format of cow (currently ==  CowOperation)
+struct CowOperationV3 {
+    // The operation code (see the constants and structures below).
+    uint8_t type;
+
+    // If this operation reads from the data section of the COW, this contains
+    // the length.
+    uint16_t data_length;
+
+    // The block of data in the new image that this operation modifies.
+    uint32_t new_block;
+
+    // The value of |source| depends on the operation code.
+    //
+    // CopyOp: a 32-bit block location in the source image.
+    // ReplaceOp: an absolute byte offset within the COW's data section.
+    // XorOp: an absolute byte offset in the source image.
+    // ZeroOp: unused
+    // LabelOp: a 64-bit opaque identifier.
+    //
+    // For ops other than Label:
+    //  Bits 47-62 are reserved and must be zero.
+    // A block is compressed if it’s data is < block_sz
+    uint64_t source_info;
+} __attribute__((packed));
+
+static_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));
 
 static constexpr uint8_t kCowCopyOp = 1;
 static constexpr uint8_t kCowReplaceOp = 2;
@@ -208,11 +200,14 @@
 static constexpr uint8_t kCowReadAheadInProgress = 1;
 static constexpr uint8_t kCowReadAheadDone = 2;
 
+static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
+static constexpr uint64_t kCowOpSourceInfoCompressBit = (1ULL << 63);
+
 static inline uint64_t GetCowOpSourceInfoData(const CowOperation* op) {
-    return op->source;
+    return op->source_info & kCowOpSourceInfoDataMask;
 }
 static inline bool GetCowOpSourceInfoCompression(const CowOperation* op) {
-    return op->compression != kCowCompressNone;
+    return !!(op->source_info & kCowOpSourceInfoCompressBit);
 }
 
 struct CowFooter {
@@ -236,10 +231,12 @@
 // 2MB Scratch space used for read-ahead
 static constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);
 
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);
+
 std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
 
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
 
 // Ops that are internal to the Cow Format and not OTA data
 bool IsMetadataOp(const CowOperation& op);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 67d301d..1d6fd62 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -165,9 +165,10 @@
     void UpdateMergeOpsCompleted(int num_merge_ops) { header_.num_merge_ops += num_merge_ops; }
 
   private:
+    bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);
     bool PrepMergeOps();
     uint64_t FindNumCopyops();
-    uint8_t GetCompressionType(const CowOperation* op);
+    uint8_t GetCompressionType();
 
     android::base::unique_fd owned_fd_;
     android::base::borrowed_fd fd_;
@@ -184,6 +185,7 @@
     std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
     ReaderFlags reader_flag_;
     bool is_merge_{};
+    uint8_t compression_type_ = kCowCompressNone;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index ff3ccec..58dca64 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -14,11 +14,14 @@
 // limitations under the License.
 //
 
+#include <inttypes.h>
 #include <libsnapshot/cow_format.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/cow_format.h>
 #include "writer_v2.h"
 
 namespace android {
@@ -26,45 +29,82 @@
 
 using android::base::unique_fd;
 
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
-    os << "CowOperation(type:";
-    if (op.type == kCowCopyOp)
-        os << "kCowCopyOp,    ";
-    else if (op.type == kCowReplaceOp)
-        os << "kCowReplaceOp, ";
-    else if (op.type == kCowZeroOp)
-        os << "kZeroOp,       ";
-    else if (op.type == kCowFooterOp)
-        os << "kCowFooterOp,  ";
-    else if (op.type == kCowLabelOp)
-        os << "kCowLabelOp,   ";
-    else if (op.type == kCowClusterOp)
-        os << "kCowClusterOp  ";
-    else if (op.type == kCowXorOp)
-        os << "kCowXorOp      ";
-    else if (op.type == kCowSequenceOp)
-        os << "kCowSequenceOp ";
-    else if (op.type == kCowFooterOp)
-        os << "kCowFooterOp  ";
-    else
-        os << (int)op.type << "?,";
-    os << "compression:";
-    if (op.compression == kCowCompressNone)
-        os << "kCowCompressNone,   ";
-    else if (op.compression == kCowCompressGz)
-        os << "kCowCompressGz,     ";
-    else if (op.compression == kCowCompressBrotli)
-        os << "kCowCompressBrotli, ";
-    else
-        os << (int)op.compression << "?, ";
-    os << "data_length:" << op.data_length << ",\t";
-    os << "new_block:" << op.new_block << ",\t";
+std::ostream& EmitCowTypeString(std::ostream& os, uint8_t cow_type) {
+    switch (cow_type) {
+        case kCowCopyOp:
+            return os << "kCowCopyOp";
+        case kCowReplaceOp:
+            return os << "kCowReplaceOp";
+        case kCowZeroOp:
+            return os << "kZeroOp";
+        case kCowFooterOp:
+            return os << "kCowFooterOp";
+        case kCowLabelOp:
+            return os << "kCowLabelOp";
+        case kCowClusterOp:
+            return os << "kCowClusterOp";
+        case kCowXorOp:
+            return os << "kCowXorOp";
+        case kCowSequenceOp:
+            return os << "kCowSequenceOp";
+        default:
+            return os << (int)cow_type << "unknown";
+    }
+}
+
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& op) {
+    os << "CowOperationV2(";
+    EmitCowTypeString(os, op.type) << ", ";
+    switch (op.compression) {
+        case kCowCompressNone:
+            os << "uncompressed, ";
+            break;
+        case kCowCompressGz:
+            os << "gz, ";
+            break;
+        case kCowCompressBrotli:
+            os << "brotli, ";
+            break;
+        case kCowCompressLz4:
+            os << "lz4, ";
+            break;
+        case kCowCompressZstd:
+            os << "zstd, ";
+            break;
+    }
+    os << "data_length:" << op.data_length << ", ";
+    os << "new_block:" << op.new_block << ", ";
     os << "source:" << op.source;
     os << ")";
     return os;
 }
 
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
+std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
+    os << "CowOperation(";
+    EmitCowTypeString(os, op.type);
+    if (op.type == kCowReplaceOp || op.type == kCowXorOp || op.type == kCowSequenceOp) {
+        if (op.source_info & kCowOpSourceInfoCompressBit) {
+            os << ", compressed";
+        } else {
+            os << ", uncompressed";
+        }
+        os << ", data_length:" << op.data_length;
+    }
+    if (op.type != kCowClusterOp && op.type != kCowSequenceOp && op.type != kCowLabelOp) {
+        os << ", new_block:" << op.new_block;
+    }
+    if (op.type == kCowXorOp || op.type == kCowReplaceOp || op.type == kCowCopyOp) {
+        os << ", source:" << (op.source_info & kCowOpSourceInfoDataMask);
+    } else if (op.type == kCowClusterOp) {
+        os << ", cluster_data:" << (op.source_info & kCowOpSourceInfoDataMask);
+    } else {
+        os << ", label:0x" << android::base::StringPrintf("%" PRIx64, op.source_info);
+    }
+    os << ")";
+    return os;
+}
+
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
     if (op.type == kCowClusterOp) {
         return op.source;
     } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
@@ -74,11 +114,11 @@
     }
 }
 
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_ops) {
     if (op.type == kCowClusterOp) {
-        return cluster_ops * sizeof(CowOperation);
+        return cluster_ops * sizeof(CowOperationV2);
     } else if (cluster_ops == 0) {
-        return sizeof(CowOperation);
+        return sizeof(CowOperationV2);
     } else {
         return 0;
     }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 1b5d724..bf50f2f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -55,6 +55,7 @@
     cow->data_loc_ = data_loc_;
     cow->block_pos_index_ = block_pos_index_;
     cow->is_merge_ = is_merge_;
+    cow->compression_type_ = compression_type_;
     return cow;
 }
 
@@ -101,8 +102,44 @@
     footer_ = parser.footer();
     fd_size_ = parser.fd_size();
     last_label_ = parser.last_label();
-    ops_ = parser.ops();
     data_loc_ = parser.data_loc();
+    ops_ = std::make_shared<std::vector<CowOperation>>(parser.ops()->size());
+
+    // Translate the operation buffer from on disk to in memory
+    for (size_t i = 0; i < parser.ops()->size(); i++) {
+        const auto& v2_op = parser.ops()->at(i);
+
+        auto& new_op = ops_->at(i);
+        new_op.type = v2_op.type;
+        new_op.data_length = v2_op.data_length;
+
+        if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
+            LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
+            return false;
+        }
+        new_op.new_block = v2_op.new_block;
+
+        uint64_t source_info = v2_op.source;
+        if (new_op.type != kCowLabelOp) {
+            source_info &= kCowOpSourceInfoDataMask;
+            if (source_info != v2_op.source) {
+                LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
+                return false;
+            }
+        }
+        if (v2_op.compression != kCowCompressNone) {
+            if (compression_type_ == kCowCompressNone) {
+                compression_type_ = v2_op.compression;
+            } else if (compression_type_ != v2_op.compression) {
+                LOG(ERROR) << "COW has mixed compression types which is not supported;"
+                           << " previously saw " << compression_type_ << ", got "
+                           << v2_op.compression << ", op: " << v2_op;
+                return false;
+            }
+            source_info |= kCowOpSourceInfoCompressBit;
+        }
+        new_op.source_info = source_info;
+    }
 
     // If we're resuming a write, we're not ready to merge
     if (label.has_value()) return true;
@@ -597,14 +634,14 @@
     size_t remaining_;
 };
 
-uint8_t CowReader::GetCompressionType(const CowOperation* op) {
-    return op->compression;
+uint8_t CowReader::GetCompressionType() {
+    return compression_type_;
 }
 
 ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
                             size_t ignore_bytes) {
     std::unique_ptr<IDecompressor> decompressor;
-    switch (GetCompressionType(op)) {
+    switch (GetCompressionType()) {
         case kCowCompressNone:
             break;
         case kCowCompressGz:
@@ -624,7 +661,7 @@
             }
             break;
         default:
-            LOG(ERROR) << "Unknown compression type: " << GetCompressionType(op);
+            LOG(ERROR) << "Unknown compression type: " << GetCompressionType();
             return -1;
     }
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index fdb5c59..8edeae1 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -66,18 +66,18 @@
                    << sizeof(CowFooter);
         return false;
     }
-    if (header_.op_size != sizeof(CowOperation)) {
+    if (header_.op_size != sizeof(CowOperationV2)) {
         LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
-                   << sizeof(CowOperation);
+                   << sizeof(CowOperationV2);
         return false;
     }
     if (header_.cluster_ops == 1) {
         LOG(ERROR) << "Clusters must contain at least two operations to function.";
         return false;
     }
-    if (header_.op_size != sizeof(CowOperation)) {
+    if (header_.op_size != sizeof(CowOperationV2)) {
         LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
-                   << sizeof(CowOperation);
+                   << sizeof(CowOperationV2);
         return false;
     }
     if (header_.cluster_ops == 1) {
@@ -123,23 +123,23 @@
     uint64_t data_pos = 0;
 
     if (header_.cluster_ops) {
-        data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+        data_pos = pos + header_.cluster_ops * sizeof(CowOperationV2);
     } else {
-        data_pos = pos + sizeof(CowOperation);
+        data_pos = pos + sizeof(CowOperationV2);
     }
 
-    auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+    auto ops_buffer = std::make_shared<std::vector<CowOperationV2>>();
     uint64_t current_op_num = 0;
     uint64_t cluster_ops = header_.cluster_ops ?: 1;
     bool done = false;
 
     // Alternating op clusters and data
     while (!done) {
-        uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+        uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperationV2));
         if (to_add == 0) break;
         ops_buffer->resize(current_op_num + to_add);
         if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
-                                      to_add * sizeof(CowOperation))) {
+                                      to_add * sizeof(CowOperationV2))) {
             PLOG(ERROR) << "read op failed";
             return false;
         }
@@ -150,7 +150,7 @@
             if (current_op.type == kCowXorOp) {
                 data_loc->insert({current_op.new_block, data_pos});
             }
-            pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+            pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
             data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
 
             if (current_op.type == kCowClusterOp) {
@@ -222,7 +222,7 @@
                        << ops_buffer->size();
             return false;
         }
-        if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
+        if (ops_buffer->size() * sizeof(CowOperationV2) != footer_->op.ops_size) {
             LOG(ERROR) << "ops size does not match ";
             return false;
         }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
index afcf687..92e2b08 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -33,7 +33,7 @@
 
     const CowHeader& header() const { return header_; }
     const std::optional<CowFooter>& footer() const { return footer_; }
-    std::shared_ptr<std::vector<CowOperation>> ops() { return ops_; }
+    std::shared_ptr<std::vector<CowOperationV2>> ops() { return ops_; }
     std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
     uint64_t fd_size() const { return fd_size_; }
     const std::optional<uint64_t>& last_label() const { return last_label_; }
@@ -43,7 +43,7 @@
 
     CowHeader header_ = {};
     std::optional<CowFooter> footer_;
-    std::shared_ptr<std::vector<CowOperation>> ops_;
+    std::shared_ptr<std::vector<CowOperationV2>> ops_;
     std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 9676bf9..8f3f03f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -86,10 +86,9 @@
     while (!iter->AtEnd()) {
         auto op = iter->Get();
         ASSERT_EQ(op->type, kCowCopyOp);
-        ASSERT_EQ(op->compression, kCowCompressNone);
         ASSERT_EQ(op->data_length, 0);
         ASSERT_EQ(op->new_block, 10 + i);
-        ASSERT_EQ(op->source, 1000 + i);
+        ASSERT_EQ(op->source_info, 1000 + i);
         iter->Next();
         i += 1;
     }
@@ -133,10 +132,9 @@
     auto op = iter->Get();
 
     ASSERT_EQ(op->type, kCowCopyOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 10);
-    ASSERT_EQ(op->source, 20);
+    ASSERT_EQ(op->source_info, 20);
 
     std::string sink(data.size(), '\0');
 
@@ -157,20 +155,18 @@
 
     // Note: the zero operation gets split into two blocks.
     ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 51);
-    ASSERT_EQ(op->source, 0);
+    ASSERT_EQ(op->source_info, 0);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
     ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 52);
-    ASSERT_EQ(op->source, 0);
+    ASSERT_EQ(op->source_info, 0);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -212,10 +208,9 @@
     auto op = iter->Get();
 
     ASSERT_EQ(op->type, kCowCopyOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 10);
-    ASSERT_EQ(op->source, 20);
+    ASSERT_EQ(op->source_info, 20);
 
     std::string sink(data.size(), '\0');
 
@@ -237,20 +232,18 @@
 
     // Note: the zero operation gets split into two blocks.
     ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 51);
-    ASSERT_EQ(op->source, 0);
+    ASSERT_EQ(op->source_info, 0);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
     ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 52);
-    ASSERT_EQ(op->source, 0);
+    ASSERT_EQ(op->source_info, 0);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -677,7 +670,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 3);
+    ASSERT_EQ(op->source_info, 3);
 
     iter->Next();
 
@@ -730,7 +723,7 @@
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 0);
+    ASSERT_EQ(op->source_info, 0);
 
     iter->Next();
 
@@ -788,7 +781,7 @@
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 5);
+    ASSERT_EQ(op->source_info, 5);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -857,7 +850,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 4);
+    ASSERT_EQ(op->source_info, 4);
 
     iter->Next();
 
@@ -875,7 +868,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 5);
+    ASSERT_EQ(op->source_info, 5);
 
     iter->Next();
 
@@ -928,7 +921,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 4);
+    ASSERT_EQ(op->source_info, 4);
 
     iter->Next();
 
@@ -953,7 +946,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 5);
+    ASSERT_EQ(op->source_info, 5);
 
     iter->Next();
 
@@ -972,7 +965,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 6);
+    ASSERT_EQ(op->source_info, 6);
 
     iter->Next();
 
@@ -1019,7 +1012,7 @@
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
     ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 50);
+    ASSERT_EQ(op->source_info, 50);
 
     iter->Next();
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 699529b..a52297f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -110,7 +110,7 @@
     header_.prefix.minor_version = kCowVersionMinor;
     header_.prefix.header_size = sizeof(CowHeader);
     header_.footer_size = sizeof(CowFooter);
-    header_.op_size = sizeof(CowOperation);
+    header_.op_size = sizeof(CowOperationV2);
     header_.block_size = options_.block_size;
     header_.num_merge_ops = options_.num_merge_ops;
     header_.cluster_ops = options_.cluster_ops;
@@ -159,9 +159,9 @@
         struct iovec* cowop_ptr = cowop_vec_.get();
         struct iovec* data_ptr = data_vec_.get();
         for (size_t i = 0; i < header_.cluster_ops; i++) {
-            std::unique_ptr<CowOperation> op = std::make_unique<CowOperation>();
+            std::unique_ptr<CowOperationV2> op = std::make_unique<CowOperationV2>();
             cowop_ptr[i].iov_base = op.get();
-            cowop_ptr[i].iov_len = sizeof(CowOperation);
+            cowop_ptr[i].iov_len = sizeof(CowOperationV2);
             opbuffer_vec_.push_back(std::move(op));
 
             std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(header_.block_size * 2);
@@ -214,19 +214,19 @@
 }
 
 void CowWriterV2::InitPos() {
-    next_op_pos_ = sizeof(header_) + header_.buffer_size;
-    cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
+    next_op_pos_ = sizeof(CowHeader) + header_.buffer_size;
+    cluster_size_ = header_.cluster_ops * sizeof(CowOperationV2);
     if (header_.cluster_ops) {
         next_data_pos_ = next_op_pos_ + cluster_size_;
     } else {
-        next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+        next_data_pos_ = next_op_pos_ + sizeof(CowOperationV2);
     }
     current_cluster_size_ = 0;
     current_data_size_ = 0;
 }
 
 bool CowWriterV2::OpenForWrite() {
-    // This limitation is tied to the data field size in CowOperation.
+    // This limitation is tied to the data field size in CowOperationV2.
     if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
         LOG(ERROR) << "Block size is too large";
         return false;
@@ -313,7 +313,7 @@
     CHECK(!merge_in_progress_);
 
     for (size_t i = 0; i < num_blocks; i++) {
-        CowOperation op = {};
+        CowOperationV2 op = {};
         op.type = kCowCopyOp;
         op.new_block = new_block + i;
         op.source = old_block + i;
@@ -399,7 +399,7 @@
         num_blocks -= pending_blocks;
 
         while (i < size / header_.block_size && pending_blocks) {
-            CowOperation op = {};
+            CowOperationV2 op = {};
             op.new_block = new_block_start + i;
             op.type = type;
             if (type == kCowXorOp) {
@@ -451,7 +451,7 @@
 bool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     CHECK(!merge_in_progress_);
     for (uint64_t i = 0; i < num_blocks; i++) {
-        CowOperation op = {};
+        CowOperationV2 op = {};
         op.type = kCowZeroOp;
         op.new_block = new_block_start + i;
         op.source = 0;
@@ -462,7 +462,7 @@
 
 bool CowWriterV2::EmitLabel(uint64_t label) {
     CHECK(!merge_in_progress_);
-    CowOperation op = {};
+    CowOperationV2 op = {};
     op.type = kCowLabelOp;
     op.source = label;
     return WriteOperation(op) && Sync();
@@ -473,7 +473,7 @@
     size_t to_add = 0;
     size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
     while (num_ops > 0) {
-        CowOperation op = {};
+        CowOperationV2 op = {};
         op.type = kCowSequenceOp;
         op.source = next_data_pos_;
         to_add = std::min(num_ops, max_ops);
@@ -489,16 +489,16 @@
 }
 
 bool CowWriterV2::EmitCluster() {
-    CowOperation op = {};
+    CowOperationV2 op = {};
     op.type = kCowClusterOp;
     // Next cluster starts after remainder of current cluster and the next data block.
-    op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
+    op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperationV2);
     return WriteOperation(op);
 }
 
 bool CowWriterV2::EmitClusterIfNeeded() {
     // If there isn't room for another op and the cluster end op, end the current cluster
-    if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
+    if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperationV2)) {
         if (!EmitCluster()) return false;
     }
     return true;
@@ -539,7 +539,7 @@
         extra_cluster = true;
     }
 
-    footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperation);
+    footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperationV2);
     if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
         PLOG(ERROR) << "Failed to seek to footer position.";
         return false;
@@ -611,9 +611,9 @@
 
     if (op_vec_index_) {
         ret = pwritev(fd_.get(), cowop_vec_.get(), op_vec_index_, current_op_pos_);
-        if (ret != (op_vec_index_ * sizeof(CowOperation))) {
-            PLOG(ERROR) << "pwritev failed for CowOperation. Expected: "
-                        << (op_vec_index_ * sizeof(CowOperation));
+        if (ret != (op_vec_index_ * sizeof(CowOperationV2))) {
+            PLOG(ERROR) << "pwritev failed for CowOperationV2. Expected: "
+                        << (op_vec_index_ * sizeof(CowOperationV2));
             return false;
         }
     }
@@ -635,15 +635,16 @@
     return true;
 }
 
-bool CowWriterV2::WriteOperation(const CowOperation& op, const void* data, size_t size) {
+bool CowWriterV2::WriteOperation(const CowOperationV2& op, const void* data, size_t size) {
     if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op)) ||
         !EnsureSpaceAvailable(next_data_pos_ + size)) {
         return false;
     }
 
     if (batch_write_) {
-        CowOperation* cow_op = reinterpret_cast<CowOperation*>(cowop_vec_[op_vec_index_].iov_base);
-        std::memcpy(cow_op, &op, sizeof(CowOperation));
+        CowOperationV2* cow_op =
+                reinterpret_cast<CowOperationV2*>(cowop_vec_[op_vec_index_].iov_base);
+        std::memcpy(cow_op, &op, sizeof(CowOperationV2));
         op_vec_index_ += 1;
 
         if (data != nullptr && size > 0) {
@@ -681,7 +682,7 @@
     return EmitClusterIfNeeded();
 }
 
-void CowWriterV2::AddOperation(const CowOperation& op) {
+void CowWriterV2::AddOperation(const CowOperationV2& op) {
     footer_.op.num_ops++;
 
     if (op.type == kCowClusterOp) {
@@ -693,7 +694,7 @@
     }
 
     next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
-    next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
+    next_op_pos_ += sizeof(CowOperationV2) + GetNextOpOffset(op, header_.cluster_ops);
 }
 
 bool CowWriterV2::WriteRawData(const void* data, const size_t size) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 131a068..c72a9b4 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -50,8 +50,8 @@
     bool OpenForAppend(uint64_t label);
     bool GetDataPos(uint64_t* pos);
     bool WriteRawData(const void* data, size_t size);
-    bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
-    void AddOperation(const CowOperation& op);
+    bool WriteOperation(const CowOperationV2& op, const void* data = nullptr, size_t size = 0);
+    void AddOperation(const CowOperationV2& op);
     void InitPos();
     void InitBatchWrites();
     void InitWorkers();
@@ -84,7 +84,7 @@
     std::vector<std::basic_string<uint8_t>> compressed_buf_;
     std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
 
-    std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
+    std::vector<std::unique_ptr<CowOperationV2>> opbuffer_vec_;
     std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
     std::unique_ptr<struct iovec[]> cowop_vec_;
     int op_vec_index_ = 0;