Mike Frysinger | 8155d08 | 2012-04-06 15:23:18 -0400 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include <sys/mount.h> |
| 6 | #include <inttypes.h> |
| 7 | |
| 8 | #include <algorithm> |
| 9 | #include <string> |
| 10 | #include <vector> |
| 11 | |
Darin Petkov | 9fa7ec5 | 2010-10-18 11:45:23 -0700 | [diff] [blame] | 12 | #include <base/file_util.h> |
Chris Masone | d903c3b | 2011-05-12 15:35:46 -0700 | [diff] [blame] | 13 | #include <base/memory/scoped_ptr.h> |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 14 | #include <base/string_util.h> |
Mike Frysinger | 8155d08 | 2012-04-06 15:23:18 -0400 | [diff] [blame] | 15 | #include <base/stringprintf.h> |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 16 | #include <google/protobuf/repeated_field.h> |
| 17 | #include <gtest/gtest.h> |
| 18 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 19 | #include "update_engine/delta_diff_generator.h" |
| 20 | #include "update_engine/delta_performer.h" |
Andrew de los Reyes | 353777c | 2010-10-08 10:34:30 -0700 | [diff] [blame] | 21 | #include "update_engine/extent_ranges.h" |
Darin Petkov | 7a22d79 | 2010-11-08 14:10:00 -0800 | [diff] [blame] | 22 | #include "update_engine/full_update_generator.h" |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 23 | #include "update_engine/graph_types.h" |
Jay Srinivasan | f057205 | 2012-10-23 18:12:56 -0700 | [diff] [blame] | 24 | #include "update_engine/mock_system_state.h" |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 25 | #include "update_engine/payload_signer.h" |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 26 | #include "update_engine/prefs_mock.h" |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 27 | #include "update_engine/test_utils.h" |
| 28 | #include "update_engine/update_metadata.pb.h" |
| 29 | #include "update_engine/utils.h" |
| 30 | |
| 31 | namespace chromeos_update_engine { |
| 32 | |
| 33 | using std::min; |
| 34 | using std::string; |
| 35 | using std::vector; |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 36 | using testing::_; |
| 37 | using testing::Return; |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 38 | |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 39 | extern const char* kUnittestPrivateKeyPath; |
Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 40 | extern const char* kUnittestPublicKeyPath; |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 41 | extern const char* kUnittestPrivateKey2Path; |
| 42 | extern const char* kUnittestPublicKey2Path; |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 43 | |
Andrew de los Reyes | 27f7d37 | 2010-10-07 11:26:07 -0700 | [diff] [blame] | 44 | namespace { |
Andrew de los Reyes | 29da8aa | 2011-02-15 13:34:57 -0800 | [diff] [blame] | 45 | const size_t kBlockSize = 4096; |
Andrew de los Reyes | 27f7d37 | 2010-10-07 11:26:07 -0700 | [diff] [blame] | 46 | } // namespace {} |
| 47 | |
| 48 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 49 | class DeltaPerformerTest : public ::testing::Test { }; |
| 50 | |
| 51 | TEST(DeltaPerformerTest, ExtentsToByteStringTest) { |
| 52 | uint64_t test[] = {1, 1, 4, 2, kSparseHole, 1, 0, 1}; |
| 53 | COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven); |
| 54 | const uint64_t block_size = 4096; |
| 55 | const uint64_t file_length = 5 * block_size - 13; |
| 56 | |
| 57 | google::protobuf::RepeatedPtrField<Extent> extents; |
| 58 | for (size_t i = 0; i < arraysize(test); i += 2) { |
| 59 | Extent* extent = extents.Add(); |
| 60 | extent->set_start_block(test[i]); |
| 61 | extent->set_num_blocks(test[i + 1]); |
| 62 | } |
| 63 | |
| 64 | string expected_output = "4096:4096,16384:8192,-1:4096,0:4083"; |
| 65 | string actual_output; |
| 66 | EXPECT_TRUE(DeltaPerformer::ExtentsToBsdiffPositionsString(extents, |
| 67 | block_size, |
| 68 | file_length, |
| 69 | &actual_output)); |
| 70 | EXPECT_EQ(expected_output, actual_output); |
| 71 | } |
| 72 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 73 | void CompareFilesByBlock(const string& a_file, const string& b_file) { |
| 74 | vector<char> a_data, b_data; |
Andrew de los Reyes | 3270f74 | 2010-07-15 22:28:14 -0700 | [diff] [blame] | 75 | EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file; |
| 76 | EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file; |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 77 | |
| 78 | EXPECT_EQ(a_data.size(), b_data.size()); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 79 | EXPECT_EQ(0, a_data.size() % kBlockSize); |
| 80 | for (size_t i = 0; i < a_data.size(); i += kBlockSize) { |
| 81 | EXPECT_EQ(0, i % kBlockSize); |
| 82 | vector<char> a_sub(&a_data[i], &a_data[i + kBlockSize]); |
| 83 | vector<char> b_sub(&b_data[i], &b_data[i + kBlockSize]); |
| 84 | EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs"; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | namespace { |
| 89 | bool WriteSparseFile(const string& path, off_t size) { |
| 90 | int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644); |
| 91 | TEST_AND_RETURN_FALSE_ERRNO(fd >= 0); |
| 92 | ScopedFdCloser fd_closer(&fd); |
| 93 | off_t rc = lseek(fd, size + 1, SEEK_SET); |
| 94 | TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1)); |
| 95 | int return_code = ftruncate(fd, size); |
| 96 | TEST_AND_RETURN_FALSE_ERRNO(return_code == 0); |
| 97 | return true; |
| 98 | } |
Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 99 | } // namespace {} |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 100 | |
Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 101 | namespace { |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 102 | enum SignatureTest { |
| 103 | kSignatureNone, // No payload signing. |
| 104 | kSignatureGenerator, // Sign the payload at generation time. |
| 105 | kSignatureGenerated, // Sign the payload after it's generated. |
| 106 | kSignatureGeneratedShell, // Sign the generated payload through shell cmds. |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 107 | kSignatureGeneratedShellBadKey, // Sign with a bad key through shell cmds. |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 108 | kSignatureGeneratedShellRotateCl1, // Rotate key, test client v1 |
| 109 | kSignatureGeneratedShellRotateCl2, // Rotate key, test client v2 |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 110 | }; |
| 111 | |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 112 | size_t GetSignatureSize(const string& private_key_path) { |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 113 | const vector<char> data(1, 'x'); |
| 114 | vector<char> hash; |
| 115 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(data, &hash)); |
| 116 | vector<char> signature; |
| 117 | EXPECT_TRUE(PayloadSigner::SignHash(hash, |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 118 | private_key_path, |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 119 | &signature)); |
| 120 | return signature.size(); |
| 121 | } |
| 122 | |
| 123 | void SignGeneratedPayload(const string& payload_path) { |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 124 | int signature_size = GetSignatureSize(kUnittestPrivateKeyPath); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 125 | vector<char> hash; |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 126 | ASSERT_TRUE(PayloadSigner::HashPayloadForSigning( |
| 127 | payload_path, |
| 128 | vector<int>(1, signature_size), |
| 129 | &hash)); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 130 | vector<char> signature; |
| 131 | ASSERT_TRUE(PayloadSigner::SignHash(hash, |
| 132 | kUnittestPrivateKeyPath, |
| 133 | &signature)); |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 134 | ASSERT_TRUE(PayloadSigner::AddSignatureToPayload( |
| 135 | payload_path, |
| 136 | vector<vector<char> >(1, signature), |
| 137 | payload_path)); |
| 138 | EXPECT_TRUE(PayloadSigner::VerifySignedPayload( |
| 139 | payload_path, |
| 140 | kUnittestPublicKeyPath, |
| 141 | kSignatureMessageOriginalVersion)); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 142 | } |
| 143 | |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 144 | void SignGeneratedShellPayload(SignatureTest signature_test, |
| 145 | const string& payload_path) { |
| 146 | string private_key_path = kUnittestPrivateKeyPath; |
| 147 | if (signature_test == kSignatureGeneratedShellBadKey) { |
| 148 | ASSERT_TRUE(utils::MakeTempFile("/tmp/key.XXXXXX", |
| 149 | &private_key_path, |
| 150 | NULL)); |
| 151 | } else { |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 152 | ASSERT_TRUE(signature_test == kSignatureGeneratedShell || |
| 153 | signature_test == kSignatureGeneratedShellRotateCl1 || |
| 154 | signature_test == kSignatureGeneratedShellRotateCl2); |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 155 | } |
| 156 | ScopedPathUnlinker key_unlinker(private_key_path); |
| 157 | key_unlinker.set_should_remove(signature_test == |
| 158 | kSignatureGeneratedShellBadKey); |
| 159 | // Generates a new private key that will not match the public key. |
| 160 | if (signature_test == kSignatureGeneratedShellBadKey) { |
| 161 | LOG(INFO) << "Generating a mismatched private key."; |
| 162 | ASSERT_EQ(0, |
| 163 | System(StringPrintf( |
Mike Frysinger | 2149be4 | 2012-03-12 19:23:47 -0400 | [diff] [blame] | 164 | "openssl genrsa -out %s 2048", |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 165 | private_key_path.c_str()))); |
| 166 | } |
| 167 | int signature_size = GetSignatureSize(private_key_path); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 168 | string hash_file; |
| 169 | ASSERT_TRUE(utils::MakeTempFile("/tmp/hash.XXXXXX", &hash_file, NULL)); |
| 170 | ScopedPathUnlinker hash_unlinker(hash_file); |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 171 | string signature_size_string; |
| 172 | if (signature_test == kSignatureGeneratedShellRotateCl1 || |
| 173 | signature_test == kSignatureGeneratedShellRotateCl2) |
| 174 | signature_size_string = StringPrintf("%d:%d", |
| 175 | signature_size, signature_size); |
| 176 | else |
| 177 | signature_size_string = StringPrintf("%d", signature_size); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 178 | ASSERT_EQ(0, |
| 179 | System(StringPrintf( |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 180 | "./delta_generator -in_file %s -signature_size %s " |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 181 | "-out_hash_file %s", |
| 182 | payload_path.c_str(), |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 183 | signature_size_string.c_str(), |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 184 | hash_file.c_str()))); |
| 185 | |
Andrew de los Reyes | bdfaaf0 | 2011-03-30 10:35:12 -0700 | [diff] [blame] | 186 | // Pad the hash |
| 187 | vector<char> hash; |
| 188 | ASSERT_TRUE(utils::ReadFile(hash_file, &hash)); |
| 189 | ASSERT_TRUE(PayloadSigner::PadRSA2048SHA256Hash(&hash)); |
| 190 | ASSERT_TRUE(WriteFileVector(hash_file, hash)); |
| 191 | |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 192 | string sig_file; |
| 193 | ASSERT_TRUE(utils::MakeTempFile("/tmp/signature.XXXXXX", &sig_file, NULL)); |
| 194 | ScopedPathUnlinker sig_unlinker(sig_file); |
| 195 | ASSERT_EQ(0, |
| 196 | System(StringPrintf( |
Mike Frysinger | 2149be4 | 2012-03-12 19:23:47 -0400 | [diff] [blame] | 197 | "openssl rsautl -raw -sign -inkey %s -in %s -out %s", |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 198 | private_key_path.c_str(), |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 199 | hash_file.c_str(), |
| 200 | sig_file.c_str()))); |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 201 | string sig_file2; |
| 202 | ASSERT_TRUE(utils::MakeTempFile("/tmp/signature.XXXXXX", &sig_file2, NULL)); |
| 203 | ScopedPathUnlinker sig2_unlinker(sig_file2); |
| 204 | if (signature_test == kSignatureGeneratedShellRotateCl1 || |
| 205 | signature_test == kSignatureGeneratedShellRotateCl2) { |
| 206 | ASSERT_EQ(0, |
| 207 | System(StringPrintf( |
Mike Frysinger | 2149be4 | 2012-03-12 19:23:47 -0400 | [diff] [blame] | 208 | "openssl rsautl -raw -sign -inkey %s -in %s -out %s", |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 209 | kUnittestPrivateKey2Path, |
| 210 | hash_file.c_str(), |
| 211 | sig_file2.c_str()))); |
| 212 | // Append second sig file to first path |
| 213 | sig_file += ":" + sig_file2; |
| 214 | } |
| 215 | |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 216 | ASSERT_EQ(0, |
| 217 | System(StringPrintf( |
| 218 | "./delta_generator -in_file %s -signature_file %s " |
| 219 | "-out_file %s", |
| 220 | payload_path.c_str(), |
| 221 | sig_file.c_str(), |
| 222 | payload_path.c_str()))); |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 223 | int verify_result = |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 224 | System(StringPrintf( |
| 225 | "./delta_generator -in_file %s -public_key %s -public_key_version %d", |
| 226 | payload_path.c_str(), |
| 227 | signature_test == kSignatureGeneratedShellRotateCl2 ? |
| 228 | kUnittestPublicKey2Path : kUnittestPublicKeyPath, |
| 229 | signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1)); |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 230 | if (signature_test == kSignatureGeneratedShellBadKey) { |
| 231 | ASSERT_NE(0, verify_result); |
| 232 | } else { |
| 233 | ASSERT_EQ(0, verify_result); |
| 234 | } |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 235 | } |
| 236 | |
Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 237 | void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop, |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 238 | SignatureTest signature_test) { |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 239 | string a_img, b_img; |
| 240 | EXPECT_TRUE(utils::MakeTempFile("/tmp/a_img.XXXXXX", &a_img, NULL)); |
| 241 | ScopedPathUnlinker a_img_unlinker(a_img); |
| 242 | EXPECT_TRUE(utils::MakeTempFile("/tmp/b_img.XXXXXX", &b_img, NULL)); |
| 243 | ScopedPathUnlinker b_img_unlinker(b_img); |
| 244 | |
| 245 | CreateExtImageAtPath(a_img, NULL); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 246 | |
Darin Petkov | 7ea3233 | 2010-10-13 10:46:11 -0700 | [diff] [blame] | 247 | int image_size = static_cast<int>(utils::FileSize(a_img)); |
| 248 | |
| 249 | // Extend the "partitions" holding the file system a bit. |
| 250 | EXPECT_EQ(0, System(base::StringPrintf( |
| 251 | "dd if=/dev/zero of=%s seek=%d bs=1 count=1", |
| 252 | a_img.c_str(), |
| 253 | image_size + 1024 * 1024 - 1))); |
Darin Petkov | 7ea3233 | 2010-10-13 10:46:11 -0700 | [diff] [blame] | 254 | EXPECT_EQ(image_size + 1024 * 1024, utils::FileSize(a_img)); |
Darin Petkov | 7ea3233 | 2010-10-13 10:46:11 -0700 | [diff] [blame] | 255 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 256 | // Make some changes to the A image. |
| 257 | { |
| 258 | string a_mnt; |
| 259 | ScopedLoopMounter b_mounter(a_img, &a_mnt, 0); |
| 260 | |
| 261 | EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress", |
| 262 | a_mnt.c_str()).c_str(), |
| 263 | reinterpret_cast<const char*>(kRandomString), |
| 264 | sizeof(kRandomString) - 1)); |
| 265 | // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff |
| 266 | // patch fails to zero out the final block. |
| 267 | vector<char> ones(1024 * 1024, 0xff); |
| 268 | EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/ones", |
| 269 | a_mnt.c_str()).c_str(), |
| 270 | &ones[0], |
| 271 | ones.size())); |
| 272 | } |
| 273 | |
Darin Petkov | 9fa7ec5 | 2010-10-18 11:45:23 -0700 | [diff] [blame] | 274 | if (noop) { |
| 275 | EXPECT_TRUE(file_util::CopyFile(FilePath(a_img), FilePath(b_img))); |
| 276 | } else { |
| 277 | CreateExtImageAtPath(b_img, NULL); |
| 278 | EXPECT_EQ(0, System(base::StringPrintf( |
| 279 | "dd if=/dev/zero of=%s seek=%d bs=1 count=1", |
| 280 | b_img.c_str(), |
| 281 | image_size + 1024 * 1024 - 1))); |
| 282 | EXPECT_EQ(image_size + 1024 * 1024, utils::FileSize(b_img)); |
| 283 | |
| 284 | // Make some changes to the B image. |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 285 | string b_mnt; |
| 286 | ScopedLoopMounter b_mounter(b_img, &b_mnt, 0); |
| 287 | |
| 288 | EXPECT_EQ(0, system(StringPrintf("cp %s/hello %s/hello2", b_mnt.c_str(), |
| 289 | b_mnt.c_str()).c_str())); |
| 290 | EXPECT_EQ(0, system(StringPrintf("rm %s/hello", b_mnt.c_str()).c_str())); |
| 291 | EXPECT_EQ(0, system(StringPrintf("mv %s/hello2 %s/hello", b_mnt.c_str(), |
| 292 | b_mnt.c_str()).c_str())); |
| 293 | EXPECT_EQ(0, system(StringPrintf("echo foo > %s/foo", |
| 294 | b_mnt.c_str()).c_str())); |
| 295 | EXPECT_EQ(0, system(StringPrintf("touch %s/emptyfile", |
| 296 | b_mnt.c_str()).c_str())); |
| 297 | EXPECT_TRUE(WriteSparseFile(StringPrintf("%s/fullsparse", b_mnt.c_str()), |
| 298 | 1024 * 1024)); |
| 299 | EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/partsparese bs=1 " |
| 300 | "seek=4096 count=1", |
| 301 | b_mnt.c_str()).c_str())); |
Andrew de los Reyes | 29da8aa | 2011-02-15 13:34:57 -0800 | [diff] [blame] | 302 | EXPECT_EQ(0, system(StringPrintf("cp %s/srchardlink0 %s/tmp && " |
| 303 | "mv %s/tmp %s/srchardlink1", |
| 304 | b_mnt.c_str(), b_mnt.c_str(), |
| 305 | b_mnt.c_str(), b_mnt.c_str()).c_str())); |
Andrew de los Reyes | 48a0a48 | 2011-02-22 15:32:11 -0800 | [diff] [blame] | 306 | EXPECT_EQ(0, system(StringPrintf("rm %s/boguslink && " |
| 307 | "echo foobar > %s/boguslink", |
| 308 | b_mnt.c_str(), b_mnt.c_str()).c_str())); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 309 | EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress", |
| 310 | b_mnt.c_str()).c_str(), |
| 311 | reinterpret_cast<const char*>(kRandomString), |
| 312 | sizeof(kRandomString))); |
| 313 | } |
| 314 | |
Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 315 | string old_kernel; |
| 316 | EXPECT_TRUE(utils::MakeTempFile("/tmp/old_kernel.XXXXXX", &old_kernel, NULL)); |
| 317 | ScopedPathUnlinker old_kernel_unlinker(old_kernel); |
| 318 | |
| 319 | string new_kernel; |
| 320 | EXPECT_TRUE(utils::MakeTempFile("/tmp/new_kernel.XXXXXX", &new_kernel, NULL)); |
| 321 | ScopedPathUnlinker new_kernel_unlinker(new_kernel); |
| 322 | |
| 323 | vector<char> old_kernel_data(4096); // Something small for a test |
| 324 | vector<char> new_kernel_data(old_kernel_data.size()); |
| 325 | FillWithData(&old_kernel_data); |
| 326 | FillWithData(&new_kernel_data); |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 327 | |
Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 328 | // change the new kernel data |
| 329 | const char* new_data_string = "This is new data."; |
| 330 | strcpy(&new_kernel_data[0], new_data_string); |
| 331 | |
Darin Petkov | 9fa7ec5 | 2010-10-18 11:45:23 -0700 | [diff] [blame] | 332 | if (noop) { |
| 333 | old_kernel_data = new_kernel_data; |
| 334 | } |
| 335 | |
Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 336 | // Write kernels to disk |
| 337 | EXPECT_TRUE(utils::WriteFile( |
| 338 | old_kernel.c_str(), &old_kernel_data[0], old_kernel_data.size())); |
| 339 | EXPECT_TRUE(utils::WriteFile( |
| 340 | new_kernel.c_str(), &new_kernel_data[0], new_kernel_data.size())); |
| 341 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 342 | string delta_path; |
| 343 | EXPECT_TRUE(utils::MakeTempFile("/tmp/delta.XXXXXX", &delta_path, NULL)); |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 344 | LOG(INFO) << "delta path: " << delta_path; |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 345 | ScopedPathUnlinker delta_path_unlinker(delta_path); |
| 346 | { |
| 347 | string a_mnt, b_mnt; |
| 348 | ScopedLoopMounter a_mounter(a_img, &a_mnt, MS_RDONLY); |
| 349 | ScopedLoopMounter b_mounter(b_img, &b_mnt, MS_RDONLY); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 350 | const string private_key = |
| 351 | signature_test == kSignatureGenerator ? kUnittestPrivateKeyPath : ""; |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 352 | EXPECT_TRUE( |
Darin Petkov | 68c10d1 | 2010-10-14 09:24:37 -0700 | [diff] [blame] | 353 | DeltaDiffGenerator::GenerateDeltaUpdateFile( |
Darin Petkov | 7a22d79 | 2010-11-08 14:10:00 -0800 | [diff] [blame] | 354 | full_rootfs ? "" : a_mnt, |
| 355 | full_rootfs ? "" : a_img, |
Darin Petkov | 68c10d1 | 2010-10-14 09:24:37 -0700 | [diff] [blame] | 356 | b_mnt, |
| 357 | b_img, |
| 358 | full_kernel ? "" : old_kernel, |
| 359 | new_kernel, |
| 360 | delta_path, |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 361 | private_key)); |
Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 362 | } |
| 363 | |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 364 | if (signature_test == kSignatureGenerated) { |
| 365 | SignGeneratedPayload(delta_path); |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 366 | } else if (signature_test == kSignatureGeneratedShell || |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 367 | signature_test == kSignatureGeneratedShellBadKey || |
| 368 | signature_test == kSignatureGeneratedShellRotateCl1 || |
| 369 | signature_test == kSignatureGeneratedShellRotateCl2) { |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 370 | SignGeneratedShellPayload(signature_test, delta_path); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 371 | } |
| 372 | |
| 373 | // Read delta into memory. |
| 374 | vector<char> delta; |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 375 | uint64_t metadata_size; |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 376 | |
Darin Petkov | 36a5822 | 2010-10-07 22:00:09 -0700 | [diff] [blame] | 377 | // Check the metadata. |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 378 | { |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 379 | DeltaArchiveManifest manifest; |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 380 | EXPECT_TRUE(PayloadSigner::LoadPayload(delta_path, &delta, &manifest, |
| 381 | &metadata_size)); |
| 382 | LOG(INFO) << "Metadata size: " << metadata_size; |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 383 | |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 384 | if (signature_test == kSignatureNone) { |
| 385 | EXPECT_FALSE(manifest.has_signatures_offset()); |
| 386 | EXPECT_FALSE(manifest.has_signatures_size()); |
| 387 | } else { |
| 388 | EXPECT_TRUE(manifest.has_signatures_offset()); |
| 389 | EXPECT_TRUE(manifest.has_signatures_size()); |
| 390 | Signatures sigs_message; |
| 391 | EXPECT_TRUE(sigs_message.ParseFromArray( |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 392 | &delta[metadata_size + manifest.signatures_offset()], |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 393 | manifest.signatures_size())); |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 394 | if (signature_test == kSignatureGeneratedShellRotateCl1 || |
| 395 | signature_test == kSignatureGeneratedShellRotateCl2) |
| 396 | EXPECT_EQ(2, sigs_message.signatures_size()); |
| 397 | else |
| 398 | EXPECT_EQ(1, sigs_message.signatures_size()); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 399 | const Signatures_Signature& signature = sigs_message.signatures(0); |
| 400 | EXPECT_EQ(1, signature.version()); |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 401 | |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 402 | uint64_t expected_sig_data_length = 0; |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 403 | vector<string> key_paths (1, kUnittestPrivateKeyPath); |
| 404 | if (signature_test == kSignatureGeneratedShellRotateCl1 || |
| 405 | signature_test == kSignatureGeneratedShellRotateCl2) { |
| 406 | key_paths.push_back(kUnittestPrivateKey2Path); |
| 407 | } |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 408 | EXPECT_TRUE(PayloadSigner::SignatureBlobLength( |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 409 | key_paths, |
| 410 | &expected_sig_data_length)); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 411 | EXPECT_EQ(expected_sig_data_length, manifest.signatures_size()); |
| 412 | EXPECT_FALSE(signature.data().empty()); |
| 413 | } |
Darin Petkov | 36a5822 | 2010-10-07 22:00:09 -0700 | [diff] [blame] | 414 | |
Darin Petkov | 9fa7ec5 | 2010-10-18 11:45:23 -0700 | [diff] [blame] | 415 | if (noop) { |
| 416 | EXPECT_EQ(1, manifest.install_operations_size()); |
| 417 | EXPECT_EQ(1, manifest.kernel_install_operations_size()); |
| 418 | } |
| 419 | |
Darin Petkov | d43d690 | 2010-10-14 11:17:50 -0700 | [diff] [blame] | 420 | if (full_kernel) { |
| 421 | EXPECT_FALSE(manifest.has_old_kernel_info()); |
| 422 | } else { |
| 423 | EXPECT_EQ(old_kernel_data.size(), manifest.old_kernel_info().size()); |
| 424 | EXPECT_FALSE(manifest.old_kernel_info().hash().empty()); |
| 425 | } |
Darin Petkov | 698d041 | 2010-10-13 10:59:44 -0700 | [diff] [blame] | 426 | |
Darin Petkov | 7a22d79 | 2010-11-08 14:10:00 -0800 | [diff] [blame] | 427 | if (full_rootfs) { |
| 428 | EXPECT_FALSE(manifest.has_old_rootfs_info()); |
| 429 | } else { |
| 430 | EXPECT_EQ(image_size, manifest.old_rootfs_info().size()); |
| 431 | EXPECT_FALSE(manifest.old_rootfs_info().hash().empty()); |
| 432 | } |
| 433 | |
Darin Petkov | 36a5822 | 2010-10-07 22:00:09 -0700 | [diff] [blame] | 434 | EXPECT_EQ(new_kernel_data.size(), manifest.new_kernel_info().size()); |
Darin Petkov | 7ea3233 | 2010-10-13 10:46:11 -0700 | [diff] [blame] | 435 | EXPECT_EQ(image_size, manifest.new_rootfs_info().size()); |
Darin Petkov | 36a5822 | 2010-10-07 22:00:09 -0700 | [diff] [blame] | 436 | |
Darin Petkov | 36a5822 | 2010-10-07 22:00:09 -0700 | [diff] [blame] | 437 | EXPECT_FALSE(manifest.new_kernel_info().hash().empty()); |
Darin Petkov | 36a5822 | 2010-10-07 22:00:09 -0700 | [diff] [blame] | 438 | EXPECT_FALSE(manifest.new_rootfs_info().hash().empty()); |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 439 | } |
| 440 | |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 441 | PrefsMock prefs; |
| 442 | EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 443 | metadata_size)).WillOnce(Return(true)); |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 444 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _)) |
| 445 | .WillRepeatedly(Return(true)); |
Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 446 | EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _)) |
| 447 | .WillOnce(Return(false)); |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 448 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _)) |
| 449 | .WillRepeatedly(Return(true)); |
Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 450 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _)) |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 451 | .WillRepeatedly(Return(true)); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 452 | if (signature_test != kSignatureNone) { |
| 453 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _)) |
| 454 | .WillOnce(Return(true)); |
Darin Petkov | 4f0a07b | 2011-05-25 16:47:20 -0700 | [diff] [blame] | 455 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _)) |
| 456 | .WillOnce(Return(true)); |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 457 | } |
Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 458 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 459 | // Update the A image in place. |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 460 | InstallPlan install_plan; |
Jay Srinivasan | f057205 | 2012-10-23 18:12:56 -0700 | [diff] [blame] | 461 | MockSystemState mock_system_state; |
| 462 | DeltaPerformer performer(&prefs, &mock_system_state, &install_plan); |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 463 | EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath)); |
| 464 | performer.set_public_key_path(kUnittestPublicKeyPath); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 465 | |
Darin Petkov | d43d690 | 2010-10-14 11:17:50 -0700 | [diff] [blame] | 466 | EXPECT_EQ(image_size, |
| 467 | OmahaHashCalculator::RawHashOfFile(a_img, |
| 468 | image_size, |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 469 | &install_plan.rootfs_hash)); |
Darin Petkov | d43d690 | 2010-10-14 11:17:50 -0700 | [diff] [blame] | 470 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(old_kernel_data, |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 471 | &install_plan.kernel_hash)); |
Darin Petkov | d43d690 | 2010-10-14 11:17:50 -0700 | [diff] [blame] | 472 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 473 | EXPECT_EQ(0, performer.Open(a_img.c_str(), 0, 0)); |
Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 474 | EXPECT_TRUE(performer.OpenKernel(old_kernel.c_str())); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 475 | |
| 476 | // Write at some number of bytes per operation. Arbitrarily chose 5. |
| 477 | const size_t kBytesPerWrite = 5; |
| 478 | for (size_t i = 0; i < delta.size(); i += kBytesPerWrite) { |
| 479 | size_t count = min(delta.size() - i, kBytesPerWrite); |
Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 480 | EXPECT_TRUE(performer.Write(&delta[i], count)); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 481 | } |
| 482 | |
| 483 | // Wrapper around close. Returns 0 on success or -errno on error. |
| 484 | EXPECT_EQ(0, performer.Close()); |
| 485 | |
Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 486 | CompareFilesByBlock(old_kernel, new_kernel); |
Darin Petkov | 2dd0109 | 2010-10-08 15:43:05 -0700 | [diff] [blame] | 487 | CompareFilesByBlock(a_img, b_img); |
Andrew de los Reyes | 932bc4c | 2010-08-23 18:14:09 -0700 | [diff] [blame] | 488 | |
Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 489 | vector<char> updated_kernel_partition; |
| 490 | EXPECT_TRUE(utils::ReadFile(old_kernel, &updated_kernel_partition)); |
| 491 | EXPECT_EQ(0, strncmp(&updated_kernel_partition[0], new_data_string, |
| 492 | strlen(new_data_string))); |
Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 493 | |
Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 494 | ActionExitCode expect_verify_result = kActionCodeSuccess; |
| 495 | switch (signature_test) { |
| 496 | case kSignatureNone: |
| 497 | expect_verify_result = kActionCodeSignedDeltaPayloadExpectedError; |
| 498 | break; |
| 499 | case kSignatureGeneratedShellBadKey: |
| 500 | expect_verify_result = kActionCodeDownloadPayloadPubKeyVerificationError; |
| 501 | break; |
| 502 | default: break; // appease gcc |
| 503 | } |
| 504 | EXPECT_EQ(expect_verify_result, performer.VerifyPayload( |
Andrew de los Reyes | fb830ba | 2011-04-04 11:42:43 -0700 | [diff] [blame] | 505 | OmahaHashCalculator::OmahaHashOfData(delta), |
Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 506 | delta.size())); |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 507 | |
| 508 | performer.set_public_key_path("/public/key/does/not/exists"); |
Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 509 | EXPECT_EQ(kActionCodeSuccess, performer.VerifyPayload( |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 510 | OmahaHashCalculator::OmahaHashOfData(delta), |
Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 511 | delta.size())); |
Darin Petkov | 3aefa86 | 2010-12-07 14:45:00 -0800 | [diff] [blame] | 512 | |
| 513 | uint64_t new_kernel_size; |
| 514 | vector<char> new_kernel_hash; |
| 515 | uint64_t new_rootfs_size; |
| 516 | vector<char> new_rootfs_hash; |
| 517 | EXPECT_TRUE(performer.GetNewPartitionInfo(&new_kernel_size, |
| 518 | &new_kernel_hash, |
| 519 | &new_rootfs_size, |
| 520 | &new_rootfs_hash)); |
| 521 | EXPECT_EQ(4096, new_kernel_size); |
| 522 | vector<char> expected_new_kernel_hash; |
| 523 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(new_kernel_data, |
| 524 | &expected_new_kernel_hash)); |
| 525 | EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash); |
| 526 | EXPECT_EQ(image_size, new_rootfs_size); |
| 527 | vector<char> expected_new_rootfs_hash; |
| 528 | EXPECT_EQ(image_size, |
| 529 | OmahaHashCalculator::RawHashOfFile(b_img, |
| 530 | image_size, |
| 531 | &expected_new_rootfs_hash)); |
| 532 | EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash); |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 533 | } |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 534 | |
| 535 | // TODO(jaysri): Refactor the previous unit test so we can reuse a lot of |
| 536 | // code between these two methods. |
| 537 | void DoManifestTest() { |
| 538 | bool full_kernel = false; |
| 539 | bool full_rootfs = false; |
| 540 | string a_img, b_img; |
| 541 | EXPECT_TRUE(utils::MakeTempFile("/tmp/a_img.XXXXXX", &a_img, NULL)); |
| 542 | ScopedPathUnlinker a_img_unlinker(a_img); |
| 543 | EXPECT_TRUE(utils::MakeTempFile("/tmp/b_img.XXXXXX", &b_img, NULL)); |
| 544 | ScopedPathUnlinker b_img_unlinker(b_img); |
| 545 | |
| 546 | CreateExtImageAtPath(a_img, NULL); |
| 547 | |
| 548 | int image_size = static_cast<int>(utils::FileSize(a_img)); |
| 549 | |
| 550 | // Extend the "partitions" holding the file system a bit. |
| 551 | EXPECT_EQ(0, System(base::StringPrintf( |
| 552 | "dd if=/dev/zero of=%s seek=%d bs=1 count=1", |
| 553 | a_img.c_str(), |
| 554 | image_size + 1024 * 1024 - 1))); |
| 555 | EXPECT_EQ(image_size + 1024 * 1024, utils::FileSize(a_img)); |
| 556 | |
| 557 | // Make some changes to the A image. |
| 558 | { |
| 559 | string a_mnt; |
| 560 | ScopedLoopMounter b_mounter(a_img, &a_mnt, 0); |
| 561 | |
| 562 | EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress", |
| 563 | a_mnt.c_str()).c_str(), |
| 564 | reinterpret_cast<const char*>(kRandomString), |
| 565 | sizeof(kRandomString) - 1)); |
| 566 | // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff |
| 567 | // patch fails to zero out the final block. |
| 568 | vector<char> ones(1024 * 1024, 0xff); |
| 569 | EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/ones", |
| 570 | a_mnt.c_str()).c_str(), |
| 571 | &ones[0], |
| 572 | ones.size())); |
| 573 | } |
| 574 | |
| 575 | { |
| 576 | CreateExtImageAtPath(b_img, NULL); |
| 577 | EXPECT_EQ(0, System(base::StringPrintf( |
| 578 | "dd if=/dev/zero of=%s seek=%d bs=1 count=1", |
| 579 | b_img.c_str(), |
| 580 | image_size + 1024 * 1024 - 1))); |
| 581 | EXPECT_EQ(image_size + 1024 * 1024, utils::FileSize(b_img)); |
| 582 | |
| 583 | // Make some changes to the B image. |
| 584 | string b_mnt; |
| 585 | ScopedLoopMounter b_mounter(b_img, &b_mnt, 0); |
| 586 | |
| 587 | EXPECT_EQ(0, system(StringPrintf("cp %s/hello %s/hello2", b_mnt.c_str(), |
| 588 | b_mnt.c_str()).c_str())); |
| 589 | EXPECT_EQ(0, system(StringPrintf("rm %s/hello", b_mnt.c_str()).c_str())); |
| 590 | EXPECT_EQ(0, system(StringPrintf("mv %s/hello2 %s/hello", b_mnt.c_str(), |
| 591 | b_mnt.c_str()).c_str())); |
| 592 | EXPECT_EQ(0, system(StringPrintf("echo foo > %s/foo", |
| 593 | b_mnt.c_str()).c_str())); |
| 594 | EXPECT_EQ(0, system(StringPrintf("touch %s/emptyfile", |
| 595 | b_mnt.c_str()).c_str())); |
| 596 | EXPECT_TRUE(WriteSparseFile(StringPrintf("%s/fullsparse", b_mnt.c_str()), |
| 597 | 1024 * 1024)); |
| 598 | EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/partsparese bs=1 " |
| 599 | "seek=4096 count=1", |
| 600 | b_mnt.c_str()).c_str())); |
| 601 | EXPECT_EQ(0, system(StringPrintf("cp %s/srchardlink0 %s/tmp && " |
| 602 | "mv %s/tmp %s/srchardlink1", |
| 603 | b_mnt.c_str(), b_mnt.c_str(), |
| 604 | b_mnt.c_str(), b_mnt.c_str()).c_str())); |
| 605 | EXPECT_EQ(0, system(StringPrintf("rm %s/boguslink && " |
| 606 | "echo foobar > %s/boguslink", |
| 607 | b_mnt.c_str(), b_mnt.c_str()).c_str())); |
| 608 | EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress", |
| 609 | b_mnt.c_str()).c_str(), |
| 610 | reinterpret_cast<const char*>(kRandomString), |
| 611 | sizeof(kRandomString))); |
| 612 | } |
| 613 | |
| 614 | string old_kernel; |
| 615 | EXPECT_TRUE(utils::MakeTempFile("/tmp/old_kernel.XXXXXX", &old_kernel, NULL)); |
| 616 | ScopedPathUnlinker old_kernel_unlinker(old_kernel); |
| 617 | |
| 618 | string new_kernel; |
| 619 | EXPECT_TRUE(utils::MakeTempFile("/tmp/new_kernel.XXXXXX", &new_kernel, NULL)); |
| 620 | ScopedPathUnlinker new_kernel_unlinker(new_kernel); |
| 621 | |
| 622 | vector<char> old_kernel_data(4096); // Something small for a test |
| 623 | vector<char> new_kernel_data(old_kernel_data.size()); |
| 624 | FillWithData(&old_kernel_data); |
| 625 | FillWithData(&new_kernel_data); |
| 626 | |
| 627 | // change the new kernel data |
| 628 | const char* new_data_string = "This is new data."; |
| 629 | strcpy(&new_kernel_data[0], new_data_string); |
| 630 | |
| 631 | // Write kernels to disk |
| 632 | EXPECT_TRUE(utils::WriteFile( |
| 633 | old_kernel.c_str(), &old_kernel_data[0], old_kernel_data.size())); |
| 634 | EXPECT_TRUE(utils::WriteFile( |
| 635 | new_kernel.c_str(), &new_kernel_data[0], new_kernel_data.size())); |
| 636 | |
| 637 | string delta_path; |
| 638 | EXPECT_TRUE(utils::MakeTempFile("/tmp/delta.XXXXXX", &delta_path, NULL)); |
| 639 | LOG(INFO) << "delta path: " << delta_path; |
| 640 | ScopedPathUnlinker delta_path_unlinker(delta_path); |
| 641 | { |
| 642 | string a_mnt, b_mnt; |
| 643 | ScopedLoopMounter a_mounter(a_img, &a_mnt, MS_RDONLY); |
| 644 | ScopedLoopMounter b_mounter(b_img, &b_mnt, MS_RDONLY); |
| 645 | const string private_key = kUnittestPrivateKeyPath; |
| 646 | EXPECT_TRUE( |
| 647 | DeltaDiffGenerator::GenerateDeltaUpdateFile( |
| 648 | full_rootfs ? "" : a_mnt, |
| 649 | full_rootfs ? "" : a_img, |
| 650 | b_mnt, |
| 651 | b_img, |
| 652 | full_kernel ? "" : old_kernel, |
| 653 | new_kernel, |
| 654 | delta_path, |
| 655 | private_key)); |
| 656 | } |
| 657 | |
| 658 | // Read delta into memory. |
| 659 | vector<char> delta; |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 660 | uint64_t metadata_size; |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 661 | // Check the metadata. |
| 662 | { |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 663 | DeltaArchiveManifest manifest; |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 664 | EXPECT_TRUE(PayloadSigner::LoadPayload(delta_path, &delta, &manifest, |
| 665 | &metadata_size)); |
| 666 | |
| 667 | LOG(INFO) << "Metadata size: " << metadata_size; |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 668 | |
| 669 | { |
| 670 | EXPECT_TRUE(manifest.has_signatures_offset()); |
| 671 | EXPECT_TRUE(manifest.has_signatures_size()); |
| 672 | Signatures sigs_message; |
| 673 | EXPECT_TRUE(sigs_message.ParseFromArray( |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 674 | &delta[metadata_size + manifest.signatures_offset()], |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 675 | manifest.signatures_size())); |
| 676 | EXPECT_EQ(1, sigs_message.signatures_size()); |
| 677 | const Signatures_Signature& signature = sigs_message.signatures(0); |
| 678 | EXPECT_EQ(1, signature.version()); |
| 679 | |
| 680 | uint64_t expected_sig_data_length = 0; |
| 681 | vector<string> key_paths (1, kUnittestPrivateKeyPath); |
| 682 | EXPECT_TRUE(PayloadSigner::SignatureBlobLength( |
| 683 | key_paths, |
| 684 | &expected_sig_data_length)); |
| 685 | EXPECT_EQ(expected_sig_data_length, manifest.signatures_size()); |
| 686 | EXPECT_FALSE(signature.data().empty()); |
| 687 | } |
| 688 | |
| 689 | if (full_kernel) { |
| 690 | EXPECT_FALSE(manifest.has_old_kernel_info()); |
| 691 | } else { |
| 692 | EXPECT_EQ(old_kernel_data.size(), manifest.old_kernel_info().size()); |
| 693 | EXPECT_FALSE(manifest.old_kernel_info().hash().empty()); |
| 694 | } |
| 695 | |
| 696 | if (full_rootfs) { |
| 697 | EXPECT_FALSE(manifest.has_old_rootfs_info()); |
| 698 | } else { |
| 699 | EXPECT_EQ(image_size, manifest.old_rootfs_info().size()); |
| 700 | EXPECT_FALSE(manifest.old_rootfs_info().hash().empty()); |
| 701 | } |
| 702 | |
| 703 | EXPECT_EQ(new_kernel_data.size(), manifest.new_kernel_info().size()); |
| 704 | EXPECT_EQ(image_size, manifest.new_rootfs_info().size()); |
| 705 | |
| 706 | EXPECT_FALSE(manifest.new_kernel_info().hash().empty()); |
| 707 | EXPECT_FALSE(manifest.new_rootfs_info().hash().empty()); |
| 708 | |
| 709 | } |
| 710 | |
| 711 | PrefsMock prefs; |
| 712 | EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 713 | metadata_size)).WillOnce(Return(true)); |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 714 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _)) |
| 715 | .WillRepeatedly(Return(true)); |
| 716 | EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _)) |
| 717 | .WillOnce(Return(false)); |
| 718 | EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _)) |
| 719 | .WillRepeatedly(Return(true)); |
| 720 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _)) |
| 721 | .WillRepeatedly(Return(true)); |
| 722 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _)) |
| 723 | .WillRepeatedly(Return(true)); |
| 724 | EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _)) |
| 725 | .WillOnce(Return(true)); |
| 726 | |
| 727 | // Update the A image in place. |
| 728 | InstallPlan install_plan; |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 729 | install_plan.metadata_size = metadata_size; |
| 730 | LOG(INFO) << "Setting payload metadata size in Omaha = " << metadata_size; |
| 731 | ASSERT_TRUE(PayloadSigner::GetMetadataSignature( |
| 732 | &delta[0], |
| 733 | metadata_size, |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 734 | kUnittestPrivateKeyPath, |
Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 735 | &install_plan.metadata_signature)); |
| 736 | EXPECT_FALSE(install_plan.metadata_signature.empty()); |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 737 | |
Jay Srinivasan | f057205 | 2012-10-23 18:12:56 -0700 | [diff] [blame] | 738 | MockSystemState mock_system_state; |
| 739 | DeltaPerformer performer(&prefs, &mock_system_state, &install_plan); |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 740 | EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath)); |
| 741 | performer.set_public_key_path(kUnittestPublicKeyPath); |
| 742 | |
| 743 | EXPECT_EQ(image_size, |
| 744 | OmahaHashCalculator::RawHashOfFile(a_img, |
| 745 | image_size, |
| 746 | &install_plan.rootfs_hash)); |
| 747 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(old_kernel_data, |
| 748 | &install_plan.kernel_hash)); |
| 749 | |
| 750 | EXPECT_EQ(0, performer.Open(a_img.c_str(), 0, 0)); |
| 751 | EXPECT_TRUE(performer.OpenKernel(old_kernel.c_str())); |
| 752 | |
| 753 | // Write at some number of bytes per operation. Arbitrarily chose 5. |
| 754 | const size_t kBytesPerWrite = 5; |
| 755 | for (size_t i = 0; i < delta.size(); i += kBytesPerWrite) { |
| 756 | size_t count = min(delta.size() - i, kBytesPerWrite); |
| 757 | EXPECT_TRUE(performer.Write(&delta[i], count)); |
| 758 | } |
| 759 | |
| 760 | // Wrapper around close. Returns 0 on success or -errno on error. |
| 761 | EXPECT_EQ(0, performer.Close()); |
| 762 | |
| 763 | CompareFilesByBlock(old_kernel, new_kernel); |
| 764 | CompareFilesByBlock(a_img, b_img); |
| 765 | |
| 766 | vector<char> updated_kernel_partition; |
| 767 | EXPECT_TRUE(utils::ReadFile(old_kernel, &updated_kernel_partition)); |
| 768 | EXPECT_EQ(0, strncmp(&updated_kernel_partition[0], new_data_string, |
| 769 | strlen(new_data_string))); |
| 770 | |
| 771 | ActionExitCode expect_verify_result = kActionCodeSuccess; |
| 772 | LOG(INFO) << "Verifying Payload ..."; |
| 773 | EXPECT_EQ(expect_verify_result, performer.VerifyPayload( |
| 774 | OmahaHashCalculator::OmahaHashOfData(delta), |
| 775 | delta.size())); |
| 776 | |
| 777 | LOG(INFO) << "Verified Payload"; |
| 778 | |
| 779 | uint64_t new_kernel_size; |
| 780 | vector<char> new_kernel_hash; |
| 781 | uint64_t new_rootfs_size; |
| 782 | vector<char> new_rootfs_hash; |
| 783 | EXPECT_TRUE(performer.GetNewPartitionInfo(&new_kernel_size, |
| 784 | &new_kernel_hash, |
| 785 | &new_rootfs_size, |
| 786 | &new_rootfs_hash)); |
| 787 | EXPECT_EQ(4096, new_kernel_size); |
| 788 | vector<char> expected_new_kernel_hash; |
| 789 | EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(new_kernel_data, |
| 790 | &expected_new_kernel_hash)); |
| 791 | EXPECT_TRUE(expected_new_kernel_hash == new_kernel_hash); |
| 792 | EXPECT_EQ(image_size, new_rootfs_size); |
| 793 | vector<char> expected_new_rootfs_hash; |
| 794 | EXPECT_EQ(image_size, |
| 795 | OmahaHashCalculator::RawHashOfFile(b_img, |
| 796 | image_size, |
| 797 | &expected_new_rootfs_hash)); |
| 798 | EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash); |
| 799 | } |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 800 | } // namespace {} |
Darin Petkov | 68c10d1 | 2010-10-14 09:24:37 -0700 | [diff] [blame] | 801 | |
| 802 | TEST(DeltaPerformerTest, RunAsRootSmallImageTest) { |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 803 | DoSmallImageTest(false, false, false, kSignatureGenerator); |
Darin Petkov | 68c10d1 | 2010-10-14 09:24:37 -0700 | [diff] [blame] | 804 | } |
| 805 | |
| 806 | TEST(DeltaPerformerTest, RunAsRootFullKernelSmallImageTest) { |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 807 | DoSmallImageTest(true, false, false, kSignatureGenerator); |
Darin Petkov | 7a22d79 | 2010-11-08 14:10:00 -0800 | [diff] [blame] | 808 | } |
| 809 | |
| 810 | TEST(DeltaPerformerTest, RunAsRootFullSmallImageTest) { |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 811 | DoSmallImageTest(true, true, false, kSignatureGenerator); |
Darin Petkov | 9fa7ec5 | 2010-10-18 11:45:23 -0700 | [diff] [blame] | 812 | } |
| 813 | |
| 814 | TEST(DeltaPerformerTest, RunAsRootNoopSmallImageTest) { |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 815 | DoSmallImageTest(false, false, true, kSignatureGenerator); |
Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 816 | } |
| 817 | |
Darin Petkov | cbfb017 | 2011-01-14 15:24:45 -0800 | [diff] [blame] | 818 | TEST(DeltaPerformerTest, RunAsRootSmallImageSignNoneTest) { |
| 819 | DoSmallImageTest(false, false, false, kSignatureNone); |
| 820 | } |
| 821 | |
| 822 | TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedTest) { |
| 823 | DoSmallImageTest(false, false, false, kSignatureGenerated); |
| 824 | } |
| 825 | |
| 826 | TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellTest) { |
| 827 | DoSmallImageTest(false, false, false, kSignatureGeneratedShell); |
Andrew de los Reyes | 27f7d37 | 2010-10-07 11:26:07 -0700 | [diff] [blame] | 828 | } |
| 829 | |
Darin Petkov | 52dcaeb | 2011-01-14 15:33:06 -0800 | [diff] [blame] | 830 | TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) { |
| 831 | DoSmallImageTest(false, false, false, kSignatureGeneratedShellBadKey); |
| 832 | } |
| 833 | |
Andrew de los Reyes | c24e3f3 | 2011-08-30 15:45:20 -0700 | [diff] [blame] | 834 | TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) { |
| 835 | DoSmallImageTest(false, false, false, kSignatureGeneratedShellRotateCl1); |
| 836 | } |
| 837 | |
| 838 | TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) { |
| 839 | DoSmallImageTest(false, false, false, kSignatureGeneratedShellRotateCl2); |
| 840 | } |
| 841 | |
Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 842 | TEST(DeltaPerformerTest, BadDeltaMagicTest) { |
| 843 | PrefsMock prefs; |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 844 | InstallPlan install_plan; |
Jay Srinivasan | f057205 | 2012-10-23 18:12:56 -0700 | [diff] [blame] | 845 | MockSystemState mock_system_state; |
| 846 | DeltaPerformer performer(&prefs, &mock_system_state, &install_plan); |
Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 847 | EXPECT_EQ(0, performer.Open("/dev/null", 0, 0)); |
| 848 | EXPECT_TRUE(performer.OpenKernel("/dev/null")); |
Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 849 | EXPECT_TRUE(performer.Write("junk", 4)); |
| 850 | EXPECT_TRUE(performer.Write("morejunk", 8)); |
| 851 | EXPECT_FALSE(performer.Write("morejunk", 8)); |
Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 852 | EXPECT_LT(performer.Close(), 0); |
| 853 | } |
| 854 | |
Andrew de los Reyes | 353777c | 2010-10-08 10:34:30 -0700 | [diff] [blame] | 855 | TEST(DeltaPerformerTest, IsIdempotentOperationTest) { |
| 856 | DeltaArchiveManifest_InstallOperation op; |
| 857 | EXPECT_TRUE(DeltaPerformer::IsIdempotentOperation(op)); |
| 858 | *(op.add_dst_extents()) = ExtentForRange(0, 5); |
| 859 | EXPECT_TRUE(DeltaPerformer::IsIdempotentOperation(op)); |
| 860 | *(op.add_src_extents()) = ExtentForRange(4, 1); |
| 861 | EXPECT_FALSE(DeltaPerformer::IsIdempotentOperation(op)); |
| 862 | op.clear_src_extents(); |
| 863 | *(op.add_src_extents()) = ExtentForRange(5, 3); |
| 864 | EXPECT_TRUE(DeltaPerformer::IsIdempotentOperation(op)); |
| 865 | *(op.add_dst_extents()) = ExtentForRange(20, 6); |
| 866 | EXPECT_TRUE(DeltaPerformer::IsIdempotentOperation(op)); |
| 867 | *(op.add_src_extents()) = ExtentForRange(19, 2); |
| 868 | EXPECT_FALSE(DeltaPerformer::IsIdempotentOperation(op)); |
| 869 | } |
| 870 | |
Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 871 | TEST(DeltaPerformerTest, RunAsRootRunValidManifestTest) { |
| 872 | DoManifestTest(); |
| 873 | } |
| 874 | |
| 875 | |
Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 876 | } // namespace chromeos_update_engine |