Implement IMGDIFF operation in the client.
This operation is used to apply update on gzipped data with much smaller
diff data than bsdiff.
update_engine only calls ApplyImagePatch() with all the data, the actuall
implementation of the imgdiff format is in bootable/recovery/applypatch.
Test: mma & added unittest
Bug: 26628339
Change-Id: I2b8a097aa68727b06be101cc06a3a896b835a815
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index f490c08..074e724 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -26,6 +26,7 @@
#include <string>
#include <vector>
+#include <applypatch/imgpatch.h>
#include <base/files/file_util.h>
#include <base/format_macros.h>
#include <base/strings/string_util.h>
@@ -677,6 +678,9 @@
case InstallOperation::SOURCE_BSDIFF:
op_result = PerformSourceBsdiffOperation(op);
break;
+ case InstallOperation::IMGDIFF:
+ op_result = PerformImgdiffOperation(op);
+ break;
default:
op_result = false;
}
@@ -1232,6 +1236,57 @@
return true;
}
+bool DeltaPerformer::PerformImgdiffOperation(
+ const InstallOperation& operation) {
+ // Since we delete data off the beginning of the buffer as we use it,
+ // the data we need should be exactly at the beginning of the buffer.
+ TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
+ TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
+
+ uint64_t src_blocks = GetBlockCount(operation.src_extents());
+ brillo::Blob src_data(src_blocks * block_size_);
+
+ ssize_t bytes_read = 0;
+ for (const Extent& extent : operation.src_extents()) {
+ ssize_t bytes_read_this_iteration = 0;
+ ssize_t bytes_to_read = extent.num_blocks() * block_size_;
+ TEST_AND_RETURN_FALSE(utils::PReadAll(source_fd_,
+ &src_data[bytes_read],
+ bytes_to_read,
+ extent.start_block() * block_size_,
+ &bytes_read_this_iteration));
+ TEST_AND_RETURN_FALSE(bytes_read_this_iteration == bytes_to_read);
+ bytes_read += bytes_read_this_iteration;
+ }
+
+ if (operation.has_src_sha256_hash()) {
+ brillo::Blob src_hash;
+ TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfData(src_data, &src_hash));
+ TEST_AND_RETURN_FALSE(ValidateSourceHash(src_hash, operation));
+ }
+
+ vector<Extent> target_extents(operation.dst_extents().begin(),
+ operation.dst_extents().end());
+ DirectExtentWriter writer;
+ TEST_AND_RETURN_FALSE(writer.Init(target_fd_, target_extents, block_size_));
+ TEST_AND_RETURN_FALSE(
+ ApplyImagePatch(src_data.data(),
+ src_data.size(),
+ buffer_.data(),
+ operation.data_length(),
+ [](const unsigned char* data, ssize_t len, void* token) {
+ return reinterpret_cast<ExtentWriter*>(token)
+ ->Write(data, len)
+ ? len
+ : 0;
+ },
+ &writer) == 0);
+ TEST_AND_RETURN_FALSE(writer.End());
+
+ DiscardBuffer(true, buffer_.size());
+ return true;
+}
+
bool DeltaPerformer::ExtractSignatureMessageFromOperation(
const InstallOperation& operation) {
if (operation.type() != InstallOperation::REPLACE ||