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;