Refactor off COW header v3

Cow reader will store header v3 in memory. ReadCowHeader can remain
mostly unchanged since the cow prefix is the same.
header->prefix.header_size will then tell us if we're reading a v3 or v2
header. v3 header is strictly a superset of v2 header so we can read a
v2 header into a v3 struct.

Added a test case to test_v3 where we write a header using v2_writer and
ensure CowReader is able to read it.

Test: cow_api_test
Change-Id: I142f18d871322930b7dc341c342c8b63a481341c
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 2721937..debaf36 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -172,7 +172,7 @@
 
     android::base::unique_fd owned_fd_;
     android::base::borrowed_fd fd_;
-    CowHeader header_;
+    CowHeaderV3 header_;
     std::optional<CowFooter> footer_;
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
@@ -188,7 +188,10 @@
     uint8_t compression_type_ = kCowCompressNone;
 };
 
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
+// Though this function takes in a CowHeaderV3, the struct could be populated as a v1/v2 CowHeader.
+// The extra fields will just be filled as 0. V3 header is strictly a superset of v1/v2 header and
+// contains all of the latter's field
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header);
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 607d610..3b84c95 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -28,12 +28,13 @@
 #include <zlib.h>
 
 #include "cow_decompress.h"
+#include "libsnapshot/cow_format.h"
 #include "parser_v2.h"
 
 namespace android {
 namespace snapshot {
 
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header) {
     if (lseek(fd.get(), 0, SEEK_SET) < 0) {
         PLOG(ERROR) << "lseek header failed";
         return false;
@@ -49,9 +50,9 @@
                    << "Expected: " << kCowMagicNumber;
         return false;
     }
-    if (header->prefix.header_size > sizeof(CowHeader)) {
+    if (header->prefix.header_size > sizeof(CowHeaderV3)) {
         LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
-                   << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
+                   << " bytes, expected at most " << sizeof(CowHeaderV3) << " bytes)";
         return false;
     }
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 83b5a12..a291469 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -90,7 +90,7 @@
 }
 
 static bool ShowRawOpStream(borrowed_fd fd) {
-    CowHeader header;
+    CowHeaderV3 header;
     if (!ReadCowHeader(fd, &header)) {
         LOG(ERROR) << "parse header failed";
         return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 2373d4d..cc8dd83 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -26,7 +26,9 @@
 #include <libsnapshot/cow_writer.h>
 #include "cow_decompress.h"
 #include "libsnapshot/cow_format.h"
+#include "writer_v2.h"
 #include "writer_v3.h"
+
 using android::base::unique_fd;
 using testing::AssertionFailure;
 using testing::AssertionResult;
@@ -49,5 +51,27 @@
     std::unique_ptr<TemporaryFile> cow_;
 };
 
+TEST_F(CowOperationV3Test, CowHeaderV2Test) {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    options.block_size = 4096;
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    auto writer_v2 = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer_v2->Initialize());
+    ASSERT_TRUE(writer_v2->Finalize());
+
+    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, kCowVersionMajor);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.cluster_ops, options.cluster_ops);
+}
+
 }  // namespace snapshot
 }  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 63bed07..83a9b1b 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -269,9 +269,11 @@
 }
 
 bool CowWriterV2::OpenForAppend(uint64_t label) {
-    if (!ReadCowHeader(fd_, &header_)) {
+    CowHeaderV3 header_v3;
+    if (!ReadCowHeader(fd_, &header_v3)) {
         return false;
     }
+    header_ = header_v3;
 
     CowParserV2 parser;
     if (!parser.Parse(fd_, header_, {label})) {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index f5732fc..eb4beb7 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3658,7 +3658,7 @@
         return nullptr;
     }
 
-    CowHeader header;
+    CowHeaderV3 header;
     if (!ReadCowHeader(cow_fd, &header)) {
         LOG(ERROR) << "OpenCompressedSnapshotWriter: read header failed";
         return nullptr;