blob: b0a52b1e84b83ed950c3e8973a8b32acc478b5b2 [file] [log] [blame]
Tao Baob8cb2e62018-07-06 12:14:36 -07001/*
2 * Copyright (C) 2016 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 agree 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 <stdio.h>
18#include <stdlib.h>
19
20#include <string>
21#include <vector>
22
23#include <android-base/file.h>
24#include <android-base/logging.h>
25#include <android-base/stringprintf.h>
26#include <android-base/test_utils.h>
27#include <bsdiff/bsdiff.h>
28#include <gtest/gtest.h>
29#include <openssl/sha.h>
30#include <zlib.h>
31
32#include "applypatch/applypatch_modes.h"
33#include "common/test_constants.h"
34#include "otautil/paths.h"
35#include "otautil/print_sha1.h"
36
37using namespace std::string_literals;
38
39// TODO(b/67849209) Remove after debug the flakiness.
40static void DecompressAndDumpRecoveryImage(const std::string& image_path) {
41 // Expected recovery_image structure
42 // chunk normal: 45066 bytes
43 // chunk deflate: 479442 bytes
44 // chunk normal: 5199 bytes
45 std::string recovery_content;
46 ASSERT_TRUE(android::base::ReadFileToString(image_path, &recovery_content));
47 ASSERT_GT(recovery_content.size(), 45066 + 5199);
48
49 z_stream strm = {};
50 strm.avail_in = recovery_content.size() - 45066 - 5199;
51 strm.next_in =
52 const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(recovery_content.data())) + 45066;
53
54 ASSERT_EQ(Z_OK, inflateInit2(&strm, -15));
55
56 constexpr unsigned int BUFFER_SIZE = 32768;
57 std::vector<uint8_t> uncompressed_data(BUFFER_SIZE);
58 size_t uncompressed_length = 0;
59 SHA_CTX ctx;
60 SHA1_Init(&ctx);
61 int ret;
62 do {
63 strm.avail_out = BUFFER_SIZE;
64 strm.next_out = uncompressed_data.data();
65
66 ret = inflate(&strm, Z_NO_FLUSH);
67 ASSERT_GE(ret, 0);
68
69 SHA1_Update(&ctx, uncompressed_data.data(), BUFFER_SIZE - strm.avail_out);
70 uncompressed_length += BUFFER_SIZE - strm.avail_out;
71 } while (ret != Z_STREAM_END);
72 inflateEnd(&strm);
73
74 uint8_t digest[SHA_DIGEST_LENGTH];
75 SHA1_Final(digest, &ctx);
76 GTEST_LOG_(INFO) << "uncompressed length " << uncompressed_length
77 << " sha1: " << short_sha1(digest);
78}
79
80static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) {
81 ASSERT_TRUE(sha1 != nullptr);
82
83 std::string data;
84 ASSERT_TRUE(android::base::ReadFileToString(fname, &data));
85
86 if (fsize != nullptr) {
87 *fsize = data.size();
88 }
89
90 uint8_t digest[SHA_DIGEST_LENGTH];
91 SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
92 *sha1 = print_sha1(digest);
93}
94
95static void test_logger(android::base::LogId /* id */, android::base::LogSeverity severity,
96 const char* /* tag */, const char* /* file */, unsigned int /* line */,
97 const char* message) {
98 if (severity >= android::base::GetMinimumLogSeverity()) {
99 fprintf(stdout, "%s\n", message);
100 }
101}
102
103class ApplyPatchModesTest : public ::testing::Test {
104 protected:
105 void SetUp() override {
106 Paths::Get().set_cache_temp_source(cache_source.path);
107 android::base::InitLogging(nullptr, &test_logger);
108 android::base::SetMinimumLogSeverity(android::base::LogSeverity::DEBUG);
109 }
110
111 TemporaryFile cache_source;
112};
113
114TEST_F(ApplyPatchModesTest, InvalidArgs) {
115 // At least two args (including the filename).
116 ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
117
118 // Unrecognized args.
119 ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
120}
121
122TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
123 std::string boot_img = from_testdata_base("boot.img");
124 size_t boot_img_size;
125 std::string boot_img_sha1;
126 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
127
128 std::string recovery_img = from_testdata_base("recovery.img");
129 size_t recovery_img_size;
130 std::string recovery_img_sha1;
131 sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
132 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
133
134 std::string bonus_file = from_testdata_base("bonus.file");
135
136 // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
137 std::string src_file_arg =
138 "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
139 TemporaryFile tgt_file;
140 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
141 std::string patch_arg = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
142 std::vector<const char*> args = { "applypatch",
143 "-b",
144 bonus_file.c_str(),
145 src_file_arg.c_str(),
146 tgt_file_arg.c_str(),
147 recovery_img_sha1.c_str(),
148 recovery_img_size_arg.c_str(),
149 patch_arg.c_str() };
150 ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
151}
152
153// Tests patching the EMMC target without a separate bonus file (i.e. recovery-from-boot patch has
154// everything).
155TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithoutBonusFile) {
156 std::string boot_img = from_testdata_base("boot.img");
157 size_t boot_img_size;
158 std::string boot_img_sha1;
159 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
160
161 std::string recovery_img = from_testdata_base("recovery.img");
162 size_t recovery_img_size;
163 std::string recovery_img_sha1;
164 sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
165 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
166
167 // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
168 std::string src_file_arg =
169 "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
170 TemporaryFile tgt_file;
171 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
172 std::string patch_arg =
173 boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
174 std::vector<const char*> args = { "applypatch",
175 src_file_arg.c_str(),
176 tgt_file_arg.c_str(),
177 recovery_img_sha1.c_str(),
178 recovery_img_size_arg.c_str(),
179 patch_arg.c_str() };
180
181 if (applypatch_modes(args.size(), args.data()) != 0) {
182 DecompressAndDumpRecoveryImage(tgt_file.path);
183 FAIL();
184 }
185}
186
187TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithMultiplePatches) {
188 std::string boot_img = from_testdata_base("boot.img");
189 size_t boot_img_size;
190 std::string boot_img_sha1;
191 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
192
193 std::string recovery_img = from_testdata_base("recovery.img");
194 size_t recovery_img_size;
195 std::string recovery_img_sha1;
196 sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
197 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
198
199 std::string bonus_file = from_testdata_base("bonus.file");
200
201 // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \
202 // <src-sha1-fake1>:<patch1> <src-sha1>:<patch2> <src-sha1-fake2>:<patch3>
203 std::string src_file_arg =
204 "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
205 TemporaryFile tgt_file;
206 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
207 std::string bad_sha1_a = android::base::StringPrintf("%040x", rand());
208 std::string bad_sha1_b = android::base::StringPrintf("%040x", rand());
209 std::string patch1 = bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p");
210 std::string patch2 = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
211 std::string patch3 = bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p");
212 std::vector<const char*> args = { "applypatch",
213 "-b",
214 bonus_file.c_str(),
215 src_file_arg.c_str(),
216 tgt_file_arg.c_str(),
217 recovery_img_sha1.c_str(),
218 recovery_img_size_arg.c_str(),
219 patch1.c_str(),
220 patch2.c_str(),
221 patch3.c_str() };
222 // TODO(b/67849209): Remove after addressing the flakiness.
223 printf("Calling applypatch_modes with the following args:\n");
224 for (const auto& arg : args) {
225 printf(" %s\n", arg);
226 }
227
228 if (applypatch_modes(args.size(), args.data()) != 0) {
229 DecompressAndDumpRecoveryImage(tgt_file.path);
230 FAIL();
231 }
232}
233
234// Ensures that applypatch works with a bsdiff based recovery-from-boot.p.
235TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
236 std::string boot_img_file = from_testdata_base("boot.img");
237 std::string boot_img_sha1;
238 size_t boot_img_size;
239 sha1sum(boot_img_file, &boot_img_sha1, &boot_img_size);
240
241 std::string recovery_img_file = from_testdata_base("recovery.img");
242 std::string recovery_img_sha1;
243 size_t recovery_img_size;
244 sha1sum(recovery_img_file, &recovery_img_sha1, &recovery_img_size);
245
246 // Generate the bsdiff patch of recovery-from-boot.p.
247 std::string src_content;
248 ASSERT_TRUE(android::base::ReadFileToString(boot_img_file, &src_content));
249
250 std::string tgt_content;
251 ASSERT_TRUE(android::base::ReadFileToString(recovery_img_file, &tgt_content));
252
253 TemporaryFile patch_file;
254 ASSERT_EQ(0,
255 bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
256 reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
257 patch_file.path, nullptr));
258
259 // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
260 std::string src_file_arg =
261 "EMMC:" + boot_img_file + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
262 TemporaryFile tgt_file;
263 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
264 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
265 std::string patch_arg = boot_img_sha1 + ":" + patch_file.path;
266 std::vector<const char*> args = { "applypatch",
267 src_file_arg.c_str(),
268 tgt_file_arg.c_str(),
269 recovery_img_sha1.c_str(),
270 recovery_img_size_arg.c_str(),
271 patch_arg.c_str() };
272 ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
273
274 // Double check the patched recovery image.
275 std::string tgt_file_sha1;
276 size_t tgt_file_size;
277 sha1sum(tgt_file.path, &tgt_file_sha1, &tgt_file_size);
278 ASSERT_EQ(recovery_img_size, tgt_file_size);
279 ASSERT_EQ(recovery_img_sha1, tgt_file_sha1);
280}
281
282TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
283 // Invalid bonus file.
284 ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
285
286 std::string bonus_file = from_testdata_base("bonus.file");
287 // With bonus file, but missing args.
288 ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() }));
289
290 std::string boot_img = from_testdata_base("boot.img");
291 size_t boot_img_size;
292 std::string boot_img_sha1;
293 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
294
295 std::string recovery_img = from_testdata_base("recovery.img");
296 size_t size;
297 std::string recovery_img_sha1;
298 sha1sum(recovery_img, &recovery_img_sha1, &size);
299 std::string recovery_img_size = std::to_string(size);
300
301 // Bonus file is not supported in flash mode.
302 // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size>
303 TemporaryFile tmp4;
304 std::vector<const char*> args4 = {
305 "applypatch",
306 "-b",
307 bonus_file.c_str(),
308 boot_img.c_str(),
309 tmp4.path,
310 recovery_img_sha1.c_str(),
311 recovery_img_size.c_str(),
312 };
313 ASSERT_NE(0, applypatch_modes(args4.size(), args4.data()));
314
315 // Failed to parse patch args.
316 TemporaryFile tmp5;
317 std::string bad_arg1 =
318 "invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p");
319 std::vector<const char*> args5 = {
320 "applypatch",
321 boot_img.c_str(),
322 tmp5.path,
323 recovery_img_sha1.c_str(),
324 recovery_img_size.c_str(),
325 bad_arg1.c_str(),
326 };
327 ASSERT_NE(0, applypatch_modes(args5.size(), args5.data()));
328
329 // Target size cannot be zero.
330 TemporaryFile tmp6;
331 std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
332 std::vector<const char*> args6 = {
333 "applypatch", boot_img.c_str(), tmp6.path, recovery_img_sha1.c_str(),
334 "0", // target size
335 patch.c_str(),
336 };
337 ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
338}
339
340TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
341 // Insufficient args.
342 ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
343}
344
345TEST_F(ApplyPatchModesTest, ShowLicenses) {
346 ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
347}