From 53bd5585e966808da40cf2f8fc5cab053623c654 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 23 Oct 2023 21:24:50 -0700 Subject: [PATCH] libsnapshot: Implement CowWriterV3::EmitzeroBlocks. Add zero block operation to writer v3. We currently write this operation to after the scratch space. Once resume point support is added we may need to change this to write after the resume point. This CL only supports zero blocks, so writeoperation does not take in data yet. Bug: 307452468 Test: cow_api_test Change-Id: I659b2e2e4f6e0d96e374ed29012318cc34b4158d --- .../include/libsnapshot/cow_reader.h | 1 + .../include/libsnapshot/cow_writer.h | 3 ++ .../libsnapshot/libsnapshot_cow/test_v3.cpp | 31 ++++++++++++++ .../libsnapshot/libsnapshot_cow/writer_base.h | 2 + .../libsnapshot/libsnapshot_cow/writer_v3.cpp | 41 +++++++++++++++++-- .../libsnapshot/libsnapshot_cow/writer_v3.h | 10 ++++- 6 files changed, 84 insertions(+), 4 deletions(-) diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h index 4cbcab137..755a3afa2 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h @@ -145,6 +145,7 @@ class CowReader final : public ICowReader { size_t ignore_bytes = 0) override; CowHeader& GetHeader() override { return header_; } + const CowHeaderV3& header_v3() const { return header_; } bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read); bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read); diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h index 3016e933b..5b1e56c44 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h @@ -54,6 +54,9 @@ struct CowOptions { // Batch write cluster ops bool batch_write = false; + + // Size of the cow operation buffer; used in v3 only. + uint32_t op_count_max = 0; }; // Interface for writing to a snapuserd COW. All operations are ordered; merges diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp index a07ced55c..07c1d4fd8 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp @@ -89,5 +89,36 @@ TEST_F(CowTestV3, Header) { ASSERT_EQ(header.cluster_ops, 0); } +TEST_F(CowTestV3, ZeroOp) { + CowOptions options; + options.op_count_max = 20; + auto writer = CreateCowWriter(3, options, GetCowFd()); + ASSERT_TRUE(writer->AddZeroBlocks(1, 2)); + ASSERT_TRUE(writer->Finalize()); + + CowReader reader; + ASSERT_TRUE(reader.Parse(cow_->fd)); + ASSERT_EQ(reader.header_v3().op_count, 2); + + auto iter = reader.GetOpIter(); + ASSERT_NE(iter, nullptr); + ASSERT_FALSE(iter->AtEnd()); + + auto op = iter->Get(); + ASSERT_EQ(op->type, kCowZeroOp); + ASSERT_EQ(op->data_length, 0); + ASSERT_EQ(op->new_block, 1); + ASSERT_EQ(op->source_info, 0); + + iter->Next(); + ASSERT_FALSE(iter->AtEnd()); + op = iter->Get(); + + ASSERT_EQ(op->type, kCowZeroOp); + ASSERT_EQ(op->data_length, 0); + ASSERT_EQ(op->new_block, 2); + ASSERT_EQ(op->source_info, 0); +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h index 709b248e6..5274456f3 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h +++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h @@ -62,6 +62,8 @@ class CowWriterBase : public ICowWriter { bool InitFd(); bool ValidateNewBlock(uint64_t new_block); + bool IsEstimating() const { return is_dev_null_; } + CowOptions options_; android::base::unique_fd fd_; diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp index d69254ff4..ecbf97e2b 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp @@ -155,6 +155,7 @@ bool CowWriterV3::OpenForWrite() { return false; } } + header_.op_count_max = options_.op_count_max; if (!Sync()) { LOG(ERROR) << "Header sync failed"; @@ -184,9 +185,17 @@ bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size } bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) { - LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called"; - if (new_block_start && num_blocks) return false; - return false; + for (uint64_t i = 0; i < num_blocks; i++) { + CowOperationV3 op; + op.type = kCowZeroOp; + op.data_length = 0; + op.new_block = new_block_start + i; + op.source_info = 0; + if (!WriteOperation(op)) { + return false; + } + } + return true; } bool CowWriterV3::EmitLabel(uint64_t label) { @@ -201,7 +210,33 @@ bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) { return false; } +bool CowWriterV3::WriteOperation(const CowOperationV3& op) { + if (IsEstimating()) { + header_.op_count++; + header_.op_count_max++; + return true; + } + + if (header_.op_count + 1 > header_.op_count_max) { + LOG(ERROR) << "Maximum number of ops reached: " << header_.op_count_max; + return false; + } + + const off_t offset = GetOpOffset(header_.op_count); + if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) { + return false; + } + + header_.op_count++; + return true; +} + bool CowWriterV3::Finalize() { + CHECK_GE(header_.prefix.header_size, sizeof(CowHeaderV3)); + CHECK_LE(header_.prefix.header_size, sizeof(header_)); + if (!android::base::WriteFullyAtOffset(fd_, &header_, header_.prefix.header_size, 0)) { + return false; + } return Sync(); } diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h index a0d7ab950..8717c0187 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h +++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h @@ -14,7 +14,8 @@ #pragma once -#include +#include + #include "writer_base.h" namespace android { @@ -42,6 +43,13 @@ class CowWriterV3 : public CowWriterBase { void SetupHeaders(); bool ParseOptions(); bool OpenForWrite(); + bool WriteOperation(const CowOperationV3& op); + + off_t GetOpOffset(uint32_t op_index) const { + CHECK_LT(op_index, header_.op_count_max); + return header_.prefix.header_size + header_.buffer_size + + (op_index * sizeof(CowOperationV3)); + } private: CowHeaderV3 header_{};