Implement REPLACE_XZ on the update_engine.
This patch introduces the REPLACE_XZ operation, very similar to
REPLACE_BZ but using the XzExtentWriter instead. Minor cleanup of the
growing list of operations included in this patch.
Bug: 23604708
Test: Added unittests.
Change-Id: Iefa9e2cda609fceab9311add16b7369cb88c98a2
diff --git a/delta_performer.cc b/delta_performer.cc
index 0071834..c8a00d3 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -47,6 +47,7 @@
#include "update_engine/subprocess.h"
#include "update_engine/terminator.h"
#include "update_engine/update_attempter.h"
+#include "update_engine/xz_extent_writer.h"
using google::protobuf::RepeatedPtrField;
using std::min;
@@ -594,28 +595,28 @@
ScopedTerminatorExitUnblocker(); // Avoids a compiler unused var bug.
bool op_result;
- if (op.type() == InstallOperation::REPLACE ||
- op.type() == InstallOperation::REPLACE_BZ)
- op_result = HandleOpResult(
- PerformReplaceOperation(op, is_kernel_partition), "replace", error);
- else if (op.type() == InstallOperation::MOVE)
- op_result = HandleOpResult(
- PerformMoveOperation(op, is_kernel_partition), "move", error);
- else if (op.type() == InstallOperation::BSDIFF)
- op_result = HandleOpResult(
- PerformBsdiffOperation(op, is_kernel_partition), "bsdiff", error);
- else if (op.type() == InstallOperation::SOURCE_COPY)
- op_result =
- HandleOpResult(PerformSourceCopyOperation(op, is_kernel_partition),
- "source_copy", error);
- else if (op.type() == InstallOperation::SOURCE_BSDIFF)
- op_result =
- HandleOpResult(PerformSourceBsdiffOperation(op, is_kernel_partition),
- "source_bsdiff", error);
- else
- op_result = HandleOpResult(false, "unknown", error);
-
- if (!op_result)
+ switch (op.type()) {
+ case InstallOperation::REPLACE:
+ case InstallOperation::REPLACE_BZ:
+ case InstallOperation::REPLACE_XZ:
+ op_result = PerformReplaceOperation(op, is_kernel_partition);
+ break;
+ case InstallOperation::MOVE:
+ op_result = PerformMoveOperation(op, is_kernel_partition);
+ break;
+ case InstallOperation::BSDIFF:
+ op_result = PerformBsdiffOperation(op, is_kernel_partition);
+ break;
+ case InstallOperation::SOURCE_COPY:
+ op_result = PerformSourceCopyOperation(op, is_kernel_partition);
+ break;
+ case InstallOperation::SOURCE_BSDIFF:
+ op_result = PerformSourceBsdiffOperation(op, is_kernel_partition);
+ break;
+ default:
+ op_result = false;
+ }
+ if (!HandleOpResult(op_result, InstallOperationTypeName(op.type()), error))
return false;
next_operation_num_++;
@@ -650,7 +651,8 @@
bool DeltaPerformer::PerformReplaceOperation(const InstallOperation& operation,
bool is_kernel_partition) {
CHECK(operation.type() == InstallOperation::REPLACE ||
- operation.type() == InstallOperation::REPLACE_BZ);
+ operation.type() == InstallOperation::REPLACE_BZ ||
+ operation.type() == InstallOperation::REPLACE_XZ);
// 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.
@@ -665,8 +667,11 @@
chromeos::make_unique_ptr(new ZeroPadExtentWriter(
chromeos::make_unique_ptr(new DirectExtentWriter())));
- if (operation.type() == InstallOperation::REPLACE_BZ)
+ if (operation.type() == InstallOperation::REPLACE_BZ) {
writer.reset(new BzipExtentWriter(std::move(writer)));
+ } else if (operation.type() == InstallOperation::REPLACE_XZ) {
+ writer.reset(new XzExtentWriter(std::move(writer)));
+ }
// Create a vector of extents to pass to the ExtentWriter.
vector<Extent> extents;
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index cc3d6e2..0b509e3 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -23,9 +23,9 @@
#include <base/files/file_path.h>
#include <base/files/file_util.h>
-#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
#include <google/protobuf/repeated_field.h>
#include <gtest/gtest.h>
@@ -46,10 +46,8 @@
using std::string;
using std::vector;
-using testing::Return;
-using testing::_;
-using test_utils::kRandomString;
using test_utils::System;
+using test_utils::kRandomString;
extern const char* kUnittestPrivateKeyPath;
extern const char* kUnittestPublicKeyPath;
@@ -72,6 +70,16 @@
kValidMetadataSignature,
};
+// Compressed data without checksum, generated with:
+// echo -n a | xz -9 --check=none | hexdump -v -e '" " 12/1 "0x%02x, " "\n"'
+const uint8_t kXzCompressedData[] = {
+ 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00, 0xff, 0x12, 0xd9, 0x41,
+ 0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
+ 0x01, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x01,
+ 0xad, 0xa6, 0x58, 0x04, 0x06, 0x72, 0x9e, 0x7a, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x59, 0x5a,
+};
+
} // namespace
class DeltaPerformerTest : public ::testing::Test {
@@ -315,6 +323,27 @@
EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
}
+TEST_F(DeltaPerformerTest, ReplaceXzOperationTest) {
+ chromeos::Blob xz_data(std::begin(kXzCompressedData),
+ std::end(kXzCompressedData));
+ // The compressed xz data contains only a single "a", but the operation should
+ // pad the rest of the two blocks with zeros.
+ chromeos::Blob expected_data = chromeos::Blob(4096, 0);
+ expected_data[0] = 'a';
+
+ AnnotatedOperation aop;
+ *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+ aop.op.set_data_offset(0);
+ aop.op.set_data_length(xz_data.size());
+ aop.op.set_type(InstallOperation::REPLACE_XZ);
+ vector<AnnotatedOperation> aops = {aop};
+
+ chromeos::Blob payload_data = GeneratePayload(xz_data, aops, false,
+ kSourceMinorPayloadVersion);
+
+ EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
+}
+
TEST_F(DeltaPerformerTest, SourceCopyOperationTest) {
chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
std::end(kRandomString));
diff --git a/payload_constants.cc b/payload_constants.cc
index aeddf11..b28461b 100644
--- a/payload_constants.cc
+++ b/payload_constants.cc
@@ -31,4 +31,28 @@
const char kDeltaMagic[] = "CrAU";
const char kBspatchPath[] = "bspatch";
+const char* InstallOperationTypeName(InstallOperation_Type op_type) {
+ switch (op_type) {
+ case InstallOperation::BSDIFF:
+ return "BSDIFF";
+ case InstallOperation::MOVE:
+ return "MOVE";
+ case InstallOperation::REPLACE:
+ return "REPLACE";
+ case InstallOperation::REPLACE_BZ:
+ return "REPLACE_BZ";
+ case InstallOperation::SOURCE_COPY:
+ return "SOURCE_COPY";
+ case InstallOperation::SOURCE_BSDIFF:
+ return "SOURCE_BSDIFF";
+ case InstallOperation::ZERO:
+ return "ZERO";
+ case InstallOperation::DISCARD:
+ return "DISCARD";
+ case InstallOperation::REPLACE_XZ:
+ return "REPLACE_XZ";
+ }
+ return "<unknown_op>";
+}
+
}; // namespace chromeos_update_engine
diff --git a/payload_constants.h b/payload_constants.h
index 21dab00..81bc36a 100644
--- a/payload_constants.h
+++ b/payload_constants.h
@@ -21,6 +21,8 @@
#include <limits>
+#include "update_engine/update_metadata.pb.h"
+
namespace chromeos_update_engine {
// The major version used by Chrome OS.
@@ -52,6 +54,9 @@
// section of blocks not present on disk on a sparse file.
const uint64_t kSparseHole = std::numeric_limits<uint64_t>::max();
+// Return the name of the operation type.
+const char* InstallOperationTypeName(InstallOperation_Type op_type);
+
} // namespace chromeos_update_engine
#endif // UPDATE_ENGINE_PAYLOAD_CONSTANTS_H_
diff --git a/payload_generator/annotated_operation.cc b/payload_generator/annotated_operation.cc
index 3fbea2c..ca900ce 100644
--- a/payload_generator/annotated_operation.cc
+++ b/payload_generator/annotated_operation.cc
@@ -20,10 +20,9 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
+#include "update_engine/payload_constants.h"
#include "update_engine/utils.h"
-using std::string;
-
namespace chromeos_update_engine {
namespace {
@@ -46,30 +45,6 @@
return true;
}
-string InstallOperationTypeName(InstallOperation_Type op_type) {
- switch (op_type) {
- case InstallOperation::BSDIFF:
- return "BSDIFF";
- case InstallOperation::MOVE:
- return "MOVE";
- case InstallOperation::REPLACE:
- return "REPLACE";
- case InstallOperation::REPLACE_BZ:
- return "REPLACE_BZ";
- case InstallOperation::SOURCE_COPY:
- return "SOURCE_COPY";
- case InstallOperation::SOURCE_BSDIFF:
- return "SOURCE_BSDIFF";
- case InstallOperation::ZERO:
- return "ZERO";
- case InstallOperation::DISCARD:
- return "DISCARD";
- case InstallOperation::REPLACE_XZ:
- return "REPLACE_XZ";
- }
- return "UNK";
-}
-
std::ostream& operator<<(std::ostream& os, const AnnotatedOperation& aop) {
// For example, this prints:
// REPLACE_BZ 500 @3000
diff --git a/payload_generator/annotated_operation.h b/payload_generator/annotated_operation.h
index a6afbaa..08a0d0b 100644
--- a/payload_generator/annotated_operation.h
+++ b/payload_generator/annotated_operation.h
@@ -44,8 +44,6 @@
// For logging purposes.
std::ostream& operator<<(std::ostream& os, const AnnotatedOperation& aop);
-std::string InstallOperationTypeName(InstallOperation_Type op_type);
-
} // namespace chromeos_update_engine
#endif // UPDATE_ENGINE_PAYLOAD_GENERATOR_ANNOTATED_OPERATION_H_