Merge "update_engine: Install payload signing public key"
diff --git a/delta_performer.cc b/delta_performer.cc
index 2538329..0482a8f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -59,7 +59,6 @@
const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8;
const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 1;
const uint64_t DeltaPerformer::kSupportedMinorPayloadVersion = 2;
-const uint64_t DeltaPerformer::kFullPayloadMinorVersion = 0;
const char DeltaPerformer::kUpdatePayloadPublicKeyPath[] =
"/usr/share/update_engine/update-payload-key.pub.pem";
@@ -68,11 +67,6 @@
const unsigned DeltaPerformer::kProgressDownloadWeight = 50;
const unsigned DeltaPerformer::kProgressOperationsWeight = 50;
-const uint64_t kChromeOSMajorPayloadVersion = 1;
-const uint64_t kBrilloMajorPayloadVersion = 2;
-const uint32_t kInPlaceMinorPayloadVersion = 1;
-const uint32_t kSourceMinorPayloadVersion = 2;
-
namespace {
const int kUpdateStateOperationInvalid = -1;
const int kMaxResumedUpdateFailures = 10;
@@ -283,7 +277,7 @@
int DeltaPerformer::Close() {
int err = 0;
- if (!kernel_fd_->Close()) {
+ if (kernel_fd_ && !kernel_fd_->Close()) {
err = errno;
PLOG(ERROR) << "Unable to close kernel fd:";
}
@@ -449,7 +443,6 @@
<< install_plan_->metadata_size
<< ") in Omaha response as validation is not mandatory. "
<< "Trusting metadata size in payload = " << metadata_size_;
- SendUmaStat(ErrorCode::kDownloadInvalidMetadataSize);
}
// We have the full metadata in |payload|. Verify its integrity
@@ -465,7 +458,6 @@
// For non-mandatory cases, just send a UMA stat.
LOG(WARNING) << "Ignoring metadata signature validation failures";
- SendUmaStat(*error);
*error = ErrorCode::kSuccess;
}
@@ -595,7 +587,6 @@
// For non-mandatory cases, just send a UMA stat.
LOG(WARNING) << "Ignoring operation validation errors";
- SendUmaStat(*error);
*error = ErrorCode::kSuccess;
}
}
@@ -1036,9 +1027,7 @@
return ErrorCode::kDownloadMetadataSignatureMissingError;
}
- // For non-mandatory cases, just send a UMA stat.
LOG(WARNING) << "Cannot validate metadata as the signature is empty";
- SendUmaStat(ErrorCode::kDownloadMetadataSignatureMissingError);
return ErrorCode::kSuccess;
}
@@ -1171,10 +1160,8 @@
return ErrorCode::kDownloadOperationHashMissingError;
}
- // For non-mandatory cases, just send a UMA stat.
LOG(WARNING) << "Cannot validate operation " << next_operation_num_ + 1
<< " as there's no operation hash in manifest";
- SendUmaStat(ErrorCode::kDownloadOperationHashMissingError);
}
return ErrorCode::kSuccess;
}
@@ -1540,8 +1527,4 @@
return true;
}
-void DeltaPerformer::SendUmaStat(ErrorCode code) {
- utils::SendErrorCodeToUma(system_state_, code);
-}
-
} // namespace chromeos_update_engine
diff --git a/delta_performer.h b/delta_performer.h
index 38a4151..d088796 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -36,18 +36,6 @@
namespace chromeos_update_engine {
-// The major version used by Chrome OS.
-extern const uint64_t kChromeOSMajorPayloadVersion;
-
-// The major version used by Brillo.
-extern const uint64_t kBrilloMajorPayloadVersion;
-
-// The minor version used by the in-place delta generator algorithm.
-extern const uint32_t kInPlaceMinorPayloadVersion;
-
-// The minor version used by the A to B delta generator algorithm.
-extern const uint32_t kSourceMinorPayloadVersion;
-
class PrefsInterface;
// This class performs the actions in a delta update synchronously. The delta
@@ -65,7 +53,6 @@
static const uint64_t kDeltaManifestSizeSize;
static const uint64_t kSupportedMajorPayloadVersion;
static const uint64_t kSupportedMinorPayloadVersion;
- static const uint64_t kFullPayloadMinorVersion;
static const char kUpdatePayloadPublicKeyPath[];
// Defines the granularity of progress logging in terms of how many "completed
@@ -225,7 +212,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
@@ -308,9 +295,6 @@
// update. Returns false otherwise.
bool PrimeUpdateState();
- // Sends UMA statistics for the given error code.
- void SendUmaStat(ErrorCode code);
-
// If the Omaha response contains a public RSA key and we're allowed
// to use it (e.g. if we're in developer mode), extract the key from
// the response and store it in a temporary file and return true. In
diff --git a/delta_performer_integration_test.cc b/delta_performer_integration_test.cc
new file mode 100644
index 0000000..1e3dea8
--- /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 = 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, 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 f0f399d..cc3d6e2 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -17,27 +17,27 @@
#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_number_conversions.h>
#include <base/strings/string_util.h>
#include <google/protobuf/repeated_field.h>
#include <gtest/gtest.h>
+#include "update_engine/bzip.h"
#include "update_engine/constants.h"
#include "update_engine/fake_hardware.h"
+#include "update_engine/fake_prefs.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/extent_ranges.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"
@@ -49,13 +49,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"
@@ -65,50 +62,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.
@@ -118,978 +72,274 @@
kValidMetadataSignature,
};
-enum OperationHashTest {
- kInvalidOperationData,
- kValidOperationData,
-};
-
} // namespace
class DeltaPerformerTest : public ::testing::Test {
- public:
- // Test helper placed where it can easily be friended from DeltaPerformer.
- static void RunManifestValidation(const DeltaArchiveManifest& manifest,
- bool full_payload,
- ErrorCode expected) {
- MockPrefs prefs;
- InstallPlan install_plan;
- FakeSystemState fake_system_state;
- DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
+ protected:
+ // Test helper placed where it can easily be friended from DeltaPerformer.
+ void RunManifestValidation(const DeltaArchiveManifest& manifest,
+ bool full_payload,
+ ErrorCode expected) {
// The install plan is for Full or Delta.
- install_plan.is_full_update = full_payload;
+ install_plan_.is_full_update = full_payload;
// The Manifest we are validating.
- performer.manifest_.CopyFrom(manifest);
+ performer_.manifest_.CopyFrom(manifest);
- EXPECT_EQ(expected, performer.ValidateManifest());
+ EXPECT_EQ(expected, performer_.ValidateManifest());
}
- static void SetSupportedVersion(DeltaPerformer* performer,
- uint64_t minor_version) {
- performer->supported_minor_version_ = minor_version;
- }
-};
+ chromeos::Blob GeneratePayload(const chromeos::Blob& blob_data,
+ const vector<AnnotatedOperation>& aops,
+ bool sign_payload,
+ int32_t minor_version) {
+ string blob_path;
+ EXPECT_TRUE(utils::MakeTempFile("Blob-XXXXXX", &blob_path, nullptr));
+ ScopedPathUnlinker blob_unlinker(blob_path);
+ EXPECT_TRUE(utils::WriteFile(blob_path.c_str(),
+ blob_data.data(),
+ blob_data.size()));
-static void CompareFilesByBlock(const string& a_file, const string& b_file,
- size_t image_size) {
- EXPECT_EQ(0, image_size % kBlockSize);
+ PayloadGenerationConfig config;
+ config.major_version = kChromeOSMajorPayloadVersion;
+ config.minor_version = minor_version;
+ config.target.rootfs.path = blob_path;
+ config.target.rootfs.size = blob_data.size();
+ config.target.kernel.path = blob_path;
+ config.target.kernel.size = blob_data.size();
- 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;
+ PayloadFile payload;
+ EXPECT_TRUE(payload.Init(config));
- 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();
- }
-}
+ payload.AddPartition(config.source.rootfs, config.target.rootfs, aops);
-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;
-}
+ string payload_path;
+ EXPECT_TRUE(utils::MakeTempFile("Payload-XXXXXX", &payload_path, nullptr));
+ ScopedPathUnlinker payload_unlinker(payload_path);
+ EXPECT_TRUE(payload.WritePayload(payload_path, blob_path,
+ sign_payload ? kUnittestPrivateKeyPath : "",
+ &install_plan_.metadata_size));
-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;
+ chromeos::Blob payload_data;
+ EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
+ return payload_data;
}
- 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);
- }
-}
+ // Apply |payload_data| on partition specified in |source_path|.
+ chromeos::Blob ApplyPayload(const chromeos::Blob& payload_data,
+ const string& source_path) {
+ install_plan_.source_path = source_path;
+ install_plan_.kernel_source_path = "/dev/null";
-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));
+ string new_part;
+ EXPECT_TRUE(utils::MakeTempFile("Partition-XXXXXX", &new_part, nullptr));
+ ScopedPathUnlinker partition_unlinker(new_part);
- // 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);
+ EXPECT_EQ(0, performer_.Open(new_part.c_str(), 0, 0));
+ EXPECT_TRUE(performer_.Write(payload_data.data(), payload_data.size()));
+ EXPECT_EQ(0, performer_.Close());
- 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");
+ chromeos::Blob partition_data;
+ EXPECT_TRUE(utils::ReadFile(new_part, &partition_data));
+ return partition_data;
}
- 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");
+ // 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
+ // expected_metadata_size and that the result of the parsing are as per
+ // hash_checks_mandatory flag.
+ void DoMetadataSizeTest(uint64_t expected_metadata_size,
+ uint64_t actual_metadata_size,
+ bool hash_checks_mandatory) {
+ install_plan_.hash_checks_mandatory = hash_checks_mandatory;
+ EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+ EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
- // Make some changes to the A image.
- {
- string a_mnt;
- ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0);
+ // Set a valid magic string and version number 1.
+ EXPECT_TRUE(performer_.Write("CrAU", 4));
+ uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
+ EXPECT_TRUE(performer_.Write(&version, 8));
- 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());
+ install_plan_.metadata_size = expected_metadata_size;
+ ErrorCode error_code;
+ // When filling in size in manifest, exclude the size of the 20-byte header.
+ uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
+ bool result = performer_.Write(&size_in_manifest, 8, &error_code);
+ if (expected_metadata_size == actual_metadata_size ||
+ !hash_checks_mandatory) {
+ EXPECT_TRUE(result);
} else {
- if (payload_config.hard_chunk_size == -1)
- // Use 1 MiB chunk size for the full unittests.
- payload_config.hard_chunk_size = 1024 * 1024;
+ EXPECT_FALSE(result);
+ EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error_code);
}
- 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));
+ EXPECT_LT(performer_.Close(), 0);
}
- 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));
+ // Generates a valid delta file but tests the delta performer by suppling
+ // different metadata signatures as per metadata_signature_test flag and
+ // sees if the result of the parsing are as per hash_checks_mandatory flag.
+ void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
+ bool sign_payload,
+ bool hash_checks_mandatory) {
- 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;
- }
- }
+ // Loads the payload and parses the manifest.
+ chromeos::Blob payload = GeneratePayload(chromeos::Blob(),
+ vector<AnnotatedOperation>(), sign_payload,
+ kFullPayloadMinorVersion);
- 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);
- }
-}
+ LOG(INFO) << "Payload size: " << payload.size();
-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;
+ install_plan_.hash_checks_mandatory = hash_checks_mandatory;
+ DeltaPerformer::MetadataParseResult expected_result, actual_result;
+ ErrorCode expected_error, actual_error;
-
- 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.";
+ // Fill up the metadata signature in install plan according to the test.
+ switch (metadata_signature_test) {
+ case kEmptyMetadataSignature:
+ install_plan_.metadata_signature.clear();
+ expected_result = DeltaPerformer::kMetadataParseError;
+ expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
break;
- }
+
+ case kInvalidMetadataSignature:
+ install_plan_.metadata_signature = kBogusMetadataSignature1;
+ expected_result = DeltaPerformer::kMetadataParseError;
+ expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
+ break;
+
+ case kValidMetadataSignature:
+ default:
+ // Set the install plan's metadata size to be the same as the one
+ // in the manifest so that we pass the metadata size checks. Only
+ // then we can get to manifest signature checks.
+ ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
+ payload.data(),
+ install_plan_.metadata_size,
+ kUnittestPrivateKeyPath,
+ &install_plan_.metadata_signature));
+ EXPECT_FALSE(install_plan_.metadata_signature.empty());
+ expected_result = DeltaPerformer::kMetadataParseSuccess;
+ expected_error = ErrorCode::kSuccess;
+ 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
-// expected_metadata_size and that the result of the parsing are as per
-// hash_checks_mandatory flag.
-void DoMetadataSizeTest(uint64_t expected_metadata_size,
- uint64_t actual_metadata_size,
- bool hash_checks_mandatory) {
- MockPrefs prefs;
- InstallPlan install_plan;
- install_plan.hash_checks_mandatory = hash_checks_mandatory;
- FakeSystemState fake_system_state;
- DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
- EXPECT_EQ(0, performer.Open("/dev/null", 0, 0));
- EXPECT_TRUE(performer.OpenKernel("/dev/null"));
-
- // Set a valid magic string and version number 1.
- EXPECT_TRUE(performer.Write("CrAU", 4));
- uint64_t version = htobe64(1);
- EXPECT_TRUE(performer.Write(&version, 8));
-
- install_plan.metadata_size = expected_metadata_size;
- ErrorCode error_code;
- // When filling in size in manifest, exclude the size of the 20-byte header.
- uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
- bool result = performer.Write(&size_in_manifest, 8, &error_code);
- if (expected_metadata_size == actual_metadata_size ||
- !hash_checks_mandatory) {
- EXPECT_TRUE(result);
- } else {
- EXPECT_FALSE(result);
- EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error_code);
- }
-
- EXPECT_LT(performer.Close(), 0);
-}
-
-// Generates a valid delta file but tests the delta performer by suppling
-// different metadata signatures as per omaha_metadata_signature flag and
-// sees if the result of the parsing are as per hash_checks_mandatory flag.
-void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
- SignatureTest signature_test,
- bool hash_checks_mandatory) {
- DeltaState state;
-
- // Using kSignatureNone since it doesn't affect the results of our test.
- // If we've to use other signature options, then we'd have to get the
- // metadata size again after adding the signing operation to the manifest.
- GenerateDeltaFile(true, true, false, -1, signature_test, &state,
- DeltaPerformer::kFullPayloadMinorVersion);
-
- 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);
-
- // Loads the payload and parses the manifest.
- chromeos::Blob payload;
- EXPECT_TRUE(utils::ReadFile(state.delta_path, &payload));
- LOG(INFO) << "Payload size: " << payload.size();
-
- InstallPlan install_plan;
- install_plan.hash_checks_mandatory = hash_checks_mandatory;
- install_plan.metadata_size = state.metadata_size;
-
- DeltaPerformer::MetadataParseResult expected_result, actual_result;
- ErrorCode expected_error, actual_error;
-
- // Fill up the metadata signature in install plan according to the test.
- switch (metadata_signature_test) {
- case kEmptyMetadataSignature:
- install_plan.metadata_signature.clear();
- expected_result = DeltaPerformer::kMetadataParseError;
- expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
- break;
-
- case kInvalidMetadataSignature:
- install_plan.metadata_signature = kBogusMetadataSignature1;
- expected_result = DeltaPerformer::kMetadataParseError;
- expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
- break;
-
- case kValidMetadataSignature:
- default:
- // Set the install plan's metadata size to be the same as the one
- // in the manifest so that we pass the metadata size checks. Only
- // then we can get to manifest signature checks.
- ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
- payload.data(),
- state.metadata_size,
- kUnittestPrivateKeyPath,
- &install_plan.metadata_signature));
- EXPECT_FALSE(install_plan.metadata_signature.empty());
+ // Ignore the expected result/error if hash checks are not mandatory.
+ if (!hash_checks_mandatory) {
expected_result = DeltaPerformer::kMetadataParseSuccess;
expected_error = ErrorCode::kSuccess;
- break;
+ }
+
+ // Use the public key corresponding to the private key used above to
+ // sign the metadata.
+ EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
+ performer_.set_public_key_path(kUnittestPublicKeyPath);
+
+ // Init actual_error with an invalid value so that we make sure
+ // ParsePayloadMetadata properly populates it in all cases.
+ actual_error = ErrorCode::kUmaReportedMax;
+ actual_result = performer_.ParsePayloadMetadata(payload, &actual_error);
+
+ EXPECT_EQ(expected_result, actual_result);
+ EXPECT_EQ(expected_error, actual_error);
+
+ // Check that the parsed metadata size is what's expected. This test
+ // implicitly confirms that the metadata signature is valid, if required.
+ EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
}
- // Ignore the expected result/error if hash checks are not mandatory.
- if (!hash_checks_mandatory) {
- expected_result = DeltaPerformer::kMetadataParseSuccess;
- expected_error = ErrorCode::kSuccess;
- }
+ FakePrefs prefs_;
+ InstallPlan install_plan_;
+ FakeSystemState fake_system_state_;
+ DeltaPerformer performer_{&prefs_, &fake_system_state_, &install_plan_};
+};
- // Create the delta performer object.
- MockPrefs prefs;
- DeltaPerformer delta_performer(&prefs,
- &state.fake_system_state,
- &install_plan);
+TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
+ install_plan_.is_full_update = true;
+ chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+ std::end(kRandomString));
+ expected_data.resize(4096); // block size
+ vector<AnnotatedOperation> aops;
+ AnnotatedOperation aop;
+ *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+ aop.op.set_data_offset(0);
+ aop.op.set_data_length(expected_data.size());
+ aop.op.set_type(InstallOperation::REPLACE);
+ aops.push_back(aop);
- // Use the public key corresponding to the private key used above to
- // sign the metadata.
- EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
- delta_performer.set_public_key_path(kUnittestPublicKeyPath);
+ chromeos::Blob payload_data = GeneratePayload(expected_data, aops, false,
+ kFullPayloadMinorVersion);
- // Init actual_error with an invalid value so that we make sure
- // ParsePayloadMetadata properly populates it in all cases.
- actual_error = ErrorCode::kUmaReportedMax;
- actual_result = delta_performer.ParsePayloadMetadata(payload, &actual_error);
-
- EXPECT_EQ(expected_result, actual_result);
- EXPECT_EQ(expected_error, actual_error);
-
- // Check that the parsed metadata size is what's expected. This test
- // implicitly confirms that the metadata signature is valid, if required.
- EXPECT_EQ(state.metadata_size, delta_performer.GetMetadataSize());
+ EXPECT_EQ(expected_data, ApplyPayload(payload_data, ""));
}
-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_F(DeltaPerformerTest, ReplaceOperationTest) {
+ chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+ std::end(kRandomString));
+ expected_data.resize(4096); // block size
+ vector<AnnotatedOperation> aops;
+ AnnotatedOperation aop;
+ *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+ aop.op.set_data_offset(0);
+ aop.op.set_data_length(expected_data.size());
+ aop.op.set_type(InstallOperation::REPLACE);
+ aops.push_back(aop);
+
+ chromeos::Blob payload_data = GeneratePayload(expected_data, aops, false,
+ kSourceMinorPayloadVersion);
+
+ EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
}
-TEST(DeltaPerformerTest, ExtentsToByteStringTest) {
+TEST_F(DeltaPerformerTest, ReplaceBzOperationTest) {
+ chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
+ std::end(kRandomString));
+ expected_data.resize(4096); // block size
+ chromeos::Blob bz_data;
+ EXPECT_TRUE(BzipCompress(expected_data, &bz_data));
+
+ vector<AnnotatedOperation> aops;
+ AnnotatedOperation aop;
+ *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+ aop.op.set_data_offset(0);
+ aop.op.set_data_length(bz_data.size());
+ aop.op.set_type(InstallOperation::REPLACE_BZ);
+ aops.push_back(aop);
+
+ chromeos::Blob payload_data = GeneratePayload(bz_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));
+ expected_data.resize(4096); // block size
+ vector<AnnotatedOperation> aops;
+ AnnotatedOperation aop;
+ *(aop.op.add_src_extents()) = ExtentForRange(0, 1);
+ *(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
+ aop.op.set_type(InstallOperation::SOURCE_COPY);
+ aops.push_back(aop);
+
+ chromeos::Blob payload_data = GeneratePayload(chromeos::Blob(), aops, false,
+ kSourceMinorPayloadVersion);
+ string source_path;
+ EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX",
+ &source_path, nullptr));
+ ScopedPathUnlinker path_unlinker(source_path);
+ EXPECT_TRUE(utils::WriteFile(source_path.c_str(),
+ expected_data.data(),
+ expected_data.size()));
+
+ EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path));
+}
+
+TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
uint64_t test[] = {1, 1, 4, 2, 0, 1};
COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven);
const uint64_t block_size = 4096;
@@ -1097,9 +347,7 @@
google::protobuf::RepeatedPtrField<Extent> extents;
for (size_t i = 0; i < arraysize(test); i += 2) {
- Extent* extent = extents.Add();
- extent->set_start_block(test[i]);
- extent->set_num_blocks(test[i + 1]);
+ *(extents.Add()) = ExtentForRange(test[i], test[i + 1]);
}
string expected_output = "4096:4096,16384:8192,0:4083";
@@ -1111,18 +359,17 @@
EXPECT_EQ(expected_output, actual_output);
}
-TEST(DeltaPerformerTest, ValidateManifestFullGoodTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullGoodTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_new_kernel_info();
manifest.mutable_new_rootfs_info();
- manifest.set_minor_version(DeltaPerformer::kFullPayloadMinorVersion);
+ manifest.set_minor_version(kFullPayloadMinorVersion);
- DeltaPerformerTest::RunManifestValidation(manifest, true,
- ErrorCode::kSuccess);
+ RunManifestValidation(manifest, true, ErrorCode::kSuccess);
}
-TEST(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_old_kernel_info();
@@ -1131,28 +378,25 @@
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
- DeltaPerformerTest::RunManifestValidation(manifest, false,
- ErrorCode::kSuccess);
+ RunManifestValidation(manifest, false, ErrorCode::kSuccess);
}
-TEST(DeltaPerformerTest, ValidateManifestFullUnsetMinorVersion) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullUnsetMinorVersion) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
- DeltaPerformerTest::RunManifestValidation(manifest, true,
- ErrorCode::kSuccess);
+ RunManifestValidation(manifest, true, ErrorCode::kSuccess);
}
-TEST(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
- DeltaPerformerTest::RunManifestValidation(
- manifest, false,
- ErrorCode::kUnsupportedMinorPayloadVersion);
+ RunManifestValidation(manifest, false,
+ ErrorCode::kUnsupportedMinorPayloadVersion);
}
-TEST(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_old_kernel_info();
@@ -1160,12 +404,10 @@
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
- DeltaPerformerTest::RunManifestValidation(
- manifest, true,
- ErrorCode::kPayloadMismatchedType);
+ RunManifestValidation(manifest, true, ErrorCode::kPayloadMismatchedType);
}
-TEST(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_old_rootfs_info();
@@ -1173,12 +415,10 @@
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
- DeltaPerformerTest::RunManifestValidation(
- manifest, true,
- ErrorCode::kPayloadMismatchedType);
+ RunManifestValidation(manifest, true, ErrorCode::kPayloadMismatchedType);
}
-TEST(DeltaPerformerTest, ValidateManifestBadMinorVersion) {
+TEST_F(DeltaPerformerTest, ValidateManifestBadMinorVersion) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
@@ -1186,172 +426,83 @@
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion +
10000);
- DeltaPerformerTest::RunManifestValidation(
- manifest, false,
- ErrorCode::kUnsupportedMinorPayloadVersion);
+ RunManifestValidation(manifest, false,
+ ErrorCode::kUnsupportedMinorPayloadVersion);
}
-TEST(DeltaPerformerTest, RunAsRootSmallImageTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
- false, kInPlaceMinorPayloadVersion);
+TEST_F(DeltaPerformerTest, BadDeltaMagicTest) {
+ EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+ EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
+ EXPECT_TRUE(performer_.Write("junk", 4));
+ EXPECT_TRUE(performer_.Write("morejunk", 8));
+ EXPECT_FALSE(performer_.Write("morejunk", 8));
+ EXPECT_LT(performer_.Close(), 0);
}
-TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderTest) {
- DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
- false, kInPlaceMinorPayloadVersion);
-}
+TEST_F(DeltaPerformerTest, WriteUpdatesPayloadState) {
+ EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+ EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
-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;
- FakeSystemState fake_system_state;
- DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
- EXPECT_EQ(0, performer.Open("/dev/null", 0, 0));
- EXPECT_TRUE(performer.OpenKernel("/dev/null"));
- EXPECT_TRUE(performer.Write("junk", 4));
- EXPECT_TRUE(performer.Write("morejunk", 8));
- EXPECT_FALSE(performer.Write("morejunk", 8));
- EXPECT_LT(performer.Close(), 0);
-}
-
-TEST(DeltaPerformerTest, WriteUpdatesPayloadState) {
- MockPrefs prefs;
- InstallPlan install_plan;
- FakeSystemState fake_system_state;
- DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
- EXPECT_EQ(0, performer.Open("/dev/null", 0, 0));
- EXPECT_TRUE(performer.OpenKernel("/dev/null"));
-
- EXPECT_CALL(*(fake_system_state.mock_payload_state()),
+ EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
DownloadProgress(4)).Times(1);
- EXPECT_CALL(*(fake_system_state.mock_payload_state()),
+ EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
DownloadProgress(8)).Times(2);
- EXPECT_TRUE(performer.Write("junk", 4));
- EXPECT_TRUE(performer.Write("morejunk", 8));
- EXPECT_FALSE(performer.Write("morejunk", 8));
- EXPECT_LT(performer.Close(), 0);
+ EXPECT_TRUE(performer_.Write("junk", 4));
+ EXPECT_TRUE(performer_.Write("morejunk", 8));
+ EXPECT_FALSE(performer_.Write("morejunk", 8));
+ EXPECT_LT(performer_.Close(), 0);
}
-TEST(DeltaPerformerTest, MissingMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, MissingMandatoryMetadataSizeTest) {
DoMetadataSizeTest(0, 75456, true);
}
-TEST(DeltaPerformerTest, MissingNonMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, MissingNonMandatoryMetadataSizeTest) {
DoMetadataSizeTest(0, 123456, false);
}
-TEST(DeltaPerformerTest, InvalidMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, InvalidMandatoryMetadataSizeTest) {
DoMetadataSizeTest(13000, 140000, true);
}
-TEST(DeltaPerformerTest, InvalidNonMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, InvalidNonMandatoryMetadataSizeTest) {
DoMetadataSizeTest(40000, 50000, false);
}
-TEST(DeltaPerformerTest, ValidMandatoryMetadataSizeTest) {
+TEST_F(DeltaPerformerTest, ValidMandatoryMetadataSizeTest) {
DoMetadataSizeTest(85376, 85376, true);
}
-TEST(DeltaPerformerTest, RunAsRootMandatoryEmptyMetadataSignatureTest) {
- DoMetadataSignatureTest(kEmptyMetadataSignature, kSignatureGenerated, true);
+TEST_F(DeltaPerformerTest, MandatoryEmptyMetadataSignatureTest) {
+ DoMetadataSignatureTest(kEmptyMetadataSignature, true, true);
}
-TEST(DeltaPerformerTest, RunAsRootNonMandatoryEmptyMetadataSignatureTest) {
- DoMetadataSignatureTest(kEmptyMetadataSignature, kSignatureGenerated, false);
+TEST_F(DeltaPerformerTest, NonMandatoryEmptyMetadataSignatureTest) {
+ DoMetadataSignatureTest(kEmptyMetadataSignature, true, false);
}
-TEST(DeltaPerformerTest, RunAsRootMandatoryInvalidMetadataSignatureTest) {
- DoMetadataSignatureTest(kInvalidMetadataSignature, kSignatureGenerated, true);
+TEST_F(DeltaPerformerTest, MandatoryInvalidMetadataSignatureTest) {
+ DoMetadataSignatureTest(kInvalidMetadataSignature, true, true);
}
-TEST(DeltaPerformerTest, RunAsRootNonMandatoryInvalidMetadataSignatureTest) {
- DoMetadataSignatureTest(kInvalidMetadataSignature, kSignatureGenerated,
- false);
+TEST_F(DeltaPerformerTest, NonMandatoryInvalidMetadataSignatureTest) {
+ DoMetadataSignatureTest(kInvalidMetadataSignature, true, false);
}
-TEST(DeltaPerformerTest, RunAsRootMandatoryValidMetadataSignature1Test) {
- DoMetadataSignatureTest(kValidMetadataSignature, kSignatureNone, true);
+TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature1Test) {
+ DoMetadataSignatureTest(kValidMetadataSignature, false, true);
}
-TEST(DeltaPerformerTest, RunAsRootMandatoryValidMetadataSignature2Test) {
- DoMetadataSignatureTest(kValidMetadataSignature, kSignatureGenerated, true);
+TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature2Test) {
+ DoMetadataSignatureTest(kValidMetadataSignature, true, true);
}
-TEST(DeltaPerformerTest, RunAsRootNonMandatoryValidMetadataSignatureTest) {
- DoMetadataSignatureTest(kValidMetadataSignature, kSignatureGenerated, false);
+TEST_F(DeltaPerformerTest, NonMandatoryValidMetadataSignatureTest) {
+ DoMetadataSignatureTest(kValidMetadataSignature, true, false);
}
-TEST(DeltaPerformerTest, RunAsRootMandatoryOperationHashMismatchTest) {
- DoOperationHashMismatchTest(kInvalidOperationData, true);
-}
-
-TEST(DeltaPerformerTest, UsePublicKeyFromResponse) {
- MockPrefs prefs;
- FakeSystemState fake_system_state;
- InstallPlan install_plan;
+TEST_F(DeltaPerformerTest, UsePublicKeyFromResponse) {
base::FilePath key_path;
// The result of the GetPublicKeyResponse() method is based on three things
@@ -1366,10 +517,7 @@
// a. it's not an official build; and
// b. there is no key in the root filesystem.
- DeltaPerformer *performer = new DeltaPerformer(&prefs,
- &fake_system_state,
- &install_plan);
- FakeHardware* fake_hardware = fake_system_state.fake_hardware();
+ FakeHardware* fake_hardware = fake_system_state_.fake_hardware();
string temp_dir;
EXPECT_TRUE(utils::MakeTempDirectory("PublicKeyFromResponseTests.XXXXXX",
@@ -1380,61 +528,66 @@
// Non-official build, non-existing public-key, key in response -> true
fake_hardware->SetIsOfficialBuild(false);
- performer->public_key_path_ = non_existing_file;
- install_plan.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
- EXPECT_TRUE(performer->GetPublicKeyFromResponse(&key_path));
+ performer_.public_key_path_ = non_existing_file;
+ install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
+ EXPECT_TRUE(performer_.GetPublicKeyFromResponse(&key_path));
EXPECT_FALSE(key_path.empty());
EXPECT_EQ(unlink(key_path.value().c_str()), 0);
// Same with official build -> false
fake_hardware->SetIsOfficialBuild(true);
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, existing public-key, key in response -> false
fake_hardware->SetIsOfficialBuild(false);
- performer->public_key_path_ = existing_file;
- install_plan.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ performer_.public_key_path_ = existing_file;
+ install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware->SetIsOfficialBuild(true);
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, non-existing public-key, no key in response -> false
fake_hardware->SetIsOfficialBuild(false);
- performer->public_key_path_ = non_existing_file;
- install_plan.public_key_rsa = "";
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ performer_.public_key_path_ = non_existing_file;
+ install_plan_.public_key_rsa = "";
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware->SetIsOfficialBuild(true);
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, existing public-key, no key in response -> false
fake_hardware->SetIsOfficialBuild(false);
- performer->public_key_path_ = existing_file;
- install_plan.public_key_rsa = "";
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ performer_.public_key_path_ = existing_file;
+ install_plan_.public_key_rsa = "";
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware->SetIsOfficialBuild(true);
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, non-existing public-key, key in response
// but invalid base64 -> false
fake_hardware->SetIsOfficialBuild(false);
- performer->public_key_path_ = non_existing_file;
- install_plan.public_key_rsa = "not-valid-base64";
- EXPECT_FALSE(performer->GetPublicKeyFromResponse(&key_path));
+ performer_.public_key_path_ = non_existing_file;
+ install_plan_.public_key_rsa = "not-valid-base64";
+ EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
- delete performer;
EXPECT_TRUE(test_utils::RecursiveUnlinkDir(temp_dir));
}
-TEST(DeltaPerformerTest, MinorVersionsMatch) {
- // Test that the minor version in update_engine.conf that is installed to
- // the image matches the supported delta minor version in the update engine.
+TEST_F(DeltaPerformerTest, ConfVersionsMatch) {
+ // Test that the versions in update_engine.conf that is installed to the
+ // image match the supported delta versions in the update engine.
uint32_t minor_version;
chromeos::KeyValueStore store;
EXPECT_TRUE(store.Load(base::FilePath("update_engine.conf")));
EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
EXPECT_EQ(DeltaPerformer::kSupportedMinorPayloadVersion, minor_version);
+
+ string major_version_str;
+ uint64_t major_version;
+ EXPECT_TRUE(store.GetString("PAYLOAD_MAJOR_VERSION", &major_version_str));
+ EXPECT_TRUE(base::StringToUint64(major_version_str, &major_version));
+ EXPECT_EQ(DeltaPerformer::kSupportedMajorPayloadVersion, major_version);
}
} // namespace chromeos_update_engine
diff --git a/error_code.h b/error_code.h
index 417e585..fdef895 100644
--- a/error_code.h
+++ b/error_code.h
@@ -90,7 +90,7 @@
// particular value for this enum so that it's just one more than the last
// one above and thus always represents the correct count of UMA metrics
// buckets, even when new enums are added above this line in future. See
- // utils::SendErrorCodeToUma on how this enum is used.
+ // metrics::ReportUpdateAttemptMetrics() on how this enum is used.
kUmaReportedMax,
// use the 2xxx range to encode HTTP errors. These errors are available in
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index 33cedf2..f3a9c3e 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -711,6 +711,42 @@
}
}
+TYPED_TEST(HttpFetcherTest, NoResponseTest) {
+ // This test starts a new http server but the server doesn't respond and just
+ // closes the connection.
+ if (this->test_.IsMock())
+ return;
+
+ PythonHttpServer* server = new PythonHttpServer();
+ int port = server->GetPort();
+ ASSERT_TRUE(server->started_);
+
+ // Handles destruction and claims ownership.
+ FailureHttpFetcherTestDelegate delegate(server);
+ unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+ fetcher->set_delegate(&delegate);
+ // The server will not reply at all, so we can limit the execution time of the
+ // test by reducing the low-speed timeout to something small. The test will
+ // finish once the TimeoutCallback() triggers (every second) and the timeout
+ // expired.
+ fetcher->set_low_speed_limit(kDownloadLowSpeedLimitBps, 1);
+
+ this->loop_.PostTask(FROM_HERE, base::Bind(
+ StartTransfer,
+ fetcher.get(),
+ LocalServerUrlForPath(port, "/hang")));
+ this->loop_.Run();
+
+ // Check that no other callback runs in the next two seconds. That would
+ // indicate a leaked callback.
+ bool timeout = false;
+ auto callback = base::Bind([&timeout]{ timeout = true;});
+ this->loop_.PostDelayedTask(FROM_HERE, callback,
+ base::TimeDelta::FromSeconds(2));
+ EXPECT_TRUE(this->loop_.RunOnce(true));
+ EXPECT_TRUE(timeout);
+}
+
TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
// This test starts a new http server and kills it after receiving its first
// set of bytes. It test whether or not our fetcher eventually gives up on
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index e8a8c4b..5b6c605 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -514,9 +514,6 @@
}
void LibcurlHttpFetcher::TimeoutCallback() {
- if (transfer_in_progress_)
- CurlPerformOnce();
-
// We always re-schedule the callback, even if we don't want to be called
// anymore. We will remove the event source separately if we don't want to
// be called back.
@@ -524,6 +521,11 @@
FROM_HERE,
base::Bind(&LibcurlHttpFetcher::TimeoutCallback, base::Unretained(this)),
TimeDelta::FromSeconds(idle_seconds_));
+
+ // CurlPerformOnce() may call CleanUp(), so we need to schedule our callback
+ // first, since it could be canceled by this call.
+ if (transfer_in_progress_)
+ CurlPerformOnce();
}
void LibcurlHttpFetcher::CleanUp() {
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 05d950f..8967762 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -1343,13 +1343,7 @@
if (!prefs->SetInt64(kPrefsInstallDateDays, install_date_days))
return false;
- string metric_name = "Installer.InstallDateProvisioningSource";
- system_state->metrics_lib()->SendEnumToUMA(
- metric_name,
- static_cast<int>(source), // Sample.
- kProvisionedMax); // Maximum.
-
- metric_name = metrics::kMetricInstallDateProvisioningSource;
+ string metric_name = metrics::kMetricInstallDateProvisioningSource;
system_state->metrics_lib()->SendEnumToUMA(
metric_name,
static_cast<int>(source), // Sample.
diff --git a/payload_constants.cc b/payload_constants.cc
index 9d6fb3d..aeddf11 100644
--- a/payload_constants.cc
+++ b/payload_constants.cc
@@ -18,6 +18,13 @@
namespace chromeos_update_engine {
+const uint64_t kChromeOSMajorPayloadVersion = 1;
+const uint64_t kBrilloMajorPayloadVersion = 2;
+
+const uint32_t kFullPayloadMinorVersion = 0;
+const uint32_t kInPlaceMinorPayloadVersion = 1;
+const uint32_t kSourceMinorPayloadVersion = 2;
+
const char kLegacyPartitionNameKernel[] = "boot";
const char kLegacyPartitionNameRoot[] = "system";
diff --git a/payload_constants.h b/payload_constants.h
index 188ea84..21dab00 100644
--- a/payload_constants.h
+++ b/payload_constants.h
@@ -23,6 +23,22 @@
namespace chromeos_update_engine {
+// The major version used by Chrome OS.
+extern const uint64_t kChromeOSMajorPayloadVersion;
+
+// The major version used by Brillo.
+extern const uint64_t kBrilloMajorPayloadVersion;
+
+// The minor version used for all full payloads.
+extern const uint32_t kFullPayloadMinorVersion;
+
+// The minor version used by the in-place delta generator algorithm.
+extern const uint32_t kInPlaceMinorPayloadVersion;
+
+// The minor version used by the A to B delta generator algorithm.
+extern const uint32_t kSourceMinorPayloadVersion;
+
+
// The kernel and rootfs partition names used by the BootControlInterface when
// handling update payloads with a major version 1. The names of the updated
// partitions are include in the payload itself for major version 2.
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index 2f39126..6c1047c 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -21,7 +21,7 @@
#include <gtest/gtest.h>
-#include "update_engine/delta_performer.h"
+#include "update_engine/payload_constants.h"
#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/test_utils.h"
@@ -35,7 +35,7 @@
protected:
void SetUp() override {
config_.is_delta = false;
- config_.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+ config_.minor_version = kFullPayloadMinorVersion;
config_.hard_chunk_size = 128 * 1024;
config_.block_size = 4096;
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index e122fb9..d38c5c5 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -30,6 +30,7 @@
#include <chromeos/flag_helper.h>
#include "update_engine/delta_performer.h"
+#include "update_engine/payload_constants.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/delta_diff_utils.h"
#include "update_engine/payload_generator/payload_generation_config.h"
@@ -424,7 +425,7 @@
payload_config.minor_version = kInPlaceMinorPayloadVersion;
}
} else {
- payload_config.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+ payload_config.minor_version = kFullPayloadMinorVersion;
}
LOG(INFO) << "Auto-detected minor_version=" << payload_config.minor_version;
} else {
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index a2d9311..786c4e2 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -115,8 +115,7 @@
} else {
// All the "source" image fields must be empty for full payloads.
TEST_AND_RETURN_FALSE(source.ValidateIsEmpty());
- TEST_AND_RETURN_FALSE(minor_version ==
- DeltaPerformer::kFullPayloadMinorVersion);
+ TEST_AND_RETURN_FALSE(minor_version == kFullPayloadMinorVersion);
}
// In all cases, the target image must exists.
diff --git a/payload_state.cc b/payload_state.cc
index 78c048b..9a1c740 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -114,7 +114,6 @@
SetNumResponsesSeen(num_responses_seen_ + 1);
SetResponseSignature(new_response_signature);
ResetPersistedState();
- ReportUpdatesAbandonedEventCountMetric();
return;
}
@@ -420,18 +419,6 @@
void PayloadState::IncrementPayloadAttemptNumber() {
// Update the payload attempt number for both payload types: full and delta.
SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
-
- // Report the metric every time the value is incremented.
- string metric = "Installer.PayloadAttemptNumber";
- int value = GetPayloadAttemptNumber();
-
- LOG(INFO) << "Uploading " << value << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- value,
- 1, // min value
- 50, // max value
- kNumDefaultUmaBuckets);
}
void PayloadState::IncrementFullPayloadAttemptNumber() {
@@ -444,18 +431,6 @@
LOG(INFO) << "Incrementing the full payload attempt number";
SetFullPayloadAttemptNumber(GetFullPayloadAttemptNumber() + 1);
UpdateBackoffExpiryTime();
-
- // Report the metric every time the value is incremented.
- string metric = "Installer.FullPayloadAttemptNumber";
- int value = GetFullPayloadAttemptNumber();
-
- LOG(INFO) << "Uploading " << value << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- value,
- 1, // min value
- 50, // max value
- kNumDefaultUmaBuckets);
}
void PayloadState::IncrementUrlIndex() {
@@ -679,7 +654,6 @@
string metric;
// Report metrics collected from all known download sources to UMA.
- int64_t successful_bytes_by_source[kNumDownloadSources];
int64_t total_bytes_by_source[kNumDownloadSources];
int64_t successful_bytes = 0;
int64_t total_bytes = 0;
@@ -697,7 +671,6 @@
// zero-byte events in the histogram.
bytes = GetCurrentBytesDownloaded(source);
- successful_bytes_by_source[i] = bytes;
successful_bytes += bytes;
successful_mbs += bytes / kNumBytesInOneMiB;
SetCurrentBytesDownloaded(source, 0, true);
@@ -722,7 +695,6 @@
SetNumReboots(0);
TimeDelta duration = GetUpdateDuration();
- TimeDelta duration_uptime = GetUpdateDurationUptime();
prefs_->Delete(kPrefsUpdateTimestampStart);
prefs_->Delete(kPrefsUpdateDurationUptime);
@@ -745,142 +717,6 @@
duration,
reboot_count,
url_switch_count);
-
- // TODO(zeuthen): This is the old metric reporting code which is
- // slated for removal soon. See http://crbug.com/355745 for details.
-
- // The old metrics code is using MiB's instead of bytes to calculate
- // the overhead which due to rounding makes the numbers slightly
- // different.
- download_overhead_percentage = 0;
- if (successful_mbs > 0) {
- download_overhead_percentage = (total_mbs - successful_mbs) * 100ULL /
- successful_mbs;
- }
-
- int download_sources_used = 0;
- for (int i = 0; i < kNumDownloadSources; i++) {
- DownloadSource source = static_cast<DownloadSource>(i);
- const int kMaxMiBs = 10240; // Anything above 10GB goes in the last bucket.
- int64_t mbs;
-
- // Only consider this download source (and send byte counts) as
- // having been used if we downloaded a non-trivial amount of bytes
- // (e.g. at least 1 MiB) that contributed to the final success of
- // the update. Otherwise we're going to end up with a lot of
- // zero-byte events in the histogram.
-
- mbs = successful_bytes_by_source[i] / kNumBytesInOneMiB;
- if (mbs > 0) {
- metric = "Installer.SuccessfulMBsDownloadedFrom" +
- utils::ToString(source);
- LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(metric,
- mbs,
- 0, // min
- kMaxMiBs,
- kNumDefaultUmaBuckets);
- }
-
- mbs = total_bytes_by_source[i] / kNumBytesInOneMiB;
- if (mbs > 0) {
- metric = "Installer.TotalMBsDownloadedFrom" + utils::ToString(source);
- LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(metric,
- mbs,
- 0, // min
- kMaxMiBs,
- kNumDefaultUmaBuckets);
- download_sources_used |= (1 << i);
- }
- }
-
- metric = "Installer.DownloadSourcesUsed";
- LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
- << " (bit flags) for metric " << metric;
- int num_buckets = min(1 << kNumDownloadSources, kNumDefaultUmaBuckets);
- system_state_->metrics_lib()->SendToUMA(metric,
- download_sources_used,
- 0, // min
- 1 << kNumDownloadSources,
- num_buckets);
-
- metric = "Installer.DownloadOverheadPercentage";
- LOG(INFO) << "Uploading " << download_overhead_percentage
- << "% for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(metric,
- download_overhead_percentage,
- 0, // min: 0% overhead
- 1000, // max: 1000% overhead
- kNumDefaultUmaBuckets);
-
- metric = "Installer.UpdateURLSwitches";
- LOG(INFO) << "Uploading " << url_switch_count
- << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- url_switch_count,
- 0, // min value
- 100, // max value
- kNumDefaultUmaBuckets);
-
- metric = "Installer.UpdateNumReboots";
- LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
- << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- reboot_count, // sample
- 0, // min = 0.
- 50, // max
- 25); // buckets
-
- metric = "Installer.UpdateDurationMinutes";
- system_state_->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(duration.InMinutes()),
- 1, // min: 1 minute
- 365*24*60, // max: 1 year (approx)
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
- << " for metric " << metric;
-
- metric = "Installer.UpdateDurationUptimeMinutes";
- system_state_->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(duration_uptime.InMinutes()),
- 1, // min: 1 minute
- 30*24*60, // max: 1 month (approx)
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
- << " for metric " << metric;
-
- metric = "Installer.PayloadFormat";
- system_state_->metrics_lib()->SendEnumToUMA(
- metric,
- payload_type,
- kNumPayloadTypes);
- LOG(INFO) << "Uploading " << utils::ToString(payload_type)
- << " for metric " << metric;
-
- metric = "Installer.AttemptsCount.Total";
- system_state_->metrics_lib()->SendToUMA(
- metric,
- attempt_count,
- 1, // min
- 50, // max
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << attempt_count
- << " for metric " << metric;
-
- metric = "Installer.UpdatesAbandonedCount";
- LOG(INFO) << "Uploading " << updates_abandoned_count
- << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- updates_abandoned_count,
- 0, // min value
- 100, // max value
- kNumDefaultUmaBuckets);
}
void PayloadState::UpdateNumReboots() {
@@ -1309,24 +1145,6 @@
prefs_->SetInt64(kPrefsNumResponsesSeen, num_responses_seen_);
}
-void PayloadState::ReportUpdatesAbandonedEventCountMetric() {
- string metric = "Installer.UpdatesAbandonedEventCount";
- int value = num_responses_seen_ - 1;
-
- // Do not send an "abandoned" event when 0 payloads were abandoned since the
- // last successful update.
- if (value == 0)
- return;
-
- LOG(INFO) << "Uploading " << value << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- value,
- 0, // min value
- 100, // max value
- kNumDefaultUmaBuckets);
-}
-
void PayloadState::ComputeCandidateUrls() {
bool http_url_ok = true;
@@ -1361,16 +1179,7 @@
void PayloadState::BootedIntoUpdate(TimeDelta time_to_reboot) {
// Send |time_to_reboot| as a UMA stat.
- string metric = "Installer.TimeToRebootMinutes";
- system_state_->metrics_lib()->SendToUMA(metric,
- time_to_reboot.InMinutes(),
- 0, // min: 0 minute
- 30*24*60, // max: 1 month (approx)
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(time_to_reboot)
- << " for metric " << metric;
-
- metric = metrics::kMetricTimeToRebootMinutes;
+ string metric = metrics::kMetricTimeToRebootMinutes;
system_state_->metrics_lib()->SendToUMA(metric,
time_to_reboot.InMinutes(),
0, // min: 0 minute
@@ -1440,18 +1249,7 @@
}
// Report the UMA metric of the current boot failure.
- string metric = "Installer.RebootToNewPartitionAttempt";
-
- LOG(INFO) << "Uploading " << target_attempt
- << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- target_attempt,
- 1, // min value
- 50, // max value
- kNumDefaultUmaBuckets);
-
- metric = metrics::kMetricFailedUpdateCount;
+ string metric = metrics::kMetricFailedUpdateCount;
LOG(INFO) << "Uploading " << target_attempt
<< " (count) for metric " << metric;
system_state_->metrics_lib()->SendToUMA(
diff --git a/payload_state.h b/payload_state.h
index 23c01a1..3616826 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -377,13 +377,6 @@
// Initializes |num_responses_seen_| from persisted state.
void LoadNumResponsesSeen();
- // Reports metric conveying how many times updates were abandoned since
- // the last update was applied. The difference between this metric and the
- // previous ReportUpdatesAbandonedCountMetric() one is that this metric is
- // reported every time an update is abandoned, as oposed to the mentioned
- // metric that is reported once the new update was applied.
- void ReportUpdatesAbandonedEventCountMetric();
-
// Initializes |num_reboots_| from the persisted state.
void LoadNumReboots();
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index e4eca89..012a3fc 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -324,10 +324,6 @@
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
.Times(AnyNumber());
- // The first response doesn't send an abandoned event.
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.UpdatesAbandonedEventCount", 0, _, _, _)).Times(0);
-
// Set the first response.
SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
EXPECT_EQ(1, payload_state.GetNumResponsesSeen());
@@ -338,9 +334,6 @@
EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.UpdatesAbandonedEventCount", 1, _, _, _));
-
// Now, slightly change the response and set it again.
SetupPayloadStateWith2Urls("Hash8225", true, &payload_state, &response);
EXPECT_EQ(2, payload_state.GetNumResponsesSeen());
@@ -350,9 +343,6 @@
EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.UpdatesAbandonedEventCount", 2, _, _, _));
-
// Return a third different response.
SetupPayloadStateWith2Urls("Hash9999", true, &payload_state, &response);
EXPECT_EQ(3, payload_state.GetNumResponsesSeen());
@@ -546,11 +536,6 @@
SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.PayloadAttemptNumber", 1, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.FullPayloadAttemptNumber", 1, _, _, _));
-
// This should just advance the payload attempt number;
EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
@@ -591,13 +576,6 @@
SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
- // Metrics for Full payload attempt number is not sent with Delta payloads.
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.PayloadAttemptNumber", 1, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.FullPayloadAttemptNumber", _, _, _, _))
- .Times(0);
-
// This should just advance the payload attempt number;
EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
@@ -873,7 +851,6 @@
int third_chunk = 32345678;
int http_chunk = first_chunk + third_chunk;
http_total += third_chunk;
- int https_chunk = second_chunk;
payload_state.DownloadProgress(third_chunk);
// Test that third chunk is again back on HTTP. HTTPS remains on second chunk.
@@ -901,58 +878,20 @@
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
.Times(AnyNumber());
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.SuccessfulMBsDownloadedFromHttpServer",
- http_chunk / kNumBytesInOneMiB, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.TotalMBsDownloadedFromHttpServer",
- http_total / kNumBytesInOneMiB, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.SuccessfulMBsDownloadedFromHttpsServer",
- https_chunk / kNumBytesInOneMiB, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.TotalMBsDownloadedFromHttpsServer",
- https_total / kNumBytesInOneMiB, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.SuccessfulMBsDownloadedFromHttpPeer",
- p2p_total / kNumBytesInOneMiB, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.TotalMBsDownloadedFromHttpPeer",
- p2p_total / kNumBytesInOneMiB, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.UpdateURLSwitches",
- 3, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricSuccessfulUpdateUrlSwitchCount,
3, _, _, _));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.UpdateDurationMinutes",
- _, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricSuccessfulUpdateTotalDurationMinutes,
_, _, _, _));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.UpdateDurationUptimeMinutes",
- _, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.DownloadSourcesUsed",
- (1 << kDownloadSourceHttpsServer) | (1 << kDownloadSourceHttpServer) |
- (1 << kDownloadSourceHttpPeer),
- _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.DownloadOverheadPercentage", 318, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
314, _, _, _));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- "Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricAttemptPayloadType, kPayloadTypeFull, kNumPayloadTypes));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
kNumPayloadTypes));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.AttemptsCount.Total", 1, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricSuccessfulUpdateAttemptCount, 1, _, _, _));
payload_state.UpdateSucceeded();
@@ -991,10 +930,6 @@
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
.Times(AnyNumber());
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.DownloadSourcesUsed",
- (1 << kDownloadSourceHttpServer),
- _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricSuccessfulUpdateDownloadSourcesUsed,
(1 << kDownloadSourceHttpServer),
_, _, _));
@@ -1199,9 +1134,6 @@
// Expect 500 - 30 seconds = 470 seconds ~= 7 min 50 sec
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.TimeToRebootMinutes",
- 7, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricTimeToRebootMinutes,
7, _, _, _));
fake_system_state.set_system_rebooted(true);
@@ -1394,8 +1326,6 @@
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
.Times(AnyNumber());
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- "Installer.PayloadFormat", kPayloadTypeDelta, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricAttemptPayloadType, kPayloadTypeDelta, kNumPayloadTypes));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
@@ -1414,8 +1344,6 @@
payload_state.DownloadComplete();
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- "Installer.PayloadFormat", kPayloadTypeDelta, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricAttemptPayloadType, kPayloadTypeDelta,
kNumPayloadTypes));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
@@ -1444,8 +1372,6 @@
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
.Times(AnyNumber());
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- "Installer.PayloadFormat", kPayloadTypeForcedFull, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricAttemptPayloadType, kPayloadTypeForcedFull,
kNumPayloadTypes));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
@@ -1475,8 +1401,6 @@
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
.Times(AnyNumber());
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
- "Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
metrics::kMetricAttemptPayloadType, kPayloadTypeFull,
kNumPayloadTypes));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(
@@ -1502,8 +1426,6 @@
// Reboot into the same environment to get an UMA metric with a value of 1.
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.RebootToNewPartitionAttempt", 1, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricFailedUpdateCount, 1, _, _, _));
payload_state.ReportFailedBootIfNeeded();
Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
@@ -1513,8 +1435,6 @@
payload_state.ExpectRebootInNewVersion("Version:12345678");
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.RebootToNewPartitionAttempt", 2, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricFailedUpdateCount, 2, _, _, _));
payload_state.ReportFailedBootIfNeeded();
Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
@@ -1523,8 +1443,6 @@
// different payload. This should send a value of 1 this time.
payload_state.ExpectRebootInNewVersion("Version:3141592");
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.RebootToNewPartitionAttempt", 1, _, _, _));
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricFailedUpdateCount, 1, _, _, _));
payload_state.ReportFailedBootIfNeeded();
Mock::VerifyAndClearExpectations(fake_system_state.mock_metrics_lib());
@@ -1552,9 +1470,6 @@
fake_boot_control->SetCurrentSlot(1);
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.RebootToNewPartitionAttempt", _, _, _, _))
- .Times(0);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricFailedUpdateCount, _, _, _, _))
.Times(0);
payload_state.ReportFailedBootIfNeeded();
@@ -1581,9 +1496,6 @@
payload_state.ExpectRebootInNewVersion("Version:12345678");
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.RebootToNewPartitionAttempt", _, _, _, _))
- .Times(0);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricFailedUpdateCount, _, _, _, _))
.Times(0);
@@ -1603,9 +1515,6 @@
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
- "Installer.RebootToNewPartitionAttempt", _, _, _, _))
- .Times(0);
- EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
metrics::kMetricFailedUpdateCount, _, _, _, _))
.Times(0);
diff --git a/test_http_server.cc b/test_http_server.cc
index f4190c3..7326eb5 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -41,6 +41,7 @@
#include <vector>
#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
@@ -450,6 +451,12 @@
}
}
+void HandleHang(int fd) {
+ LOG(INFO) << "Hanging until the other side of the connection is closed.";
+ char c;
+ while (HANDLE_EINTR(read(fd, &c, 1)) > 0) {}
+}
+
void HandleDefault(int fd, const HttpRequest& request) {
const off_t start_offset = request.start_offset;
const string data("unhandled path");
@@ -517,6 +524,8 @@
} else if (base::StartsWithASCII(url, "/error-if-offset/", true)) {
const UrlTerms terms(url, 3);
HandleErrorIfOffset(fd, request, terms.GetSizeT(1), terms.GetInt(2));
+ } else if (url == "/hang") {
+ HandleHang(fd);
} else {
HandleDefault(fd, request);
}
diff --git a/update_attempter.cc b/update_attempter.cc
index cda244d..d4e1654 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -257,16 +257,6 @@
return;
}
- string metric = "Installer.OSAgeDays";
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(age)
- << " for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(age.InDays()),
- 0, // min: 0 days
- 6*30, // max: 6 months (approx)
- kNumDefaultUmaBuckets);
-
metrics::ReportDailyMetrics(system_state_, age);
}
@@ -1001,10 +991,6 @@
// next boot.
system_state_->payload_state()->ExpectRebootInNewVersion(
target_version_uid);
-
- // Also report the success code so that the percentiles can be
- // interpreted properly for the remaining error codes in UMA.
- utils::SendErrorCodeToUma(system_state_, code);
} else {
// If we just finished a rollback, then we expect to have no Omaha
// response. Otherwise, it's an error.
@@ -1314,11 +1300,8 @@
LOG(ERROR) << "Update failed.";
system_state_->payload_state()->UpdateFailed(error_event_->error_code);
- // Send it to Uma.
- LOG(INFO) << "Reporting the error event";
- utils::SendErrorCodeToUma(system_state_, error_event_->error_code);
-
// Send it to Omaha.
+ LOG(INFO) << "Reporting the error event";
shared_ptr<OmahaRequestAction> error_event_action(
new OmahaRequestAction(system_state_,
error_event_.release(), // Pass ownership.
diff --git a/update_engine.conf b/update_engine.conf
index dc65ebe..dea31e4 100644
--- a/update_engine.conf
+++ b/update_engine.conf
@@ -1 +1,2 @@
+PAYLOAD_MAJOR_VERSION=1
PAYLOAD_MINOR_VERSION=2
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',
diff --git a/utils.cc b/utils.cc
index 4018b37..98f3576 100644
--- a/utils.cc
+++ b/utils.cc
@@ -1246,56 +1246,6 @@
return metrics::ConnectionType::kUnknown;
}
-// Returns a printable version of the various flags denoted in the higher order
-// bits of the given code. Returns an empty string if none of those bits are
-// set.
-string GetFlagNames(uint32_t code) {
- uint32_t flags = (static_cast<uint32_t>(code) &
- static_cast<uint32_t>(ErrorCode::kSpecialFlags));
- string flag_names;
- string separator = "";
- for (size_t i = 0; i < sizeof(flags) * 8; i++) {
- uint32_t flag = flags & (1 << i);
- if (flag) {
- flag_names += separator + CodeToString(static_cast<ErrorCode>(flag));
- separator = ", ";
- }
- }
-
- return flag_names;
-}
-
-void SendErrorCodeToUma(SystemState* system_state, ErrorCode code) {
- if (!system_state)
- return;
-
- ErrorCode uma_error_code = GetBaseErrorCode(code);
-
- // If the code doesn't have flags computed already, compute them now based on
- // the state of the current update attempt.
- uint32_t flags =
- static_cast<int>(code) & static_cast<int>(ErrorCode::kSpecialFlags);
- if (!flags)
- flags = system_state->update_attempter()->GetErrorCodeFlags();
-
- // Determine the UMA bucket depending on the flags. But, ignore the resumed
- // flag, as it's perfectly normal for production devices to resume their
- // downloads and so we want to record those cases also in NormalErrorCodes
- // bucket.
- string metric =
- flags & ~static_cast<uint32_t>(ErrorCode::kResumedFlag) ?
- "Installer.DevModeErrorCodes" : "Installer.NormalErrorCodes";
-
- LOG(INFO) << "Sending error code " << uma_error_code
- << " (" << CodeToString(uma_error_code) << ")"
- << " to UMA metric: " << metric
- << ". Flags = " << (flags ? GetFlagNames(flags) : "None");
-
- system_state->metrics_lib()->SendEnumToUMA(
- metric, static_cast<int>(uma_error_code),
- static_cast<int>(ErrorCode::kUmaReportedMax));
-}
-
string CodeToString(ErrorCode code) {
// If the given code has both parts (i.e. the error code part and the flags
// part) then strip off the flags part since the switch statement below
diff --git a/utils.h b/utils.h
index 252feab..c1ee5a9 100644
--- a/utils.h
+++ b/utils.h
@@ -334,11 +334,6 @@
metrics::ConnectionType GetConnectionType(NetworkConnectionType type,
NetworkTethering tethering);
-// Sends the error code to UMA using the metrics interface object in the given
-// system state. It also uses the system state to determine the right UMA
-// bucket for the error code.
-void SendErrorCodeToUma(SystemState* system_state, ErrorCode code);
-
// Returns a string representation of the ErrorCodes (either the base
// error codes or the bit flags) for logging purposes.
std::string CodeToString(ErrorCode code);