Split delta performer integration test to separate file.
Currently most of the unit tests are integration test, so they are moved.
Bug: 23720829
TEST=cros_workon_make update_engine --test
Change-Id: I91ba810fcd9816e48970a00532f9d3e48f4b14f2
diff --git a/delta_performer.h b/delta_performer.h
index 04ae083..ef0bd9b 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -225,7 +225,7 @@
private:
friend class DeltaPerformerTest;
- FRIEND_TEST(DeltaPerformerTest, IsIdempotentOperationTest);
+ friend class DeltaPerformerIntegrationTest;
FRIEND_TEST(DeltaPerformerTest, UsePublicKeyFromResponse);
// Appends up to |*count_p| bytes from |*bytes_p| to |buffer_|, but only to
diff --git a/delta_performer_integration_test.cc b/delta_performer_integration_test.cc
new file mode 100644
index 0000000..4a2505c
--- /dev/null
+++ b/delta_performer_integration_test.cc
@@ -0,0 +1,1005 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/delta_performer.h"
+
+#include <inttypes.h>
+#include <sys/mount.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/strings/string_util.h>
+#include <google/protobuf/repeated_field.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/constants.h"
+#include "update_engine/fake_hardware.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/mock_prefs.h"
+#include "update_engine/payload_constants.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/payload_signer.h"
+#include "update_engine/payload_verifier.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_metadata.pb.h"
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+using testing::Return;
+using testing::_;
+using test_utils::kRandomString;
+using test_utils::ScopedLoopMounter;
+using test_utils::System;
+
+extern const char* kUnittestPrivateKeyPath;
+extern const char* kUnittestPublicKeyPath;
+extern const char* kUnittestPrivateKey2Path;
+extern const char* kUnittestPublicKey2Path;
+
+static const int kDefaultKernelSize = 4096; // Something small for a test
+static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
+ 'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'};
+
+namespace {
+struct DeltaState {
+ string a_img;
+ string b_img;
+ string result_img;
+ size_t image_size;
+
+ string delta_path;
+ uint64_t metadata_size;
+
+ string old_kernel;
+ chromeos::Blob old_kernel_data;
+
+ string new_kernel;
+ chromeos::Blob new_kernel_data;
+
+ string result_kernel;
+ chromeos::Blob result_kernel_data;
+ size_t kernel_size;
+
+ // The in-memory copy of delta file.
+ chromeos::Blob delta;
+
+ // The mock system state object with which we initialize the
+ // delta performer.
+ FakeSystemState fake_system_state;
+};
+
+enum SignatureTest {
+ kSignatureNone, // No payload signing.
+ kSignatureGenerator, // Sign the payload at generation time.
+ kSignatureGenerated, // Sign the payload after it's generated.
+ kSignatureGeneratedPlaceholder, // Insert placeholder signatures, then real.
+ kSignatureGeneratedPlaceholderMismatch, // Insert a wrong sized placeholder.
+ kSignatureGeneratedShell, // Sign the generated payload through shell cmds.
+ kSignatureGeneratedShellBadKey, // Sign with a bad key through shell cmds.
+ kSignatureGeneratedShellRotateCl1, // Rotate key, test client v1
+ kSignatureGeneratedShellRotateCl2, // Rotate key, test client v2
+};
+
+enum OperationHashTest {
+ kInvalidOperationData,
+ kValidOperationData,
+};
+
+} // namespace
+
+class DeltaPerformerIntegrationTest : public ::testing::Test {
+ public:
+ static void SetSupportedVersion(DeltaPerformer* performer,
+ uint64_t minor_version) {
+ performer->supported_minor_version_ = minor_version;
+ }
+};
+
+static void CompareFilesByBlock(const string& a_file, const string& b_file,
+ size_t image_size) {
+ EXPECT_EQ(0, image_size % kBlockSize);
+
+ chromeos::Blob a_data, b_data;
+ EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
+ EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
+
+ EXPECT_GE(a_data.size(), image_size);
+ EXPECT_GE(b_data.size(), image_size);
+ for (size_t i = 0; i < image_size; i += kBlockSize) {
+ EXPECT_EQ(0, i % kBlockSize);
+ chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
+ chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
+ EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
+ }
+ if (::testing::Test::HasNonfatalFailure()) {
+ LOG(INFO) << "Compared filesystems with size " << image_size
+ << ", partition A " << a_file << " size: " << a_data.size()
+ << ", partition B " << b_file << " size: " << b_data.size();
+ }
+}
+
+static bool WriteSparseFile(const string& path, off_t size) {
+ int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
+ ScopedFdCloser fd_closer(&fd);
+ off_t rc = lseek(fd, size + 1, SEEK_SET);
+ TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
+ int return_code = ftruncate(fd, size);
+ TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
+ return true;
+}
+
+static size_t GetSignatureSize(const string& private_key_path) {
+ const chromeos::Blob data(1, 'x');
+ chromeos::Blob hash;
+ EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(data, &hash));
+ chromeos::Blob signature;
+ EXPECT_TRUE(PayloadSigner::SignHash(hash,
+ private_key_path,
+ &signature));
+ return signature.size();
+}
+
+static bool InsertSignaturePlaceholder(int signature_size,
+ const string& payload_path,
+ uint64_t* out_metadata_size) {
+ vector<chromeos::Blob> signatures;
+ signatures.push_back(chromeos::Blob(signature_size, 0));
+
+ return PayloadSigner::AddSignatureToPayload(
+ payload_path,
+ signatures,
+ payload_path,
+ out_metadata_size);
+}
+
+static void SignGeneratedPayload(const string& payload_path,
+ uint64_t* out_metadata_size) {
+ int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
+ chromeos::Blob hash;
+ ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
+ payload_path,
+ vector<int>(1, signature_size),
+ &hash));
+ chromeos::Blob signature;
+ ASSERT_TRUE(PayloadSigner::SignHash(hash,
+ kUnittestPrivateKeyPath,
+ &signature));
+ ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
+ payload_path,
+ vector<chromeos::Blob>(1, signature),
+ payload_path,
+ out_metadata_size));
+ EXPECT_TRUE(PayloadVerifier::VerifySignedPayload(
+ payload_path,
+ kUnittestPublicKeyPath,
+ kSignatureMessageOriginalVersion));
+}
+
+static void SignGeneratedShellPayload(SignatureTest signature_test,
+ const string& payload_path) {
+ string private_key_path = kUnittestPrivateKeyPath;
+ if (signature_test == kSignatureGeneratedShellBadKey) {
+ ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX",
+ &private_key_path,
+ nullptr));
+ } else {
+ ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
+ signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2);
+ }
+ ScopedPathUnlinker key_unlinker(private_key_path);
+ key_unlinker.set_should_remove(signature_test ==
+ kSignatureGeneratedShellBadKey);
+ // Generates a new private key that will not match the public key.
+ if (signature_test == kSignatureGeneratedShellBadKey) {
+ LOG(INFO) << "Generating a mismatched private key.";
+ ASSERT_EQ(0, System(base::StringPrintf(
+ "openssl genrsa -out %s 2048", private_key_path.c_str())));
+ }
+ int signature_size = GetSignatureSize(private_key_path);
+ string hash_file;
+ ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
+ ScopedPathUnlinker hash_unlinker(hash_file);
+ string signature_size_string;
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2)
+ signature_size_string = base::StringPrintf("%d:%d",
+ signature_size, signature_size);
+ else
+ signature_size_string = base::StringPrintf("%d", signature_size);
+ ASSERT_EQ(0,
+ System(base::StringPrintf(
+ "./delta_generator -in_file=%s -signature_size=%s "
+ "-out_hash_file=%s",
+ payload_path.c_str(),
+ signature_size_string.c_str(),
+ hash_file.c_str())));
+
+ // Pad the hash
+ chromeos::Blob hash;
+ ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
+ ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash));
+ ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash));
+
+ string sig_file;
+ ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
+ ScopedPathUnlinker sig_unlinker(sig_file);
+ ASSERT_EQ(0,
+ System(base::StringPrintf(
+ "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+ private_key_path.c_str(),
+ hash_file.c_str(),
+ sig_file.c_str())));
+ string sig_file2;
+ ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
+ ScopedPathUnlinker sig2_unlinker(sig_file2);
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
+ ASSERT_EQ(0,
+ System(base::StringPrintf(
+ "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+ kUnittestPrivateKey2Path,
+ hash_file.c_str(),
+ sig_file2.c_str())));
+ // Append second sig file to first path
+ sig_file += ":" + sig_file2;
+ }
+
+ ASSERT_EQ(0,
+ System(base::StringPrintf(
+ "./delta_generator -in_file=%s -signature_file=%s "
+ "-out_file=%s",
+ payload_path.c_str(),
+ sig_file.c_str(),
+ payload_path.c_str())));
+ int verify_result =
+ System(base::StringPrintf(
+ "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d",
+ payload_path.c_str(),
+ signature_test == kSignatureGeneratedShellRotateCl2 ?
+ kUnittestPublicKey2Path : kUnittestPublicKeyPath,
+ signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
+ if (signature_test == kSignatureGeneratedShellBadKey) {
+ ASSERT_NE(0, verify_result);
+ } else {
+ ASSERT_EQ(0, verify_result);
+ }
+}
+
+static void GenerateDeltaFile(bool full_kernel,
+ bool full_rootfs,
+ bool noop,
+ ssize_t chunk_size,
+ SignatureTest signature_test,
+ DeltaState *state,
+ uint32_t minor_version) {
+ EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
+ EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
+
+ // result_img is used in minor version 2. Instead of applying the update
+ // in-place on A, we apply it to a new image, result_img.
+ EXPECT_TRUE(
+ utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
+ test_utils::CreateExtImageAtPath(state->a_img, nullptr);
+
+ state->image_size = utils::FileSize(state->a_img);
+
+ // Extend the "partitions" holding the file system a bit.
+ EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
+ state->image_size + 1024 * 1024)));
+ EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
+
+ // Create ImageInfo A & B
+ ImageInfo old_image_info;
+ ImageInfo new_image_info;
+
+ if (!full_rootfs) {
+ old_image_info.set_channel("src-channel");
+ old_image_info.set_board("src-board");
+ old_image_info.set_version("src-version");
+ old_image_info.set_key("src-key");
+ old_image_info.set_build_channel("src-build-channel");
+ old_image_info.set_build_version("src-build-version");
+ }
+
+ new_image_info.set_channel("test-channel");
+ new_image_info.set_board("test-board");
+ new_image_info.set_version("test-version");
+ new_image_info.set_key("test-key");
+ new_image_info.set_build_channel("test-build-channel");
+ new_image_info.set_build_version("test-build-version");
+
+ // Make some changes to the A image.
+ {
+ string a_mnt;
+ ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
+
+ chromeos::Blob hardtocompress;
+ while (hardtocompress.size() < 3 * kBlockSize) {
+ hardtocompress.insert(hardtocompress.end(),
+ std::begin(kRandomString), std::end(kRandomString));
+ }
+ EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
+ a_mnt.c_str()).c_str(),
+ hardtocompress.data(),
+ hardtocompress.size()));
+
+ chromeos::Blob zeros(16 * 1024, 0);
+ EXPECT_EQ(zeros.size(),
+ base::WriteFile(base::FilePath(base::StringPrintf(
+ "%s/move-to-sparse", a_mnt.c_str())),
+ reinterpret_cast<const char*>(zeros.data()),
+ zeros.size()));
+
+ EXPECT_TRUE(
+ WriteSparseFile(base::StringPrintf("%s/move-from-sparse",
+ a_mnt.c_str()), 16 * 1024));
+
+ EXPECT_EQ(0,
+ System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse"
+ " bs=1 seek=4096 count=1 status=none",
+ a_mnt.c_str()).c_str()));
+
+ // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
+ // patch fails to zero out the final block.
+ chromeos::Blob ones(1024 * 1024, 0xff);
+ EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones",
+ a_mnt.c_str()).c_str(),
+ ones.data(),
+ ones.size()));
+ }
+
+ if (noop) {
+ EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
+ base::FilePath(state->b_img)));
+ old_image_info = new_image_info;
+ } else {
+ if (minor_version == kSourceMinorPayloadVersion) {
+ // Create a result image with image_size bytes of garbage, followed by
+ // zeroes after the rootfs, like image A and B have.
+ chromeos::Blob ones(state->image_size, 0xff);
+ ones.insert(ones.end(), 1024 * 1024, 0);
+ EXPECT_TRUE(utils::WriteFile(state->result_img.c_str(),
+ ones.data(),
+ ones.size()));
+ EXPECT_EQ(utils::FileSize(state->a_img),
+ utils::FileSize(state->result_img));
+ }
+
+ test_utils::CreateExtImageAtPath(state->b_img, nullptr);
+ EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
+ state->image_size + 1024 * 1024)));
+ EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
+
+ // Make some changes to the B image.
+ string b_mnt;
+ ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
+
+ EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2",
+ b_mnt.c_str(),
+ b_mnt.c_str()).c_str()));
+ EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello",
+ b_mnt.c_str()).c_str()));
+ EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello",
+ b_mnt.c_str(),
+ b_mnt.c_str()).c_str()));
+ EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo",
+ b_mnt.c_str()).c_str()));
+ EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile",
+ b_mnt.c_str()).c_str()));
+ EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse",
+ b_mnt.c_str()),
+ 1024 * 1024));
+
+ EXPECT_TRUE(
+ WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
+ 16 * 1024));
+
+ chromeos::Blob zeros(16 * 1024, 0);
+ EXPECT_EQ(zeros.size(),
+ base::WriteFile(base::FilePath(base::StringPrintf(
+ "%s/move-from-sparse", b_mnt.c_str())),
+ reinterpret_cast<const char*>(zeros.data()),
+ zeros.size()));
+
+ EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
+ "of=%s/move-semi-sparse "
+ "bs=1 seek=4096 count=1 status=none",
+ b_mnt.c_str()).c_str()));
+
+ EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
+ "of=%s/partsparse bs=1 "
+ "seek=4096 count=1 status=none",
+ b_mnt.c_str()).c_str()));
+ EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && "
+ "mv %s/tmp %s/srchardlink1",
+ b_mnt.c_str(),
+ b_mnt.c_str(),
+ b_mnt.c_str(),
+ b_mnt.c_str()).c_str()));
+ EXPECT_EQ(0, System(
+ base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink",
+ b_mnt.c_str(), b_mnt.c_str()).c_str()));
+
+ chromeos::Blob hardtocompress;
+ while (hardtocompress.size() < 3 * kBlockSize) {
+ hardtocompress.insert(hardtocompress.end(),
+ std::begin(kRandomString), std::end(kRandomString));
+ }
+ EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
+ b_mnt.c_str()).c_str(),
+ hardtocompress.data(),
+ hardtocompress.size()));
+ }
+
+ string old_kernel;
+ EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX",
+ &state->old_kernel,
+ nullptr));
+
+ string new_kernel;
+ EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX",
+ &state->new_kernel,
+ nullptr));
+
+ string result_kernel;
+ EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX",
+ &state->result_kernel,
+ nullptr));
+
+ state->kernel_size = kDefaultKernelSize;
+ state->old_kernel_data.resize(kDefaultKernelSize);
+ state->new_kernel_data.resize(state->old_kernel_data.size());
+ state->result_kernel_data.resize(state->old_kernel_data.size());
+ test_utils::FillWithData(&state->old_kernel_data);
+ test_utils::FillWithData(&state->new_kernel_data);
+ test_utils::FillWithData(&state->result_kernel_data);
+
+ // change the new kernel data
+ std::copy(std::begin(kNewData), std::end(kNewData),
+ state->new_kernel_data.begin());
+
+ if (noop) {
+ state->old_kernel_data = state->new_kernel_data;
+ }
+
+ // Write kernels to disk
+ EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
+ state->old_kernel_data.data(),
+ state->old_kernel_data.size()));
+ EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
+ state->new_kernel_data.data(),
+ state->new_kernel_data.size()));
+ EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
+ state->result_kernel_data.data(),
+ state->result_kernel_data.size()));
+
+ EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX",
+ &state->delta_path,
+ nullptr));
+ LOG(INFO) << "delta path: " << state->delta_path;
+ {
+ const string private_key =
+ signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : "";
+
+ PayloadGenerationConfig payload_config;
+ payload_config.is_delta = !full_rootfs;
+ payload_config.hard_chunk_size = chunk_size;
+ payload_config.rootfs_partition_size = kRootFSPartitionSize;
+ payload_config.major_version = kChromeOSMajorPayloadVersion;
+ payload_config.minor_version = minor_version;
+ if (!full_rootfs) {
+ payload_config.source.rootfs.path = state->a_img;
+ if (!full_kernel)
+ payload_config.source.kernel.path = state->old_kernel;
+ payload_config.source.image_info = old_image_info;
+ EXPECT_TRUE(payload_config.source.LoadImageSize());
+ EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
+ EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
+ } else {
+ if (payload_config.hard_chunk_size == -1)
+ // Use 1 MiB chunk size for the full unittests.
+ payload_config.hard_chunk_size = 1024 * 1024;
+ }
+ payload_config.target.rootfs.path = state->b_img;
+ payload_config.target.kernel.path = state->new_kernel;
+ payload_config.target.image_info = new_image_info;
+ EXPECT_TRUE(payload_config.target.LoadImageSize());
+ EXPECT_TRUE(payload_config.target.rootfs.OpenFilesystem());
+ EXPECT_TRUE(payload_config.target.kernel.OpenFilesystem());
+
+ EXPECT_TRUE(payload_config.Validate());
+ EXPECT_TRUE(
+ GenerateUpdatePayloadFile(
+ payload_config,
+ state->delta_path,
+ private_key,
+ &state->metadata_size));
+ }
+
+ if (signature_test == kSignatureGeneratedPlaceholder ||
+ signature_test == kSignatureGeneratedPlaceholderMismatch) {
+ int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
+ LOG(INFO) << "Inserting placeholder signature.";
+ ASSERT_TRUE(InsertSignaturePlaceholder(signature_size, state->delta_path,
+ &state->metadata_size));
+
+ if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
+ signature_size -= 1;
+ LOG(INFO) << "Inserting mismatched placeholder signature.";
+ ASSERT_FALSE(InsertSignaturePlaceholder(signature_size, state->delta_path,
+ &state->metadata_size));
+ return;
+ }
+ }
+
+ if (signature_test == kSignatureGenerated ||
+ signature_test == kSignatureGeneratedPlaceholder ||
+ signature_test == kSignatureGeneratedPlaceholderMismatch) {
+ // Generate the signed payload and update the metadata size in state to
+ // reflect the new size after adding the signature operation to the
+ // manifest.
+ LOG(INFO) << "Signing payload.";
+ SignGeneratedPayload(state->delta_path, &state->metadata_size);
+ } else if (signature_test == kSignatureGeneratedShell ||
+ signature_test == kSignatureGeneratedShellBadKey ||
+ signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
+ SignGeneratedShellPayload(signature_test, state->delta_path);
+ }
+}
+
+static void ApplyDeltaFile(bool full_kernel, bool full_rootfs, bool noop,
+ SignatureTest signature_test, DeltaState* state,
+ bool hash_checks_mandatory,
+ OperationHashTest op_hash_test,
+ DeltaPerformer** performer,
+ uint32_t minor_version) {
+ // Check the metadata.
+ {
+ DeltaArchiveManifest manifest;
+ EXPECT_TRUE(PayloadVerifier::LoadPayload(state->delta_path,
+ &state->delta,
+ &manifest,
+ &state->metadata_size));
+ LOG(INFO) << "Metadata size: " << state->metadata_size;
+
+
+
+ if (signature_test == kSignatureNone) {
+ EXPECT_FALSE(manifest.has_signatures_offset());
+ EXPECT_FALSE(manifest.has_signatures_size());
+ } else {
+ EXPECT_TRUE(manifest.has_signatures_offset());
+ EXPECT_TRUE(manifest.has_signatures_size());
+ Signatures sigs_message;
+ EXPECT_TRUE(sigs_message.ParseFromArray(
+ &state->delta[state->metadata_size + manifest.signatures_offset()],
+ manifest.signatures_size()));
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2)
+ EXPECT_EQ(2, sigs_message.signatures_size());
+ else
+ EXPECT_EQ(1, sigs_message.signatures_size());
+ const Signatures_Signature& signature = sigs_message.signatures(0);
+ EXPECT_EQ(1, signature.version());
+
+ uint64_t expected_sig_data_length = 0;
+ vector<string> key_paths{kUnittestPrivateKeyPath};
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
+ key_paths.push_back(kUnittestPrivateKey2Path);
+ }
+ EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
+ key_paths,
+ &expected_sig_data_length));
+ EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
+ EXPECT_FALSE(signature.data().empty());
+ }
+
+ if (noop) {
+ EXPECT_EQ(0, manifest.install_operations_size());
+ EXPECT_EQ(1, manifest.kernel_install_operations_size());
+ }
+
+ if (full_kernel) {
+ EXPECT_FALSE(manifest.has_old_kernel_info());
+ } else {
+ EXPECT_EQ(state->old_kernel_data.size(),
+ manifest.old_kernel_info().size());
+ EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
+ }
+
+ EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
+ EXPECT_EQ(manifest.new_image_info().board(), "test-board");
+ EXPECT_EQ(manifest.new_image_info().version(), "test-version");
+ EXPECT_EQ(manifest.new_image_info().key(), "test-key");
+ EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
+ EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
+
+ if (!full_rootfs) {
+ if (noop) {
+ EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
+ EXPECT_EQ(manifest.old_image_info().board(), "test-board");
+ EXPECT_EQ(manifest.old_image_info().version(), "test-version");
+ EXPECT_EQ(manifest.old_image_info().key(), "test-key");
+ EXPECT_EQ(manifest.old_image_info().build_channel(),
+ "test-build-channel");
+ EXPECT_EQ(manifest.old_image_info().build_version(),
+ "test-build-version");
+ } else {
+ EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
+ EXPECT_EQ(manifest.old_image_info().board(), "src-board");
+ EXPECT_EQ(manifest.old_image_info().version(), "src-version");
+ EXPECT_EQ(manifest.old_image_info().key(), "src-key");
+ EXPECT_EQ(manifest.old_image_info().build_channel(),
+ "src-build-channel");
+ EXPECT_EQ(manifest.old_image_info().build_version(),
+ "src-build-version");
+ }
+ }
+
+
+ if (full_rootfs) {
+ EXPECT_FALSE(manifest.has_old_rootfs_info());
+ EXPECT_FALSE(manifest.has_old_image_info());
+ EXPECT_TRUE(manifest.has_new_image_info());
+ } else {
+ EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
+ EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
+ }
+
+ EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
+ EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
+
+ EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
+ EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
+ }
+
+ MockPrefs prefs;
+ EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize,
+ state->metadata_size)).WillOnce(Return(true));
+ EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
+ .WillOnce(Return(false));
+ EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
+ .WillRepeatedly(Return(true));
+ if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
+ EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
+ .WillOnce(Return(true));
+ }
+
+ // Update the A image in place.
+ InstallPlan install_plan;
+ install_plan.hash_checks_mandatory = hash_checks_mandatory;
+ install_plan.metadata_size = state->metadata_size;
+ install_plan.is_full_update = full_kernel && full_rootfs;
+ install_plan.source_path = state->a_img.c_str();
+ install_plan.kernel_source_path = state->old_kernel.c_str();
+
+ LOG(INFO) << "Setting payload metadata size in Omaha = "
+ << state->metadata_size;
+ ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
+ state->delta.data(),
+ state->metadata_size,
+ kUnittestPrivateKeyPath,
+ &install_plan.metadata_signature));
+ EXPECT_FALSE(install_plan.metadata_signature.empty());
+
+ *performer = new DeltaPerformer(&prefs,
+ &state->fake_system_state,
+ &install_plan);
+ EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+ (*performer)->set_public_key_path(kUnittestPublicKeyPath);
+ DeltaPerformerIntegrationTest::SetSupportedVersion(*performer, minor_version);
+
+ EXPECT_EQ(state->image_size,
+ OmahaHashCalculator::RawHashOfFile(
+ state->a_img,
+ state->image_size,
+ &install_plan.source_rootfs_hash));
+ EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(
+ state->old_kernel_data,
+ &install_plan.source_kernel_hash));
+
+ // With minor version 2, we want the target to be the new image, result_img,
+ // but with version 1, we want to update A in place.
+ if (minor_version == kSourceMinorPayloadVersion) {
+ EXPECT_EQ(0, (*performer)->Open(state->result_img.c_str(), 0, 0));
+ EXPECT_TRUE((*performer)->OpenKernel(state->result_kernel.c_str()));
+ } else {
+ EXPECT_EQ(0, (*performer)->Open(state->a_img.c_str(), 0, 0));
+ EXPECT_TRUE((*performer)->OpenKernel(state->old_kernel.c_str()));
+ }
+
+
+ ErrorCode expected_error, actual_error;
+ bool continue_writing;
+ switch (op_hash_test) {
+ case kInvalidOperationData: {
+ // Muck with some random offset post the metadata size so that
+ // some operation hash will result in a mismatch.
+ int some_offset = state->metadata_size + 300;
+ LOG(INFO) << "Tampered value at offset: " << some_offset;
+ state->delta[some_offset]++;
+ expected_error = ErrorCode::kDownloadOperationHashMismatch;
+ continue_writing = false;
+ break;
+ }
+
+ case kValidOperationData:
+ default:
+ // no change.
+ expected_error = ErrorCode::kSuccess;
+ continue_writing = true;
+ break;
+ }
+
+ // Write at some number of bytes per operation. Arbitrarily chose 5.
+ const size_t kBytesPerWrite = 5;
+ for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
+ size_t count = std::min(state->delta.size() - i, kBytesPerWrite);
+ bool write_succeeded = ((*performer)->Write(&state->delta[i],
+ count,
+ &actual_error));
+ // Normally write_succeeded should be true every time and
+ // actual_error should be ErrorCode::kSuccess. If so, continue the loop.
+ // But if we seeded an operation hash error above, then write_succeeded
+ // will be false. The failure may happen at any operation n. So, all
+ // Writes until n-1 should succeed and the nth operation will fail with
+ // actual_error. In this case, we should bail out of the loop because
+ // we cannot proceed applying the delta.
+ if (!write_succeeded) {
+ LOG(INFO) << "Write failed. Checking if it failed with expected error";
+ EXPECT_EQ(expected_error, actual_error);
+ if (!continue_writing) {
+ LOG(INFO) << "Cannot continue writing. Bailing out.";
+ break;
+ }
+ }
+
+ EXPECT_EQ(ErrorCode::kSuccess, actual_error);
+ }
+
+ // If we had continued all the way through, Close should succeed.
+ // Otherwise, it should fail. Check appropriately.
+ bool close_result = (*performer)->Close();
+ if (continue_writing)
+ EXPECT_EQ(0, close_result);
+ else
+ EXPECT_LE(0, close_result);
+}
+
+void VerifyPayloadResult(DeltaPerformer* performer,
+ DeltaState* state,
+ ErrorCode expected_result,
+ uint32_t minor_version) {
+ if (!performer) {
+ EXPECT_TRUE(!"Skipping payload verification since performer is null.");
+ return;
+ }
+
+ int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
+ EXPECT_CALL(*(state->fake_system_state.mock_payload_state()),
+ DownloadComplete()).Times(expected_times);
+
+ LOG(INFO) << "Verifying payload for expected result "
+ << expected_result;
+ EXPECT_EQ(expected_result, performer->VerifyPayload(
+ OmahaHashCalculator::OmahaHashOfData(state->delta),
+ state->delta.size()));
+ LOG(INFO) << "Verified payload.";
+
+ if (expected_result != ErrorCode::kSuccess) {
+ // no need to verify new partition if VerifyPayload failed.
+ return;
+ }
+
+ chromeos::Blob updated_kernel_partition;
+ if (minor_version == kSourceMinorPayloadVersion) {
+ CompareFilesByBlock(state->result_kernel, state->new_kernel,
+ state->kernel_size);
+ CompareFilesByBlock(state->result_img, state->b_img,
+ state->image_size);
+ EXPECT_TRUE(utils::ReadFile(state->result_kernel,
+ &updated_kernel_partition));
+ } else {
+ CompareFilesByBlock(state->old_kernel, state->new_kernel,
+ state->kernel_size);
+ CompareFilesByBlock(state->a_img, state->b_img,
+ state->image_size);
+ EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
+ }
+
+ ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
+ EXPECT_TRUE(std::equal(std::begin(kNewData), std::end(kNewData),
+ updated_kernel_partition.begin()));
+
+ uint64_t new_kernel_size;
+ chromeos::Blob new_kernel_hash;
+ uint64_t new_rootfs_size;
+ chromeos::Blob new_rootfs_hash;
+ EXPECT_TRUE(performer->GetNewPartitionInfo(&new_kernel_size,
+ &new_kernel_hash,
+ &new_rootfs_size,
+ &new_rootfs_hash));
+ EXPECT_EQ(kDefaultKernelSize, new_kernel_size);
+ chromeos::Blob expected_new_kernel_hash;
+ EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(state->new_kernel_data,
+ &expected_new_kernel_hash));
+ EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash);
+ EXPECT_EQ(state->image_size, new_rootfs_size);
+ chromeos::Blob expected_new_rootfs_hash;
+ EXPECT_EQ(state->image_size,
+ OmahaHashCalculator::RawHashOfFile(state->b_img,
+ state->image_size,
+ &expected_new_rootfs_hash));
+ EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash);
+}
+
+void VerifyPayload(DeltaPerformer* performer,
+ DeltaState* state,
+ SignatureTest signature_test,
+ uint32_t minor_version) {
+ ErrorCode expected_result = ErrorCode::kSuccess;
+ switch (signature_test) {
+ case kSignatureNone:
+ expected_result = ErrorCode::kSignedDeltaPayloadExpectedError;
+ break;
+ case kSignatureGeneratedShellBadKey:
+ expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError;
+ break;
+ default: break; // appease gcc
+ }
+
+ VerifyPayloadResult(performer, state, expected_result, minor_version);
+}
+
+void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
+ ssize_t chunk_size,
+ SignatureTest signature_test,
+ bool hash_checks_mandatory, uint32_t minor_version) {
+ DeltaState state;
+ DeltaPerformer *performer = nullptr;
+ GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
+ signature_test, &state, minor_version);
+
+ ScopedPathUnlinker a_img_unlinker(state.a_img);
+ ScopedPathUnlinker b_img_unlinker(state.b_img);
+ ScopedPathUnlinker new_img_unlinker(state.result_img);
+ ScopedPathUnlinker delta_unlinker(state.delta_path);
+ ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
+ ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
+ ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
+ ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test,
+ &state, hash_checks_mandatory, kValidOperationData,
+ &performer, minor_version);
+ VerifyPayload(performer, &state, signature_test, minor_version);
+ delete performer;
+}
+
+void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
+ bool hash_checks_mandatory) {
+ DeltaState state;
+ uint64_t minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+ GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state,
+ minor_version);
+ ScopedPathUnlinker a_img_unlinker(state.a_img);
+ ScopedPathUnlinker b_img_unlinker(state.b_img);
+ ScopedPathUnlinker delta_unlinker(state.delta_path);
+ ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
+ ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
+ DeltaPerformer *performer = nullptr;
+ ApplyDeltaFile(true, true, false, kSignatureGenerated, &state,
+ hash_checks_mandatory, op_hash_test, &performer,
+ minor_version);
+ delete performer;
+}
+
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
+ DeltaState state;
+ GenerateDeltaFile(false, false, false, -1,
+ kSignatureGeneratedPlaceholderMismatch, &state,
+ kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) {
+ DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) {
+ DoSmallImageTest(true, false, false, -1, kSignatureGenerator,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) {
+ DoSmallImageTest(true, true, false, -1, kSignatureGenerator,
+ true, DeltaPerformer::kFullPayloadMinorVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootNoopSmallImageTest) {
+ DoSmallImageTest(false, false, true, -1, kSignatureGenerator,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureNone,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGenerated,
+ true, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2,
+ false, kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) {
+ DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
+ false, kSourceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest, RunAsRootMandatoryOperationHashMismatchTest) {
+ DoOperationHashMismatchTest(kInvalidOperationData, true);
+}
+
+} // namespace chromeos_update_engine
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 87ed369..d024691 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -17,9 +17,7 @@
#include "update_engine/delta_performer.h"
#include <inttypes.h>
-#include <sys/mount.h>
-#include <algorithm>
#include <string>
#include <vector>
@@ -35,10 +33,8 @@
#include "update_engine/fake_system_state.h"
#include "update_engine/mock_prefs.h"
#include "update_engine/payload_constants.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/payload_file.h"
#include "update_engine/payload_generator/payload_signer.h"
-#include "update_engine/payload_verifier.h"
#include "update_engine/test_utils.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/utils.h"
@@ -50,13 +46,10 @@
using testing::Return;
using testing::_;
using test_utils::kRandomString;
-using test_utils::ScopedLoopMounter;
using test_utils::System;
extern const char* kUnittestPrivateKeyPath;
extern const char* kUnittestPublicKeyPath;
-extern const char* kUnittestPrivateKey2Path;
-extern const char* kUnittestPublicKey2Path;
static const char* kBogusMetadataSignature1 =
"awSFIUdUZz2VWFiR+ku0Pj00V7bPQPQFYQSXjEXr3vaw3TE4xHV5CraY3/YrZpBv"
@@ -66,50 +59,7 @@
"fjoTeLYZpt+WN65Vu7jJ0cQN8e1y+2yka5112wpRf/LLtPgiAjEZnsoYpLUd7CoV"
"pLRtClp97kN2+tXGNBQqkA==";
-static const int kDefaultKernelSize = 4096; // Something small for a test
-static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ',
- 'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'};
-
namespace {
-struct DeltaState {
- string a_img;
- string b_img;
- string result_img;
- size_t image_size;
-
- string delta_path;
- uint64_t metadata_size;
-
- string old_kernel;
- chromeos::Blob old_kernel_data;
-
- string new_kernel;
- chromeos::Blob new_kernel_data;
-
- string result_kernel;
- chromeos::Blob result_kernel_data;
- size_t kernel_size;
-
- // The in-memory copy of delta file.
- chromeos::Blob delta;
-
- // The mock system state object with which we initialize the
- // delta performer.
- FakeSystemState fake_system_state;
-};
-
-enum SignatureTest {
- kSignatureNone, // No payload signing.
- kSignatureGenerator, // Sign the payload at generation time.
- kSignatureGenerated, // Sign the payload after it's generated.
- kSignatureGeneratedPlaceholder, // Insert placeholder signatures, then real.
- kSignatureGeneratedPlaceholderMismatch, // Insert a wrong sized placeholder.
- kSignatureGeneratedShell, // Sign the generated payload through shell cmds.
- kSignatureGeneratedShellBadKey, // Sign with a bad key through shell cmds.
- kSignatureGeneratedShellRotateCl1, // Rotate key, test client v1
- kSignatureGeneratedShellRotateCl2, // Rotate key, test client v2
-};
-
// Different options that determine what we should fill into the
// install_plan.metadata_signature to simulate the contents received in the
// Omaha response.
@@ -119,11 +69,6 @@
kValidMetadataSignature,
};
-enum OperationHashTest {
- kInvalidOperationData,
- kValidOperationData,
-};
-
} // namespace
class DeltaPerformerTest : public ::testing::Test {
@@ -146,11 +91,6 @@
EXPECT_EQ(expected, performer.ValidateManifest());
}
- static void SetSupportedVersion(DeltaPerformer* performer,
- uint64_t minor_version) {
- performer->supported_minor_version_ = minor_version;
- }
-
static chromeos::Blob GeneratePayload(const chromeos::Blob& blob_data,
const vector<AnnotatedOperation>& aops,
bool sign_payload,
@@ -189,797 +129,6 @@
}
};
-static void CompareFilesByBlock(const string& a_file, const string& b_file,
- size_t image_size) {
- EXPECT_EQ(0, image_size % kBlockSize);
-
- chromeos::Blob a_data, b_data;
- EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
- EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
-
- EXPECT_GE(a_data.size(), image_size);
- EXPECT_GE(b_data.size(), image_size);
- for (size_t i = 0; i < image_size; i += kBlockSize) {
- EXPECT_EQ(0, i % kBlockSize);
- chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
- chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
- EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
- }
- if (::testing::Test::HasNonfatalFailure()) {
- LOG(INFO) << "Compared filesystems with size " << image_size
- << ", partition A " << a_file << " size: " << a_data.size()
- << ", partition B " << b_file << " size: " << b_data.size();
- }
-}
-
-static bool WriteSparseFile(const string& path, off_t size) {
- int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
- TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
- ScopedFdCloser fd_closer(&fd);
- off_t rc = lseek(fd, size + 1, SEEK_SET);
- TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
- int return_code = ftruncate(fd, size);
- TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
- return true;
-}
-
-static size_t GetSignatureSize(const string& private_key_path) {
- const chromeos::Blob data(1, 'x');
- chromeos::Blob hash;
- EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(data, &hash));
- chromeos::Blob signature;
- EXPECT_TRUE(PayloadSigner::SignHash(hash,
- private_key_path,
- &signature));
- return signature.size();
-}
-
-static bool InsertSignaturePlaceholder(int signature_size,
- const string& payload_path,
- uint64_t* out_metadata_size) {
- vector<chromeos::Blob> signatures;
- signatures.push_back(chromeos::Blob(signature_size, 0));
-
- return PayloadSigner::AddSignatureToPayload(
- payload_path,
- signatures,
- payload_path,
- out_metadata_size);
-}
-
-static void SignGeneratedPayload(const string& payload_path,
- uint64_t* out_metadata_size) {
- int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
- chromeos::Blob hash;
- ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
- payload_path,
- vector<int>(1, signature_size),
- &hash));
- chromeos::Blob signature;
- ASSERT_TRUE(PayloadSigner::SignHash(hash,
- kUnittestPrivateKeyPath,
- &signature));
- ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
- payload_path,
- vector<chromeos::Blob>(1, signature),
- payload_path,
- out_metadata_size));
- EXPECT_TRUE(PayloadVerifier::VerifySignedPayload(
- payload_path,
- kUnittestPublicKeyPath,
- kSignatureMessageOriginalVersion));
-}
-
-static void SignGeneratedShellPayload(SignatureTest signature_test,
- const string& payload_path) {
- string private_key_path = kUnittestPrivateKeyPath;
- if (signature_test == kSignatureGeneratedShellBadKey) {
- ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX",
- &private_key_path,
- nullptr));
- } else {
- ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
- signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2);
- }
- ScopedPathUnlinker key_unlinker(private_key_path);
- key_unlinker.set_should_remove(signature_test ==
- kSignatureGeneratedShellBadKey);
- // Generates a new private key that will not match the public key.
- if (signature_test == kSignatureGeneratedShellBadKey) {
- LOG(INFO) << "Generating a mismatched private key.";
- ASSERT_EQ(0, System(base::StringPrintf(
- "openssl genrsa -out %s 2048", private_key_path.c_str())));
- }
- int signature_size = GetSignatureSize(private_key_path);
- string hash_file;
- ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr));
- ScopedPathUnlinker hash_unlinker(hash_file);
- string signature_size_string;
- if (signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2)
- signature_size_string = base::StringPrintf("%d:%d",
- signature_size, signature_size);
- else
- signature_size_string = base::StringPrintf("%d", signature_size);
- ASSERT_EQ(0,
- System(base::StringPrintf(
- "./delta_generator -in_file=%s -signature_size=%s "
- "-out_hash_file=%s",
- payload_path.c_str(),
- signature_size_string.c_str(),
- hash_file.c_str())));
-
- // Pad the hash
- chromeos::Blob hash;
- ASSERT_TRUE(utils::ReadFile(hash_file, &hash));
- ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash));
- ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash));
-
- string sig_file;
- ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr));
- ScopedPathUnlinker sig_unlinker(sig_file);
- ASSERT_EQ(0,
- System(base::StringPrintf(
- "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
- private_key_path.c_str(),
- hash_file.c_str(),
- sig_file.c_str())));
- string sig_file2;
- ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr));
- ScopedPathUnlinker sig2_unlinker(sig_file2);
- if (signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2) {
- ASSERT_EQ(0,
- System(base::StringPrintf(
- "openssl rsautl -raw -sign -inkey %s -in %s -out %s",
- kUnittestPrivateKey2Path,
- hash_file.c_str(),
- sig_file2.c_str())));
- // Append second sig file to first path
- sig_file += ":" + sig_file2;
- }
-
- ASSERT_EQ(0,
- System(base::StringPrintf(
- "./delta_generator -in_file=%s -signature_file=%s "
- "-out_file=%s",
- payload_path.c_str(),
- sig_file.c_str(),
- payload_path.c_str())));
- int verify_result =
- System(base::StringPrintf(
- "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d",
- payload_path.c_str(),
- signature_test == kSignatureGeneratedShellRotateCl2 ?
- kUnittestPublicKey2Path : kUnittestPublicKeyPath,
- signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
- if (signature_test == kSignatureGeneratedShellBadKey) {
- ASSERT_NE(0, verify_result);
- } else {
- ASSERT_EQ(0, verify_result);
- }
-}
-
-static void GenerateDeltaFile(bool full_kernel,
- bool full_rootfs,
- bool noop,
- ssize_t chunk_size,
- SignatureTest signature_test,
- DeltaState *state,
- uint32_t minor_version) {
- EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
- EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
-
- // result_img is used in minor version 2. Instead of applying the update
- // in-place on A, we apply it to a new image, result_img.
- EXPECT_TRUE(
- utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
- test_utils::CreateExtImageAtPath(state->a_img, nullptr);
-
- state->image_size = utils::FileSize(state->a_img);
-
- // Extend the "partitions" holding the file system a bit.
- EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
- state->image_size + 1024 * 1024)));
- EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
-
- // Create ImageInfo A & B
- ImageInfo old_image_info;
- ImageInfo new_image_info;
-
- if (!full_rootfs) {
- old_image_info.set_channel("src-channel");
- old_image_info.set_board("src-board");
- old_image_info.set_version("src-version");
- old_image_info.set_key("src-key");
- old_image_info.set_build_channel("src-build-channel");
- old_image_info.set_build_version("src-build-version");
- }
-
- new_image_info.set_channel("test-channel");
- new_image_info.set_board("test-board");
- new_image_info.set_version("test-version");
- new_image_info.set_key("test-key");
- new_image_info.set_build_channel("test-build-channel");
- new_image_info.set_build_version("test-build-version");
-
- // Make some changes to the A image.
- {
- string a_mnt;
- ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
-
- chromeos::Blob hardtocompress;
- while (hardtocompress.size() < 3 * kBlockSize) {
- hardtocompress.insert(hardtocompress.end(),
- std::begin(kRandomString), std::end(kRandomString));
- }
- EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
- a_mnt.c_str()).c_str(),
- hardtocompress.data(),
- hardtocompress.size()));
-
- chromeos::Blob zeros(16 * 1024, 0);
- EXPECT_EQ(zeros.size(),
- base::WriteFile(base::FilePath(base::StringPrintf(
- "%s/move-to-sparse", a_mnt.c_str())),
- reinterpret_cast<const char*>(zeros.data()),
- zeros.size()));
-
- EXPECT_TRUE(
- WriteSparseFile(base::StringPrintf("%s/move-from-sparse",
- a_mnt.c_str()), 16 * 1024));
-
- EXPECT_EQ(0,
- System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse"
- " bs=1 seek=4096 count=1 status=none",
- a_mnt.c_str()).c_str()));
-
- // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
- // patch fails to zero out the final block.
- chromeos::Blob ones(1024 * 1024, 0xff);
- EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones",
- a_mnt.c_str()).c_str(),
- ones.data(),
- ones.size()));
- }
-
- if (noop) {
- EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
- base::FilePath(state->b_img)));
- old_image_info = new_image_info;
- } else {
- if (minor_version == kSourceMinorPayloadVersion) {
- // Create a result image with image_size bytes of garbage, followed by
- // zeroes after the rootfs, like image A and B have.
- chromeos::Blob ones(state->image_size, 0xff);
- ones.insert(ones.end(), 1024 * 1024, 0);
- EXPECT_TRUE(utils::WriteFile(state->result_img.c_str(),
- ones.data(),
- ones.size()));
- EXPECT_EQ(utils::FileSize(state->a_img),
- utils::FileSize(state->result_img));
- }
-
- test_utils::CreateExtImageAtPath(state->b_img, nullptr);
- EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
- state->image_size + 1024 * 1024)));
- EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
-
- // Make some changes to the B image.
- string b_mnt;
- ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
-
- EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2",
- b_mnt.c_str(),
- b_mnt.c_str()).c_str()));
- EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello",
- b_mnt.c_str()).c_str()));
- EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello",
- b_mnt.c_str(),
- b_mnt.c_str()).c_str()));
- EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo",
- b_mnt.c_str()).c_str()));
- EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile",
- b_mnt.c_str()).c_str()));
- EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse",
- b_mnt.c_str()),
- 1024 * 1024));
-
- EXPECT_TRUE(
- WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
- 16 * 1024));
-
- chromeos::Blob zeros(16 * 1024, 0);
- EXPECT_EQ(zeros.size(),
- base::WriteFile(base::FilePath(base::StringPrintf(
- "%s/move-from-sparse", b_mnt.c_str())),
- reinterpret_cast<const char*>(zeros.data()),
- zeros.size()));
-
- EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
- "of=%s/move-semi-sparse "
- "bs=1 seek=4096 count=1 status=none",
- b_mnt.c_str()).c_str()));
-
- EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero "
- "of=%s/partsparse bs=1 "
- "seek=4096 count=1 status=none",
- b_mnt.c_str()).c_str()));
- EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && "
- "mv %s/tmp %s/srchardlink1",
- b_mnt.c_str(),
- b_mnt.c_str(),
- b_mnt.c_str(),
- b_mnt.c_str()).c_str()));
- EXPECT_EQ(0, System(
- base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink",
- b_mnt.c_str(), b_mnt.c_str()).c_str()));
-
- chromeos::Blob hardtocompress;
- while (hardtocompress.size() < 3 * kBlockSize) {
- hardtocompress.insert(hardtocompress.end(),
- std::begin(kRandomString), std::end(kRandomString));
- }
- EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress",
- b_mnt.c_str()).c_str(),
- hardtocompress.data(),
- hardtocompress.size()));
- }
-
- string old_kernel;
- EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX",
- &state->old_kernel,
- nullptr));
-
- string new_kernel;
- EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX",
- &state->new_kernel,
- nullptr));
-
- string result_kernel;
- EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX",
- &state->result_kernel,
- nullptr));
-
- state->kernel_size = kDefaultKernelSize;
- state->old_kernel_data.resize(kDefaultKernelSize);
- state->new_kernel_data.resize(state->old_kernel_data.size());
- state->result_kernel_data.resize(state->old_kernel_data.size());
- test_utils::FillWithData(&state->old_kernel_data);
- test_utils::FillWithData(&state->new_kernel_data);
- test_utils::FillWithData(&state->result_kernel_data);
-
- // change the new kernel data
- std::copy(std::begin(kNewData), std::end(kNewData),
- state->new_kernel_data.begin());
-
- if (noop) {
- state->old_kernel_data = state->new_kernel_data;
- }
-
- // Write kernels to disk
- EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
- state->old_kernel_data.data(),
- state->old_kernel_data.size()));
- EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(),
- state->new_kernel_data.data(),
- state->new_kernel_data.size()));
- EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(),
- state->result_kernel_data.data(),
- state->result_kernel_data.size()));
-
- EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX",
- &state->delta_path,
- nullptr));
- LOG(INFO) << "delta path: " << state->delta_path;
- {
- const string private_key =
- signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : "";
-
- PayloadGenerationConfig payload_config;
- payload_config.is_delta = !full_rootfs;
- payload_config.hard_chunk_size = chunk_size;
- payload_config.rootfs_partition_size = kRootFSPartitionSize;
- payload_config.major_version = kChromeOSMajorPayloadVersion;
- payload_config.minor_version = minor_version;
- if (!full_rootfs) {
- payload_config.source.rootfs.path = state->a_img;
- if (!full_kernel)
- payload_config.source.kernel.path = state->old_kernel;
- payload_config.source.image_info = old_image_info;
- EXPECT_TRUE(payload_config.source.LoadImageSize());
- EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
- EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
- } else {
- if (payload_config.hard_chunk_size == -1)
- // Use 1 MiB chunk size for the full unittests.
- payload_config.hard_chunk_size = 1024 * 1024;
- }
- payload_config.target.rootfs.path = state->b_img;
- payload_config.target.kernel.path = state->new_kernel;
- payload_config.target.image_info = new_image_info;
- EXPECT_TRUE(payload_config.target.LoadImageSize());
- EXPECT_TRUE(payload_config.target.rootfs.OpenFilesystem());
- EXPECT_TRUE(payload_config.target.kernel.OpenFilesystem());
-
- EXPECT_TRUE(payload_config.Validate());
- EXPECT_TRUE(
- GenerateUpdatePayloadFile(
- payload_config,
- state->delta_path,
- private_key,
- &state->metadata_size));
- }
-
- if (signature_test == kSignatureGeneratedPlaceholder ||
- signature_test == kSignatureGeneratedPlaceholderMismatch) {
- int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
- LOG(INFO) << "Inserting placeholder signature.";
- ASSERT_TRUE(InsertSignaturePlaceholder(signature_size, state->delta_path,
- &state->metadata_size));
-
- if (signature_test == kSignatureGeneratedPlaceholderMismatch) {
- signature_size -= 1;
- LOG(INFO) << "Inserting mismatched placeholder signature.";
- ASSERT_FALSE(InsertSignaturePlaceholder(signature_size, state->delta_path,
- &state->metadata_size));
- return;
- }
- }
-
- if (signature_test == kSignatureGenerated ||
- signature_test == kSignatureGeneratedPlaceholder ||
- signature_test == kSignatureGeneratedPlaceholderMismatch) {
- // Generate the signed payload and update the metadata size in state to
- // reflect the new size after adding the signature operation to the
- // manifest.
- LOG(INFO) << "Signing payload.";
- SignGeneratedPayload(state->delta_path, &state->metadata_size);
- } else if (signature_test == kSignatureGeneratedShell ||
- signature_test == kSignatureGeneratedShellBadKey ||
- signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2) {
- SignGeneratedShellPayload(signature_test, state->delta_path);
- }
-}
-
-static void ApplyDeltaFile(bool full_kernel, bool full_rootfs, bool noop,
- SignatureTest signature_test, DeltaState* state,
- bool hash_checks_mandatory,
- OperationHashTest op_hash_test,
- DeltaPerformer** performer,
- uint32_t minor_version) {
- // Check the metadata.
- {
- DeltaArchiveManifest manifest;
- EXPECT_TRUE(PayloadVerifier::LoadPayload(state->delta_path,
- &state->delta,
- &manifest,
- &state->metadata_size));
- LOG(INFO) << "Metadata size: " << state->metadata_size;
-
-
-
- if (signature_test == kSignatureNone) {
- EXPECT_FALSE(manifest.has_signatures_offset());
- EXPECT_FALSE(manifest.has_signatures_size());
- } else {
- EXPECT_TRUE(manifest.has_signatures_offset());
- EXPECT_TRUE(manifest.has_signatures_size());
- Signatures sigs_message;
- EXPECT_TRUE(sigs_message.ParseFromArray(
- &state->delta[state->metadata_size + manifest.signatures_offset()],
- manifest.signatures_size()));
- if (signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2)
- EXPECT_EQ(2, sigs_message.signatures_size());
- else
- EXPECT_EQ(1, sigs_message.signatures_size());
- const Signatures_Signature& signature = sigs_message.signatures(0);
- EXPECT_EQ(1, signature.version());
-
- uint64_t expected_sig_data_length = 0;
- vector<string> key_paths{kUnittestPrivateKeyPath};
- if (signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2) {
- key_paths.push_back(kUnittestPrivateKey2Path);
- }
- EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
- key_paths,
- &expected_sig_data_length));
- EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
- EXPECT_FALSE(signature.data().empty());
- }
-
- if (noop) {
- EXPECT_EQ(0, manifest.install_operations_size());
- EXPECT_EQ(1, manifest.kernel_install_operations_size());
- }
-
- if (full_kernel) {
- EXPECT_FALSE(manifest.has_old_kernel_info());
- } else {
- EXPECT_EQ(state->old_kernel_data.size(),
- manifest.old_kernel_info().size());
- EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
- }
-
- EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
- EXPECT_EQ(manifest.new_image_info().board(), "test-board");
- EXPECT_EQ(manifest.new_image_info().version(), "test-version");
- EXPECT_EQ(manifest.new_image_info().key(), "test-key");
- EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
- EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
-
- if (!full_rootfs) {
- if (noop) {
- EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
- EXPECT_EQ(manifest.old_image_info().board(), "test-board");
- EXPECT_EQ(manifest.old_image_info().version(), "test-version");
- EXPECT_EQ(manifest.old_image_info().key(), "test-key");
- EXPECT_EQ(manifest.old_image_info().build_channel(),
- "test-build-channel");
- EXPECT_EQ(manifest.old_image_info().build_version(),
- "test-build-version");
- } else {
- EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
- EXPECT_EQ(manifest.old_image_info().board(), "src-board");
- EXPECT_EQ(manifest.old_image_info().version(), "src-version");
- EXPECT_EQ(manifest.old_image_info().key(), "src-key");
- EXPECT_EQ(manifest.old_image_info().build_channel(),
- "src-build-channel");
- EXPECT_EQ(manifest.old_image_info().build_version(),
- "src-build-version");
- }
- }
-
-
- if (full_rootfs) {
- EXPECT_FALSE(manifest.has_old_rootfs_info());
- EXPECT_FALSE(manifest.has_old_image_info());
- EXPECT_TRUE(manifest.has_new_image_info());
- } else {
- EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
- EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
- }
-
- EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
- EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
-
- EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
- EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
- }
-
- MockPrefs prefs;
- EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize,
- state->metadata_size)).WillOnce(Return(true));
- EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
- .WillOnce(Return(false));
- EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
- .WillRepeatedly(Return(true));
- if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
- EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
- .WillOnce(Return(true));
- EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))
- .WillOnce(Return(true));
- }
-
- // Update the A image in place.
- InstallPlan install_plan;
- install_plan.hash_checks_mandatory = hash_checks_mandatory;
- install_plan.metadata_size = state->metadata_size;
- install_plan.is_full_update = full_kernel && full_rootfs;
- install_plan.source_path = state->a_img.c_str();
- install_plan.kernel_source_path = state->old_kernel.c_str();
-
- LOG(INFO) << "Setting payload metadata size in Omaha = "
- << state->metadata_size;
- ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
- state->delta.data(),
- state->metadata_size,
- kUnittestPrivateKeyPath,
- &install_plan.metadata_signature));
- EXPECT_FALSE(install_plan.metadata_signature.empty());
-
- *performer = new DeltaPerformer(&prefs,
- &state->fake_system_state,
- &install_plan);
- EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
- (*performer)->set_public_key_path(kUnittestPublicKeyPath);
- DeltaPerformerTest::SetSupportedVersion(*performer, minor_version);
-
- EXPECT_EQ(state->image_size,
- OmahaHashCalculator::RawHashOfFile(
- state->a_img,
- state->image_size,
- &install_plan.source_rootfs_hash));
- EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(
- state->old_kernel_data,
- &install_plan.source_kernel_hash));
-
- // With minor version 2, we want the target to be the new image, result_img,
- // but with version 1, we want to update A in place.
- if (minor_version == kSourceMinorPayloadVersion) {
- EXPECT_EQ(0, (*performer)->Open(state->result_img.c_str(), 0, 0));
- EXPECT_TRUE((*performer)->OpenKernel(state->result_kernel.c_str()));
- } else {
- EXPECT_EQ(0, (*performer)->Open(state->a_img.c_str(), 0, 0));
- EXPECT_TRUE((*performer)->OpenKernel(state->old_kernel.c_str()));
- }
-
-
- ErrorCode expected_error, actual_error;
- bool continue_writing;
- switch (op_hash_test) {
- case kInvalidOperationData: {
- // Muck with some random offset post the metadata size so that
- // some operation hash will result in a mismatch.
- int some_offset = state->metadata_size + 300;
- LOG(INFO) << "Tampered value at offset: " << some_offset;
- state->delta[some_offset]++;
- expected_error = ErrorCode::kDownloadOperationHashMismatch;
- continue_writing = false;
- break;
- }
-
- case kValidOperationData:
- default:
- // no change.
- expected_error = ErrorCode::kSuccess;
- continue_writing = true;
- break;
- }
-
- // Write at some number of bytes per operation. Arbitrarily chose 5.
- const size_t kBytesPerWrite = 5;
- for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
- size_t count = std::min(state->delta.size() - i, kBytesPerWrite);
- bool write_succeeded = ((*performer)->Write(&state->delta[i],
- count,
- &actual_error));
- // Normally write_succeeded should be true every time and
- // actual_error should be ErrorCode::kSuccess. If so, continue the loop.
- // But if we seeded an operation hash error above, then write_succeeded
- // will be false. The failure may happen at any operation n. So, all
- // Writes until n-1 should succeed and the nth operation will fail with
- // actual_error. In this case, we should bail out of the loop because
- // we cannot proceed applying the delta.
- if (!write_succeeded) {
- LOG(INFO) << "Write failed. Checking if it failed with expected error";
- EXPECT_EQ(expected_error, actual_error);
- if (!continue_writing) {
- LOG(INFO) << "Cannot continue writing. Bailing out.";
- break;
- }
- }
-
- EXPECT_EQ(ErrorCode::kSuccess, actual_error);
- }
-
- // If we had continued all the way through, Close should succeed.
- // Otherwise, it should fail. Check appropriately.
- bool close_result = (*performer)->Close();
- if (continue_writing)
- EXPECT_EQ(0, close_result);
- else
- EXPECT_LE(0, close_result);
-}
-
-void VerifyPayloadResult(DeltaPerformer* performer,
- DeltaState* state,
- ErrorCode expected_result,
- uint32_t minor_version) {
- if (!performer) {
- EXPECT_TRUE(!"Skipping payload verification since performer is null.");
- return;
- }
-
- int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0;
- EXPECT_CALL(*(state->fake_system_state.mock_payload_state()),
- DownloadComplete()).Times(expected_times);
-
- LOG(INFO) << "Verifying payload for expected result "
- << expected_result;
- EXPECT_EQ(expected_result, performer->VerifyPayload(
- OmahaHashCalculator::OmahaHashOfData(state->delta),
- state->delta.size()));
- LOG(INFO) << "Verified payload.";
-
- if (expected_result != ErrorCode::kSuccess) {
- // no need to verify new partition if VerifyPayload failed.
- return;
- }
-
- chromeos::Blob updated_kernel_partition;
- if (minor_version == kSourceMinorPayloadVersion) {
- CompareFilesByBlock(state->result_kernel, state->new_kernel,
- state->kernel_size);
- CompareFilesByBlock(state->result_img, state->b_img,
- state->image_size);
- EXPECT_TRUE(utils::ReadFile(state->result_kernel,
- &updated_kernel_partition));
- } else {
- CompareFilesByBlock(state->old_kernel, state->new_kernel,
- state->kernel_size);
- CompareFilesByBlock(state->a_img, state->b_img,
- state->image_size);
- EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
- }
-
- ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
- EXPECT_TRUE(std::equal(std::begin(kNewData), std::end(kNewData),
- updated_kernel_partition.begin()));
-
- uint64_t new_kernel_size;
- chromeos::Blob new_kernel_hash;
- uint64_t new_rootfs_size;
- chromeos::Blob new_rootfs_hash;
- EXPECT_TRUE(performer->GetNewPartitionInfo(&new_kernel_size,
- &new_kernel_hash,
- &new_rootfs_size,
- &new_rootfs_hash));
- EXPECT_EQ(kDefaultKernelSize, new_kernel_size);
- chromeos::Blob expected_new_kernel_hash;
- EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(state->new_kernel_data,
- &expected_new_kernel_hash));
- EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash);
- EXPECT_EQ(state->image_size, new_rootfs_size);
- chromeos::Blob expected_new_rootfs_hash;
- EXPECT_EQ(state->image_size,
- OmahaHashCalculator::RawHashOfFile(state->b_img,
- state->image_size,
- &expected_new_rootfs_hash));
- EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash);
-}
-
-void VerifyPayload(DeltaPerformer* performer,
- DeltaState* state,
- SignatureTest signature_test,
- uint32_t minor_version) {
- ErrorCode expected_result = ErrorCode::kSuccess;
- switch (signature_test) {
- case kSignatureNone:
- expected_result = ErrorCode::kSignedDeltaPayloadExpectedError;
- break;
- case kSignatureGeneratedShellBadKey:
- expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError;
- break;
- default: break; // appease gcc
- }
-
- VerifyPayloadResult(performer, state, expected_result, minor_version);
-}
-
-void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
- ssize_t chunk_size,
- SignatureTest signature_test,
- bool hash_checks_mandatory, uint32_t minor_version) {
- DeltaState state;
- DeltaPerformer *performer = nullptr;
- GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
- signature_test, &state, minor_version);
-
- ScopedPathUnlinker a_img_unlinker(state.a_img);
- ScopedPathUnlinker b_img_unlinker(state.b_img);
- ScopedPathUnlinker new_img_unlinker(state.result_img);
- ScopedPathUnlinker delta_unlinker(state.delta_path);
- ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
- ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
- ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
- ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test,
- &state, hash_checks_mandatory, kValidOperationData,
- &performer, minor_version);
- VerifyPayload(performer, &state, signature_test, minor_version);
- delete performer;
-}
-
// Calls delta performer's Write method by pretending to pass in bytes from a
// delta file whose metadata size is actual_metadata_size and tests if all
// checks are correctly performed if the install plan contains
@@ -1098,24 +247,6 @@
EXPECT_EQ(install_plan.metadata_size, delta_performer.GetMetadataSize());
}
-void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
- bool hash_checks_mandatory) {
- DeltaState state;
- uint64_t minor_version = DeltaPerformer::kFullPayloadMinorVersion;
- GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state,
- minor_version);
- ScopedPathUnlinker a_img_unlinker(state.a_img);
- ScopedPathUnlinker b_img_unlinker(state.b_img);
- ScopedPathUnlinker delta_unlinker(state.delta_path);
- ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
- ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
- DeltaPerformer *performer = nullptr;
- ApplyDeltaFile(true, true, false, kSignatureGenerated, &state,
- hash_checks_mandatory, op_hash_test, &performer,
- minor_version);
- delete performer;
-}
-
TEST(DeltaPerformerTest, ExtentsToByteStringTest) {
uint64_t test[] = {1, 1, 4, 2, 0, 1};
COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven);
@@ -1218,78 +349,6 @@
ErrorCode::kUnsupportedMinorPayloadVersion);
}
-TEST(DeltaPerformerTest, RunAsRootSmallImageTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
- DeltaState state;
- GenerateDeltaFile(false, false, false, -1,
- kSignatureGeneratedPlaceholderMismatch, &state,
- kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageChunksTest) {
- DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootFullKernelSmallImageTest) {
- DoSmallImageTest(true, false, false, -1, kSignatureGenerator,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootFullSmallImageTest) {
- DoSmallImageTest(true, true, false, -1, kSignatureGenerator,
- true, DeltaPerformer::kFullPayloadMinorVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootNoopSmallImageTest) {
- DoSmallImageTest(false, false, true, -1, kSignatureGenerator,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignNoneTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureNone,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGenerated,
- true, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
- DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
- DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2,
- false, kInPlaceMinorPayloadVersion);
-}
-
-TEST(DeltaPerformerTest, RunAsRootSmallImageSourceOpsTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
- false, kSourceMinorPayloadVersion);
-}
-
TEST(DeltaPerformerTest, BadDeltaMagicTest) {
MockPrefs prefs;
InstallPlan install_plan;
@@ -1370,10 +429,6 @@
DoMetadataSignatureTest(kValidMetadataSignature, true, false);
}
-TEST(DeltaPerformerTest, RunAsRootMandatoryOperationHashMismatchTest) {
- DoOperationHashMismatchTest(kInvalidOperationData, true);
-}
-
TEST(DeltaPerformerTest, UsePublicKeyFromResponse) {
MockPrefs prefs;
FakeSystemState fake_system_state;
diff --git a/update_engine.gyp b/update_engine.gyp
index 6138b87..05bb6b4 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -390,6 +390,7 @@
'chrome_browser_proxy_resolver_unittest.cc',
'connection_manager_unittest.cc',
'dbus_service_unittest.cc',
+ 'delta_performer_integration_test.cc',
'delta_performer_unittest.cc',
'download_action_unittest.cc',
'extent_writer_unittest.cc',