diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp index c20194d28..93c31bbc9 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp @@ -601,8 +601,8 @@ bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, si bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) { // Validate the offset, taking care to acknowledge possible overflow of offset+len. - if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) || - len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) { + if (offset < header_.prefix.header_size || offset >= fd_size_ || offset + len > fd_size_ || + len >= fd_size_) { LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes"; return false; } diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp index 07c1d4fd8..214eb1cd3 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp @@ -51,6 +51,11 @@ class CowTestV3 : public ::testing::Test { std::unique_ptr cow_; }; +// Helper to check read sizes. +static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) { + return reader.ReadData(op, buffer, size) == size; +} + TEST_F(CowTestV3, CowHeaderV2Test) { CowOptions options; options.cluster_ops = 5; @@ -120,5 +125,86 @@ TEST_F(CowTestV3, ZeroOp) { ASSERT_EQ(op->source_info, 0); } +TEST_F(CowTestV3, ReplaceOp) { + CowOptions options; + options.op_count_max = 20; + options.scratch_space = false; + auto writer = CreateCowWriter(3, options, GetCowFd()); + std::string data = "This is some data, believe it"; + data.resize(options.block_size, '\0'); + + ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size())); + ASSERT_TRUE(writer->Finalize()); + + CowReader reader; + ASSERT_TRUE(reader.Parse(cow_->fd)); + + const auto& header = reader.header_v3(); + ASSERT_EQ(header.prefix.magic, kCowMagicNumber); + ASSERT_EQ(header.prefix.major_version, 3); + ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor); + ASSERT_EQ(header.block_size, options.block_size); + ASSERT_EQ(header.op_count, 1); + + auto iter = reader.GetOpIter(); + ASSERT_NE(iter, nullptr); + ASSERT_FALSE(iter->AtEnd()); + + auto op = iter->Get(); + std::string sink(data.size(), '\0'); + + ASSERT_EQ(op->type, kCowReplaceOp); + ASSERT_EQ(op->data_length, 4096); + ASSERT_EQ(op->new_block, 5); + ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); + ASSERT_EQ(sink, data); +} + +TEST_F(CowTestV3, ConsecutiveReplaceOp) { + CowOptions options; + options.op_count_max = 20; + options.scratch_space = false; + auto writer = CreateCowWriter(3, options, GetCowFd()); + std::string data; + data.resize(options.block_size * 5); + for (int i = 0; i < data.size(); i++) { + data[i] = char(rand() % 256); + } + + ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size())); + ASSERT_TRUE(writer->Finalize()); + + CowReader reader; + ASSERT_TRUE(reader.Parse(cow_->fd)); + + const auto& header = reader.header_v3(); + ASSERT_EQ(header.prefix.magic, kCowMagicNumber); + ASSERT_EQ(header.prefix.major_version, 3); + ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor); + ASSERT_EQ(header.block_size, options.block_size); + ASSERT_EQ(header.op_count, 5); + + auto iter = reader.GetOpIter(); + ASSERT_NE(iter, nullptr); + ASSERT_FALSE(iter->AtEnd()); + + size_t i = 0; + std::string sink(data.size(), '\0'); + + while (!iter->AtEnd()) { + auto op = iter->Get(); + ASSERT_EQ(op->type, kCowReplaceOp); + ASSERT_EQ(op->data_length, options.block_size); + ASSERT_EQ(op->new_block, 5 + i); + ASSERT_TRUE( + ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size)); + iter->Next(); + i++; + } + ASSERT_EQ(sink, data); + + ASSERT_EQ(i, 5); +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp index ecbf97e2b..d7aee139b 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp @@ -161,6 +161,9 @@ bool CowWriterV3::OpenForWrite() { LOG(ERROR) << "Header sync failed"; return false; } + next_data_pos_ = + sizeof(CowHeaderV3) + header_.buffer_size + header_.op_count_max * sizeof(CowOperation); + return true; } @@ -171,10 +174,7 @@ bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_ } bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) { - LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called"; - - if (new_block_start || data || size) return false; - return false; + return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp); } bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, @@ -184,6 +184,33 @@ bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size return false; } +bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size, + uint64_t old_block, uint16_t offset, uint8_t type) { + const uint8_t* iter = reinterpret_cast(data); + + // Placing here until we support XOR ops + CHECK_EQ(old_block, 0); + CHECK_EQ(offset, 0); + + const size_t num_blocks = (size / header_.block_size); + + for (size_t i = 0; i < num_blocks; i++) { + CowOperation op = {}; + op.new_block = new_block_start + i; + + op.type = type; + op.source_info = next_data_pos_; + op.data_length = static_cast(header_.block_size); + if (!WriteOperation(op, iter, header_.block_size)) { + LOG(ERROR) << "AddRawBlocks: write failed"; + return false; + } + iter += header_.block_size; + } + + return true; +} + bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) { for (uint64_t i = 0; i < num_blocks; i++) { CowOperationV3 op; @@ -210,7 +237,7 @@ bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) { return false; } -bool CowWriterV3::WriteOperation(const CowOperationV3& op) { +bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) { if (IsEstimating()) { header_.op_count++; header_.op_count_max++; @@ -224,10 +251,20 @@ bool CowWriterV3::WriteOperation(const CowOperationV3& op) { const off_t offset = GetOpOffset(header_.op_count); if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) { + PLOG(ERROR) << "write failed for " << op << " at " << offset; return false; } - + if (data && size > 0) { + if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) { + PLOG(ERROR) << "write failed for data of size: " << size + << " at offset: " << next_data_pos_; + return false; + } + } header_.op_count++; + next_data_pos_ += op.data_length; + next_op_pos_ += sizeof(CowOperationV3); + return true; } diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h index 8717c0187..af71a030c 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h +++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h @@ -43,7 +43,9 @@ class CowWriterV3 : public CowWriterBase { void SetupHeaders(); bool ParseOptions(); bool OpenForWrite(); - bool WriteOperation(const CowOperationV3& op); + bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0); + bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block, + uint16_t offset, uint8_t type); off_t GetOpOffset(uint32_t op_index) const { CHECK_LT(op_index, header_.op_count_max); @@ -55,6 +57,9 @@ class CowWriterV3 : public CowWriterBase { CowHeaderV3 header_{}; CowCompression compression_; + uint64_t next_op_pos_ = 0; + uint64_t next_data_pos_ = 0; + // in the case that we are using one thread for compression, we can store and re-use the same // compressor int num_compress_threads_ = 1;