Loading fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -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; } Loading fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp +86 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,11 @@ class CowTestV3 : public ::testing::Test { std::unique_ptr<TemporaryFile> 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; Loading Loading @@ -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 fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp +43 −6 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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, Loading @@ -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<const uint8_t*>(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<uint16_t>(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; Loading @@ -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++; Loading @@ -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; } Loading fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h +6 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading Loading
fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -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; } Loading
fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp +86 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,11 @@ class CowTestV3 : public ::testing::Test { std::unique_ptr<TemporaryFile> 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; Loading Loading @@ -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
fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp +43 −6 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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, Loading @@ -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<const uint8_t*>(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<uint16_t>(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; Loading @@ -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++; Loading @@ -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; } Loading
fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h +6 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading