Add a library function for performing a cow dry run
During OTA generation, delta_generator will perform a dry run of VABC
OTA by convert install ops to cow ops and apply them to a cow writer.
This code can be re-used by cow_converter, which is a util for
converting OTA payload to COW image.
Since the conversion happens at generation time, we will have access
to both the source and target images. We can just read data from target
image and write to the cow, no need to actually do
bsdiff/decompression/puffdiff. Therefore we add some utility functions
for writing COW with target image, these functions operate under
different assumptions than update_engine on android devices.
Test: th
Change-Id: I9ab91918c8ee6773bdab04ecbf654e33b7a7d827
diff --git a/payload_generator/cow_size_estimator.cc b/payload_generator/cow_size_estimator.cc
index f3f5264..fc66e67 100644
--- a/payload_generator/cow_size_estimator.cc
+++ b/payload_generator/cow_size_estimator.cc
@@ -24,13 +24,14 @@
#include <libsnapshot/cow_writer.h>
#include "update_engine/common/cow_operation_convert.h"
-#include "update_engine/payload_consumer/vabc_partition_writer.h"
+#include "update_engine/common/utils.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
using android::snapshot::CowWriter;
-void PerformReplaceOp(const InstallOperation& op,
+namespace {
+bool PerformReplaceOp(const InstallOperation& op,
CowWriter* writer,
FileDescriptorPtr target_fd,
size_t block_size) {
@@ -45,22 +46,62 @@
buffer.size(),
extent.start_block() * block_size,
&bytes_read);
- CHECK(success);
+ TEST_AND_RETURN_FALSE(success);
CHECK_EQ(static_cast<size_t>(bytes_read), buffer.size());
- writer->AddRawBlocks(extent.start_block(), buffer.data(), buffer.size());
+ TEST_AND_RETURN_FALSE(writer->AddRawBlocks(
+ extent.start_block(), buffer.data(), buffer.size()));
}
+ return true;
}
-void PerformZeroOp(const InstallOperation& op,
+bool PerformZeroOp(const InstallOperation& op,
CowWriter* writer,
size_t block_size) {
for (const auto& extent : op.dst_extents()) {
- writer->AddZeroBlocks(extent.start_block(), extent.num_blocks());
+ TEST_AND_RETURN_FALSE(
+ writer->AddZeroBlocks(extent.start_block(), extent.num_blocks()));
}
+ return true;
}
+bool WriteAllCowOps(size_t block_size,
+ const std::vector<CowOperation>& converted,
+ android::snapshot::ICowWriter* cow_writer,
+ FileDescriptorPtr target_fd) {
+ std::vector<uint8_t> buffer(block_size);
+
+ for (const auto& cow_op : converted) {
+ switch (cow_op.op) {
+ case CowOperation::CowCopy:
+ if (cow_op.src_block == cow_op.dst_block) {
+ continue;
+ }
+ TEST_AND_RETURN_FALSE(
+ cow_writer->AddCopy(cow_op.dst_block, cow_op.src_block));
+ break;
+ case CowOperation::CowReplace:
+ ssize_t bytes_read = 0;
+ TEST_AND_RETURN_FALSE(chromeos_update_engine::utils::ReadAll(
+ target_fd,
+ buffer.data(),
+ block_size,
+ cow_op.dst_block * block_size,
+ &bytes_read));
+ if (bytes_read <= 0 || static_cast<size_t>(bytes_read) != block_size) {
+ LOG(ERROR) << "source_fd->Read failed: " << bytes_read;
+ return false;
+ }
+ TEST_AND_RETURN_FALSE(cow_writer->AddRawBlocks(
+ cow_op.dst_block, buffer.data(), block_size));
+ break;
+ }
+ }
+
+ return true;
+}
+} // namespace
+
size_t EstimateCowSize(
- FileDescriptorPtr source_fd,
FileDescriptorPtr target_fd,
const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
const google::protobuf::RepeatedPtrField<CowMergeOperation>&
@@ -73,21 +114,32 @@
// CowWriter treats -1 as special value, will discard all the data but still
// reports Cow size. Good for estimation purposes
cow_writer.Initialize(android::base::borrowed_fd{-1});
+ CHECK(CowDryRun(
+ target_fd, operations, merge_operations, block_size, &cow_writer));
+ return cow_writer.GetCowSize();
+}
+bool CowDryRun(
+ FileDescriptorPtr target_fd,
+ const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
+ const google::protobuf::RepeatedPtrField<CowMergeOperation>&
+ merge_operations,
+ size_t block_size,
+ android::snapshot::CowWriter* cow_writer) {
const auto converted = ConvertToCowOperations(operations, merge_operations);
- VABCPartitionWriter::WriteAllCowOps(
- block_size, converted, &cow_writer, source_fd);
- cow_writer.AddLabel(0);
+ WriteAllCowOps(block_size, converted, cow_writer, target_fd);
+ cow_writer->AddLabel(0);
for (const auto& op : operations) {
switch (op.type()) {
case InstallOperation::REPLACE:
case InstallOperation::REPLACE_BZ:
case InstallOperation::REPLACE_XZ:
- PerformReplaceOp(op, &cow_writer, target_fd, block_size);
+ TEST_AND_RETURN_FALSE(
+ PerformReplaceOp(op, cow_writer, target_fd, block_size));
break;
case InstallOperation::ZERO:
case InstallOperation::DISCARD:
- PerformZeroOp(op, &cow_writer, block_size);
+ TEST_AND_RETURN_FALSE(PerformZeroOp(op, cow_writer, block_size));
break;
case InstallOperation::SOURCE_COPY:
case InstallOperation::MOVE:
@@ -99,15 +151,16 @@
case InstallOperation::BSDIFF:
// We might do something special by adding CowBsdiff to CowWriter.
// For now proceed the same way as normal REPLACE operation.
- PerformReplaceOp(op, &cow_writer, target_fd, block_size);
+ TEST_AND_RETURN_FALSE(
+ PerformReplaceOp(op, cow_writer, target_fd, block_size));
break;
}
// Arbitrary label number, we won't be resuming use these labels here.
// They are emitted just to keep size estimates accurate. As update_engine
// emits 1 label for every op.
- cow_writer.AddLabel(2);
+ cow_writer->AddLabel(2);
}
// TODO(zhangkelvin) Take FEC extents into account once VABC stabilizes
- return cow_writer.GetCowSize();
+ return true;
}
} // namespace chromeos_update_engine
diff --git a/payload_generator/cow_size_estimator.h b/payload_generator/cow_size_estimator.h
index cad4bad..a94df88 100644
--- a/payload_generator/cow_size_estimator.h
+++ b/payload_generator/cow_size_estimator.h
@@ -16,19 +16,19 @@
#include <cstddef>
#include <string>
+#include <libsnapshot/cow_writer.h>
#include <update_engine/update_metadata.pb.h>
#include "update_engine/payload_consumer/file_descriptor.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
namespace chromeos_update_engine {
-// Given file descriptor to the source image, target image, and list of
+// Given file descriptor to the target image, and list of
// operations, estimate the size of COW image if the operations are applied on
// Virtual AB Compression enabled device. This is intended to be used by update
// generators to put an estimate cow size in OTA payload. When installing an OTA
// update, libsnapshot will take this estimate as a hint to allocate spaces.
size_t EstimateCowSize(
- FileDescriptorPtr source_fd,
FileDescriptorPtr target_fd,
const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
const google::protobuf::RepeatedPtrField<CowMergeOperation>&
@@ -36,4 +36,13 @@
size_t block_size,
std::string compression);
+// Convert InstallOps to CowOps and apply the converted cow op to |cow_writer|
+bool CowDryRun(
+ FileDescriptorPtr target_fd,
+ const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
+ const google::protobuf::RepeatedPtrField<CowMergeOperation>&
+ merge_operations,
+ size_t block_size,
+ android::snapshot::CowWriter* cow_writer);
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index a43c782..a87dabf 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -132,7 +132,6 @@
*operations.Add() = aop.op;
}
*cow_size_ = EstimateCowSize(
- std::move(source_fd),
std::move(target_fd),
std::move(operations),
{cow_merge_sequence_->begin(), cow_merge_sequence_->end()},