| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 1 | // | 
|  | 2 | // Copyright (C) 2012 The Android Open Source Project | 
|  | 3 | // | 
|  | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | // you may not use this file except in compliance with the License. | 
|  | 6 | // You may obtain a copy of the License at | 
|  | 7 | // | 
|  | 8 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | // | 
|  | 10 | // Unless required by applicable law or agreed to in writing, software | 
|  | 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | // See the License for the specific language governing permissions and | 
|  | 14 | // limitations under the License. | 
|  | 15 | // | 
|  | 16 |  | 
|  | 17 | #include "update_engine/delta_performer.h" | 
|  | 18 |  | 
|  | 19 | #include <inttypes.h> | 
|  | 20 | #include <sys/mount.h> | 
|  | 21 |  | 
|  | 22 | #include <algorithm> | 
|  | 23 | #include <string> | 
|  | 24 | #include <vector> | 
|  | 25 |  | 
|  | 26 | #include <base/files/file_path.h> | 
|  | 27 | #include <base/files/file_util.h> | 
|  | 28 | #include <base/strings/stringprintf.h> | 
|  | 29 | #include <base/strings/string_util.h> | 
|  | 30 | #include <google/protobuf/repeated_field.h> | 
|  | 31 | #include <gtest/gtest.h> | 
|  | 32 |  | 
|  | 33 | #include "update_engine/constants.h" | 
|  | 34 | #include "update_engine/fake_hardware.h" | 
|  | 35 | #include "update_engine/fake_system_state.h" | 
|  | 36 | #include "update_engine/mock_prefs.h" | 
|  | 37 | #include "update_engine/payload_constants.h" | 
|  | 38 | #include "update_engine/payload_generator/delta_diff_generator.h" | 
|  | 39 | #include "update_engine/payload_generator/payload_signer.h" | 
|  | 40 | #include "update_engine/payload_verifier.h" | 
|  | 41 | #include "update_engine/test_utils.h" | 
|  | 42 | #include "update_engine/update_metadata.pb.h" | 
|  | 43 | #include "update_engine/utils.h" | 
|  | 44 |  | 
|  | 45 | namespace chromeos_update_engine { | 
|  | 46 |  | 
|  | 47 | using std::string; | 
|  | 48 | using std::vector; | 
|  | 49 | using testing::Return; | 
|  | 50 | using testing::_; | 
|  | 51 | using test_utils::kRandomString; | 
|  | 52 | using test_utils::ScopedLoopMounter; | 
|  | 53 | using test_utils::System; | 
|  | 54 |  | 
|  | 55 | extern const char* kUnittestPrivateKeyPath; | 
|  | 56 | extern const char* kUnittestPublicKeyPath; | 
|  | 57 | extern const char* kUnittestPrivateKey2Path; | 
|  | 58 | extern const char* kUnittestPublicKey2Path; | 
|  | 59 |  | 
|  | 60 | static const int kDefaultKernelSize = 4096;  // Something small for a test | 
|  | 61 | static const uint8_t kNewData[] = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ', | 
|  | 62 | 'n', 'e', 'w', ' ', 'd', 'a', 't', 'a', '.'}; | 
|  | 63 |  | 
|  | 64 | namespace { | 
|  | 65 | struct DeltaState { | 
|  | 66 | string a_img; | 
|  | 67 | string b_img; | 
|  | 68 | string result_img; | 
|  | 69 | size_t image_size; | 
|  | 70 |  | 
|  | 71 | string delta_path; | 
|  | 72 | uint64_t metadata_size; | 
|  | 73 |  | 
|  | 74 | string old_kernel; | 
|  | 75 | chromeos::Blob old_kernel_data; | 
|  | 76 |  | 
|  | 77 | string new_kernel; | 
|  | 78 | chromeos::Blob new_kernel_data; | 
|  | 79 |  | 
|  | 80 | string result_kernel; | 
|  | 81 | chromeos::Blob result_kernel_data; | 
|  | 82 | size_t kernel_size; | 
|  | 83 |  | 
|  | 84 | // The in-memory copy of delta file. | 
|  | 85 | chromeos::Blob delta; | 
|  | 86 |  | 
|  | 87 | // The mock system state object with which we initialize the | 
|  | 88 | // delta performer. | 
|  | 89 | FakeSystemState fake_system_state; | 
|  | 90 | }; | 
|  | 91 |  | 
|  | 92 | enum SignatureTest { | 
|  | 93 | kSignatureNone,  // No payload signing. | 
|  | 94 | kSignatureGenerator,  // Sign the payload at generation time. | 
|  | 95 | kSignatureGenerated,  // Sign the payload after it's generated. | 
|  | 96 | kSignatureGeneratedPlaceholder,  // Insert placeholder signatures, then real. | 
|  | 97 | kSignatureGeneratedPlaceholderMismatch,  // Insert a wrong sized placeholder. | 
|  | 98 | kSignatureGeneratedShell,  // Sign the generated payload through shell cmds. | 
|  | 99 | kSignatureGeneratedShellBadKey,  // Sign with a bad key through shell cmds. | 
|  | 100 | kSignatureGeneratedShellRotateCl1,  // Rotate key, test client v1 | 
|  | 101 | kSignatureGeneratedShellRotateCl2,  // Rotate key, test client v2 | 
|  | 102 | }; | 
|  | 103 |  | 
|  | 104 | enum OperationHashTest { | 
|  | 105 | kInvalidOperationData, | 
|  | 106 | kValidOperationData, | 
|  | 107 | }; | 
|  | 108 |  | 
|  | 109 | }  // namespace | 
|  | 110 |  | 
|  | 111 | class DeltaPerformerIntegrationTest : public ::testing::Test { | 
|  | 112 | public: | 
|  | 113 | static void SetSupportedVersion(DeltaPerformer* performer, | 
|  | 114 | uint64_t minor_version) { | 
|  | 115 | performer->supported_minor_version_ = minor_version; | 
|  | 116 | } | 
|  | 117 | }; | 
|  | 118 |  | 
|  | 119 | static void CompareFilesByBlock(const string& a_file, const string& b_file, | 
|  | 120 | size_t image_size) { | 
|  | 121 | EXPECT_EQ(0, image_size % kBlockSize); | 
|  | 122 |  | 
|  | 123 | chromeos::Blob a_data, b_data; | 
|  | 124 | EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file; | 
|  | 125 | EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file; | 
|  | 126 |  | 
|  | 127 | EXPECT_GE(a_data.size(), image_size); | 
|  | 128 | EXPECT_GE(b_data.size(), image_size); | 
|  | 129 | for (size_t i = 0; i < image_size; i += kBlockSize) { | 
|  | 130 | EXPECT_EQ(0, i % kBlockSize); | 
|  | 131 | chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]); | 
|  | 132 | chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]); | 
|  | 133 | EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs"; | 
|  | 134 | } | 
|  | 135 | if (::testing::Test::HasNonfatalFailure()) { | 
|  | 136 | LOG(INFO) << "Compared filesystems with size " << image_size | 
|  | 137 | << ", partition A " << a_file << " size: " << a_data.size() | 
|  | 138 | << ", partition B " << b_file << " size: " << b_data.size(); | 
|  | 139 | } | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | static bool WriteSparseFile(const string& path, off_t size) { | 
|  | 143 | int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644); | 
|  | 144 | TEST_AND_RETURN_FALSE_ERRNO(fd >= 0); | 
|  | 145 | ScopedFdCloser fd_closer(&fd); | 
|  | 146 | off_t rc = lseek(fd, size + 1, SEEK_SET); | 
|  | 147 | TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1)); | 
|  | 148 | int return_code = ftruncate(fd, size); | 
|  | 149 | TEST_AND_RETURN_FALSE_ERRNO(return_code == 0); | 
|  | 150 | return true; | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 | static size_t GetSignatureSize(const string& private_key_path) { | 
|  | 154 | const chromeos::Blob data(1, 'x'); | 
|  | 155 | chromeos::Blob hash; | 
|  | 156 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(data, &hash)); | 
|  | 157 | chromeos::Blob signature; | 
|  | 158 | EXPECT_TRUE(PayloadSigner::SignHash(hash, | 
|  | 159 | private_key_path, | 
|  | 160 | &signature)); | 
|  | 161 | return signature.size(); | 
|  | 162 | } | 
|  | 163 |  | 
|  | 164 | static bool InsertSignaturePlaceholder(int signature_size, | 
|  | 165 | const string& payload_path, | 
|  | 166 | uint64_t* out_metadata_size) { | 
|  | 167 | vector<chromeos::Blob> signatures; | 
|  | 168 | signatures.push_back(chromeos::Blob(signature_size, 0)); | 
|  | 169 |  | 
|  | 170 | return PayloadSigner::AddSignatureToPayload( | 
|  | 171 | payload_path, | 
|  | 172 | signatures, | 
|  | 173 | payload_path, | 
|  | 174 | out_metadata_size); | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | static void SignGeneratedPayload(const string& payload_path, | 
|  | 178 | uint64_t* out_metadata_size) { | 
|  | 179 | int signature_size = GetSignatureSize(kUnittestPrivateKeyPath); | 
|  | 180 | chromeos::Blob hash; | 
|  | 181 | ASSERT_TRUE(PayloadSigner::HashPayloadForSigning( | 
|  | 182 | payload_path, | 
|  | 183 | vector<int>(1, signature_size), | 
|  | 184 | &hash)); | 
|  | 185 | chromeos::Blob signature; | 
|  | 186 | ASSERT_TRUE(PayloadSigner::SignHash(hash, | 
|  | 187 | kUnittestPrivateKeyPath, | 
|  | 188 | &signature)); | 
|  | 189 | ASSERT_TRUE(PayloadSigner::AddSignatureToPayload( | 
|  | 190 | payload_path, | 
|  | 191 | vector<chromeos::Blob>(1, signature), | 
|  | 192 | payload_path, | 
|  | 193 | out_metadata_size)); | 
|  | 194 | EXPECT_TRUE(PayloadVerifier::VerifySignedPayload( | 
|  | 195 | payload_path, | 
| Alex Deymo | b552a68 | 2015-09-30 09:36:49 -0700 | [diff] [blame] | 196 | kUnittestPublicKeyPath)); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 197 | } | 
|  | 198 |  | 
|  | 199 | static void SignGeneratedShellPayload(SignatureTest signature_test, | 
|  | 200 | const string& payload_path) { | 
|  | 201 | string private_key_path = kUnittestPrivateKeyPath; | 
|  | 202 | if (signature_test == kSignatureGeneratedShellBadKey) { | 
|  | 203 | ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX", | 
|  | 204 | &private_key_path, | 
|  | 205 | nullptr)); | 
|  | 206 | } else { | 
|  | 207 | ASSERT_TRUE(signature_test == kSignatureGeneratedShell || | 
|  | 208 | signature_test == kSignatureGeneratedShellRotateCl1 || | 
|  | 209 | signature_test == kSignatureGeneratedShellRotateCl2); | 
|  | 210 | } | 
|  | 211 | ScopedPathUnlinker key_unlinker(private_key_path); | 
|  | 212 | key_unlinker.set_should_remove(signature_test == | 
|  | 213 | kSignatureGeneratedShellBadKey); | 
|  | 214 | // Generates a new private key that will not match the public key. | 
|  | 215 | if (signature_test == kSignatureGeneratedShellBadKey) { | 
|  | 216 | LOG(INFO) << "Generating a mismatched private key."; | 
|  | 217 | ASSERT_EQ(0, System(base::StringPrintf( | 
|  | 218 | "openssl genrsa -out %s 2048", private_key_path.c_str()))); | 
|  | 219 | } | 
|  | 220 | int signature_size = GetSignatureSize(private_key_path); | 
|  | 221 | string hash_file; | 
|  | 222 | ASSERT_TRUE(utils::MakeTempFile("hash.XXXXXX", &hash_file, nullptr)); | 
|  | 223 | ScopedPathUnlinker hash_unlinker(hash_file); | 
|  | 224 | string signature_size_string; | 
|  | 225 | if (signature_test == kSignatureGeneratedShellRotateCl1 || | 
|  | 226 | signature_test == kSignatureGeneratedShellRotateCl2) | 
|  | 227 | signature_size_string = base::StringPrintf("%d:%d", | 
|  | 228 | signature_size, signature_size); | 
|  | 229 | else | 
|  | 230 | signature_size_string = base::StringPrintf("%d", signature_size); | 
|  | 231 | ASSERT_EQ(0, | 
|  | 232 | System(base::StringPrintf( | 
|  | 233 | "./delta_generator -in_file=%s -signature_size=%s " | 
|  | 234 | "-out_hash_file=%s", | 
|  | 235 | payload_path.c_str(), | 
|  | 236 | signature_size_string.c_str(), | 
|  | 237 | hash_file.c_str()))); | 
|  | 238 |  | 
|  | 239 | // Pad the hash | 
|  | 240 | chromeos::Blob hash; | 
|  | 241 | ASSERT_TRUE(utils::ReadFile(hash_file, &hash)); | 
|  | 242 | ASSERT_TRUE(PayloadVerifier::PadRSA2048SHA256Hash(&hash)); | 
|  | 243 | ASSERT_TRUE(test_utils::WriteFileVector(hash_file, hash)); | 
|  | 244 |  | 
|  | 245 | string sig_file; | 
|  | 246 | ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file, nullptr)); | 
|  | 247 | ScopedPathUnlinker sig_unlinker(sig_file); | 
|  | 248 | ASSERT_EQ(0, | 
|  | 249 | System(base::StringPrintf( | 
|  | 250 | "openssl rsautl -raw -sign -inkey %s -in %s -out %s", | 
|  | 251 | private_key_path.c_str(), | 
|  | 252 | hash_file.c_str(), | 
|  | 253 | sig_file.c_str()))); | 
|  | 254 | string sig_file2; | 
|  | 255 | ASSERT_TRUE(utils::MakeTempFile("signature.XXXXXX", &sig_file2, nullptr)); | 
|  | 256 | ScopedPathUnlinker sig2_unlinker(sig_file2); | 
|  | 257 | if (signature_test == kSignatureGeneratedShellRotateCl1 || | 
|  | 258 | signature_test == kSignatureGeneratedShellRotateCl2) { | 
|  | 259 | ASSERT_EQ(0, | 
|  | 260 | System(base::StringPrintf( | 
|  | 261 | "openssl rsautl -raw -sign -inkey %s -in %s -out %s", | 
|  | 262 | kUnittestPrivateKey2Path, | 
|  | 263 | hash_file.c_str(), | 
|  | 264 | sig_file2.c_str()))); | 
|  | 265 | // Append second sig file to first path | 
|  | 266 | sig_file += ":" + sig_file2; | 
|  | 267 | } | 
|  | 268 |  | 
|  | 269 | ASSERT_EQ(0, | 
|  | 270 | System(base::StringPrintf( | 
|  | 271 | "./delta_generator -in_file=%s -signature_file=%s " | 
|  | 272 | "-out_file=%s", | 
|  | 273 | payload_path.c_str(), | 
|  | 274 | sig_file.c_str(), | 
|  | 275 | payload_path.c_str()))); | 
|  | 276 | int verify_result = | 
|  | 277 | System(base::StringPrintf( | 
|  | 278 | "./delta_generator -in_file=%s -public_key=%s -public_key_version=%d", | 
|  | 279 | payload_path.c_str(), | 
|  | 280 | signature_test == kSignatureGeneratedShellRotateCl2 ? | 
|  | 281 | kUnittestPublicKey2Path : kUnittestPublicKeyPath, | 
|  | 282 | signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1)); | 
|  | 283 | if (signature_test == kSignatureGeneratedShellBadKey) { | 
|  | 284 | ASSERT_NE(0, verify_result); | 
|  | 285 | } else { | 
|  | 286 | ASSERT_EQ(0, verify_result); | 
|  | 287 | } | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | static void GenerateDeltaFile(bool full_kernel, | 
|  | 291 | bool full_rootfs, | 
|  | 292 | bool noop, | 
|  | 293 | ssize_t chunk_size, | 
|  | 294 | SignatureTest signature_test, | 
|  | 295 | DeltaState *state, | 
|  | 296 | uint32_t minor_version) { | 
|  | 297 | EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr)); | 
|  | 298 | EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr)); | 
|  | 299 |  | 
|  | 300 | // result_img is used in minor version 2. Instead of applying the update | 
|  | 301 | // in-place on A, we apply it to a new image, result_img. | 
|  | 302 | EXPECT_TRUE( | 
|  | 303 | utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr)); | 
|  | 304 | test_utils::CreateExtImageAtPath(state->a_img, nullptr); | 
|  | 305 |  | 
|  | 306 | state->image_size = utils::FileSize(state->a_img); | 
|  | 307 |  | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 308 | // Create ImageInfo A & B | 
|  | 309 | ImageInfo old_image_info; | 
|  | 310 | ImageInfo new_image_info; | 
|  | 311 |  | 
|  | 312 | if (!full_rootfs) { | 
|  | 313 | old_image_info.set_channel("src-channel"); | 
|  | 314 | old_image_info.set_board("src-board"); | 
|  | 315 | old_image_info.set_version("src-version"); | 
|  | 316 | old_image_info.set_key("src-key"); | 
|  | 317 | old_image_info.set_build_channel("src-build-channel"); | 
|  | 318 | old_image_info.set_build_version("src-build-version"); | 
|  | 319 | } | 
|  | 320 |  | 
|  | 321 | new_image_info.set_channel("test-channel"); | 
|  | 322 | new_image_info.set_board("test-board"); | 
|  | 323 | new_image_info.set_version("test-version"); | 
|  | 324 | new_image_info.set_key("test-key"); | 
|  | 325 | new_image_info.set_build_channel("test-build-channel"); | 
|  | 326 | new_image_info.set_build_version("test-build-version"); | 
|  | 327 |  | 
|  | 328 | // Make some changes to the A image. | 
|  | 329 | { | 
|  | 330 | string a_mnt; | 
|  | 331 | ScopedLoopMounter b_mounter(state->a_img, &a_mnt, 0); | 
|  | 332 |  | 
|  | 333 | chromeos::Blob hardtocompress; | 
|  | 334 | while (hardtocompress.size() < 3 * kBlockSize) { | 
|  | 335 | hardtocompress.insert(hardtocompress.end(), | 
|  | 336 | std::begin(kRandomString), std::end(kRandomString)); | 
|  | 337 | } | 
|  | 338 | EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress", | 
|  | 339 | a_mnt.c_str()).c_str(), | 
|  | 340 | hardtocompress.data(), | 
|  | 341 | hardtocompress.size())); | 
|  | 342 |  | 
|  | 343 | chromeos::Blob zeros(16 * 1024, 0); | 
|  | 344 | EXPECT_EQ(zeros.size(), | 
|  | 345 | base::WriteFile(base::FilePath(base::StringPrintf( | 
|  | 346 | "%s/move-to-sparse", a_mnt.c_str())), | 
|  | 347 | reinterpret_cast<const char*>(zeros.data()), | 
|  | 348 | zeros.size())); | 
|  | 349 |  | 
|  | 350 | EXPECT_TRUE( | 
|  | 351 | WriteSparseFile(base::StringPrintf("%s/move-from-sparse", | 
|  | 352 | a_mnt.c_str()), 16 * 1024)); | 
|  | 353 |  | 
|  | 354 | EXPECT_EQ(0, | 
|  | 355 | System(base::StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse" | 
|  | 356 | " bs=1 seek=4096 count=1 status=none", | 
|  | 357 | a_mnt.c_str()).c_str())); | 
|  | 358 |  | 
|  | 359 | // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff | 
|  | 360 | // patch fails to zero out the final block. | 
|  | 361 | chromeos::Blob ones(1024 * 1024, 0xff); | 
|  | 362 | EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/ones", | 
|  | 363 | a_mnt.c_str()).c_str(), | 
|  | 364 | ones.data(), | 
|  | 365 | ones.size())); | 
|  | 366 | } | 
|  | 367 |  | 
|  | 368 | if (noop) { | 
|  | 369 | EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img), | 
|  | 370 | base::FilePath(state->b_img))); | 
|  | 371 | old_image_info = new_image_info; | 
|  | 372 | } else { | 
|  | 373 | if (minor_version == kSourceMinorPayloadVersion) { | 
| Sen Jiang | f2af4c6 | 2015-09-30 16:38:09 -0700 | [diff] [blame] | 374 | // Create a result image with image_size bytes of garbage. | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 375 | chromeos::Blob ones(state->image_size, 0xff); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 376 | EXPECT_TRUE(utils::WriteFile(state->result_img.c_str(), | 
|  | 377 | ones.data(), | 
|  | 378 | ones.size())); | 
|  | 379 | EXPECT_EQ(utils::FileSize(state->a_img), | 
|  | 380 | utils::FileSize(state->result_img)); | 
|  | 381 | } | 
|  | 382 |  | 
|  | 383 | test_utils::CreateExtImageAtPath(state->b_img, nullptr); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 384 |  | 
|  | 385 | // Make some changes to the B image. | 
|  | 386 | string b_mnt; | 
|  | 387 | ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0); | 
|  | 388 |  | 
|  | 389 | EXPECT_EQ(0, System(base::StringPrintf("cp %s/hello %s/hello2", | 
|  | 390 | b_mnt.c_str(), | 
|  | 391 | b_mnt.c_str()).c_str())); | 
|  | 392 | EXPECT_EQ(0, System(base::StringPrintf("rm %s/hello", | 
|  | 393 | b_mnt.c_str()).c_str())); | 
|  | 394 | EXPECT_EQ(0, System(base::StringPrintf("mv %s/hello2 %s/hello", | 
|  | 395 | b_mnt.c_str(), | 
|  | 396 | b_mnt.c_str()).c_str())); | 
|  | 397 | EXPECT_EQ(0, System(base::StringPrintf("echo foo > %s/foo", | 
|  | 398 | b_mnt.c_str()).c_str())); | 
|  | 399 | EXPECT_EQ(0, System(base::StringPrintf("touch %s/emptyfile", | 
|  | 400 | b_mnt.c_str()).c_str())); | 
|  | 401 | EXPECT_TRUE(WriteSparseFile(base::StringPrintf("%s/fullsparse", | 
|  | 402 | b_mnt.c_str()), | 
|  | 403 | 1024 * 1024)); | 
|  | 404 |  | 
|  | 405 | EXPECT_TRUE( | 
|  | 406 | WriteSparseFile(base::StringPrintf("%s/move-to-sparse", b_mnt.c_str()), | 
|  | 407 | 16 * 1024)); | 
|  | 408 |  | 
|  | 409 | chromeos::Blob zeros(16 * 1024, 0); | 
|  | 410 | EXPECT_EQ(zeros.size(), | 
|  | 411 | base::WriteFile(base::FilePath(base::StringPrintf( | 
|  | 412 | "%s/move-from-sparse", b_mnt.c_str())), | 
|  | 413 | reinterpret_cast<const char*>(zeros.data()), | 
|  | 414 | zeros.size())); | 
|  | 415 |  | 
|  | 416 | EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero " | 
|  | 417 | "of=%s/move-semi-sparse " | 
|  | 418 | "bs=1 seek=4096 count=1 status=none", | 
|  | 419 | b_mnt.c_str()).c_str())); | 
|  | 420 |  | 
|  | 421 | EXPECT_EQ(0, System(base::StringPrintf("dd if=/dev/zero " | 
|  | 422 | "of=%s/partsparse bs=1 " | 
|  | 423 | "seek=4096 count=1 status=none", | 
|  | 424 | b_mnt.c_str()).c_str())); | 
|  | 425 | EXPECT_EQ(0, System(base::StringPrintf("cp %s/srchardlink0 %s/tmp && " | 
|  | 426 | "mv %s/tmp %s/srchardlink1", | 
|  | 427 | b_mnt.c_str(), | 
|  | 428 | b_mnt.c_str(), | 
|  | 429 | b_mnt.c_str(), | 
|  | 430 | b_mnt.c_str()).c_str())); | 
|  | 431 | EXPECT_EQ(0, System( | 
|  | 432 | base::StringPrintf("rm %s/boguslink && echo foobar > %s/boguslink", | 
|  | 433 | b_mnt.c_str(), b_mnt.c_str()).c_str())); | 
|  | 434 |  | 
|  | 435 | chromeos::Blob hardtocompress; | 
|  | 436 | while (hardtocompress.size() < 3 * kBlockSize) { | 
|  | 437 | hardtocompress.insert(hardtocompress.end(), | 
|  | 438 | std::begin(kRandomString), std::end(kRandomString)); | 
|  | 439 | } | 
|  | 440 | EXPECT_TRUE(utils::WriteFile(base::StringPrintf("%s/hardtocompress", | 
|  | 441 | b_mnt.c_str()).c_str(), | 
|  | 442 | hardtocompress.data(), | 
|  | 443 | hardtocompress.size())); | 
|  | 444 | } | 
|  | 445 |  | 
|  | 446 | string old_kernel; | 
|  | 447 | EXPECT_TRUE(utils::MakeTempFile("old_kernel.XXXXXX", | 
|  | 448 | &state->old_kernel, | 
|  | 449 | nullptr)); | 
|  | 450 |  | 
|  | 451 | string new_kernel; | 
|  | 452 | EXPECT_TRUE(utils::MakeTempFile("new_kernel.XXXXXX", | 
|  | 453 | &state->new_kernel, | 
|  | 454 | nullptr)); | 
|  | 455 |  | 
|  | 456 | string result_kernel; | 
|  | 457 | EXPECT_TRUE(utils::MakeTempFile("result_kernel.XXXXXX", | 
|  | 458 | &state->result_kernel, | 
|  | 459 | nullptr)); | 
|  | 460 |  | 
|  | 461 | state->kernel_size = kDefaultKernelSize; | 
|  | 462 | state->old_kernel_data.resize(kDefaultKernelSize); | 
|  | 463 | state->new_kernel_data.resize(state->old_kernel_data.size()); | 
|  | 464 | state->result_kernel_data.resize(state->old_kernel_data.size()); | 
|  | 465 | test_utils::FillWithData(&state->old_kernel_data); | 
|  | 466 | test_utils::FillWithData(&state->new_kernel_data); | 
|  | 467 | test_utils::FillWithData(&state->result_kernel_data); | 
|  | 468 |  | 
|  | 469 | // change the new kernel data | 
|  | 470 | std::copy(std::begin(kNewData), std::end(kNewData), | 
|  | 471 | state->new_kernel_data.begin()); | 
|  | 472 |  | 
|  | 473 | if (noop) { | 
|  | 474 | state->old_kernel_data = state->new_kernel_data; | 
|  | 475 | } | 
|  | 476 |  | 
|  | 477 | // Write kernels to disk | 
|  | 478 | EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(), | 
|  | 479 | state->old_kernel_data.data(), | 
|  | 480 | state->old_kernel_data.size())); | 
|  | 481 | EXPECT_TRUE(utils::WriteFile(state->new_kernel.c_str(), | 
|  | 482 | state->new_kernel_data.data(), | 
|  | 483 | state->new_kernel_data.size())); | 
|  | 484 | EXPECT_TRUE(utils::WriteFile(state->result_kernel.c_str(), | 
|  | 485 | state->result_kernel_data.data(), | 
|  | 486 | state->result_kernel_data.size())); | 
|  | 487 |  | 
|  | 488 | EXPECT_TRUE(utils::MakeTempFile("delta.XXXXXX", | 
|  | 489 | &state->delta_path, | 
|  | 490 | nullptr)); | 
|  | 491 | LOG(INFO) << "delta path: " << state->delta_path; | 
|  | 492 | { | 
|  | 493 | const string private_key = | 
|  | 494 | signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : ""; | 
|  | 495 |  | 
|  | 496 | PayloadGenerationConfig payload_config; | 
|  | 497 | payload_config.is_delta = !full_rootfs; | 
|  | 498 | payload_config.hard_chunk_size = chunk_size; | 
|  | 499 | payload_config.rootfs_partition_size = kRootFSPartitionSize; | 
|  | 500 | payload_config.major_version = kChromeOSMajorPayloadVersion; | 
|  | 501 | payload_config.minor_version = minor_version; | 
|  | 502 | if (!full_rootfs) { | 
| Sen Jiang | 981eb11 | 2015-08-25 17:03:18 -0700 | [diff] [blame] | 503 | payload_config.source.partitions.emplace_back(kLegacyPartitionNameRoot); | 
|  | 504 | payload_config.source.partitions.emplace_back(kLegacyPartitionNameKernel); | 
|  | 505 | payload_config.source.partitions.front().path = state->a_img; | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 506 | if (!full_kernel) | 
| Sen Jiang | 981eb11 | 2015-08-25 17:03:18 -0700 | [diff] [blame] | 507 | payload_config.source.partitions.back().path = state->old_kernel; | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 508 | payload_config.source.image_info = old_image_info; | 
|  | 509 | EXPECT_TRUE(payload_config.source.LoadImageSize()); | 
| Sen Jiang | 981eb11 | 2015-08-25 17:03:18 -0700 | [diff] [blame] | 510 | for (PartitionConfig& part : payload_config.source.partitions) | 
|  | 511 | EXPECT_TRUE(part.OpenFilesystem()); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 512 | } else { | 
|  | 513 | if (payload_config.hard_chunk_size == -1) | 
|  | 514 | // Use 1 MiB chunk size for the full unittests. | 
|  | 515 | payload_config.hard_chunk_size = 1024 * 1024; | 
|  | 516 | } | 
| Sen Jiang | 981eb11 | 2015-08-25 17:03:18 -0700 | [diff] [blame] | 517 | payload_config.target.partitions.emplace_back(kLegacyPartitionNameRoot); | 
|  | 518 | payload_config.target.partitions.back().path = state->b_img; | 
|  | 519 | payload_config.target.partitions.emplace_back(kLegacyPartitionNameKernel); | 
|  | 520 | payload_config.target.partitions.back().path = state->new_kernel; | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 521 | payload_config.target.image_info = new_image_info; | 
|  | 522 | EXPECT_TRUE(payload_config.target.LoadImageSize()); | 
| Sen Jiang | 981eb11 | 2015-08-25 17:03:18 -0700 | [diff] [blame] | 523 | for (PartitionConfig& part : payload_config.target.partitions) | 
|  | 524 | EXPECT_TRUE(part.OpenFilesystem()); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 525 |  | 
|  | 526 | EXPECT_TRUE(payload_config.Validate()); | 
|  | 527 | EXPECT_TRUE( | 
|  | 528 | GenerateUpdatePayloadFile( | 
|  | 529 | payload_config, | 
|  | 530 | state->delta_path, | 
|  | 531 | private_key, | 
|  | 532 | &state->metadata_size)); | 
|  | 533 | } | 
| Sen Jiang | f2af4c6 | 2015-09-30 16:38:09 -0700 | [diff] [blame] | 534 | // Extend the "partitions" holding the file system a bit. | 
|  | 535 | EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(), | 
|  | 536 | state->image_size + 1024 * 1024))); | 
|  | 537 | EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img)); | 
|  | 538 | EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(), | 
|  | 539 | state->image_size + 1024 * 1024))); | 
|  | 540 | EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img)); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 541 |  | 
|  | 542 | if (signature_test == kSignatureGeneratedPlaceholder || | 
|  | 543 | signature_test == kSignatureGeneratedPlaceholderMismatch) { | 
|  | 544 | int signature_size = GetSignatureSize(kUnittestPrivateKeyPath); | 
|  | 545 | LOG(INFO) << "Inserting placeholder signature."; | 
|  | 546 | ASSERT_TRUE(InsertSignaturePlaceholder(signature_size, state->delta_path, | 
|  | 547 | &state->metadata_size)); | 
|  | 548 |  | 
|  | 549 | if (signature_test == kSignatureGeneratedPlaceholderMismatch) { | 
|  | 550 | signature_size -= 1; | 
|  | 551 | LOG(INFO) << "Inserting mismatched placeholder signature."; | 
|  | 552 | ASSERT_FALSE(InsertSignaturePlaceholder(signature_size, state->delta_path, | 
|  | 553 | &state->metadata_size)); | 
|  | 554 | return; | 
|  | 555 | } | 
|  | 556 | } | 
|  | 557 |  | 
|  | 558 | if (signature_test == kSignatureGenerated || | 
|  | 559 | signature_test == kSignatureGeneratedPlaceholder || | 
|  | 560 | signature_test == kSignatureGeneratedPlaceholderMismatch) { | 
|  | 561 | // Generate the signed payload and update the metadata size in state to | 
|  | 562 | // reflect the new size after adding the signature operation to the | 
|  | 563 | // manifest. | 
|  | 564 | LOG(INFO) << "Signing payload."; | 
|  | 565 | SignGeneratedPayload(state->delta_path, &state->metadata_size); | 
|  | 566 | } else if (signature_test == kSignatureGeneratedShell || | 
|  | 567 | signature_test == kSignatureGeneratedShellBadKey || | 
|  | 568 | signature_test == kSignatureGeneratedShellRotateCl1 || | 
|  | 569 | signature_test == kSignatureGeneratedShellRotateCl2) { | 
|  | 570 | SignGeneratedShellPayload(signature_test, state->delta_path); | 
|  | 571 | } | 
|  | 572 | } | 
|  | 573 |  | 
|  | 574 | static void ApplyDeltaFile(bool full_kernel, bool full_rootfs, bool noop, | 
|  | 575 | SignatureTest signature_test, DeltaState* state, | 
|  | 576 | bool hash_checks_mandatory, | 
|  | 577 | OperationHashTest op_hash_test, | 
|  | 578 | DeltaPerformer** performer, | 
|  | 579 | uint32_t minor_version) { | 
|  | 580 | // Check the metadata. | 
|  | 581 | { | 
|  | 582 | DeltaArchiveManifest manifest; | 
|  | 583 | EXPECT_TRUE(PayloadVerifier::LoadPayload(state->delta_path, | 
|  | 584 | &state->delta, | 
|  | 585 | &manifest, | 
|  | 586 | &state->metadata_size)); | 
|  | 587 | LOG(INFO) << "Metadata size: " << state->metadata_size; | 
|  | 588 |  | 
|  | 589 |  | 
|  | 590 |  | 
|  | 591 | if (signature_test == kSignatureNone) { | 
|  | 592 | EXPECT_FALSE(manifest.has_signatures_offset()); | 
|  | 593 | EXPECT_FALSE(manifest.has_signatures_size()); | 
|  | 594 | } else { | 
|  | 595 | EXPECT_TRUE(manifest.has_signatures_offset()); | 
|  | 596 | EXPECT_TRUE(manifest.has_signatures_size()); | 
|  | 597 | Signatures sigs_message; | 
|  | 598 | EXPECT_TRUE(sigs_message.ParseFromArray( | 
|  | 599 | &state->delta[state->metadata_size + manifest.signatures_offset()], | 
|  | 600 | manifest.signatures_size())); | 
|  | 601 | if (signature_test == kSignatureGeneratedShellRotateCl1 || | 
|  | 602 | signature_test == kSignatureGeneratedShellRotateCl2) | 
|  | 603 | EXPECT_EQ(2, sigs_message.signatures_size()); | 
|  | 604 | else | 
|  | 605 | EXPECT_EQ(1, sigs_message.signatures_size()); | 
|  | 606 | const Signatures_Signature& signature = sigs_message.signatures(0); | 
|  | 607 | EXPECT_EQ(1, signature.version()); | 
|  | 608 |  | 
|  | 609 | uint64_t expected_sig_data_length = 0; | 
|  | 610 | vector<string> key_paths{kUnittestPrivateKeyPath}; | 
|  | 611 | if (signature_test == kSignatureGeneratedShellRotateCl1 || | 
|  | 612 | signature_test == kSignatureGeneratedShellRotateCl2) { | 
|  | 613 | key_paths.push_back(kUnittestPrivateKey2Path); | 
|  | 614 | } | 
|  | 615 | EXPECT_TRUE(PayloadSigner::SignatureBlobLength( | 
|  | 616 | key_paths, | 
|  | 617 | &expected_sig_data_length)); | 
|  | 618 | EXPECT_EQ(expected_sig_data_length, manifest.signatures_size()); | 
|  | 619 | EXPECT_FALSE(signature.data().empty()); | 
|  | 620 | } | 
|  | 621 |  | 
|  | 622 | if (noop) { | 
|  | 623 | EXPECT_EQ(0, manifest.install_operations_size()); | 
|  | 624 | EXPECT_EQ(1, manifest.kernel_install_operations_size()); | 
|  | 625 | } | 
|  | 626 |  | 
|  | 627 | if (full_kernel) { | 
|  | 628 | EXPECT_FALSE(manifest.has_old_kernel_info()); | 
|  | 629 | } else { | 
|  | 630 | EXPECT_EQ(state->old_kernel_data.size(), | 
|  | 631 | manifest.old_kernel_info().size()); | 
|  | 632 | EXPECT_FALSE(manifest.old_kernel_info().hash().empty()); | 
|  | 633 | } | 
|  | 634 |  | 
|  | 635 | EXPECT_EQ(manifest.new_image_info().channel(), "test-channel"); | 
|  | 636 | EXPECT_EQ(manifest.new_image_info().board(), "test-board"); | 
|  | 637 | EXPECT_EQ(manifest.new_image_info().version(), "test-version"); | 
|  | 638 | EXPECT_EQ(manifest.new_image_info().key(), "test-key"); | 
|  | 639 | EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel"); | 
|  | 640 | EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version"); | 
|  | 641 |  | 
|  | 642 | if (!full_rootfs) { | 
|  | 643 | if (noop) { | 
|  | 644 | EXPECT_EQ(manifest.old_image_info().channel(), "test-channel"); | 
|  | 645 | EXPECT_EQ(manifest.old_image_info().board(), "test-board"); | 
|  | 646 | EXPECT_EQ(manifest.old_image_info().version(), "test-version"); | 
|  | 647 | EXPECT_EQ(manifest.old_image_info().key(), "test-key"); | 
|  | 648 | EXPECT_EQ(manifest.old_image_info().build_channel(), | 
|  | 649 | "test-build-channel"); | 
|  | 650 | EXPECT_EQ(manifest.old_image_info().build_version(), | 
|  | 651 | "test-build-version"); | 
|  | 652 | } else { | 
|  | 653 | EXPECT_EQ(manifest.old_image_info().channel(), "src-channel"); | 
|  | 654 | EXPECT_EQ(manifest.old_image_info().board(), "src-board"); | 
|  | 655 | EXPECT_EQ(manifest.old_image_info().version(), "src-version"); | 
|  | 656 | EXPECT_EQ(manifest.old_image_info().key(), "src-key"); | 
|  | 657 | EXPECT_EQ(manifest.old_image_info().build_channel(), | 
|  | 658 | "src-build-channel"); | 
|  | 659 | EXPECT_EQ(manifest.old_image_info().build_version(), | 
|  | 660 | "src-build-version"); | 
|  | 661 | } | 
|  | 662 | } | 
|  | 663 |  | 
|  | 664 |  | 
|  | 665 | if (full_rootfs) { | 
|  | 666 | EXPECT_FALSE(manifest.has_old_rootfs_info()); | 
|  | 667 | EXPECT_FALSE(manifest.has_old_image_info()); | 
|  | 668 | EXPECT_TRUE(manifest.has_new_image_info()); | 
|  | 669 | } else { | 
|  | 670 | EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size()); | 
|  | 671 | EXPECT_FALSE(manifest.old_rootfs_info().hash().empty()); | 
|  | 672 | } | 
|  | 673 |  | 
|  | 674 | EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size()); | 
|  | 675 | EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size()); | 
|  | 676 |  | 
|  | 677 | EXPECT_FALSE(manifest.new_kernel_info().hash().empty()); | 
|  | 678 | EXPECT_FALSE(manifest.new_rootfs_info().hash().empty()); | 
|  | 679 | } | 
|  | 680 |  | 
|  | 681 | MockPrefs prefs; | 
|  | 682 | EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, | 
|  | 683 | state->metadata_size)).WillOnce(Return(true)); | 
|  | 684 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _)) | 
|  | 685 | .WillRepeatedly(Return(true)); | 
|  | 686 | EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _)) | 
|  | 687 | .WillOnce(Return(false)); | 
|  | 688 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _)) | 
|  | 689 | .WillRepeatedly(Return(true)); | 
|  | 690 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataLength, _)) | 
|  | 691 | .WillRepeatedly(Return(true)); | 
|  | 692 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _)) | 
|  | 693 | .WillRepeatedly(Return(true)); | 
|  | 694 | if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) { | 
|  | 695 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _)) | 
|  | 696 | .WillOnce(Return(true)); | 
|  | 697 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _)) | 
|  | 698 | .WillOnce(Return(true)); | 
|  | 699 | } | 
|  | 700 |  | 
|  | 701 | // Update the A image in place. | 
|  | 702 | InstallPlan install_plan; | 
|  | 703 | install_plan.hash_checks_mandatory = hash_checks_mandatory; | 
|  | 704 | install_plan.metadata_size = state->metadata_size; | 
|  | 705 | install_plan.is_full_update = full_kernel && full_rootfs; | 
|  | 706 | install_plan.source_path = state->a_img.c_str(); | 
|  | 707 | install_plan.kernel_source_path = state->old_kernel.c_str(); | 
|  | 708 |  | 
|  | 709 | LOG(INFO) << "Setting payload metadata size in Omaha  = " | 
|  | 710 | << state->metadata_size; | 
|  | 711 | ASSERT_TRUE(PayloadSigner::GetMetadataSignature( | 
|  | 712 | state->delta.data(), | 
|  | 713 | state->metadata_size, | 
|  | 714 | kUnittestPrivateKeyPath, | 
|  | 715 | &install_plan.metadata_signature)); | 
|  | 716 | EXPECT_FALSE(install_plan.metadata_signature.empty()); | 
|  | 717 |  | 
|  | 718 | *performer = new DeltaPerformer(&prefs, | 
|  | 719 | &state->fake_system_state, | 
|  | 720 | &install_plan); | 
|  | 721 | EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath)); | 
|  | 722 | (*performer)->set_public_key_path(kUnittestPublicKeyPath); | 
|  | 723 | DeltaPerformerIntegrationTest::SetSupportedVersion(*performer, minor_version); | 
|  | 724 |  | 
|  | 725 | EXPECT_EQ(state->image_size, | 
|  | 726 | OmahaHashCalculator::RawHashOfFile( | 
|  | 727 | state->a_img, | 
|  | 728 | state->image_size, | 
|  | 729 | &install_plan.source_rootfs_hash)); | 
|  | 730 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData( | 
|  | 731 | state->old_kernel_data, | 
|  | 732 | &install_plan.source_kernel_hash)); | 
|  | 733 |  | 
|  | 734 | // With minor version 2, we want the target to be the new image, result_img, | 
|  | 735 | // but with version 1, we want to update A in place. | 
|  | 736 | if (minor_version == kSourceMinorPayloadVersion) { | 
|  | 737 | EXPECT_EQ(0, (*performer)->Open(state->result_img.c_str(), 0, 0)); | 
|  | 738 | EXPECT_TRUE((*performer)->OpenKernel(state->result_kernel.c_str())); | 
|  | 739 | } else { | 
|  | 740 | EXPECT_EQ(0, (*performer)->Open(state->a_img.c_str(), 0, 0)); | 
|  | 741 | EXPECT_TRUE((*performer)->OpenKernel(state->old_kernel.c_str())); | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 |  | 
|  | 745 | ErrorCode expected_error, actual_error; | 
|  | 746 | bool continue_writing; | 
|  | 747 | switch (op_hash_test) { | 
|  | 748 | case kInvalidOperationData: { | 
|  | 749 | // Muck with some random offset post the metadata size so that | 
|  | 750 | // some operation hash will result in a mismatch. | 
|  | 751 | int some_offset = state->metadata_size + 300; | 
|  | 752 | LOG(INFO) << "Tampered value at offset: " << some_offset; | 
|  | 753 | state->delta[some_offset]++; | 
|  | 754 | expected_error = ErrorCode::kDownloadOperationHashMismatch; | 
|  | 755 | continue_writing = false; | 
|  | 756 | break; | 
|  | 757 | } | 
|  | 758 |  | 
|  | 759 | case kValidOperationData: | 
|  | 760 | default: | 
|  | 761 | // no change. | 
|  | 762 | expected_error = ErrorCode::kSuccess; | 
|  | 763 | continue_writing = true; | 
|  | 764 | break; | 
|  | 765 | } | 
|  | 766 |  | 
|  | 767 | // Write at some number of bytes per operation. Arbitrarily chose 5. | 
|  | 768 | const size_t kBytesPerWrite = 5; | 
|  | 769 | for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) { | 
|  | 770 | size_t count = std::min(state->delta.size() - i, kBytesPerWrite); | 
|  | 771 | bool write_succeeded = ((*performer)->Write(&state->delta[i], | 
|  | 772 | count, | 
|  | 773 | &actual_error)); | 
|  | 774 | // Normally write_succeeded should be true every time and | 
|  | 775 | // actual_error should be ErrorCode::kSuccess. If so, continue the loop. | 
|  | 776 | // But if we seeded an operation hash error above, then write_succeeded | 
|  | 777 | // will be false. The failure may happen at any operation n. So, all | 
|  | 778 | // Writes until n-1 should succeed and the nth operation will fail with | 
|  | 779 | // actual_error. In this case, we should bail out of the loop because | 
|  | 780 | // we cannot proceed applying the delta. | 
|  | 781 | if (!write_succeeded) { | 
|  | 782 | LOG(INFO) << "Write failed. Checking if it failed with expected error"; | 
|  | 783 | EXPECT_EQ(expected_error, actual_error); | 
|  | 784 | if (!continue_writing) { | 
|  | 785 | LOG(INFO) << "Cannot continue writing. Bailing out."; | 
|  | 786 | break; | 
|  | 787 | } | 
|  | 788 | } | 
|  | 789 |  | 
|  | 790 | EXPECT_EQ(ErrorCode::kSuccess, actual_error); | 
|  | 791 | } | 
|  | 792 |  | 
|  | 793 | // If we had continued all the way through, Close should succeed. | 
|  | 794 | // Otherwise, it should fail. Check appropriately. | 
|  | 795 | bool close_result = (*performer)->Close(); | 
|  | 796 | if (continue_writing) | 
|  | 797 | EXPECT_EQ(0, close_result); | 
|  | 798 | else | 
|  | 799 | EXPECT_LE(0, close_result); | 
|  | 800 | } | 
|  | 801 |  | 
|  | 802 | void VerifyPayloadResult(DeltaPerformer* performer, | 
|  | 803 | DeltaState* state, | 
|  | 804 | ErrorCode expected_result, | 
|  | 805 | uint32_t minor_version) { | 
|  | 806 | if (!performer) { | 
|  | 807 | EXPECT_TRUE(!"Skipping payload verification since performer is null."); | 
|  | 808 | return; | 
|  | 809 | } | 
|  | 810 |  | 
|  | 811 | int expected_times = (expected_result == ErrorCode::kSuccess) ? 1 : 0; | 
|  | 812 | EXPECT_CALL(*(state->fake_system_state.mock_payload_state()), | 
|  | 813 | DownloadComplete()).Times(expected_times); | 
|  | 814 |  | 
|  | 815 | LOG(INFO) << "Verifying payload for expected result " | 
|  | 816 | << expected_result; | 
|  | 817 | EXPECT_EQ(expected_result, performer->VerifyPayload( | 
|  | 818 | OmahaHashCalculator::OmahaHashOfData(state->delta), | 
|  | 819 | state->delta.size())); | 
|  | 820 | LOG(INFO) << "Verified payload."; | 
|  | 821 |  | 
|  | 822 | if (expected_result != ErrorCode::kSuccess) { | 
|  | 823 | // no need to verify new partition if VerifyPayload failed. | 
|  | 824 | return; | 
|  | 825 | } | 
|  | 826 |  | 
|  | 827 | chromeos::Blob updated_kernel_partition; | 
|  | 828 | if (minor_version == kSourceMinorPayloadVersion) { | 
|  | 829 | CompareFilesByBlock(state->result_kernel, state->new_kernel, | 
|  | 830 | state->kernel_size); | 
|  | 831 | CompareFilesByBlock(state->result_img, state->b_img, | 
|  | 832 | state->image_size); | 
|  | 833 | EXPECT_TRUE(utils::ReadFile(state->result_kernel, | 
|  | 834 | &updated_kernel_partition)); | 
|  | 835 | } else { | 
|  | 836 | CompareFilesByBlock(state->old_kernel, state->new_kernel, | 
|  | 837 | state->kernel_size); | 
|  | 838 | CompareFilesByBlock(state->a_img, state->b_img, | 
|  | 839 | state->image_size); | 
|  | 840 | EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition)); | 
|  | 841 | } | 
|  | 842 |  | 
|  | 843 | ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData)); | 
|  | 844 | EXPECT_TRUE(std::equal(std::begin(kNewData), std::end(kNewData), | 
|  | 845 | updated_kernel_partition.begin())); | 
|  | 846 |  | 
|  | 847 | uint64_t new_kernel_size; | 
|  | 848 | chromeos::Blob new_kernel_hash; | 
|  | 849 | uint64_t new_rootfs_size; | 
|  | 850 | chromeos::Blob new_rootfs_hash; | 
|  | 851 | EXPECT_TRUE(performer->GetNewPartitionInfo(&new_kernel_size, | 
|  | 852 | &new_kernel_hash, | 
|  | 853 | &new_rootfs_size, | 
|  | 854 | &new_rootfs_hash)); | 
|  | 855 | EXPECT_EQ(kDefaultKernelSize, new_kernel_size); | 
|  | 856 | chromeos::Blob expected_new_kernel_hash; | 
|  | 857 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(state->new_kernel_data, | 
|  | 858 | &expected_new_kernel_hash)); | 
|  | 859 | EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash); | 
|  | 860 | EXPECT_EQ(state->image_size, new_rootfs_size); | 
|  | 861 | chromeos::Blob expected_new_rootfs_hash; | 
|  | 862 | EXPECT_EQ(state->image_size, | 
|  | 863 | OmahaHashCalculator::RawHashOfFile(state->b_img, | 
|  | 864 | state->image_size, | 
|  | 865 | &expected_new_rootfs_hash)); | 
|  | 866 | EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash); | 
|  | 867 | } | 
|  | 868 |  | 
|  | 869 | void VerifyPayload(DeltaPerformer* performer, | 
|  | 870 | DeltaState* state, | 
|  | 871 | SignatureTest signature_test, | 
|  | 872 | uint32_t minor_version) { | 
|  | 873 | ErrorCode expected_result = ErrorCode::kSuccess; | 
|  | 874 | switch (signature_test) { | 
|  | 875 | case kSignatureNone: | 
|  | 876 | expected_result = ErrorCode::kSignedDeltaPayloadExpectedError; | 
|  | 877 | break; | 
|  | 878 | case kSignatureGeneratedShellBadKey: | 
|  | 879 | expected_result = ErrorCode::kDownloadPayloadPubKeyVerificationError; | 
|  | 880 | break; | 
|  | 881 | default: break;  // appease gcc | 
|  | 882 | } | 
|  | 883 |  | 
|  | 884 | VerifyPayloadResult(performer, state, expected_result, minor_version); | 
|  | 885 | } | 
|  | 886 |  | 
|  | 887 | void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop, | 
|  | 888 | ssize_t chunk_size, | 
|  | 889 | SignatureTest signature_test, | 
|  | 890 | bool hash_checks_mandatory, uint32_t minor_version) { | 
|  | 891 | DeltaState state; | 
|  | 892 | DeltaPerformer *performer = nullptr; | 
|  | 893 | GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size, | 
|  | 894 | signature_test, &state, minor_version); | 
|  | 895 |  | 
|  | 896 | ScopedPathUnlinker a_img_unlinker(state.a_img); | 
|  | 897 | ScopedPathUnlinker b_img_unlinker(state.b_img); | 
|  | 898 | ScopedPathUnlinker new_img_unlinker(state.result_img); | 
|  | 899 | ScopedPathUnlinker delta_unlinker(state.delta_path); | 
|  | 900 | ScopedPathUnlinker old_kernel_unlinker(state.old_kernel); | 
|  | 901 | ScopedPathUnlinker new_kernel_unlinker(state.new_kernel); | 
|  | 902 | ScopedPathUnlinker result_kernel_unlinker(state.result_kernel); | 
|  | 903 | ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test, | 
|  | 904 | &state, hash_checks_mandatory, kValidOperationData, | 
|  | 905 | &performer, minor_version); | 
|  | 906 | VerifyPayload(performer, &state, signature_test, minor_version); | 
|  | 907 | delete performer; | 
|  | 908 | } | 
|  | 909 |  | 
|  | 910 | void DoOperationHashMismatchTest(OperationHashTest op_hash_test, | 
|  | 911 | bool hash_checks_mandatory) { | 
|  | 912 | DeltaState state; | 
| Alex Deymo | cbf0989 | 2015-09-11 16:13:16 -0700 | [diff] [blame] | 913 | uint64_t minor_version = kFullPayloadMinorVersion; | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 914 | GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state, | 
|  | 915 | minor_version); | 
|  | 916 | ScopedPathUnlinker a_img_unlinker(state.a_img); | 
|  | 917 | ScopedPathUnlinker b_img_unlinker(state.b_img); | 
|  | 918 | ScopedPathUnlinker delta_unlinker(state.delta_path); | 
|  | 919 | ScopedPathUnlinker old_kernel_unlinker(state.old_kernel); | 
|  | 920 | ScopedPathUnlinker new_kernel_unlinker(state.new_kernel); | 
|  | 921 | DeltaPerformer *performer = nullptr; | 
|  | 922 | ApplyDeltaFile(true, true, false, kSignatureGenerated, &state, | 
|  | 923 | hash_checks_mandatory, op_hash_test, &performer, | 
|  | 924 | minor_version); | 
|  | 925 | delete performer; | 
|  | 926 | } | 
|  | 927 |  | 
|  | 928 |  | 
|  | 929 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) { | 
|  | 930 | DoSmallImageTest(false, false, false, -1, kSignatureGenerator, | 
|  | 931 | false, kInPlaceMinorPayloadVersion); | 
|  | 932 | } | 
|  | 933 |  | 
|  | 934 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderTest) { | 
|  | 935 | DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder, | 
|  | 936 | false, kInPlaceMinorPayloadVersion); | 
|  | 937 | } | 
|  | 938 |  | 
|  | 939 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) { | 
|  | 940 | DeltaState state; | 
|  | 941 | GenerateDeltaFile(false, false, false, -1, | 
|  | 942 | kSignatureGeneratedPlaceholderMismatch, &state, | 
|  | 943 | kInPlaceMinorPayloadVersion); | 
|  | 944 | } | 
|  | 945 |  | 
|  | 946 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) { | 
|  | 947 | DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator, | 
|  | 948 | false, kInPlaceMinorPayloadVersion); | 
|  | 949 | } | 
|  | 950 |  | 
|  | 951 | TEST(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) { | 
|  | 952 | DoSmallImageTest(true, false, false, -1, kSignatureGenerator, | 
|  | 953 | false, kInPlaceMinorPayloadVersion); | 
|  | 954 | } | 
|  | 955 |  | 
|  | 956 | TEST(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) { | 
|  | 957 | DoSmallImageTest(true, true, false, -1, kSignatureGenerator, | 
| Alex Deymo | cbf0989 | 2015-09-11 16:13:16 -0700 | [diff] [blame] | 958 | true, kFullPayloadMinorVersion); | 
| Sen Jiang | a4365d6 | 2015-09-25 10:52:25 -0700 | [diff] [blame] | 959 | } | 
|  | 960 |  | 
|  | 961 | TEST(DeltaPerformerIntegrationTest, RunAsRootNoopSmallImageTest) { | 
|  | 962 | DoSmallImageTest(false, false, true, -1, kSignatureGenerator, | 
|  | 963 | false, kInPlaceMinorPayloadVersion); | 
|  | 964 | } | 
|  | 965 |  | 
|  | 966 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) { | 
|  | 967 | DoSmallImageTest(false, false, false, -1, kSignatureNone, | 
|  | 968 | false, kInPlaceMinorPayloadVersion); | 
|  | 969 | } | 
|  | 970 |  | 
|  | 971 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) { | 
|  | 972 | DoSmallImageTest(false, false, false, -1, kSignatureGenerated, | 
|  | 973 | true, kInPlaceMinorPayloadVersion); | 
|  | 974 | } | 
|  | 975 |  | 
|  | 976 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellTest) { | 
|  | 977 | DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell, | 
|  | 978 | false, kInPlaceMinorPayloadVersion); | 
|  | 979 | } | 
|  | 980 |  | 
|  | 981 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) { | 
|  | 982 | DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey, | 
|  | 983 | false, kInPlaceMinorPayloadVersion); | 
|  | 984 | } | 
|  | 985 |  | 
|  | 986 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) { | 
|  | 987 | DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1, | 
|  | 988 | false, kInPlaceMinorPayloadVersion); | 
|  | 989 | } | 
|  | 990 |  | 
|  | 991 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) { | 
|  | 992 | DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2, | 
|  | 993 | false, kInPlaceMinorPayloadVersion); | 
|  | 994 | } | 
|  | 995 |  | 
|  | 996 | TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) { | 
|  | 997 | DoSmallImageTest(false, false, false, -1, kSignatureGenerator, | 
|  | 998 | false, kSourceMinorPayloadVersion); | 
|  | 999 | } | 
|  | 1000 |  | 
|  | 1001 | TEST(DeltaPerformerIntegrationTest, RunAsRootMandatoryOperationHashMismatchTest) { | 
|  | 1002 | DoOperationHashMismatchTest(kInvalidOperationData, true); | 
|  | 1003 | } | 
|  | 1004 |  | 
|  | 1005 | }  // namespace chromeos_update_engine |