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_{};