blob: bfdc9cabd3507d4236e54664725df04241ce8e2d [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>
Tao Baob8cb2e62018-07-06 12:14:36 -070024#include <android-base/stringprintf.h>
25#include <android-base/test_utils.h>
26#include <bsdiff/bsdiff.h>
27#include <gtest/gtest.h>
28#include <openssl/sha.h>
Tao Baob8cb2e62018-07-06 12:14:36 -070029
30#include "applypatch/applypatch_modes.h"
31#include "common/test_constants.h"
32#include "otautil/paths.h"
33#include "otautil/print_sha1.h"
34
35using namespace std::string_literals;
36
Tao Baob8cb2e62018-07-06 12:14:36 -070037static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) {
38 ASSERT_TRUE(sha1 != nullptr);
39
40 std::string data;
41 ASSERT_TRUE(android::base::ReadFileToString(fname, &data));
42
43 if (fsize != nullptr) {
44 *fsize = data.size();
45 }
46
47 uint8_t digest[SHA_DIGEST_LENGTH];
48 SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
49 *sha1 = print_sha1(digest);
50}
51
Tao Baob8cb2e62018-07-06 12:14:36 -070052class ApplyPatchModesTest : public ::testing::Test {
53 protected:
54 void SetUp() override {
55 Paths::Get().set_cache_temp_source(cache_source.path);
Tao Baob8cb2e62018-07-06 12:14:36 -070056 }
57
58 TemporaryFile cache_source;
59};
60
61TEST_F(ApplyPatchModesTest, InvalidArgs) {
62 // At least two args (including the filename).
63 ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
64
65 // Unrecognized args.
66 ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
67}
68
69TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
70 std::string boot_img = from_testdata_base("boot.img");
71 size_t boot_img_size;
72 std::string boot_img_sha1;
73 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
74
75 std::string recovery_img = from_testdata_base("recovery.img");
76 size_t recovery_img_size;
77 std::string recovery_img_sha1;
78 sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
79 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
80
81 std::string bonus_file = from_testdata_base("bonus.file");
82
83 // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
84 std::string src_file_arg =
85 "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
86 TemporaryFile tgt_file;
87 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
88 std::string patch_arg = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
89 std::vector<const char*> args = { "applypatch",
90 "-b",
91 bonus_file.c_str(),
92 src_file_arg.c_str(),
93 tgt_file_arg.c_str(),
94 recovery_img_sha1.c_str(),
95 recovery_img_size_arg.c_str(),
96 patch_arg.c_str() };
97 ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
98}
99
100// Tests patching the EMMC target without a separate bonus file (i.e. recovery-from-boot patch has
101// everything).
102TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithoutBonusFile) {
103 std::string boot_img = from_testdata_base("boot.img");
104 size_t boot_img_size;
105 std::string boot_img_sha1;
106 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
107
108 std::string recovery_img = from_testdata_base("recovery.img");
109 size_t recovery_img_size;
110 std::string recovery_img_sha1;
111 sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
112 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
113
114 // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
115 std::string src_file_arg =
116 "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
117 TemporaryFile tgt_file;
118 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
119 std::string patch_arg =
120 boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
121 std::vector<const char*> args = { "applypatch",
122 src_file_arg.c_str(),
123 tgt_file_arg.c_str(),
124 recovery_img_sha1.c_str(),
125 recovery_img_size_arg.c_str(),
126 patch_arg.c_str() };
127
Tianjie Xu73268922018-07-03 14:46:15 -0700128 ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
Tao Baob8cb2e62018-07-06 12:14:36 -0700129}
130
131TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithMultiplePatches) {
132 std::string boot_img = from_testdata_base("boot.img");
133 size_t boot_img_size;
134 std::string boot_img_sha1;
135 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
136
137 std::string recovery_img = from_testdata_base("recovery.img");
138 size_t recovery_img_size;
139 std::string recovery_img_sha1;
140 sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
141 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
142
143 std::string bonus_file = from_testdata_base("bonus.file");
144
145 // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \
146 // <src-sha1-fake1>:<patch1> <src-sha1>:<patch2> <src-sha1-fake2>:<patch3>
147 std::string src_file_arg =
148 "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
149 TemporaryFile tgt_file;
150 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
151 std::string bad_sha1_a = android::base::StringPrintf("%040x", rand());
152 std::string bad_sha1_b = android::base::StringPrintf("%040x", rand());
153 std::string patch1 = bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p");
154 std::string patch2 = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
155 std::string patch3 = bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p");
156 std::vector<const char*> args = { "applypatch",
157 "-b",
158 bonus_file.c_str(),
159 src_file_arg.c_str(),
160 tgt_file_arg.c_str(),
161 recovery_img_sha1.c_str(),
162 recovery_img_size_arg.c_str(),
163 patch1.c_str(),
164 patch2.c_str(),
165 patch3.c_str() };
Tao Baob8cb2e62018-07-06 12:14:36 -0700166
Tianjie Xu73268922018-07-03 14:46:15 -0700167 ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
Tao Baob8cb2e62018-07-06 12:14:36 -0700168}
169
170// Ensures that applypatch works with a bsdiff based recovery-from-boot.p.
171TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
172 std::string boot_img_file = from_testdata_base("boot.img");
173 std::string boot_img_sha1;
174 size_t boot_img_size;
175 sha1sum(boot_img_file, &boot_img_sha1, &boot_img_size);
176
177 std::string recovery_img_file = from_testdata_base("recovery.img");
178 std::string recovery_img_sha1;
179 size_t recovery_img_size;
180 sha1sum(recovery_img_file, &recovery_img_sha1, &recovery_img_size);
181
182 // Generate the bsdiff patch of recovery-from-boot.p.
183 std::string src_content;
184 ASSERT_TRUE(android::base::ReadFileToString(boot_img_file, &src_content));
185
186 std::string tgt_content;
187 ASSERT_TRUE(android::base::ReadFileToString(recovery_img_file, &tgt_content));
188
189 TemporaryFile patch_file;
190 ASSERT_EQ(0,
191 bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
192 reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
193 patch_file.path, nullptr));
194
195 // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
196 std::string src_file_arg =
197 "EMMC:" + boot_img_file + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
198 TemporaryFile tgt_file;
199 std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
200 std::string recovery_img_size_arg = std::to_string(recovery_img_size);
201 std::string patch_arg = boot_img_sha1 + ":" + patch_file.path;
202 std::vector<const char*> args = { "applypatch",
203 src_file_arg.c_str(),
204 tgt_file_arg.c_str(),
205 recovery_img_sha1.c_str(),
206 recovery_img_size_arg.c_str(),
207 patch_arg.c_str() };
208 ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
209
210 // Double check the patched recovery image.
211 std::string tgt_file_sha1;
212 size_t tgt_file_size;
213 sha1sum(tgt_file.path, &tgt_file_sha1, &tgt_file_size);
214 ASSERT_EQ(recovery_img_size, tgt_file_size);
215 ASSERT_EQ(recovery_img_sha1, tgt_file_sha1);
216}
217
218TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
219 // Invalid bonus file.
220 ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
221
222 std::string bonus_file = from_testdata_base("bonus.file");
223 // With bonus file, but missing args.
224 ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() }));
225
226 std::string boot_img = from_testdata_base("boot.img");
227 size_t boot_img_size;
228 std::string boot_img_sha1;
229 sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
230
231 std::string recovery_img = from_testdata_base("recovery.img");
232 size_t size;
233 std::string recovery_img_sha1;
234 sha1sum(recovery_img, &recovery_img_sha1, &size);
235 std::string recovery_img_size = std::to_string(size);
236
237 // Bonus file is not supported in flash mode.
238 // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size>
239 TemporaryFile tmp4;
240 std::vector<const char*> args4 = {
241 "applypatch",
242 "-b",
243 bonus_file.c_str(),
244 boot_img.c_str(),
245 tmp4.path,
246 recovery_img_sha1.c_str(),
247 recovery_img_size.c_str(),
248 };
249 ASSERT_NE(0, applypatch_modes(args4.size(), args4.data()));
250
251 // Failed to parse patch args.
252 TemporaryFile tmp5;
253 std::string bad_arg1 =
254 "invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p");
255 std::vector<const char*> args5 = {
256 "applypatch",
257 boot_img.c_str(),
258 tmp5.path,
259 recovery_img_sha1.c_str(),
260 recovery_img_size.c_str(),
261 bad_arg1.c_str(),
262 };
263 ASSERT_NE(0, applypatch_modes(args5.size(), args5.data()));
264
265 // Target size cannot be zero.
266 TemporaryFile tmp6;
267 std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
268 std::vector<const char*> args6 = {
269 "applypatch", boot_img.c_str(), tmp6.path, recovery_img_sha1.c_str(),
270 "0", // target size
271 patch.c_str(),
272 };
273 ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
274}
275
276TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
277 // Insufficient args.
278 ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
279}
280
281TEST_F(ApplyPatchModesTest, ShowLicenses) {
282 ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
283}